2 * Copyright (c) 2005-2006 MontaVista Software, Inc.
3 * Copyright (c) 2006-2018 Red Hat, Inc.
7 * Author: Steven Dake (sdake@redhat.com)
9 * This software licensed under BSD license, the text of which follows:
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions are met:
14 * - Redistributions of source code must retain the above copyright notice,
15 * this list of conditions and the following disclaimer.
16 * - Redistributions in binary form must reproduce the above copyright notice,
17 * this list of conditions and the following disclaimer in the documentation
18 * and/or other materials provided with the distribution.
19 * - Neither the name of the MontaVista Software, Inc. nor the names of its
20 * contributors may be used to endorse or promote products derived from this
21 * software without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
24 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
27 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
33 * THE POSSIBILITY OF SUCH DAMAGE.
38 #include <sys/types.h>
40 #include <sys/socket.h>
42 #include <netinet/in.h>
43 #include <arpa/inet.h>
54 #include <corosync/corotypes.h>
55 #include <qb/qbipc_common.h>
56 #include <corosync/cfg.h>
57 #include <qb/qblist.h>
58 #include <corosync/mar_gen.h>
59 #include <corosync/totem/totemip.h>
60 #include <corosync/totem/totem.h>
61 #include <corosync/ipc_cfg.h>
62 #include <corosync/logsys.h>
63 #include <corosync/coroapi.h>
64 #include <corosync/icmap.h>
65 #include <corosync/corodefs.h>
67 #include "totemconfig.h"
71 LOGSYS_DECLARE_SUBSYS ("CFG");
73 enum cfg_message_req_types
{
74 MESSAGE_REQ_EXEC_CFG_RINGREENABLE
= 0,
75 MESSAGE_REQ_EXEC_CFG_KILLNODE
= 1,
76 MESSAGE_REQ_EXEC_CFG_SHUTDOWN
= 2,
77 MESSAGE_REQ_EXEC_CFG_RELOAD_CONFIG
= 3
80 #define DEFAULT_SHUTDOWN_TIMEOUT 5
82 static struct qb_list_head trackers_list
;
85 * Variables controlling a requested shutdown
87 static corosync_timer_handle_t shutdown_timer
;
88 static struct cfg_info
*shutdown_con
;
89 static uint32_t shutdown_flags
;
90 static int shutdown_yes
;
91 static int shutdown_no
;
92 static int shutdown_expected
;
96 struct qb_list_head list
;
99 enum {SHUTDOWN_REPLY_UNKNOWN
, SHUTDOWN_REPLY_YES
, SHUTDOWN_REPLY_NO
} shutdown_reply
;
102 static void cfg_confchg_fn (
103 enum totem_configuration_type configuration_type
,
104 const unsigned int *member_list
, size_t member_list_entries
,
105 const unsigned int *left_list
, size_t left_list_entries
,
106 const unsigned int *joined_list
, size_t joined_list_entries
,
107 const struct memb_ring_id
*ring_id
);
109 static char *cfg_exec_init_fn (struct corosync_api_v1
*corosync_api_v1
);
111 static struct corosync_api_v1
*api
;
113 static int cfg_lib_init_fn (void *conn
);
115 static int cfg_lib_exit_fn (void *conn
);
117 static void message_handler_req_exec_cfg_ringreenable (
119 unsigned int nodeid
);
121 static void message_handler_req_exec_cfg_killnode (
123 unsigned int nodeid
);
125 static void message_handler_req_exec_cfg_shutdown (
127 unsigned int nodeid
);
129 static void message_handler_req_exec_cfg_reload_config (
131 unsigned int nodeid
);
133 static void exec_cfg_killnode_endian_convert (void *msg
);
135 static void message_handler_req_lib_cfg_ringstatusget (
139 static void message_handler_req_lib_cfg_ringreenable (
143 static void message_handler_req_lib_cfg_killnode (
147 static void message_handler_req_lib_cfg_tryshutdown (
151 static void message_handler_req_lib_cfg_replytoshutdown (
155 static void message_handler_req_lib_cfg_get_node_addrs (
159 static void message_handler_req_lib_cfg_local_get (
163 static void message_handler_req_lib_cfg_reload_config (
167 static void message_handler_req_lib_cfg_reopen_log_files (
172 * Service Handler Definition
174 static struct corosync_lib_handler cfg_lib_engine
[] =
177 .lib_handler_fn
= message_handler_req_lib_cfg_ringstatusget
,
178 .flow_control
= CS_LIB_FLOW_CONTROL_REQUIRED
181 .lib_handler_fn
= message_handler_req_lib_cfg_ringreenable
,
182 .flow_control
= CS_LIB_FLOW_CONTROL_REQUIRED
185 .lib_handler_fn
= message_handler_req_lib_cfg_killnode
,
186 .flow_control
= CS_LIB_FLOW_CONTROL_REQUIRED
189 .lib_handler_fn
= message_handler_req_lib_cfg_tryshutdown
,
190 .flow_control
= CS_LIB_FLOW_CONTROL_REQUIRED
193 .lib_handler_fn
= message_handler_req_lib_cfg_replytoshutdown
,
194 .flow_control
= CS_LIB_FLOW_CONTROL_REQUIRED
197 .lib_handler_fn
= message_handler_req_lib_cfg_get_node_addrs
,
198 .flow_control
= CS_LIB_FLOW_CONTROL_NOT_REQUIRED
201 .lib_handler_fn
= message_handler_req_lib_cfg_local_get
,
202 .flow_control
= CS_LIB_FLOW_CONTROL_NOT_REQUIRED
205 .lib_handler_fn
= message_handler_req_lib_cfg_reload_config
,
206 .flow_control
= CS_LIB_FLOW_CONTROL_REQUIRED
209 .lib_handler_fn
= message_handler_req_lib_cfg_reopen_log_files
,
210 .flow_control
= CS_LIB_FLOW_CONTROL_NOT_REQUIRED
214 static struct corosync_exec_handler cfg_exec_engine
[] =
217 .exec_handler_fn
= message_handler_req_exec_cfg_ringreenable
,
220 .exec_handler_fn
= message_handler_req_exec_cfg_killnode
,
221 .exec_endian_convert_fn
= exec_cfg_killnode_endian_convert
224 .exec_handler_fn
= message_handler_req_exec_cfg_shutdown
,
227 .exec_handler_fn
= message_handler_req_exec_cfg_reload_config
,
232 * Exports the interface for the service
234 struct corosync_service_engine cfg_service_engine
= {
235 .name
= "corosync configuration service",
238 .private_data_size
= sizeof(struct cfg_info
),
239 .flow_control
= CS_LIB_FLOW_CONTROL_NOT_REQUIRED
,
240 .allow_inquorate
= CS_LIB_ALLOW_INQUORATE
,
241 .lib_init_fn
= cfg_lib_init_fn
,
242 .lib_exit_fn
= cfg_lib_exit_fn
,
243 .lib_engine
= cfg_lib_engine
,
244 .lib_engine_count
= sizeof (cfg_lib_engine
) / sizeof (struct corosync_lib_handler
),
245 .exec_init_fn
= cfg_exec_init_fn
,
246 .exec_engine
= cfg_exec_engine
,
247 .exec_engine_count
= sizeof (cfg_exec_engine
) / sizeof (struct corosync_exec_handler
),
248 .confchg_fn
= cfg_confchg_fn
251 struct corosync_service_engine
*cfg_get_service_engine_ver0 (void)
253 return (&cfg_service_engine
);
256 struct req_exec_cfg_ringreenable
{
257 struct qb_ipc_request_header header
__attribute__((aligned(8)));
258 mar_message_source_t source
__attribute__((aligned(8)));
261 struct req_exec_cfg_reload_config
{
262 struct qb_ipc_request_header header
__attribute__((aligned(8)));
263 mar_message_source_t source
__attribute__((aligned(8)));
266 struct req_exec_cfg_killnode
{
267 struct qb_ipc_request_header header
__attribute__((aligned(8)));
268 mar_uint32_t nodeid
__attribute__((aligned(8)));
269 mar_name_t reason
__attribute__((aligned(8)));
272 struct req_exec_cfg_shutdown
{
273 struct qb_ipc_request_header header
__attribute__((aligned(8)));
278 static char *cfg_exec_init_fn (
279 struct corosync_api_v1
*corosync_api_v1
)
281 api
= corosync_api_v1
;
283 qb_list_init(&trackers_list
);
287 static void cfg_confchg_fn (
288 enum totem_configuration_type configuration_type
,
289 const unsigned int *member_list
, size_t member_list_entries
,
290 const unsigned int *left_list
, size_t left_list_entries
,
291 const unsigned int *joined_list
, size_t joined_list_entries
,
292 const struct memb_ring_id
*ring_id
)
297 * Tell other nodes we are shutting down
299 static int send_shutdown(void)
301 struct req_exec_cfg_shutdown req_exec_cfg_shutdown
;
305 req_exec_cfg_shutdown
.header
.size
=
306 sizeof (struct req_exec_cfg_shutdown
);
307 req_exec_cfg_shutdown
.header
.id
= SERVICE_ID_MAKE (CFG_SERVICE
,
308 MESSAGE_REQ_EXEC_CFG_SHUTDOWN
);
310 iovec
.iov_base
= (char *)&req_exec_cfg_shutdown
;
311 iovec
.iov_len
= sizeof (struct req_exec_cfg_shutdown
);
313 assert (api
->totem_mcast (&iovec
, 1, TOTEM_SAFE
) == 0);
319 static void send_test_shutdown(void *only_conn
, void *exclude_conn
, int status
)
321 struct res_lib_cfg_testshutdown res_lib_cfg_testshutdown
;
322 struct qb_list_head
*iter
;
325 res_lib_cfg_testshutdown
.header
.size
= sizeof(struct res_lib_cfg_testshutdown
);
326 res_lib_cfg_testshutdown
.header
.id
= MESSAGE_RES_CFG_TESTSHUTDOWN
;
327 res_lib_cfg_testshutdown
.header
.error
= status
;
328 res_lib_cfg_testshutdown
.flags
= shutdown_flags
;
331 TRACE1("sending testshutdown to only %p", only_conn
);
332 api
->ipc_dispatch_send(only_conn
, &res_lib_cfg_testshutdown
,
333 sizeof(res_lib_cfg_testshutdown
));
335 qb_list_for_each(iter
, &trackers_list
) {
336 struct cfg_info
*ci
= qb_list_entry(iter
, struct cfg_info
, list
);
338 if (ci
->conn
!= exclude_conn
) {
339 TRACE1("sending testshutdown to %p", ci
->tracker_conn
);
340 api
->ipc_dispatch_send(ci
->tracker_conn
, &res_lib_cfg_testshutdown
,
341 sizeof(res_lib_cfg_testshutdown
));
348 static void check_shutdown_status(void)
353 * Shutdown client might have gone away
361 * All replies safely gathered in ?
363 if (shutdown_yes
+ shutdown_no
>= shutdown_expected
) {
364 struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown
;
366 api
->timer_delete(shutdown_timer
);
368 if (shutdown_yes
>= shutdown_expected
||
369 shutdown_flags
== CFG_SHUTDOWN_FLAG_REGARDLESS
) {
370 TRACE1("shutdown confirmed");
372 res_lib_cfg_tryshutdown
.header
.size
= sizeof(struct res_lib_cfg_tryshutdown
);
373 res_lib_cfg_tryshutdown
.header
.id
= MESSAGE_RES_CFG_TRYSHUTDOWN
;
374 res_lib_cfg_tryshutdown
.header
.error
= CS_OK
;
377 * Tell originator that shutdown was confirmed
379 api
->ipc_response_send(shutdown_con
->conn
, &res_lib_cfg_tryshutdown
,
380 sizeof(res_lib_cfg_tryshutdown
));
384 * Tell other nodes we are going down
391 TRACE1("shutdown cancelled");
392 res_lib_cfg_tryshutdown
.header
.size
= sizeof(struct res_lib_cfg_tryshutdown
);
393 res_lib_cfg_tryshutdown
.header
.id
= MESSAGE_RES_CFG_TRYSHUTDOWN
;
394 res_lib_cfg_tryshutdown
.header
.error
= CS_ERR_BUSY
;
397 * Tell originator that shutdown was cancelled
399 api
->ipc_response_send(shutdown_con
->conn
, &res_lib_cfg_tryshutdown
,
400 sizeof(res_lib_cfg_tryshutdown
));
404 log_printf(LOGSYS_LEVEL_DEBUG
, "shutdown decision is: (yes count: %d, no count: %d) flags=%x",
405 shutdown_yes
, shutdown_no
, shutdown_flags
);
412 * Not all nodes responded to the shutdown (in time)
414 static void shutdown_timer_fn(void *arg
)
419 * Mark undecideds as "NO"
421 shutdown_no
= shutdown_expected
;
422 check_shutdown_status();
424 send_test_shutdown(NULL
, NULL
, CS_ERR_TIMEOUT
);
428 static void remove_ci_from_shutdown(struct cfg_info
*ci
)
433 * If the controlling shutdown process has quit, then cancel the
436 if (ci
== shutdown_con
) {
438 api
->timer_delete(shutdown_timer
);
441 if (!qb_list_empty(&ci
->list
)) {
442 qb_list_del(&ci
->list
);
443 qb_list_init(&ci
->list
);
449 if (ci
->shutdown_reply
== SHUTDOWN_REPLY_YES
)
451 if (ci
->shutdown_reply
== SHUTDOWN_REPLY_NO
)
456 * If we are leaving, then that's an implicit YES to shutdown
458 ci
->shutdown_reply
= SHUTDOWN_REPLY_YES
;
461 check_shutdown_status();
467 int cfg_lib_exit_fn (void *conn
)
469 struct cfg_info
*ci
= (struct cfg_info
*)api
->ipc_private_data_get (conn
);
472 remove_ci_from_shutdown(ci
);
477 static int cfg_lib_init_fn (void *conn
)
479 struct cfg_info
*ci
= (struct cfg_info
*)api
->ipc_private_data_get (conn
);
482 qb_list_init(&ci
->list
);
489 * Executive message handlers
491 static void message_handler_req_exec_cfg_ringreenable (
500 static void exec_cfg_killnode_endian_convert (void *msg
)
502 struct req_exec_cfg_killnode
*req_exec_cfg_killnode
=
503 (struct req_exec_cfg_killnode
*)msg
;
506 swab_mar_name_t(&req_exec_cfg_killnode
->reason
);
511 static void message_handler_req_exec_cfg_killnode (
515 const struct req_exec_cfg_killnode
*req_exec_cfg_killnode
= message
;
519 log_printf(LOGSYS_LEVEL_DEBUG
, "request to kill node " CS_PRI_NODE_ID
" (us=" CS_PRI_NODE_ID
")",
520 req_exec_cfg_killnode
->nodeid
, api
->totem_nodeid_get());
521 if (req_exec_cfg_killnode
->nodeid
== api
->totem_nodeid_get()) {
522 marshall_from_mar_name_t(&reason
, &req_exec_cfg_killnode
->reason
);
523 log_printf(LOGSYS_LEVEL_NOTICE
, "Killed by node " CS_PRI_NODE_ID
" : %s",
524 nodeid
, reason
.value
);
525 corosync_fatal_error(COROSYNC_FATAL_ERROR_EXIT
);
533 static void message_handler_req_exec_cfg_shutdown (
539 log_printf(LOGSYS_LEVEL_NOTICE
, "Node " CS_PRI_NODE_ID
" was shut down by sysadmin", nodeid
);
540 if (nodeid
== api
->totem_nodeid_get()) {
541 api
->shutdown_request();
546 /* strcmp replacement that can handle NULLs */
547 static int nullcheck_strcmp(const char* left
, const char *right
)
557 return strcmp(left
, right
);
561 * If a key has changed value in the new file, then warn the user and remove it from the temp_map
563 static void delete_and_notify_if_changed(icmap_map_t temp_map
, const char *key_name
)
565 if (!(icmap_key_value_eq(temp_map
, key_name
, icmap_get_global_map(), key_name
))) {
566 if (icmap_delete_r(temp_map
, key_name
) == CS_OK
) {
567 log_printf(LOGSYS_LEVEL_NOTICE
, "Modified entry '%s' in corosync.conf cannot be changed at run-time", key_name
);
572 * Remove any keys from the new config file that in the new corosync.conf but that
573 * cannot be changed at run time. A log message will be issued for each
574 * entry that the user wants to change but they cannot.
576 * Add more here as needed.
578 static void remove_ro_entries(icmap_map_t temp_map
)
580 delete_and_notify_if_changed(temp_map
, "totem.secauth");
581 delete_and_notify_if_changed(temp_map
, "totem.crypto_hash");
582 delete_and_notify_if_changed(temp_map
, "totem.crypto_cipher");
583 delete_and_notify_if_changed(temp_map
, "totem.keyfile");
584 delete_and_notify_if_changed(temp_map
, "totem.key");
585 delete_and_notify_if_changed(temp_map
, "totem.version");
586 delete_and_notify_if_changed(temp_map
, "totem.threads");
587 delete_and_notify_if_changed(temp_map
, "totem.ip_version");
588 delete_and_notify_if_changed(temp_map
, "totem.rrp_mode");
589 delete_and_notify_if_changed(temp_map
, "totem.netmtu");
590 delete_and_notify_if_changed(temp_map
, "totem.interface.ringnumber");
591 delete_and_notify_if_changed(temp_map
, "totem.interface.bindnetaddr");
592 delete_and_notify_if_changed(temp_map
, "totem.interface.mcastaddr");
593 delete_and_notify_if_changed(temp_map
, "totem.interface.broadcast");
594 delete_and_notify_if_changed(temp_map
, "totem.interface.mcastport");
595 delete_and_notify_if_changed(temp_map
, "totem.interface.ttl");
596 delete_and_notify_if_changed(temp_map
, "totem.vsftype");
597 delete_and_notify_if_changed(temp_map
, "totem.transport");
598 delete_and_notify_if_changed(temp_map
, "totem.cluster_name");
599 delete_and_notify_if_changed(temp_map
, "quorum.provider");
600 delete_and_notify_if_changed(temp_map
, "system.move_to_root_cgroup");
601 delete_and_notify_if_changed(temp_map
, "system.sched_rr");
602 delete_and_notify_if_changed(temp_map
, "system.priority");
603 delete_and_notify_if_changed(temp_map
, "system.qb_ipc_type");
604 delete_and_notify_if_changed(temp_map
, "system.state_dir");
608 * Remove entries that exist in the global map, but not in the temp_map, this will
609 * cause delete notifications to be sent to any listeners.
611 * NOTE: This routine depends entirely on the keys returned by the iterators
612 * being in alpha-sorted order.
614 static void remove_deleted_entries(icmap_map_t temp_map
, const char *prefix
)
616 icmap_iter_t old_iter
;
617 icmap_iter_t new_iter
;
618 const char *old_key
, *new_key
;
621 old_iter
= icmap_iter_init(prefix
);
622 new_iter
= icmap_iter_init_r(temp_map
, prefix
);
624 old_key
= icmap_iter_next(old_iter
, NULL
, NULL
);
625 new_key
= icmap_iter_next(new_iter
, NULL
, NULL
);
627 while (old_key
|| new_key
) {
628 ret
= nullcheck_strcmp(old_key
, new_key
);
629 if ((ret
< 0 && old_key
) || !new_key
) {
631 * new_key is greater, a line (or more) has been deleted
632 * Continue until old is >= new
635 /* Remove it from icmap & send notifications */
636 icmap_delete(old_key
);
638 old_key
= icmap_iter_next(old_iter
, NULL
, NULL
);
639 ret
= nullcheck_strcmp(old_key
, new_key
);
640 } while (ret
< 0 && old_key
);
642 else if ((ret
> 0 && new_key
) || !old_key
) {
644 * old_key is greater, a line (or more) has been added
645 * Continue until new is >= old
647 * we don't need to do anything special with this like tell
648 * icmap. That will happen when we copy the values over
651 new_key
= icmap_iter_next(new_iter
, NULL
, NULL
);
652 ret
= nullcheck_strcmp(old_key
, new_key
);
653 } while (ret
> 0 && new_key
);
656 new_key
= icmap_iter_next(new_iter
, NULL
, NULL
);
657 old_key
= icmap_iter_next(old_iter
, NULL
, NULL
);
660 icmap_iter_finalize(new_iter
);
661 icmap_iter_finalize(old_iter
);
665 * Reload configuration file
667 static void message_handler_req_exec_cfg_reload_config (
671 const struct req_exec_cfg_reload_config
*req_exec_cfg_reload_config
= message
;
672 struct res_lib_cfg_reload_config res_lib_cfg_reload_config
;
673 struct totem_config new_config
;
674 icmap_map_t temp_map
;
675 const char *error_string
;
680 log_printf(LOGSYS_LEVEL_NOTICE
, "Config reload requested by node " CS_PRI_NODE_ID
, nodeid
);
682 icmap_set_uint8("config.totemconfig_reload_in_progress", 1);
684 * Set up a new hashtable as a staging area.
686 if ((res
= icmap_init_r(&temp_map
)) != CS_OK
) {
687 log_printf(LOGSYS_LEVEL_ERROR
, "Unable to create temporary icmap. config file reload cancelled\n");
688 goto reload_fini_nomap
;
692 * Load new config into the temporary map
694 res
= coroparse_configparse(temp_map
, &error_string
);
696 log_printf (LOGSYS_LEVEL_ERROR
, "Unable to reload config file: %s", error_string
);
697 res
= CS_ERR_INVALID_PARAM
;
698 goto reload_fini_nofree
;
701 /* Signal start of the reload process */
702 icmap_set_uint8("config.reload_in_progress", 1);
704 /* Detect deleted entries and remove them from the main icmap hashtable */
705 remove_deleted_entries(temp_map
, "logging.");
706 remove_deleted_entries(temp_map
, "totem.");
707 remove_deleted_entries(temp_map
, "nodelist.");
708 remove_deleted_entries(temp_map
, "quorum.");
709 remove_deleted_entries(temp_map
, "uidgid.config.");
710 remove_deleted_entries(temp_map
, "nozzle.");
712 /* Remove entries that cannot be changed */
713 remove_ro_entries(temp_map
);
715 /* Take a copy of the current setup so we can check what has changed */
716 memset(&new_config
, 0, sizeof(new_config
));
717 new_config
.orig_interfaces
= malloc (sizeof (struct totem_interface
) * INTERFACE_MAX
);
718 assert(new_config
.orig_interfaces
!= NULL
);
720 totempg_get_config(&new_config
);
722 new_config
.interfaces
= malloc (sizeof (struct totem_interface
) * INTERFACE_MAX
);
723 assert(new_config
.interfaces
!= NULL
);
724 memset(new_config
.interfaces
, 0, sizeof (struct totem_interface
) * INTERFACE_MAX
);
726 /* For UDP[U] the configuration on link0 is static (apart from the nodelist) and only read at
727 startup. So preserve it here */
728 if ( (new_config
.transport_number
== TOTEM_TRANSPORT_UDP
) ||
729 (new_config
.transport_number
== TOTEM_TRANSPORT_UDPU
)) {
730 memcpy(&new_config
.interfaces
[0], &new_config
.orig_interfaces
[0],
731 sizeof(struct totem_interface
));
734 /* Calculate new node and interface definitions */
735 if (totemconfig_configure_new_params(&new_config
, temp_map
, &error_string
) == -1) {
736 log_printf (LOGSYS_LEVEL_ERROR
, "Cannot configure new interface definitions: %s\n", error_string
);
737 res
= CS_ERR_INVALID_PARAM
;
741 /* Read from temp_map into new_config */
742 totem_volatile_config_read(&new_config
, temp_map
, NULL
);
744 /* Validate dynamic parameters */
745 if (totem_volatile_config_validate(&new_config
, temp_map
, &error_string
) == -1) {
746 log_printf (LOGSYS_LEVEL_ERROR
, "Configuration is not valid: %s\n", error_string
);
747 res
= CS_ERR_INVALID_PARAM
;
752 * Copy new keys into live config.
754 if ( (res
= icmap_copy_map(icmap_get_global_map(), temp_map
)) != CS_OK
) {
755 log_printf (LOGSYS_LEVEL_ERROR
, "Error making new config live. cmap database may be inconsistent\n");
756 /* Return res from icmap */
760 /* Copy into live system */
761 totempg_put_config(&new_config
);
762 totemconfig_commit_new_params(&new_config
, temp_map
);
763 free(new_config
.interfaces
);
766 /* All done - let clients know */
767 icmap_set_int32("config.reload_status", res
);
768 icmap_set_uint8("config.totemconfig_reload_in_progress", 0);
769 icmap_set_uint8("config.reload_in_progress", 0);
771 /* Finished with the temporary storage */
772 free(new_config
.orig_interfaces
);
775 icmap_fini_r(temp_map
);
778 /* All done, return result to the caller if it was on this system */
779 if (nodeid
== api
->totem_nodeid_get()) {
780 res_lib_cfg_reload_config
.header
.size
= sizeof(res_lib_cfg_reload_config
);
781 res_lib_cfg_reload_config
.header
.id
= MESSAGE_RES_CFG_RELOAD_CONFIG
;
782 res_lib_cfg_reload_config
.header
.error
= res
;
783 api
->ipc_response_send(req_exec_cfg_reload_config
->source
.conn
,
784 &res_lib_cfg_reload_config
,
785 sizeof(res_lib_cfg_reload_config
));
786 api
->ipc_refcnt_dec(req_exec_cfg_reload_config
->source
.conn
);;
793 * Library Interface Implementation
795 static void message_handler_req_lib_cfg_ringstatusget (
799 struct res_lib_cfg_ringstatusget res_lib_cfg_ringstatusget
;
800 struct totem_ip_address interfaces
[INTERFACE_MAX
];
801 unsigned int iface_count
;
803 const char *totem_ip_string
;
804 char ifname
[CFG_INTERFACE_NAME_MAX_LEN
];
805 unsigned int iface_ids
[INTERFACE_MAX
];
807 cs_error_t res
= CS_OK
;
811 res_lib_cfg_ringstatusget
.header
.id
= MESSAGE_RES_CFG_RINGSTATUSGET
;
812 res_lib_cfg_ringstatusget
.header
.size
= sizeof (struct res_lib_cfg_ringstatusget
);
814 api
->totem_ifaces_get (
815 api
->totem_nodeid_get(),
822 assert(iface_count
<= CFG_MAX_INTERFACES
);
824 res_lib_cfg_ringstatusget
.interface_count
= iface_count
;
826 for (i
= 0; i
< iface_count
; i
++) {
828 = (const char *)api
->totem_ip_print (&interfaces
[i
]);
830 if (!totem_ip_string
) {
834 /* Allow for i/f number at the start */
835 if (strlen(totem_ip_string
) >= CFG_INTERFACE_NAME_MAX_LEN
-3) {
836 log_printf(LOGSYS_LEVEL_ERROR
, "String representation of interface %u is too long", i
);
837 res
= CS_ERR_NAME_TOO_LONG
;
840 snprintf(ifname
, sizeof(ifname
), "%d %s", iface_ids
[i
], totem_ip_string
);
842 if (strlen(status
[i
]) >= CFG_INTERFACE_STATUS_MAX_LEN
) {
843 log_printf(LOGSYS_LEVEL_ERROR
, "Status string for interface %u is too long", i
);
844 res
= CS_ERR_NAME_TOO_LONG
;
848 strcpy ((char *)&res_lib_cfg_ringstatusget
.interface_status
[i
],
850 strcpy ((char *)&res_lib_cfg_ringstatusget
.interface_name
[i
],
855 res_lib_cfg_ringstatusget
.header
.error
= res
;
856 api
->ipc_response_send (
858 &res_lib_cfg_ringstatusget
,
859 sizeof (struct res_lib_cfg_ringstatusget
));
864 static void message_handler_req_lib_cfg_ringreenable (
868 struct res_lib_cfg_ringreenable res_lib_cfg_ringreenable
;
871 res_lib_cfg_ringreenable
.header
.id
= MESSAGE_RES_CFG_RINGREENABLE
;
872 res_lib_cfg_ringreenable
.header
.size
= sizeof (struct res_lib_cfg_ringreenable
);
873 res_lib_cfg_ringreenable
.header
.error
= CS_ERR_NOT_SUPPORTED
;
874 api
->ipc_response_send (
875 conn
, &res_lib_cfg_ringreenable
,
876 sizeof (struct res_lib_cfg_ringreenable
));
881 static void message_handler_req_lib_cfg_killnode (
885 const struct req_lib_cfg_killnode
*req_lib_cfg_killnode
= msg
;
886 struct res_lib_cfg_killnode res_lib_cfg_killnode
;
887 struct req_exec_cfg_killnode req_exec_cfg_killnode
;
891 req_exec_cfg_killnode
.header
.size
=
892 sizeof (struct req_exec_cfg_killnode
);
893 req_exec_cfg_killnode
.header
.id
= SERVICE_ID_MAKE (CFG_SERVICE
,
894 MESSAGE_REQ_EXEC_CFG_KILLNODE
);
895 req_exec_cfg_killnode
.nodeid
= req_lib_cfg_killnode
->nodeid
;
896 marshall_to_mar_name_t(&req_exec_cfg_killnode
.reason
, &req_lib_cfg_killnode
->reason
);
898 iovec
.iov_base
= (char *)&req_exec_cfg_killnode
;
899 iovec
.iov_len
= sizeof (struct req_exec_cfg_killnode
);
901 (void)api
->totem_mcast (&iovec
, 1, TOTEM_SAFE
);
903 res_lib_cfg_killnode
.header
.size
= sizeof(struct res_lib_cfg_killnode
);
904 res_lib_cfg_killnode
.header
.id
= MESSAGE_RES_CFG_KILLNODE
;
905 res_lib_cfg_killnode
.header
.error
= CS_OK
;
907 api
->ipc_response_send(conn
, &res_lib_cfg_killnode
,
908 sizeof(res_lib_cfg_killnode
));
914 static void message_handler_req_lib_cfg_tryshutdown (
918 struct cfg_info
*ci
= (struct cfg_info
*)api
->ipc_private_data_get (conn
);
919 const struct req_lib_cfg_tryshutdown
*req_lib_cfg_tryshutdown
= msg
;
920 struct qb_list_head
*iter
;
924 if (req_lib_cfg_tryshutdown
->flags
== CFG_SHUTDOWN_FLAG_IMMEDIATE
) {
925 struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown
;
932 res_lib_cfg_tryshutdown
.header
.size
= sizeof(struct res_lib_cfg_tryshutdown
);
933 res_lib_cfg_tryshutdown
.header
.id
= MESSAGE_RES_CFG_TRYSHUTDOWN
;
934 res_lib_cfg_tryshutdown
.header
.error
= CS_OK
;
935 api
->ipc_response_send(conn
, &res_lib_cfg_tryshutdown
,
936 sizeof(res_lib_cfg_tryshutdown
));
943 * Shutdown in progress, return an error
946 struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown
;
948 res_lib_cfg_tryshutdown
.header
.size
= sizeof(struct res_lib_cfg_tryshutdown
);
949 res_lib_cfg_tryshutdown
.header
.id
= MESSAGE_RES_CFG_TRYSHUTDOWN
;
950 res_lib_cfg_tryshutdown
.header
.error
= CS_ERR_EXIST
;
952 api
->ipc_response_send(conn
, &res_lib_cfg_tryshutdown
,
953 sizeof(res_lib_cfg_tryshutdown
));
962 shutdown_con
= (struct cfg_info
*)api
->ipc_private_data_get (conn
);
963 shutdown_flags
= req_lib_cfg_tryshutdown
->flags
;
968 * Count the number of listeners
970 shutdown_expected
= 0;
972 qb_list_for_each(iter
, &trackers_list
) {
973 struct cfg_info
*testci
= qb_list_entry(iter
, struct cfg_info
, list
);
975 * It is assumed that we will allow shutdown
978 testci
->shutdown_reply
= SHUTDOWN_REPLY_UNKNOWN
;
984 * If no-one is listening for events then we can just go down now
986 if (shutdown_expected
== 0) {
987 struct res_lib_cfg_tryshutdown res_lib_cfg_tryshutdown
;
989 res_lib_cfg_tryshutdown
.header
.size
= sizeof(struct res_lib_cfg_tryshutdown
);
990 res_lib_cfg_tryshutdown
.header
.id
= MESSAGE_RES_CFG_TRYSHUTDOWN
;
991 res_lib_cfg_tryshutdown
.header
.error
= CS_OK
;
994 * Tell originator that shutdown was confirmed
996 api
->ipc_response_send(conn
, &res_lib_cfg_tryshutdown
,
997 sizeof(res_lib_cfg_tryshutdown
));
1004 unsigned int shutdown_timeout
= DEFAULT_SHUTDOWN_TIMEOUT
;
1007 * Look for a shutdown timeout in configuration map
1009 icmap_get_uint32("cfg.shutdown_timeout", &shutdown_timeout
);
1012 * Start the timer. If we don't get a full set of replies before this goes
1013 * off we'll cancel the shutdown
1015 api
->timer_add_duration((unsigned long long)shutdown_timeout
*1000000000, NULL
,
1016 shutdown_timer_fn
, &shutdown_timer
);
1019 * Tell the users we would like to shut down
1021 send_test_shutdown(NULL
, conn
, CS_OK
);
1025 * We don't sent a reply to the caller here.
1026 * We send it when we know if we can shut down or not
1032 static void message_handler_req_lib_cfg_replytoshutdown (
1036 struct cfg_info
*ci
= (struct cfg_info
*)api
->ipc_private_data_get (conn
);
1037 const struct req_lib_cfg_replytoshutdown
*req_lib_cfg_replytoshutdown
= msg
;
1038 struct res_lib_cfg_replytoshutdown res_lib_cfg_replytoshutdown
;
1042 if (!shutdown_con
) {
1043 status
= CS_ERR_ACCESS
;
1047 if (req_lib_cfg_replytoshutdown
->response
) {
1049 ci
->shutdown_reply
= SHUTDOWN_REPLY_YES
;
1053 ci
->shutdown_reply
= SHUTDOWN_REPLY_NO
;
1055 check_shutdown_status();
1058 res_lib_cfg_replytoshutdown
.header
.error
= status
;
1059 res_lib_cfg_replytoshutdown
.header
.id
= MESSAGE_RES_CFG_REPLYTOSHUTDOWN
;
1060 res_lib_cfg_replytoshutdown
.header
.size
= sizeof(res_lib_cfg_replytoshutdown
);
1062 api
->ipc_response_send(conn
, &res_lib_cfg_replytoshutdown
,
1063 sizeof(res_lib_cfg_replytoshutdown
));
1068 static void message_handler_req_lib_cfg_get_node_addrs (void *conn
,
1071 struct totem_ip_address node_ifs
[INTERFACE_MAX
];
1072 unsigned int iface_ids
[INTERFACE_MAX
];
1075 unsigned int num_interfaces
= 0;
1076 struct sockaddr_storage
*ss
;
1080 const struct req_lib_cfg_get_node_addrs
*req_lib_cfg_get_node_addrs
= msg
;
1081 struct res_lib_cfg_get_node_addrs
*res_lib_cfg_get_node_addrs
= (struct res_lib_cfg_get_node_addrs
*)buf
;
1082 unsigned int nodeid
= req_lib_cfg_get_node_addrs
->nodeid
;
1086 nodeid
= api
->totem_nodeid_get();
1088 if (api
->totem_ifaces_get(nodeid
, iface_ids
, node_ifs
, INTERFACE_MAX
, &status
, &num_interfaces
)) {
1093 res_lib_cfg_get_node_addrs
->header
.size
= sizeof(struct res_lib_cfg_get_node_addrs
) + (num_interfaces
* TOTEMIP_ADDRLEN
);
1094 res_lib_cfg_get_node_addrs
->header
.id
= MESSAGE_RES_CFG_GET_NODE_ADDRS
;
1095 res_lib_cfg_get_node_addrs
->header
.error
= ret
;
1096 if (num_interfaces
) {
1097 res_lib_cfg_get_node_addrs
->family
= node_ifs
[0].family
;
1098 for (i
= 0, addr_buf
= (char *)res_lib_cfg_get_node_addrs
->addrs
;
1099 i
< num_interfaces
; i
++) {
1100 ss
= (struct sockaddr_storage
*)&node_ifs
[i
].addr
;
1101 if (ss
->ss_family
) {
1102 memcpy(addr_buf
, node_ifs
[i
].addr
, TOTEMIP_ADDRLEN
);
1104 addr_buf
+= TOTEMIP_ADDRLEN
;
1107 res_lib_cfg_get_node_addrs
->num_addrs
= live_addrs
;
1109 res_lib_cfg_get_node_addrs
->header
.error
= CS_ERR_NOT_EXIST
;
1111 api
->ipc_response_send(conn
, res_lib_cfg_get_node_addrs
, res_lib_cfg_get_node_addrs
->header
.size
);
1114 static void message_handler_req_lib_cfg_local_get (void *conn
, const void *msg
)
1116 struct res_lib_cfg_local_get res_lib_cfg_local_get
;
1118 res_lib_cfg_local_get
.header
.size
= sizeof(res_lib_cfg_local_get
);
1119 res_lib_cfg_local_get
.header
.id
= MESSAGE_RES_CFG_LOCAL_GET
;
1120 res_lib_cfg_local_get
.header
.error
= CS_OK
;
1121 res_lib_cfg_local_get
.local_nodeid
= api
->totem_nodeid_get ();
1123 api
->ipc_response_send(conn
, &res_lib_cfg_local_get
,
1124 sizeof(res_lib_cfg_local_get
));
1127 static void message_handler_req_lib_cfg_reload_config (void *conn
, const void *msg
)
1129 struct req_exec_cfg_reload_config req_exec_cfg_reload_config
;
1134 req_exec_cfg_reload_config
.header
.size
=
1135 sizeof (struct req_exec_cfg_reload_config
);
1136 req_exec_cfg_reload_config
.header
.id
= SERVICE_ID_MAKE (CFG_SERVICE
,
1137 MESSAGE_REQ_EXEC_CFG_RELOAD_CONFIG
);
1138 api
->ipc_source_set (&req_exec_cfg_reload_config
.source
, conn
);
1139 api
->ipc_refcnt_inc(conn
);
1141 iovec
.iov_base
= (char *)&req_exec_cfg_reload_config
;
1142 iovec
.iov_len
= sizeof (struct req_exec_cfg_reload_config
);
1144 assert (api
->totem_mcast (&iovec
, 1, TOTEM_SAFE
) == 0);
1149 static void message_handler_req_lib_cfg_reopen_log_files (void *conn
, const void *msg
)
1151 struct res_lib_cfg_reopen_log_files res_lib_cfg_reopen_log_files
;
1156 log_printf(LOGSYS_LEVEL_DEBUG
, "Reopening logging files\n");
1158 res
= logsys_reopen_log_files();
1160 res_lib_cfg_reopen_log_files
.header
.size
= sizeof(res_lib_cfg_reopen_log_files
);
1161 res_lib_cfg_reopen_log_files
.header
.id
= MESSAGE_RES_CFG_REOPEN_LOG_FILES
;
1162 res_lib_cfg_reopen_log_files
.header
.error
= res
;
1163 api
->ipc_response_send(conn
,
1164 &res_lib_cfg_reopen_log_files
,
1165 sizeof(res_lib_cfg_reopen_log_files
));