This source file includes following definitions.
- st_ipc_accept
- st_ipc_dispatch
- st_ipc_closed
- st_ipc_destroy
- stonith_peer_callback
- stonith_peer_ais_callback
- stonith_peer_cs_destroy
- do_local_reply
- get_stonith_flag
- stonith_notify_client
- do_stonith_async_timeout_update
- fenced_send_notification
- fenced_send_config_notification
- node_does_watchdog_fencing
- stonith_shutdown
- stonith_cleanup
- stand_alone_cpg_cb
- st_peer_update_callback
- fencer_metadata
- build_arg_context
- main
1
2
3
4
5
6
7
8
9
10 #include <crm_internal.h>
11
12 #include <sys/param.h>
13 #include <stdio.h>
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <unistd.h>
17 #include <sys/utsname.h>
18
19 #include <stdlib.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <inttypes.h>
23
24 #include <crm/crm.h>
25 #include <crm/common/cmdline_internal.h>
26 #include <crm/common/ipc.h>
27 #include <crm/common/ipc_internal.h>
28 #include <crm/common/output_internal.h>
29
30 #include <crm/stonith-ng.h>
31 #include <crm/fencing/internal.h>
32 #include <crm/common/xml.h>
33 #include <crm/common/xml_internal.h>
34
35 #include <crm/common/mainloop.h>
36
37 #include <crm/cib/internal.h>
38
39 #include <pacemaker-fenced.h>
40
41 #define SUMMARY "daemon for executing fencing devices in a Pacemaker cluster"
42
43 char *stonith_our_uname = NULL;
44 long long stonith_watchdog_timeout_ms = 0;
45 GList *stonith_watchdog_targets = NULL;
46
47 static GMainLoop *mainloop = NULL;
48
49 gboolean stand_alone = FALSE;
50 gboolean stonith_shutdown_flag = FALSE;
51
52 static qb_ipcs_service_t *ipcs = NULL;
53 static pcmk__output_t *out = NULL;
54
55 pcmk__supported_format_t formats[] = {
56 PCMK__SUPPORTED_FORMAT_NONE,
57 PCMK__SUPPORTED_FORMAT_TEXT,
58 PCMK__SUPPORTED_FORMAT_XML,
59 { NULL, NULL, NULL }
60 };
61
62 static struct {
63 bool no_cib_connect;
64 gchar **log_files;
65 } options;
66
67 crm_exit_t exit_code = CRM_EX_OK;
68
69 static void stonith_cleanup(void);
70
71 static int32_t
72 st_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
73 {
74 if (stonith_shutdown_flag) {
75 crm_info("Ignoring new client [%d] during shutdown",
76 pcmk__client_pid(c));
77 return -ECONNREFUSED;
78 }
79
80 if (pcmk__new_client(c, uid, gid) == NULL) {
81 return -ENOMEM;
82 }
83 return 0;
84 }
85
86
87 static int32_t
88 st_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
89 {
90 uint32_t id = 0;
91 uint32_t flags = 0;
92 uint32_t call_options = st_opt_none;
93 xmlNode *request = NULL;
94 pcmk__client_t *c = pcmk__find_client(qbc);
95 const char *op = NULL;
96 int rc = pcmk_rc_ok;
97
98 if (c == NULL) {
99 crm_info("Invalid client: %p", qbc);
100 return 0;
101 }
102
103 request = pcmk__client_data2xml(c, data, &id, &flags);
104 if (request == NULL) {
105 pcmk__ipc_send_ack(c, id, flags, PCMK__XE_NACK, NULL, CRM_EX_PROTOCOL);
106 return 0;
107 }
108
109
110 op = crm_element_value(request, PCMK__XA_CRM_TASK);
111 if(pcmk__str_eq(op, CRM_OP_RM_NODE_CACHE, pcmk__str_casei)) {
112 crm_xml_add(request, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
113 crm_xml_add(request, PCMK__XA_ST_OP, op);
114 crm_xml_add(request, PCMK__XA_ST_CLIENTID, c->id);
115 crm_xml_add(request, PCMK__XA_ST_CLIENTNAME, pcmk__client_name(c));
116 crm_xml_add(request, PCMK__XA_ST_CLIENTNODE, stonith_our_uname);
117
118 pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, request);
119 free_xml(request);
120 return 0;
121 }
122
123 if (c->name == NULL) {
124 const char *value = crm_element_value(request, PCMK__XA_ST_CLIENTNAME);
125
126 c->name = crm_strdup_printf("%s.%u", pcmk__s(value, "unknown"), c->pid);
127 }
128
129 rc = pcmk__xe_get_flags(request, PCMK__XA_ST_CALLOPT, &call_options,
130 st_opt_none);
131 if (rc != pcmk_rc_ok) {
132 crm_warn("Couldn't parse options from IPC request: %s",
133 pcmk_rc_str(rc));
134 }
135
136 crm_trace("Flags %#08" PRIx32 "/%#08x for command %" PRIu32
137 " from client %s", flags, call_options, id, pcmk__client_name(c));
138
139 if (pcmk_is_set(call_options, st_opt_sync_call)) {
140 pcmk__assert(pcmk_is_set(flags, crm_ipc_client_response));
141 CRM_LOG_ASSERT(c->request_id == 0);
142 c->request_id = id;
143 }
144
145 crm_xml_add(request, PCMK__XA_ST_CLIENTID, c->id);
146 crm_xml_add(request, PCMK__XA_ST_CLIENTNAME, pcmk__client_name(c));
147 crm_xml_add(request, PCMK__XA_ST_CLIENTNODE, stonith_our_uname);
148
149 crm_log_xml_trace(request, "ipc-received");
150 stonith_command(c, id, flags, request, NULL);
151
152 free_xml(request);
153 return 0;
154 }
155
156
157 static int32_t
158 st_ipc_closed(qb_ipcs_connection_t * c)
159 {
160 pcmk__client_t *client = pcmk__find_client(c);
161
162 if (client == NULL) {
163 return 0;
164 }
165
166 crm_trace("Connection %p closed", c);
167 pcmk__free_client(client);
168
169
170 return 0;
171 }
172
173 static void
174 st_ipc_destroy(qb_ipcs_connection_t * c)
175 {
176 crm_trace("Connection %p destroyed", c);
177 st_ipc_closed(c);
178 }
179
180 static void
181 stonith_peer_callback(xmlNode * msg, void *private_data)
182 {
183 const char *remote_peer = crm_element_value(msg, PCMK__XA_SRC);
184 const char *op = crm_element_value(msg, PCMK__XA_ST_OP);
185
186 if (pcmk__str_eq(op, STONITH_OP_POKE, pcmk__str_none)) {
187 return;
188 }
189
190 crm_log_xml_trace(msg, "Peer[inbound]");
191 stonith_command(NULL, 0, 0, msg, remote_peer);
192 }
193
194 #if SUPPORT_COROSYNC
195 static void
196 stonith_peer_ais_callback(cpg_handle_t handle,
197 const struct cpg_name *groupName,
198 uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
199 {
200 uint32_t kind = 0;
201 xmlNode *xml = NULL;
202 const char *from = NULL;
203 char *data = pcmk__cpg_message_data(handle, nodeid, pid, msg, &kind, &from);
204
205 if(data == NULL) {
206 return;
207 }
208 if (kind == crm_class_cluster) {
209 xml = pcmk__xml_parse(data);
210 if (xml == NULL) {
211 crm_err("Invalid XML: '%.120s'", data);
212 free(data);
213 return;
214 }
215 crm_xml_add(xml, PCMK__XA_SRC, from);
216 stonith_peer_callback(xml, NULL);
217 }
218
219 free_xml(xml);
220 free(data);
221 return;
222 }
223
224 static void
225 stonith_peer_cs_destroy(gpointer user_data)
226 {
227 crm_crit("Lost connection to cluster layer, shutting down");
228 stonith_shutdown(0);
229 }
230 #endif
231
232 void
233 do_local_reply(const xmlNode *notify_src, pcmk__client_t *client,
234 int call_options)
235 {
236
237 int local_rc = pcmk_rc_ok;
238 int rid = 0;
239 uint32_t ipc_flags = crm_ipc_server_event;
240
241 if (pcmk_is_set(call_options, st_opt_sync_call)) {
242 CRM_LOG_ASSERT(client->request_id);
243 rid = client->request_id;
244 client->request_id = 0;
245 ipc_flags = crm_ipc_flags_none;
246 }
247
248 local_rc = pcmk__ipc_send_xml(client, rid, notify_src, ipc_flags);
249 if (local_rc == pcmk_rc_ok) {
250 crm_trace("Sent response %d to client %s",
251 rid, pcmk__client_name(client));
252 } else {
253 crm_warn("%synchronous reply to client %s failed: %s",
254 (pcmk_is_set(call_options, st_opt_sync_call)? "S" : "As"),
255 pcmk__client_name(client), pcmk_rc_str(local_rc));
256 }
257 }
258
259 uint64_t
260 get_stonith_flag(const char *name)
261 {
262 if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_FENCE, pcmk__str_none)) {
263 return st_callback_notify_fence;
264
265 } else if (pcmk__str_eq(name, STONITH_OP_DEVICE_ADD, pcmk__str_casei)) {
266 return st_callback_device_add;
267
268 } else if (pcmk__str_eq(name, STONITH_OP_DEVICE_DEL, pcmk__str_casei)) {
269 return st_callback_device_del;
270
271 } else if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_HISTORY,
272 pcmk__str_none)) {
273 return st_callback_notify_history;
274
275 } else if (pcmk__str_eq(name, PCMK__VALUE_ST_NOTIFY_HISTORY_SYNCED,
276 pcmk__str_none)) {
277 return st_callback_notify_history_synced;
278
279 }
280 return st_callback_unknown;
281 }
282
283 static void
284 stonith_notify_client(gpointer key, gpointer value, gpointer user_data)
285 {
286
287 const xmlNode *update_msg = user_data;
288 pcmk__client_t *client = value;
289 const char *type = NULL;
290
291 CRM_CHECK(client != NULL, return);
292 CRM_CHECK(update_msg != NULL, return);
293
294 type = crm_element_value(update_msg, PCMK__XA_SUBT);
295 CRM_CHECK(type != NULL, crm_log_xml_err(update_msg, "notify"); return);
296
297 if (client->ipcs == NULL) {
298 crm_trace("Skipping client with NULL channel");
299 return;
300 }
301
302 if (pcmk_is_set(client->flags, get_stonith_flag(type))) {
303 int rc = pcmk__ipc_send_xml(client, 0, update_msg,
304 crm_ipc_server_event);
305
306 if (rc != pcmk_rc_ok) {
307 crm_warn("%s notification of client %s failed: %s "
308 CRM_XS " id=%.8s rc=%d", type, pcmk__client_name(client),
309 pcmk_rc_str(rc), client->id, rc);
310 } else {
311 crm_trace("Sent %s notification to client %s",
312 type, pcmk__client_name(client));
313 }
314 }
315 }
316
317 void
318 do_stonith_async_timeout_update(const char *client_id, const char *call_id, int timeout)
319 {
320 pcmk__client_t *client = NULL;
321 xmlNode *notify_data = NULL;
322
323 if (!timeout || !call_id || !client_id) {
324 return;
325 }
326
327 client = pcmk__find_client_by_id(client_id);
328 if (!client) {
329 return;
330 }
331
332 notify_data = pcmk__xe_create(NULL, PCMK__XE_ST_ASYNC_TIMEOUT_VALUE);
333 crm_xml_add(notify_data, PCMK__XA_T, PCMK__VALUE_ST_ASYNC_TIMEOUT_VALUE);
334 crm_xml_add(notify_data, PCMK__XA_ST_CALLID, call_id);
335 crm_xml_add_int(notify_data, PCMK__XA_ST_TIMEOUT, timeout);
336
337 crm_trace("timeout update is %d for client %s and call id %s", timeout, client_id, call_id);
338
339 if (client) {
340 pcmk__ipc_send_xml(client, 0, notify_data, crm_ipc_server_event);
341 }
342
343 free_xml(notify_data);
344 }
345
346
347
348
349
350
351
352
353
354 void
355 fenced_send_notification(const char *type, const pcmk__action_result_t *result,
356 xmlNode *data)
357 {
358
359 xmlNode *update_msg = pcmk__xe_create(NULL, PCMK__XE_NOTIFY);
360
361 CRM_LOG_ASSERT(type != NULL);
362
363 crm_xml_add(update_msg, PCMK__XA_T, PCMK__VALUE_ST_NOTIFY);
364 crm_xml_add(update_msg, PCMK__XA_SUBT, type);
365 crm_xml_add(update_msg, PCMK__XA_ST_OP, type);
366 stonith__xe_set_result(update_msg, result);
367
368 if (data != NULL) {
369 xmlNode *wrapper = pcmk__xe_create(update_msg, PCMK__XE_ST_CALLDATA);
370
371 pcmk__xml_copy(wrapper, data);
372 }
373
374 crm_trace("Notifying clients");
375 pcmk__foreach_ipc_client(stonith_notify_client, update_msg);
376 free_xml(update_msg);
377 crm_trace("Notify complete");
378 }
379
380
381
382
383
384
385
386
387
388
389
390
391
392 void
393 fenced_send_config_notification(const char *op,
394 const pcmk__action_result_t *result,
395 const char *desc)
396 {
397 xmlNode *notify_data = pcmk__xe_create(NULL, op);
398
399 crm_xml_add(notify_data, PCMK__XA_ST_DEVICE_ID, desc);
400
401 fenced_send_notification(op, result, notify_data);
402 free_xml(notify_data);
403 }
404
405
406
407
408
409
410
411
412
413
414
415 gboolean
416 node_does_watchdog_fencing(const char *node)
417 {
418 return ((stonith_watchdog_targets == NULL) ||
419 pcmk__str_in_list(node, stonith_watchdog_targets, pcmk__str_casei));
420 }
421
422 void
423 stonith_shutdown(int nsig)
424 {
425 crm_info("Terminating with %d clients", pcmk__ipc_client_count());
426 stonith_shutdown_flag = TRUE;
427 if (mainloop != NULL && g_main_loop_is_running(mainloop)) {
428 g_main_loop_quit(mainloop);
429 }
430 }
431
432 static void
433 stonith_cleanup(void)
434 {
435 fenced_cib_cleanup();
436 if (ipcs) {
437 qb_ipcs_destroy(ipcs);
438 }
439
440 pcmk__cluster_destroy_node_caches();
441 pcmk__client_cleanup();
442 free_stonith_remote_op_list();
443 free_topology_list();
444 free_device_list();
445 free_metadata_cache();
446 fenced_unregister_handlers();
447
448 free(stonith_our_uname);
449 stonith_our_uname = NULL;
450 }
451
452 static gboolean
453 stand_alone_cpg_cb(const gchar *option_name, const gchar *optarg, gpointer data,
454 GError **error)
455 {
456 stand_alone = FALSE;
457 options.no_cib_connect = true;
458 return TRUE;
459 }
460
461 struct qb_ipcs_service_handlers ipc_callbacks = {
462 .connection_accept = st_ipc_accept,
463 .connection_created = NULL,
464 .msg_process = st_ipc_dispatch,
465 .connection_closed = st_ipc_closed,
466 .connection_destroyed = st_ipc_destroy
467 };
468
469
470
471
472
473
474
475
476
477 static void
478 st_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
479 {
480 if ((type != crm_status_processes)
481 && !pcmk_is_set(node->flags, crm_remote_node)) {
482
483
484
485
486 xmlNode *query = pcmk__xe_create(NULL, PCMK__XE_STONITH_COMMAND);
487
488 crm_xml_add(query, PCMK__XA_T, PCMK__VALUE_STONITH_NG);
489 crm_xml_add(query, PCMK__XA_ST_OP, STONITH_OP_POKE);
490
491 crm_debug("Broadcasting our uname because of node %u", node->id);
492 pcmk__cluster_send_message(NULL, crm_msg_stonith_ng, query);
493
494 free_xml(query);
495 }
496 }
497
498
499
500
501 static int
502 fencer_metadata(void)
503 {
504 const char *name = "pacemaker-fenced";
505 const char *desc_short = N_("Instance attributes available for all "
506 "\"stonith\"-class resources");
507 const char *desc_long = N_("Instance attributes available for all "
508 "\"stonith\"-class resources and used by "
509 "Pacemaker's fence daemon, formerly known as "
510 "stonithd");
511
512 return pcmk__daemon_metadata(out, name, desc_short, desc_long,
513 pcmk__opt_fencing);
514 }
515
516 static GOptionEntry entries[] = {
517 { "stand-alone", 's', G_OPTION_FLAG_NONE, G_OPTION_ARG_NONE, &stand_alone,
518 N_("Deprecated (will be removed in a future release)"), NULL },
519
520 { "stand-alone-w-cpg", 'c', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK,
521 stand_alone_cpg_cb, N_("Intended for use in regression testing only"), NULL },
522
523 { "logfile", 'l', G_OPTION_FLAG_NONE, G_OPTION_ARG_FILENAME_ARRAY,
524 &options.log_files, N_("Send logs to the additional named logfile"), NULL },
525
526 { NULL }
527 };
528
529 static GOptionContext *
530 build_arg_context(pcmk__common_args_t *args, GOptionGroup **group)
531 {
532 GOptionContext *context = NULL;
533
534 context = pcmk__build_arg_context(args, "text (default), xml", group, NULL);
535 pcmk__add_main_args(context, entries);
536 return context;
537 }
538
539 int
540 main(int argc, char **argv)
541 {
542 int rc = pcmk_rc_ok;
543 pcmk_cluster_t *cluster = NULL;
544 crm_ipc_t *old_instance = NULL;
545
546 GError *error = NULL;
547
548 GOptionGroup *output_group = NULL;
549 pcmk__common_args_t *args = pcmk__new_common_args(SUMMARY);
550 gchar **processed_args = pcmk__cmdline_preproc(argv, "l");
551 GOptionContext *context = build_arg_context(args, &output_group);
552
553 crm_log_preinit(NULL, argc, argv);
554
555 pcmk__register_formats(output_group, formats);
556 if (!g_option_context_parse_strv(context, &processed_args, &error)) {
557 exit_code = CRM_EX_USAGE;
558 goto done;
559 }
560
561 rc = pcmk__output_new(&out, args->output_ty, args->output_dest, argv);
562 if (rc != pcmk_rc_ok) {
563 exit_code = CRM_EX_ERROR;
564 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
565 "Error creating output format %s: %s",
566 args->output_ty, pcmk_rc_str(rc));
567 goto done;
568 }
569
570 if (args->version) {
571 out->version(out, false);
572 goto done;
573 }
574
575 if ((g_strv_length(processed_args) >= 2)
576 && pcmk__str_eq(processed_args[1], "metadata", pcmk__str_none)) {
577
578 rc = fencer_metadata();
579 if (rc != pcmk_rc_ok) {
580 exit_code = CRM_EX_FATAL;
581 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
582 "Unable to display metadata: %s", pcmk_rc_str(rc));
583 }
584 goto done;
585 }
586
587
588 pcmk__add_logfiles(options.log_files, out);
589
590 crm_log_init(NULL, LOG_INFO + args->verbosity, TRUE,
591 (args->verbosity > 0), argc, argv, FALSE);
592
593 crm_notice("Starting Pacemaker fencer");
594
595 old_instance = crm_ipc_new("stonith-ng", 0);
596 if (old_instance == NULL) {
597
598
599
600 exit_code = CRM_EX_FATAL;
601 goto done;
602 }
603
604 if (pcmk__connect_generic_ipc(old_instance) == pcmk_rc_ok) {
605
606 crm_ipc_close(old_instance);
607 crm_ipc_destroy(old_instance);
608 crm_err("pacemaker-fenced is already active, aborting startup");
609 goto done;
610 } else {
611
612 crm_ipc_destroy(old_instance);
613 old_instance = NULL;
614 }
615
616 mainloop_add_signal(SIGTERM, stonith_shutdown);
617
618 pcmk__cluster_init_node_caches();
619
620 rc = fenced_scheduler_init();
621 if (rc != pcmk_rc_ok) {
622 exit_code = CRM_EX_FATAL;
623 g_set_error(&error, PCMK__EXITC_ERROR, exit_code,
624 "Error initializing scheduler data: %s", pcmk_rc_str(rc));
625 goto done;
626 }
627
628 cluster = pcmk_cluster_new();
629
630 if (!stand_alone) {
631 #if SUPPORT_COROSYNC
632 if (pcmk_get_cluster_layer() == pcmk_cluster_layer_corosync) {
633 pcmk_cluster_set_destroy_fn(cluster, stonith_peer_cs_destroy);
634 pcmk_cpg_set_deliver_fn(cluster, stonith_peer_ais_callback);
635 pcmk_cpg_set_confchg_fn(cluster, pcmk__cpg_confchg_cb);
636 }
637 #endif
638
639 pcmk__cluster_set_status_callback(&st_peer_update_callback);
640
641 if (pcmk_cluster_connect(cluster) != pcmk_rc_ok) {
642 exit_code = CRM_EX_FATAL;
643 crm_crit("Cannot sign in to the cluster... terminating");
644 goto done;
645 }
646 pcmk__str_update(&stonith_our_uname, cluster->uname);
647
648 if (!options.no_cib_connect) {
649 setup_cib();
650 }
651
652 } else {
653 pcmk__str_update(&stonith_our_uname, "localhost");
654 crm_warn("Stand-alone mode is deprecated and will be removed "
655 "in a future release");
656 }
657
658 init_device_list();
659 init_topology_list();
660
661 pcmk__serve_fenced_ipc(&ipcs, &ipc_callbacks);
662
663
664 mainloop = g_main_loop_new(NULL, FALSE);
665 crm_notice("Pacemaker fencer successfully started and accepting connections");
666 g_main_loop_run(mainloop);
667
668 done:
669 g_strfreev(processed_args);
670 pcmk__free_arg_context(context);
671
672 g_strfreev(options.log_files);
673
674 stonith_cleanup();
675 pcmk_cluster_free(cluster);
676 fenced_scheduler_cleanup();
677
678 pcmk__output_and_clear_error(&error, out);
679
680 if (out != NULL) {
681 out->finish(out, exit_code, true, NULL);
682 pcmk__output_free(out);
683 }
684
685 pcmk__unregister_formats();
686 crm_exit(exit_code);
687 }