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