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