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