This source file includes following definitions.
- PCMK__OUTPUT_ARGS
- PCMK__OUTPUT_ARGS
- pid_cb
- standby_cb
- pcmk_ignore
- pcmk_sigquit
- pacemakerd_chown
- create_pcmk_dirs
- remove_core_file_limit
- pacemakerd_event_cb
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11 #include "pacemakerd.h"
12
13 #include <pwd.h>
14 #include <errno.h>
15 #include <unistd.h>
16 #include <stdio.h>
17 #include <stdbool.h>
18 #include <sys/stat.h>
19 #include <sys/types.h>
20 #include <sys/time.h>
21 #include <sys/resource.h>
22
23 #include <crm/crm.h>
24 #include <crm/msg_xml.h>
25 #include <crm/common/mainloop.h>
26 #include <crm/common/cmdline_internal.h>
27 #include <crm/common/ipc_pacemakerd.h>
28 #include <crm/common/output_internal.h>
29 #include <crm/cluster/internal.h>
30 #include <crm/cluster.h>
31
32 #define SUMMARY "pacemakerd - primary Pacemaker daemon that launches and monitors all subsidiary Pacemaker daemons"
33
34 struct {
35 gboolean features;
36 gboolean foreground;
37 gboolean shutdown;
38 gboolean standby;
39 } options;
40
41 static pcmk__output_t *out = NULL;
42
43 static pcmk__supported_format_t formats[] = {
44 PCMK__SUPPORTED_FORMAT_NONE,
45 PCMK__SUPPORTED_FORMAT_TEXT,
46 PCMK__SUPPORTED_FORMAT_XML,
47 { NULL, NULL, NULL }
48 };
49
50 PCMK__OUTPUT_ARGS("features")
51 static int
52 pacemakerd_features(pcmk__output_t *out, va_list args) {
53 out->info(out, "Pacemaker %s (Build: %s)\n Supporting v%s: %s", PACEMAKER_VERSION,
54 BUILD_VERSION, CRM_FEATURE_SET, CRM_FEATURES);
55 return pcmk_rc_ok;
56 }
57
58 PCMK__OUTPUT_ARGS("features")
59 static int
60 pacemakerd_features_xml(pcmk__output_t *out, va_list args) {
61 gchar **feature_list = g_strsplit(CRM_FEATURES, " ", 0);
62
63 pcmk__output_xml_create_parent(out, "pacemakerd",
64 "version", PACEMAKER_VERSION,
65 "build", BUILD_VERSION,
66 "feature_set", CRM_FEATURE_SET,
67 NULL);
68 out->begin_list(out, NULL, NULL, "features");
69
70 for (char **s = feature_list; *s != NULL; s++) {
71 pcmk__output_create_xml_text_node(out, "feature", *s);
72 }
73
74 out->end_list(out);
75
76 g_strfreev(feature_list);
77 return pcmk_rc_ok;
78 }
79
80 static pcmk__message_entry_t fmt_functions[] = {
81 { "features", "default", pacemakerd_features },
82 { "features", "xml", pacemakerd_features_xml },
83
84 { NULL, NULL, NULL }
85 };
86
87 static gboolean
88 pid_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
89 return TRUE;
90 }
91
92 static gboolean
93 standby_cb(const gchar *option_name, const gchar *optarg, gpointer data, GError **err) {
94 options.standby = TRUE;
95 pcmk__set_env_option(PCMK__ENV_NODE_START_STATE, "standby", false);
96 return TRUE;
97 }
98
99 static GOptionEntry entries[] = {
100 { "features", 'F', 0, G_OPTION_ARG_NONE, &options.features,
101 "Display full version and list of features Pacemaker was built with",
102 NULL },
103 { "foreground", 'f', 0, G_OPTION_ARG_NONE, &options.foreground,
104 "(Ignored) Pacemaker always runs in the foreground",
105 NULL },
106 { "pid-file", 'p', 0, G_OPTION_ARG_CALLBACK, pid_cb,
107 "(Ignored) Daemon pid file location",
108 "FILE" },
109 { "shutdown", 'S', 0, G_OPTION_ARG_NONE, &options.shutdown,
110 "Instruct Pacemaker to shutdown on this machine",
111 NULL },
112 { "standby", 's', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, standby_cb,
113 "Start node in standby state",
114 NULL },
115
116 { NULL }
117 };
118
119 static void
120 pcmk_ignore(int nsig)
121 {
122 crm_info("Ignoring signal %s (%d)", strsignal(nsig), nsig);
123 }
124
125 static void
126 pcmk_sigquit(int nsig)
127 {
128 pcmk__panic(__func__);
129 }
130
131 static void
132 pacemakerd_chown(const char *path, uid_t uid, gid_t gid)
133 {
134 int rc = chown(path, uid, gid);
135
136 if (rc < 0) {
137 crm_warn("Cannot change the ownership of %s to user %s and gid %d: %s",
138 path, CRM_DAEMON_USER, gid, pcmk_rc_str(errno));
139 }
140 }
141
142 static void
143 create_pcmk_dirs(void)
144 {
145 uid_t pcmk_uid = 0;
146 gid_t pcmk_gid = 0;
147
148 const char *dirs[] = {
149 CRM_PACEMAKER_DIR,
150 CRM_CORE_DIR,
151 CRM_BLACKBOX_DIR,
152 PE_STATE_DIR,
153 CRM_CONFIG_DIR,
154
155 NULL
156 };
157
158 if (pcmk_daemon_user(&pcmk_uid, &pcmk_gid) < 0) {
159 crm_err("Cluster user %s does not exist, aborting Pacemaker startup",
160 CRM_DAEMON_USER);
161 crm_exit(CRM_EX_NOUSER);
162 }
163
164
165 if ((mkdir(CRM_STATE_DIR, 0750) < 0) && (errno != EEXIST)) {
166 crm_warn("Could not create directory " CRM_STATE_DIR ": %s",
167 pcmk_rc_str(errno));
168 } else {
169 pacemakerd_chown(CRM_STATE_DIR, pcmk_uid, pcmk_gid);
170 }
171
172 for (int i = 0; dirs[i] != NULL; ++i) {
173 int rc = pcmk__build_path(dirs[i], 0750);
174
175 if (rc != pcmk_rc_ok) {
176 crm_warn("Could not create directory %s: %s",
177 dirs[i], pcmk_rc_str(rc));
178 } else {
179 pacemakerd_chown(dirs[i], pcmk_uid, pcmk_gid);
180 }
181 }
182 }
183
184 static void
185 remove_core_file_limit(void)
186 {
187 struct rlimit cores;
188
189
190 if (getrlimit(RLIMIT_CORE, &cores) < 0) {
191 crm_notice("Unable to check system core file limits "
192 "(consider ensuring the size is unlimited): %s",
193 strerror(errno));
194 return;
195 }
196
197
198 if (cores.rlim_max == 0) {
199 if (geteuid() != 0) {
200 crm_notice("Core dumps are disabled (consider enabling them)");
201 return;
202 }
203 cores.rlim_max = RLIM_INFINITY;
204 }
205
206
207 if (cores.rlim_cur != cores.rlim_max) {
208 cores.rlim_cur = cores.rlim_max;
209 if (setrlimit(RLIMIT_CORE, &cores) < 0) {
210 crm_notice("Unable to raise system limit on core file size "
211 "(consider doing so manually): %s",
212 strerror(errno));
213 return;
214 }
215 }
216
217 if (cores.rlim_cur == RLIM_INFINITY) {
218 crm_trace("Core file size is unlimited");
219 } else {
220 crm_trace("Core file size is limited to %llu bytes",
221 (unsigned long long) cores.rlim_cur);
222 }
223 }
224
225 static void
226 pacemakerd_event_cb(pcmk_ipc_api_t *pacemakerd_api,
227 enum pcmk_ipc_event event_type, crm_exit_t status,
228 void *event_data, void *user_data)
229 {
230 pcmk_pacemakerd_api_reply_t *reply = event_data;
231
232 switch (event_type) {
233 case pcmk_ipc_event_reply:
234 break;
235
236 default:
237 return;
238 }
239
240 if (status != CRM_EX_OK) {
241 out->err(out, "Bad reply from pacemakerd: %s", crm_exit_str(status));
242 return;
243 }
244
245 if (reply->reply_type != pcmk_pacemakerd_reply_shutdown) {
246 out->err(out, "Unknown reply type %d from pacemakerd",
247 reply->reply_type);
248 }
249 }
250
251 static GOptionContext *
252 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group) {
253 GOptionContext *context = NULL;
254
255 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
256 pcmk__add_main_args(context, entries);
257 return context;
258 }
259
260 int
261 main(int argc, char **argv)
262 {
263 int rc = pcmk_rc_ok;
264 crm_exit_t exit_code = CRM_EX_OK;
265
266 GError *error = NULL;
267
268 GOptionGroup *output_group = NULL;
269 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
270 gchar **processed_args = pcmk__cmdline_preproc(argv, "p");
271 GOptionContext *context = build_arg_context(args, &output_group);
272
273 bool old_instance_connected = false;
274
275 pcmk_ipc_api_t *old_instance = NULL;
276 qb_ipcs_service_t *ipcs = NULL;
277
278 subdaemon_check_progress = time(NULL);
279
280 setenv("LC_ALL", "C", 1);
281
282 crm_log_preinit(NULL, argc, argv);
283 mainloop_add_signal(SIGHUP, pcmk_ignore);
284 mainloop_add_signal(SIGQUIT, pcmk_sigquit);
285
286 pcmk__register_formats(output_group, formats);
287 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
288 exit_code = CRM_EX_USAGE;
289 goto done;
290 }
291
292 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
293 if ((rc != pcmk_rc_ok) || (out == NULL)) {
294 exit_code = CRM_EX_ERROR;
295 g_set_error(&error, PCMK__EXITC_ERROR, exit_code, "Error creating output format %s: %s",
296 args->output_ty, pcmk_rc_str(rc));
297 goto done;
298 }
299
300 pcmk__force_args(context, &error, "%s --xml-simple-list", g_get_prgname());
301
302 pcmk__register_messages(out, fmt_functions);
303
304 if (options.features) {
305 out->message(out, "features");
306 exit_code = CRM_EX_OK;
307 goto done;
308 }
309
310 if (args->version) {
311 out->version(out, false);
312 goto done;
313 }
314
315
316 pcmk__set_env_option(PCMK__ENV_MCP, "true", true);
317
318 if (options.shutdown) {
319 pcmk__cli_init_logging("pacemakerd", args->verbosity);
320 } else {
321 crm_log_init(NULL, LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
322 }
323
324 crm_debug("Checking for existing Pacemaker instance");
325
326 rc = pcmk_new_ipc_api(&old_instance, pcmk_ipc_pacemakerd);
327 if (old_instance == NULL) {
328 out->err(out, "Could not check for existing pacemakerd: %s", pcmk_rc_str(rc));
329 exit_code = pcmk_rc2exitc(rc);
330 goto done;
331 }
332
333 pcmk_register_ipc_callback(old_instance, pacemakerd_event_cb, NULL);
334 rc = pcmk__connect_ipc(old_instance, pcmk_ipc_dispatch_sync, 2);
335 if (rc != pcmk_rc_ok) {
336 crm_debug("No existing %s instance found: %s",
337 pcmk_ipc_name(old_instance, true), pcmk_rc_str(rc));
338 }
339 old_instance_connected = pcmk_ipc_is_connected(old_instance);
340
341 if (options.shutdown) {
342 if (old_instance_connected) {
343 rc = pcmk_pacemakerd_api_shutdown(old_instance, crm_system_name);
344 pcmk_dispatch_ipc(old_instance);
345
346 exit_code = pcmk_rc2exitc(rc);
347
348 if (exit_code != CRM_EX_OK) {
349 pcmk_free_ipc_api(old_instance);
350 goto done;
351 }
352
353
354
355
356
357 for (int i = 0; i < 900; i++) {
358 if (!pcmk_ipc_is_connected(old_instance)) {
359 exit_code = CRM_EX_OK;
360 pcmk_free_ipc_api(old_instance);
361 goto done;
362 }
363
364 sleep(2);
365 }
366
367 exit_code = CRM_EX_TIMEOUT;
368 pcmk_free_ipc_api(old_instance);
369 goto done;
370
371 } else {
372 out->err(out, "Could not request shutdown "
373 "of existing Pacemaker instance: %s", pcmk_rc_str(rc));
374 pcmk_free_ipc_api(old_instance);
375 exit_code = CRM_EX_DISCONNECT;
376 goto done;
377 }
378
379 } else if (old_instance_connected) {
380 pcmk_free_ipc_api(old_instance);
381 crm_err("Aborting start-up because active Pacemaker instance found");
382 exit_code = CRM_EX_FATAL;
383 goto done;
384 }
385
386 pcmk_free_ipc_api(old_instance);
387
388
389 if (out != NULL) {
390 out->finish(out, exit_code, true, NULL);
391 pcmk__output_free(out);
392 out = NULL;
393 }
394
395 #ifdef SUPPORT_COROSYNC
396 if (pacemakerd_read_config() == FALSE) {
397 crm_exit(CRM_EX_UNAVAILABLE);
398 }
399 #endif
400
401
402 {
403 const char *facility = pcmk__env_option(PCMK__ENV_LOGFACILITY);
404
405 if (!pcmk__str_eq(facility, PCMK__VALUE_NONE,
406 pcmk__str_casei|pcmk__str_null_matches)) {
407 pcmk__set_env_option("LOGFACILITY", facility, true);
408 }
409 }
410
411 crm_notice("Starting Pacemaker %s "CRM_XS" build=%s features:%s",
412 PACEMAKER_VERSION, BUILD_VERSION, CRM_FEATURES);
413 mainloop = g_main_loop_new(NULL, FALSE);
414
415 remove_core_file_limit();
416 create_pcmk_dirs();
417 pcmk__serve_pacemakerd_ipc(&ipcs, &pacemakerd_ipc_callbacks);
418
419 #ifdef SUPPORT_COROSYNC
420
421 if (!cluster_connect_cfg()) {
422 exit_code = CRM_EX_PROTOCOL;
423 goto done;
424 }
425 #endif
426
427 if (pcmk__locate_sbd() > 0) {
428 running_with_sbd = TRUE;
429 }
430
431 switch (find_and_track_existing_processes()) {
432 case pcmk_rc_ok:
433 break;
434 case pcmk_rc_ipc_unauthorized:
435 exit_code = CRM_EX_CANTCREAT;
436 goto done;
437 default:
438 exit_code = CRM_EX_FATAL;
439 goto done;
440 };
441
442 mainloop_add_signal(SIGTERM, pcmk_shutdown);
443 mainloop_add_signal(SIGINT, pcmk_shutdown);
444
445 if ((running_with_sbd) && pcmk__get_sbd_sync_resource_startup()) {
446 crm_notice("Waiting for startup-trigger from SBD.");
447 pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_WAITPING;
448 startup_trigger = mainloop_add_trigger(G_PRIORITY_HIGH, init_children_processes, NULL);
449 } else {
450 if (running_with_sbd) {
451 crm_warn("Enabling SBD_SYNC_RESOURCE_STARTUP would (if supported "
452 "by your SBD version) improve reliability of "
453 "interworking between SBD & pacemaker.");
454 }
455 pacemakerd_state = XML_PING_ATTR_PACEMAKERDSTATE_STARTINGDAEMONS;
456 init_children_processes(NULL);
457 }
458
459 crm_notice("Pacemaker daemon successfully started and accepting connections");
460 g_main_loop_run(mainloop);
461
462 if (ipcs) {
463 crm_trace("Closing IPC server");
464 mainloop_del_ipc_server(ipcs);
465 ipcs = NULL;
466 }
467
468 g_main_loop_unref(mainloop);
469 #ifdef SUPPORT_COROSYNC
470 cluster_disconnect_cfg();
471 #endif
472
473 done:
474 g_strfreev(processed_args);
475 pcmk__free_arg_context(context);
476
477 pcmk__output_and_clear_error(&error, out);
478
479 if (out != NULL) {
480 out->finish(out, exit_code, true, NULL);
481 pcmk__output_free(out);
482 }
483 pcmk__unregister_formats();
484 crm_exit(exit_code);
485 }