1    	/*
2    	 * Copyright 2004-2026 the Pacemaker project contributors
3    	 *
4    	 * The version control history for this file may have further details.
5    	 *
6    	 * This source code is licensed under the GNU General Public License version 2
7    	 * or later (GPLv2+) WITHOUT ANY WARRANTY.
8    	 */
9    	
10   	#include <crm_internal.h>
11   	
12   	#include <errno.h>                  // errno
13   	#include <grp.h>                    // initgroups
14   	#include <signal.h>                 // SIGTERM
15   	#include <stdbool.h>
16   	#include <stddef.h>                 // NULL, size_t
17   	#include <stdlib.h>                 // free
18   	#include <syslog.h>                 // LOG_INFO
19   	#include <sys/types.h>              // gid_t, uid_t
20   	#include <unistd.h>                 // setgid, setuid
21   	
22   	#include <corosync/cpg.h>           // cpg_*
23   	#include <glib.h>                   // g_*, G_*, etc.
24   	#include <libxml/tree.h>            // xmlNode
25   	
26   	#include <crm_config.h>             // CRM_CONFIG_DIR, CRM_DAEMON_USER
27   	#include <crm/cluster.h>            // pcmk_cluster_*
28   	#include <crm/cluster/internal.h>   // pcmk__node_update, etc.
29   	#include <crm/common/ipc.h>         // crm_ipc_*
30   	#include <crm/common/logging.h>     // crm_log_*
31   	#include <crm/common/mainloop.h>    // mainloop_add_signal
32   	#include <crm/common/results.h>     // CRM_EX_*, pcmk_rc_*
33   	
34   	#include "pacemaker-based.h"
35   	
36   	#define SUMMARY "daemon for managing the configuration of a Pacemaker cluster"
37   	
38   	bool cib_shutdown_flag = false;
39   	int cib_status = pcmk_rc_ok;
40   	
41   	pcmk_cluster_t *crm_cluster = NULL;
42   	
43   	GMainLoop *mainloop = NULL;
44   	gchar *cib_root = NULL;
45   	
46   	gboolean stand_alone = FALSE;
47   	
48   	static void cib_init(void);
49   	
50   	static crm_exit_t exit_code = CRM_EX_OK;
51   	
52   	/*!
53   	 * \internal
54   	 * \brief Set up options, users, and groups for stand-alone mode
55   	 *
56   	 * \param[out] error  GLib error object
57   	 *
58   	 * \return Standard Pacemaker return code
59   	 */
60   	static int
61   	setup_stand_alone(GError **error)
62   	{
63   	    uid_t uid = 0;
64   	    gid_t gid = 0;
65   	    int rc = pcmk_rc_ok;
66   	
67   	    rc = pcmk__daemon_user(&uid, &gid);
68   	    if (rc != pcmk_rc_ok) {
69   	        exit_code = CRM_EX_FATAL;
70   	        g_set_error(error, PCMK__EXITC_ERROR, exit_code,
71   	                    "Could not find user " CRM_DAEMON_USER ": %s",
72   	                    pcmk_rc_str(rc));
73   	        return rc;
74   	    }
75   	
76   	    rc = setgid(gid);
77   	    if (rc < 0) {
78   	        rc = errno;
79   	        exit_code = CRM_EX_FATAL;
80   	        g_set_error(error, PCMK__EXITC_ERROR, exit_code,
81   	                    "Could not set group to %lld: %s", (long long) gid,
82   	                    pcmk_rc_str(rc));
83   	        return rc;
84   	    }
85   	
86   	    rc = initgroups(CRM_DAEMON_USER, gid);
87   	    if (rc < 0) {
88   	        rc = errno;
89   	        exit_code = CRM_EX_FATAL;
90   	        g_set_error(error, PCMK__EXITC_ERROR, exit_code,
91   	                    "Could not set up groups for user %lld: %s",
92   	                    (long long) uid, pcmk_rc_str(rc));
93   	        return rc;
94   	    }
95   	
96   	    rc = setuid(uid);
97   	    if (rc < 0) {
98   	        rc = errno;
99   	        exit_code = CRM_EX_FATAL;
100  	        g_set_error(error, PCMK__EXITC_ERROR, exit_code,
101  	                    "Could not set user to %lld: %s", (long long) uid,
102  	                    pcmk_rc_str(rc));
103  	        return rc;
104  	    }
105  	    return pcmk_rc_ok;
106  	}
107  	
108  	/* @COMPAT Deprecated since 2.1.8. Use pcmk_list_cluster_options() or
109  	 * crm_attribute --list-options=cluster instead of querying daemon metadata.
110  	 *
111  	 * NOTE: pcs (as of at least 0.11.8) uses this
112  	 */
113  	static int
114  	based_metadata(pcmk__output_t *out)
115  	{
116  	    return pcmk__daemon_metadata(out, PCMK__SERVER_BASED,
117  	                                 "Cluster Information Base manager options",
118  	                                 "Cluster options used by Pacemaker's Cluster "
119  	                                 "Information Base manager",
120  	                                 pcmk__opt_based);
121  	}
122  	
123  	static gboolean
124  	disk_writes_cb(const gchar *option_name, const gchar *optarg, gpointer data,
125  	               GError **error)
126  	{
127  	    based_enable_writes(0);
128  	    return TRUE;
129  	}
130  	
131  	static GOptionEntry entries[] = {
132  	    { "stand-alone", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &stand_alone,
133  	      "(Advanced use only) Run in stand-alone mode", NULL },
134  	
135  	    { "disk-writes", 'w', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
136  	      disk_writes_cb,
137  	      "(Advanced use only) Enable disk writes (enabled by default unless in "
138  	      "stand-alone mode)", NULL },
139  	
140  	    { "cib-root", 'r', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME, &cib_root,
141  	      "(Advanced use only) Directory where the CIB XML file should be located "
142  	      "(default: " CRM_CONFIG_DIR ")", NULL },
143  	
144  	    { NULL }
145  	};
146  	
147  	static pcmk__supported_format_t formats[] = {
148  	    PCMK__SUPPORTED_FORMAT_NONE,
149  	    PCMK__SUPPORTED_FORMAT_TEXT,
150  	    PCMK__SUPPORTED_FORMAT_XML,
151  	    { NULL, NULL, NULL }
152  	};
153  	
154  	static GOptionContext *
155  	build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
156  	{
157  	    GOptionContext *context = NULL;
158  	
159  	    context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
160  	    pcmk__add_main_args(context, entries);
161  	    return context;
162  	}
163  	
164  	int
165  	main(int argc, char **argv)
166  	{
167  	    int rc = pcmk_rc_ok;
168  	    crm_ipc_t *old_instance = NULL;
169  	
170  	    pcmk__output_t *out = NULL;
171  	
172  	    GError *error = NULL;
173  	
174  	    GOptionGroup *output_group = NULL;
175  	    pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
176  	    gchar **processed_args = pcmk__cmdline_preproc(argv, "r");
177  	    GOptionContext *context = build_arg_context(args, &output_group);
178  	
179  	    crm_log_preinit(NULL, argc, argv);
180  	
181  	    pcmk__register_formats(output_group, formats);
(1) Event path: Condition "!g_option_context_parse_strv(context, &processed_args, &error)", taking false branch.
182  	    if (!g_option_context_parse_strv(context, &processed_args, &error)) {
183  	        exit_code = CRM_EX_USAGE;
184  	        goto done;
185  	    }
186  	
187  	    rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
(2) Event path: Condition "rc != pcmk_rc_ok", taking false branch.
188  	    if (rc != pcmk_rc_ok) {
189  	        exit_code = CRM_EX_ERROR;
190  	        g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
191  	                    "Error creating output format %s: %s",
192  	                    args->output_ty, pcmk_rc_str(rc));
193  	        goto done;
194  	    }
195  	
(3) Event path: Condition "args->version", taking false branch.
196  	    if (args->version) {
197  	        out->version(out);
198  	        goto done;
199  	    }
200  	
201  	    mainloop_add_signal(SIGTERM, based_shutdown);
202  	
203  	    based_io_init();
204  	
(4) Event path: Condition "g_strv_length(processed_args) >= 2", taking true branch.
(5) Event path: Condition "pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)", taking false branch.
205  	    if ((g_strv_length(processed_args) >= 2)
206  	        && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
207  	
208  	        rc = based_metadata(out);
209  	        if (rc != pcmk_rc_ok) {
210  	            exit_code = CRM_EX_FATAL;
211  	            g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
212  	                        "Unable to display metadata: %s", pcmk_rc_str(rc));
213  	        }
214  	        goto done;
215  	    }
216  	
217  	    pcmk__cli_init_logging(PCMK__SERVER_BASED, args->verbosity);
218  	    crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
219  	    pcmk__notice("Starting Pacemaker CIB manager");
220  	
221  	    old_instance = crm_ipc_new(PCMK__SERVER_BASED_RO, 0);
(6) Event path: Condition "old_instance == NULL", taking false branch.
222  	    if (old_instance == NULL) {
223  	        /* crm_ipc_new() will have already logged an error message with
224  	         * pcmk__err()
225  	         */
226  	        exit_code = CRM_EX_FATAL;
227  	        goto done;
228  	    }
229  	
(7) Event path: Condition "pcmk__connect_generic_ipc(old_instance) == pcmk_rc_ok", taking false branch.
230  	    if (pcmk__connect_generic_ipc(old_instance) == pcmk_rc_ok) {
231  	        /* IPC end-point already up */
232  	        crm_ipc_close(old_instance);
233  	        crm_ipc_destroy(old_instance);
234  	        pcmk__crit("Aborting start-up because another CIB manager instance is "
235  	                   "already active");
236  	        goto done;
237  	    }
238  	
239  	    // Not up or not authentic; we'll proceed either way
CID (unavailable; MK=35247479f2e768d3a8277a90aa60734e) (#1 of 1): Inconsistent C union access (INCONSISTENT_UNION_ACCESS):
(8) Event assign_union_field: The union field "in" of "_pp" is written.
(9) Event inconsistent_union_field_access: In "_pp.out", the union field used: "out" is inconsistent with the field most recently stored: "in".
240  	    g_clear_pointer(&old_instance, crm_ipc_destroy);
241  	
242  	    if (stand_alone) {
243  	        rc = setup_stand_alone(&error);
244  	        if (rc != pcmk_rc_ok) {
245  	            goto done;
246  	        }
247  	    }
248  	
249  	    if (cib_root == NULL) {
250  	        cib_root = g_strdup(CRM_CONFIG_DIR);
251  	    } else {
252  	        pcmk__notice("Using custom config location: %s", cib_root);
253  	    }
254  	
255  	    if (!pcmk__daemon_can_write(cib_root, NULL)) {
256  	        exit_code = CRM_EX_FATAL;
257  	        pcmk__err("Terminating due to bad permissions on %s", cib_root);
258  	        g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
259  	                    "Bad permissions on %s (see logs for details)", cib_root);
260  	        goto done;
261  	    }
262  	
263  	    pcmk__cluster_init_node_caches();
264  	
265  	    // Read initial CIB, connect to cluster, and start IPC servers
266  	    cib_init();
267  	
268  	    // Run the main loop
269  	    mainloop = g_main_loop_new(NULL, FALSE);
270  	    pcmk__notice("Pacemaker CIB manager successfully started and accepting "
271  	                 "connections");
272  	    g_main_loop_run(mainloop);
273  	
274  	    /* If main loop returned, clean up and exit. We disconnect in case
275  	     * based_terminate(-1) was called.
276  	     */
277  	    pcmk_cluster_disconnect(crm_cluster);
278  	    pcmk__stop_based_ipc(ipcs_ro, ipcs_rw, ipcs_shm);
279  	
280  	done:
281  	    g_strfreev(processed_args);
282  	    pcmk__free_arg_context(context);
283  	
284  	    pcmk__cluster_destroy_node_caches();
285  	    pcmk__client_cleanup();
286  	    pcmk_cluster_free(crm_cluster);
287  	    g_free(cib_root);
288  	
289  	    pcmk__output_and_clear_error(&error, out);
290  	
291  	    if (out != NULL) {
292  	        out->finish(out, exit_code, true, NULL);
293  	        pcmk__output_free(out);
294  	    }
295  	    pcmk__unregister_formats();
296  	    crm_exit(exit_code);
297  	}
298  	
299  	#if SUPPORT_COROSYNC
300  	static void
301  	cib_cs_dispatch(cpg_handle_t handle,
302  	                 const struct cpg_name *groupName,
303  	                 uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
304  	{
305  	    xmlNode *xml = NULL;
306  	    const char *from = NULL;
307  	    char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &from);
308  	
309  	    if(data == NULL) {
310  	        return;
311  	    }
312  	
313  	    xml = pcmk__xml_parse(data);
314  	    if (xml == NULL) {
315  	        pcmk__err("Invalid XML: '%.120s'", data);
316  	        free(data);
317  	        return;
318  	    }
319  	    pcmk__xe_set(xml, PCMK__XA_SRC, from);
320  	    based_peer_callback(xml, NULL);
321  	
322  	    pcmk__xml_free(xml);
323  	    free(data);
324  	}
325  	
326  	static void
327  	cib_cs_destroy(gpointer user_data)
328  	{
329  	    if (cib_shutdown_flag) {
330  	        pcmk__info("Corosync disconnection complete");
331  	    } else {
332  	        pcmk__crit("Exiting immediately after losing connection to cluster "
333  	                   "layer");
334  	        based_terminate(CRM_EX_DISCONNECT);
335  	    }
336  	}
337  	#endif
338  	
339  	static void
340  	cib_peer_update_callback(enum pcmk__node_update type,
341  	                         pcmk__node_status_t *node, const void *data)
342  	{
343  	    switch (type) {
344  	        case pcmk__node_update_name:
345  	        case pcmk__node_update_state:
346  	            if (cib_shutdown_flag && (pcmk__cluster_num_active_nodes() < 2)
347  	                && (pcmk__ipc_client_count() == 0)) {
348  	
349  	                pcmk__info("Exiting after no more peers or clients remain");
350  	                based_terminate(-1);
351  	            }
352  	            break;
353  	
354  	        default:
355  	            break;
356  	    }
357  	}
358  	
359  	static void
360  	cib_init(void)
361  	{
362  	    // based_read_cib() returns new, non-NULL XML, so this should always succeed
363  	    if (based_activate_cib(based_read_cib(), true, "start") != pcmk_rc_ok) {
364  	        pcmk__crit("Bug: failed to activate CIB. Terminating %s.",
365  	                   pcmk__server_log_name(pcmk_ipc_based));
366  	        crm_exit(CRM_EX_SOFTWARE);
367  	    }
368  	
369  	    based_remote_init();
370  	    crm_cluster = pcmk_cluster_new();
371  	
372  	#if SUPPORT_COROSYNC
373  	    if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
374  	        pcmk_cluster_set_destroy_fn(crm_cluster, cib_cs_destroy);
375  	        pcmk_cpg_set_deliver_fn(crm_cluster, cib_cs_dispatch);
376  	        pcmk_cpg_set_confchg_fn(crm_cluster, pcmk__cpg_confchg_cb);
377  	    }
378  	#endif // SUPPORT_COROSYNC
379  	
380  	    if (!stand_alone) {
381  	        pcmk__cluster_set_status_callback(&cib_peer_update_callback);
382  	
383  	        if (pcmk_cluster_connect(crm_cluster) != pcmk_rc_ok) {
384  	            pcmk__crit("Cannot sign in to the cluster... terminating");
385  	            crm_exit(CRM_EX_FATAL);
386  	        }
387  	    }
388  	
389  	    pcmk__serve_based_ipc(&ipcs_ro, &ipcs_rw, &ipcs_shm, &ipc_ro_callbacks,
390  	                          &ipc_rw_callbacks);
391  	
392  	    if (stand_alone) {
393  	        based_is_primary = true;
394  	    }
395  	}
396