1 /*
2  * Copyright 2009-2019 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 <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 
23 #include <crm/crm.h>
24 #include <crm/msg_xml.h>
25 #include <crm/common/ipc.h>
26 #include <crm/common/ipcs.h>
27 #include <crm/cluster/internal.h>
28 
29 #include <crm/stonith-ng.h>
30 #include <crm/fencing/internal.h>
31 #include <crm/common/xml.h>
32 
33 #include <crm/common/mainloop.h>
34 
35 #include <crm/cib/internal.h>
36 #include <crm/pengine/status.h>
37 #include <allocate.h>
38 
39 #include <internal.h>
40 
41 #include <standalone_config.h>
42 
43 char *stonith_our_uname = NULL;
44 char *stonith_our_uuid = NULL;
45 long stonith_watchdog_timeout_ms = 0;
46 
47 GMainLoop *mainloop = NULL;
48 
49 gboolean stand_alone = FALSE;
50 gboolean no_cib_connect = FALSE;
51 gboolean stonith_shutdown_flag = FALSE;
52 
53 qb_ipcs_service_t *ipcs = NULL;
54 xmlNode *local_cib = NULL;
55 static pe_working_set_t *fenced_data_set = NULL;
56 
57 static cib_t *cib_api = NULL;
58 static void *cib_library = NULL;
59 
60 static void stonith_shutdown(int nsig);
61 static void stonith_cleanup(void);
62 
63 static int32_t
st_ipc_accept(qb_ipcs_connection_t * c,uid_t uid,gid_t gid)64 st_ipc_accept(qb_ipcs_connection_t * c, uid_t uid, gid_t gid)
65 {
66     if (stonith_shutdown_flag) {
67         crm_info("Ignoring new client [%d] during shutdown", crm_ipcs_client_pid(c));
68         return -EPERM;
69     }
70 
71     if (crm_client_new(c, uid, gid) == NULL) {
72         return -EIO;
73     }
74     return 0;
75 }
76 
77 static void
st_ipc_created(qb_ipcs_connection_t * c)78 st_ipc_created(qb_ipcs_connection_t * c)
79 {
80     crm_trace("Connection created for %p", c);
81 }
82 
83 /* Exit code means? */
84 static int32_t
st_ipc_dispatch(qb_ipcs_connection_t * qbc,void * data,size_t size)85 st_ipc_dispatch(qb_ipcs_connection_t * qbc, void *data, size_t size)
86 {
87     uint32_t id = 0;
88     uint32_t flags = 0;
89     int call_options = 0;
90     xmlNode *request = NULL;
91     crm_client_t *c = crm_client_get(qbc);
92     const char *op = NULL;
93 
94     if (c == NULL) {
95         crm_info("Invalid client: %p", qbc);
96         return 0;
97     }
98 
99     request = crm_ipcs_recv(c, data, size, &id, &flags);
100     if (request == NULL) {
101         crm_ipcs_send_ack(c, id, flags, "nack", __FUNCTION__, __LINE__);
102         return 0;
103     }
104 
105 
106     op = crm_element_value(request, F_CRM_TASK);
107     if(safe_str_eq(op, CRM_OP_RM_NODE_CACHE)) {
108         crm_xml_add(request, F_TYPE, T_STONITH_NG);
109         crm_xml_add(request, F_STONITH_OPERATION, op);
110         crm_xml_add(request, F_STONITH_CLIENTID, c->id);
111         crm_xml_add(request, F_STONITH_CLIENTNAME, crm_client_name(c));
112         crm_xml_add(request, F_STONITH_CLIENTNODE, stonith_our_uname);
113 
114         send_cluster_message(NULL, crm_msg_stonith_ng, request, FALSE);
115         free_xml(request);
116         return 0;
117     }
118 
119     if (c->name == NULL) {
120         const char *value = crm_element_value(request, F_STONITH_CLIENTNAME);
121 
122         if (value == NULL) {
123             value = "unknown";
124         }
125         c->name = crm_strdup_printf("%s.%u", value, c->pid);
126     }
127 
128     crm_element_value_int(request, F_STONITH_CALLOPTS, &call_options);
129     crm_trace("Flags %u/%u for command %u from %s", flags, call_options, id, crm_client_name(c));
130 
131     if (is_set(call_options, st_opt_sync_call)) {
132         CRM_ASSERT(flags & crm_ipc_client_response);
133         CRM_LOG_ASSERT(c->request_id == 0);     /* This means the client has two synchronous events in-flight */
134         c->request_id = id;     /* Reply only to the last one */
135     }
136 
137     crm_xml_add(request, F_STONITH_CLIENTID, c->id);
138     crm_xml_add(request, F_STONITH_CLIENTNAME, crm_client_name(c));
139     crm_xml_add(request, F_STONITH_CLIENTNODE, stonith_our_uname);
140 
141     crm_log_xml_trace(request, "Client[inbound]");
142     stonith_command(c, id, flags, request, NULL);
143 
144     free_xml(request);
145     return 0;
146 }
147 
148 /* Error code means? */
149 static int32_t
st_ipc_closed(qb_ipcs_connection_t * c)150 st_ipc_closed(qb_ipcs_connection_t * c)
151 {
152     crm_client_t *client = crm_client_get(c);
153 
154     if (client == NULL) {
155         return 0;
156     }
157 
158     crm_trace("Connection %p closed", c);
159     crm_client_destroy(client);
160 
161     /* 0 means: yes, go ahead and destroy the connection */
162     return 0;
163 }
164 
165 static void
st_ipc_destroy(qb_ipcs_connection_t * c)166 st_ipc_destroy(qb_ipcs_connection_t * c)
167 {
168     crm_trace("Connection %p destroyed", c);
169     st_ipc_closed(c);
170 }
171 
172 static void
stonith_peer_callback(xmlNode * msg,void * private_data)173 stonith_peer_callback(xmlNode * msg, void *private_data)
174 {
175     const char *remote_peer = crm_element_value(msg, F_ORIG);
176     const char *op = crm_element_value(msg, F_STONITH_OPERATION);
177 
178     if (crm_str_eq(op, "poke", TRUE)) {
179         return;
180     }
181 
182     crm_log_xml_trace(msg, "Peer[inbound]");
183     stonith_command(NULL, 0, 0, msg, remote_peer);
184 }
185 
186 #if SUPPORT_HEARTBEAT
187 static void
stonith_peer_hb_callback(HA_Message * msg,void * private_data)188 stonith_peer_hb_callback(HA_Message * msg, void *private_data)
189 {
190     xmlNode *xml = convert_ha_message(NULL, msg, __FUNCTION__);
191 
192     stonith_peer_callback(xml, private_data);
193     free_xml(xml);
194 }
195 
196 static void
stonith_peer_hb_destroy(gpointer user_data)197 stonith_peer_hb_destroy(gpointer user_data)
198 {
199     if (stonith_shutdown_flag) {
200         crm_info("Heartbeat disconnection complete... exiting");
201     } else {
202         crm_err("Heartbeat connection lost!  Exiting.");
203     }
204     stonith_shutdown(0);
205 }
206 #endif
207 
208 #if SUPPORT_COROSYNC
209 static void
stonith_peer_ais_callback(cpg_handle_t handle,const struct cpg_name * groupName,uint32_t nodeid,uint32_t pid,void * msg,size_t msg_len)210 stonith_peer_ais_callback(cpg_handle_t handle,
211                           const struct cpg_name *groupName,
212                           uint32_t nodeid, uint32_t pid, void *msg, size_t msg_len)
213 {
214     uint32_t kind = 0;
215     xmlNode *xml = NULL;
216     const char *from = NULL;
217     char *data = pcmk_message_common_cs(handle, nodeid, pid, msg, &kind, &from);
218 
219     if(data == NULL) {
220         return;
221     }
222     if (kind == crm_class_cluster) {
223         xml = string2xml(data);
224         if (xml == NULL) {
225             crm_err("Invalid XML: '%.120s'", data);
226             free(data);
227             return;
228         }
229         crm_xml_add(xml, F_ORIG, from);
230         /* crm_xml_add_int(xml, F_SEQ, wrapper->id); */
231         stonith_peer_callback(xml, NULL);
232     }
233 
234     free_xml(xml);
235     free(data);
236     return;
237 }
238 
239 static void
stonith_peer_cs_destroy(gpointer user_data)240 stonith_peer_cs_destroy(gpointer user_data)
241 {
242     crm_err("Corosync connection terminated");
243     stonith_shutdown(0);
244 }
245 #endif
246 
247 void
do_local_reply(xmlNode * notify_src,const char * client_id,gboolean sync_reply,gboolean from_peer)248 do_local_reply(xmlNode * notify_src, const char *client_id, gboolean sync_reply, gboolean from_peer)
249 {
250     /* send callback to originating child */
251     crm_client_t *client_obj = NULL;
252     int local_rc = pcmk_ok;
253 
254     crm_trace("Sending response");
255     client_obj = crm_client_get_by_id(client_id);
256 
257     crm_trace("Sending callback to request originator");
258     if (client_obj == NULL) {
259         local_rc = -1;
260         crm_trace("No client to sent the response to.  F_STONITH_CLIENTID not set.");
261 
262     } else {
263         int rid = 0;
264 
265         if (sync_reply) {
266             CRM_LOG_ASSERT(client_obj->request_id);
267 
268             rid = client_obj->request_id;
269             client_obj->request_id = 0;
270 
271             crm_trace("Sending response %d to %s %s",
272                       rid, client_obj->name, from_peer ? "(originator of delegated request)" : "");
273 
274         } else {
275             crm_trace("Sending an event to %s %s",
276                       client_obj->name, from_peer ? "(originator of delegated request)" : "");
277         }
278 
279         local_rc = crm_ipcs_send(client_obj, rid, notify_src, sync_reply?crm_ipc_flags_none:crm_ipc_server_event);
280     }
281 
282     if (local_rc < pcmk_ok && client_obj != NULL) {
283         crm_warn("%sSync reply to %s failed: %s",
284                  sync_reply ? "" : "A-",
285                  client_obj ? client_obj->name : "<unknown>", pcmk_strerror(local_rc));
286     }
287 }
288 
289 long long
get_stonith_flag(const char * name)290 get_stonith_flag(const char *name)
291 {
292     if (safe_str_eq(name, T_STONITH_NOTIFY_FENCE)) {
293         return st_callback_notify_fence;
294 
295     } else if (safe_str_eq(name, STONITH_OP_DEVICE_ADD)) {
296         return st_callback_device_add;
297 
298     } else if (safe_str_eq(name, STONITH_OP_DEVICE_DEL)) {
299         return st_callback_device_del;
300 
301     } else if (safe_str_eq(name, T_STONITH_NOTIFY_HISTORY)) {
302         return st_callback_notify_history;
303 
304     } else if (safe_str_eq(name, T_STONITH_NOTIFY_HISTORY_SYNCED)) {
305         return st_callback_notify_history_synced;
306 
307     }
308     return st_callback_unknown;
309 }
310 
311 static void
stonith_notify_client(gpointer key,gpointer value,gpointer user_data)312 stonith_notify_client(gpointer key, gpointer value, gpointer user_data)
313 {
314 
315     xmlNode *update_msg = user_data;
316     crm_client_t *client = value;
317     const char *type = NULL;
318 
319     CRM_CHECK(client != NULL, return);
320     CRM_CHECK(update_msg != NULL, return);
321 
322     type = crm_element_value(update_msg, F_SUBTYPE);
323     CRM_CHECK(type != NULL, crm_log_xml_err(update_msg, "notify"); return);
324 
325     if (client->ipcs == NULL) {
326         crm_trace("Skipping client with NULL channel");
327         return;
328     }
329 
330     if (client->options & get_stonith_flag(type)) {
331         int rc = crm_ipcs_send(client, 0, update_msg, crm_ipc_server_event | crm_ipc_server_error);
332 
333         if (rc <= 0) {
334             crm_warn("%s notification of client %s.%.6s failed: %s (%d)",
335                      type, crm_client_name(client), client->id, pcmk_strerror(rc), rc);
336         } else {
337             crm_trace("Sent %s notification to client %s.%.6s", type, crm_client_name(client),
338                       client->id);
339         }
340     }
341 }
342 
343 void
do_stonith_async_timeout_update(const char * client_id,const char * call_id,int timeout)344 do_stonith_async_timeout_update(const char *client_id, const char *call_id, int timeout)
345 {
346     crm_client_t *client = NULL;
347     xmlNode *notify_data = NULL;
348 
349     if (!timeout || !call_id || !client_id) {
350         return;
351     }
352 
353     client = crm_client_get_by_id(client_id);
354     if (!client) {
355         return;
356     }
357 
358     notify_data = create_xml_node(NULL, T_STONITH_TIMEOUT_VALUE);
359     crm_xml_add(notify_data, F_TYPE, T_STONITH_TIMEOUT_VALUE);
360     crm_xml_add(notify_data, F_STONITH_CALLID, call_id);
361     crm_xml_add_int(notify_data, F_STONITH_TIMEOUT, timeout);
362 
363     crm_trace("timeout update is %d for client %s and call id %s", timeout, client_id, call_id);
364 
365     if (client) {
366         crm_ipcs_send(client, 0, notify_data, crm_ipc_server_event);
367     }
368 
369     free_xml(notify_data);
370 }
371 
372 void
do_stonith_notify(int options,const char * type,int result,xmlNode * data)373 do_stonith_notify(int options, const char *type, int result, xmlNode * data)
374 {
375     /* TODO: Standardize the contents of data */
376     xmlNode *update_msg = create_xml_node(NULL, "notify");
377 
378     CRM_CHECK(type != NULL,;);
379 
380     crm_xml_add(update_msg, F_TYPE, T_STONITH_NOTIFY);
381     crm_xml_add(update_msg, F_SUBTYPE, type);
382     crm_xml_add(update_msg, F_STONITH_OPERATION, type);
383     crm_xml_add_int(update_msg, F_STONITH_RC, result);
384 
385     if (data != NULL) {
386         add_message_xml(update_msg, F_STONITH_CALLDATA, data);
387     }
388 
389     crm_trace("Notifying clients");
390     g_hash_table_foreach(client_connections, stonith_notify_client, update_msg);
391     free_xml(update_msg);
392     crm_trace("Notify complete");
393 }
394 
395 static void
do_stonith_notify_config(int options,const char * op,int rc,const char * desc,int active)396 do_stonith_notify_config(int options, const char *op, int rc,
397                          const char *desc, int active)
398 {
399     xmlNode *notify_data = create_xml_node(NULL, op);
400 
401     CRM_CHECK(notify_data != NULL, return);
402 
403     crm_xml_add(notify_data, F_STONITH_DEVICE, desc);
404     crm_xml_add_int(notify_data, F_STONITH_ACTIVE, active);
405 
406     do_stonith_notify(options, op, rc, notify_data);
407     free_xml(notify_data);
408 }
409 
410 void
do_stonith_notify_device(int options,const char * op,int rc,const char * desc)411 do_stonith_notify_device(int options, const char *op, int rc, const char *desc)
412 {
413     do_stonith_notify_config(options, op, rc, desc, g_hash_table_size(device_list));
414 }
415 
416 void
do_stonith_notify_level(int options,const char * op,int rc,const char * desc)417 do_stonith_notify_level(int options, const char *op, int rc, const char *desc)
418 {
419     do_stonith_notify_config(options, op, rc, desc, g_hash_table_size(topology));
420 }
421 
422 static void
topology_remove_helper(const char * node,int level)423 topology_remove_helper(const char *node, int level)
424 {
425     int rc;
426     char *desc = NULL;
427     xmlNode *data = create_xml_node(NULL, XML_TAG_FENCING_LEVEL);
428 
429     crm_xml_add(data, F_STONITH_ORIGIN, __FUNCTION__);
430     crm_xml_add_int(data, XML_ATTR_STONITH_INDEX, level);
431     crm_xml_add(data, XML_ATTR_STONITH_TARGET, node);
432 
433     rc = stonith_level_remove(data, &desc);
434     do_stonith_notify_level(0, STONITH_OP_LEVEL_DEL, rc, desc);
435 
436     free_xml(data);
437     free(desc);
438 }
439 
440 static void
remove_cib_device(xmlXPathObjectPtr xpathObj)441 remove_cib_device(xmlXPathObjectPtr xpathObj)
442 {
443     int max = numXpathResults(xpathObj), lpc = 0;
444 
445     for (lpc = 0; lpc < max; lpc++) {
446         const char *rsc_id = NULL;
447         const char *standard = NULL;
448         xmlNode *match = getXpathResult(xpathObj, lpc);
449 
450         CRM_LOG_ASSERT(match != NULL);
451         if(match != NULL) {
452             standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
453         }
454 
455         if (safe_str_neq(standard, PCMK_RESOURCE_CLASS_STONITH)) {
456             continue;
457         }
458 
459         rsc_id = crm_element_value(match, XML_ATTR_ID);
460 
461         stonith_device_remove(rsc_id, TRUE);
462     }
463 }
464 
465 static void
handle_topology_change(xmlNode * match,bool remove)466 handle_topology_change(xmlNode *match, bool remove)
467 {
468     int rc;
469     char *desc = NULL;
470 
471     CRM_CHECK(match != NULL, return);
472     crm_trace("Updating %s", ID(match));
473 
474     if(remove) {
475         int index = 0;
476         char *key = stonith_level_key(match, -1);
477 
478         crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
479         topology_remove_helper(key, index);
480         free(key);
481     }
482 
483     rc = stonith_level_register(match, &desc);
484     do_stonith_notify_level(0, STONITH_OP_LEVEL_ADD, rc, desc);
485 
486     free(desc);
487 }
488 
489 static void
remove_fencing_topology(xmlXPathObjectPtr xpathObj)490 remove_fencing_topology(xmlXPathObjectPtr xpathObj)
491 {
492     int max = numXpathResults(xpathObj), lpc = 0;
493 
494     for (lpc = 0; lpc < max; lpc++) {
495         xmlNode *match = getXpathResult(xpathObj, lpc);
496 
497         CRM_LOG_ASSERT(match != NULL);
498         if (match && crm_element_value(match, XML_DIFF_MARKER)) {
499             /* Deletion */
500             int index = 0;
501             char *target = stonith_level_key(match, -1);
502 
503             crm_element_value_int(match, XML_ATTR_STONITH_INDEX, &index);
504             if (target == NULL) {
505                 crm_err("Invalid fencing target in element %s", ID(match));
506 
507             } else if (index <= 0) {
508                 crm_err("Invalid level for %s in element %s", target, ID(match));
509 
510             } else {
511                 topology_remove_helper(target, index);
512             }
513             /* } else { Deal with modifications during the 'addition' stage */
514         }
515     }
516 }
517 
518 static void
register_fencing_topology(xmlXPathObjectPtr xpathObj)519 register_fencing_topology(xmlXPathObjectPtr xpathObj)
520 {
521     int max = numXpathResults(xpathObj), lpc = 0;
522 
523     for (lpc = 0; lpc < max; lpc++) {
524         xmlNode *match = getXpathResult(xpathObj, lpc);
525 
526         handle_topology_change(match, TRUE);
527     }
528 }
529 
530 /* Fencing
531 <diff crm_feature_set="3.0.6">
532   <diff-removed>
533     <fencing-topology>
534       <fencing-level id="f-p1.1" target="pcmk-1" index="1" devices="poison-pill" __crm_diff_marker__="removed:top"/>
535       <fencing-level id="f-p1.2" target="pcmk-1" index="2" devices="power" __crm_diff_marker__="removed:top"/>
536       <fencing-level devices="disk,network" id="f-p2.1"/>
537     </fencing-topology>
538   </diff-removed>
539   <diff-added>
540     <fencing-topology>
541       <fencing-level id="f-p.1" target="pcmk-1" index="1" devices="poison-pill" __crm_diff_marker__="added:top"/>
542       <fencing-level id="f-p2.1" target="pcmk-2" index="1" devices="disk,something"/>
543       <fencing-level id="f-p3.1" target="pcmk-2" index="2" devices="power" __crm_diff_marker__="added:top"/>
544     </fencing-topology>
545   </diff-added>
546 </diff>
547 */
548 
549 static void
fencing_topology_init()550 fencing_topology_init()
551 {
552     xmlXPathObjectPtr xpathObj = NULL;
553     const char *xpath = "//" XML_TAG_FENCING_LEVEL;
554 
555     crm_trace("Full topology refresh");
556 
557     if(topology) {
558         g_hash_table_destroy(topology);
559         topology = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_topology_entry);
560     }
561 
562     /* Grab everything */
563     xpathObj = xpath_search(local_cib, xpath);
564     register_fencing_topology(xpathObj);
565 
566     freeXpathObject(xpathObj);
567 }
568 
569 #define rsc_name(x) x->clone_name?x->clone_name:x->id
570 
571 /*!
572  * \internal
573  * \brief Check whether our uname is in a resource's allowed node list
574  *
575  * \param[in] rsc  Resource to check
576  *
577  * \return Pointer to node object if found, NULL otherwise
578  */
579 static node_t *
our_node_allowed_for(resource_t * rsc)580 our_node_allowed_for(resource_t *rsc)
581 {
582     GHashTableIter iter;
583     node_t *node = NULL;
584 
585     if (rsc && stonith_our_uname) {
586         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
587         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
588             if (node && strcmp(node->details->uname, stonith_our_uname) == 0) {
589                 break;
590             }
591             node = NULL;
592         }
593     }
594     return node;
595 }
596 
597 /*!
598  * \internal
599  * \brief If a resource or any of its children are STONITH devices, update their
600  *        definitions given a cluster working set.
601  *
602  * \param[in] rsc       Resource to check
603  * \param[in] data_set  Cluster working set with device information
604  */
cib_device_update(resource_t * rsc,pe_working_set_t * data_set)605 static void cib_device_update(resource_t *rsc, pe_working_set_t *data_set)
606 {
607     node_t *node = NULL;
608     const char *value = NULL;
609     const char *rclass = NULL;
610     node_t *parent = NULL;
611     gboolean remove = TRUE;
612 
613     /* If this is a complex resource, check children rather than this resource itself.
614      * TODO: Mark each installed device and remove if untouched when this process finishes.
615      */
616     if(rsc->children) {
617         GListPtr gIter = NULL;
618         for (gIter = rsc->children; gIter != NULL; gIter = gIter->next) {
619             cib_device_update(gIter->data, data_set);
620             if(pe_rsc_is_clone(rsc)) {
621                 crm_trace("Only processing one copy of the clone %s", rsc->id);
622                 break;
623             }
624         }
625         return;
626     }
627 
628     /* We only care about STONITH resources. */
629     rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
630     if (safe_str_neq(rclass, PCMK_RESOURCE_CLASS_STONITH)) {
631         return;
632     }
633 
634     /* If this STONITH resource is disabled, just remove it. */
635     if (pe__resource_is_disabled(rsc)) {
636         crm_info("Device %s has been disabled", rsc->id);
637         goto update_done;
638     }
639 
640     /* Check whether our node is allowed for this resource (and its parent if in a group) */
641     node = our_node_allowed_for(rsc);
642     if (rsc->parent && (rsc->parent->variant == pe_group)) {
643         parent = our_node_allowed_for(rsc->parent);
644     }
645 
646     if(node == NULL) {
647         /* Our node is disallowed, so remove the device */
648         GHashTableIter iter;
649 
650         crm_info("Device %s has been disabled on %s: unknown", rsc->id, stonith_our_uname);
651         g_hash_table_iter_init(&iter, rsc->allowed_nodes);
652         while (g_hash_table_iter_next(&iter, NULL, (void **)&node)) {
653             crm_trace("Available: %s = %d", node->details->uname, node->weight);
654         }
655 
656         goto update_done;
657 
658     } else if(node->weight < 0 || (parent && parent->weight < 0)) {
659         /* Our node (or its group) is disallowed by score, so remove the device */
660         char *score = score2char((node->weight < 0) ? node->weight : parent->weight);
661 
662         crm_info("Device %s has been disabled on %s: score=%s", rsc->id, stonith_our_uname, score);
663         free(score);
664 
665         goto update_done;
666 
667     } else {
668         /* Our node is allowed, so update the device information */
669         xmlNode *data;
670         GHashTableIter gIter;
671         stonith_key_value_t *params = NULL;
672 
673         const char *name = NULL;
674         const char *agent = crm_element_value(rsc->xml, XML_EXPR_ATTR_TYPE);
675         const char *rsc_provides = NULL;
676 
677         crm_debug("Device %s is allowed on %s: score=%d", rsc->id, stonith_our_uname, node->weight);
678         get_rsc_attributes(rsc->parameters, rsc, node, data_set);
679         get_meta_attributes(rsc->meta, rsc, node, data_set);
680 
681         rsc_provides = g_hash_table_lookup(rsc->meta, XML_RSC_ATTR_PROVIDES);
682 
683         g_hash_table_iter_init(&gIter, rsc->parameters);
684         while (g_hash_table_iter_next(&gIter, (gpointer *) & name, (gpointer *) & value)) {
685             if (!name || !value) {
686                 continue;
687             }
688             params = stonith_key_value_add(params, name, value);
689             crm_trace(" %s=%s", name, value);
690         }
691 
692         remove = FALSE;
693         data = create_device_registration_xml(rsc_name(rsc), st_namespace_any,
694                                               agent, params, rsc_provides);
695         stonith_device_register(data, NULL, TRUE);
696 
697         stonith_key_value_freeall(params, 1, 1);
698         free_xml(data);
699     }
700 
701 update_done:
702 
703     if(remove && g_hash_table_lookup(device_list, rsc_name(rsc))) {
704         stonith_device_remove(rsc_name(rsc), TRUE);
705     }
706 }
707 
708 extern xmlNode *do_calculations(pe_working_set_t * data_set, xmlNode * xml_input, crm_time_t * now);
709 
710 /*!
711  * \internal
712  * \brief Update all STONITH device definitions based on current CIB
713  */
714 static void
cib_devices_update(void)715 cib_devices_update(void)
716 {
717     GListPtr gIter = NULL;
718 
719     crm_info("Updating devices to version %s.%s.%s",
720              crm_element_value(local_cib, XML_ATTR_GENERATION_ADMIN),
721              crm_element_value(local_cib, XML_ATTR_GENERATION),
722              crm_element_value(local_cib, XML_ATTR_NUMUPDATES));
723 
724     CRM_ASSERT(fenced_data_set != NULL);
725     fenced_data_set->input = local_cib;
726     fenced_data_set->now = crm_time_new(NULL);
727     fenced_data_set->flags |= pe_flag_quick_location;
728     fenced_data_set->localhost = stonith_our_uname;
729 
730     cluster_status(fenced_data_set);
731     do_calculations(fenced_data_set, NULL, NULL);
732 
733     for (gIter = fenced_data_set->resources; gIter != NULL; gIter = gIter->next) {
734         cib_device_update(gIter->data, fenced_data_set);
735     }
736     fenced_data_set->input = NULL; // Wasn't a copy, so don't let API free it
737     pe_reset_working_set(fenced_data_set);
738 }
739 
740 static void
update_cib_stonith_devices_v2(const char * event,xmlNode * msg)741 update_cib_stonith_devices_v2(const char *event, xmlNode * msg)
742 {
743     xmlNode *change = NULL;
744     char *reason = NULL;
745     bool needs_update = FALSE;
746     xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
747 
748     for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
749         const char *op = crm_element_value(change, XML_DIFF_OP);
750         const char *xpath = crm_element_value(change, XML_DIFF_PATH);
751         const char *shortpath = NULL;
752 
753         if ((op == NULL) ||
754             (strcmp(op, "move") == 0) ||
755             strstr(xpath, "/"XML_CIB_TAG_STATUS)) {
756             continue;
757         } else if (safe_str_eq(op, "delete") && strstr(xpath, "/"XML_CIB_TAG_RESOURCE)) {
758             const char *rsc_id = NULL;
759             char *search = NULL;
760             char *mutable = NULL;
761 
762             if (strstr(xpath, XML_TAG_ATTR_SETS) ||
763                 strstr(xpath, XML_TAG_META_SETS)) {
764                 needs_update = TRUE;
765                 reason = strdup("(meta) attribute deleted from resource");
766                 break;
767             }
768             mutable = strdup(xpath);
769             rsc_id = strstr(mutable, "primitive[@id=\'");
770             if (rsc_id != NULL) {
771                 rsc_id += strlen("primitive[@id=\'");
772                 search = strchr(rsc_id, '\'');
773             }
774             if (search != NULL) {
775                 *search = 0;
776                 stonith_device_remove(rsc_id, TRUE);
777             } else {
778                 crm_warn("Ignoring malformed CIB update (resource deletion)");
779             }
780             free(mutable);
781 
782         } else if (strstr(xpath, "/"XML_CIB_TAG_RESOURCES) ||
783                    strstr(xpath, "/"XML_CIB_TAG_CONSTRAINTS) ||
784                    strstr(xpath, "/"XML_CIB_TAG_RSCCONFIG)) {
785             shortpath = strrchr(xpath, '/'); CRM_ASSERT(shortpath);
786             reason = crm_strdup_printf("%s %s", op, shortpath+1);
787             needs_update = TRUE;
788             break;
789         }
790     }
791 
792     if(needs_update) {
793         crm_info("Updating device list from the cib: %s", reason);
794         cib_devices_update();
795     } else {
796         crm_trace("No updates for device list found in cib");
797     }
798     free(reason);
799 }
800 
801 
802 static void
update_cib_stonith_devices_v1(const char * event,xmlNode * msg)803 update_cib_stonith_devices_v1(const char *event, xmlNode * msg)
804 {
805     const char *reason = "none";
806     gboolean needs_update = FALSE;
807     xmlXPathObjectPtr xpath_obj = NULL;
808 
809     /* process new constraints */
810     xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_CONS_TAG_RSC_LOCATION);
811     if (numXpathResults(xpath_obj) > 0) {
812         int max = numXpathResults(xpath_obj), lpc = 0;
813 
814         /* Safest and simplest to always recompute */
815         needs_update = TRUE;
816         reason = "new location constraint";
817 
818         for (lpc = 0; lpc < max; lpc++) {
819             xmlNode *match = getXpathResult(xpath_obj, lpc);
820 
821             crm_log_xml_trace(match, "new constraint");
822         }
823     }
824     freeXpathObject(xpath_obj);
825 
826     /* process deletions */
827     xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_CIB_TAG_RESOURCE);
828     if (numXpathResults(xpath_obj) > 0) {
829         remove_cib_device(xpath_obj);
830     }
831     freeXpathObject(xpath_obj);
832 
833     /* process additions */
834     xpath_obj = xpath_search(msg, "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_CIB_TAG_RESOURCE);
835     if (numXpathResults(xpath_obj) > 0) {
836         int max = numXpathResults(xpath_obj), lpc = 0;
837 
838         for (lpc = 0; lpc < max; lpc++) {
839             const char *rsc_id = NULL;
840             const char *standard = NULL;
841             xmlNode *match = getXpathResult(xpath_obj, lpc);
842 
843             rsc_id = crm_element_value(match, XML_ATTR_ID);
844             standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
845 
846             if (safe_str_neq(standard, PCMK_RESOURCE_CLASS_STONITH)) {
847                 continue;
848             }
849 
850             crm_trace("Fencing resource %s was added or modified", rsc_id);
851             reason = "new resource";
852             needs_update = TRUE;
853         }
854     }
855     freeXpathObject(xpath_obj);
856 
857     if(needs_update) {
858         crm_info("Updating device list from the cib: %s", reason);
859         cib_devices_update();
860     }
861 }
862 
863 static void
update_cib_stonith_devices(const char * event,xmlNode * msg)864 update_cib_stonith_devices(const char *event, xmlNode * msg)
865 {
866     int format = 1;
867     xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
868 
869     CRM_ASSERT(patchset);
870     crm_element_value_int(patchset, "format", &format);
871     switch(format) {
872         case 1:
873             update_cib_stonith_devices_v1(event, msg);
874             break;
875         case 2:
876             update_cib_stonith_devices_v2(event, msg);
877             break;
878         default:
879             crm_warn("Unknown patch format: %d", format);
880     }
881 }
882 
883 /* Needs to hold node name + attribute name + attribute value + 75 */
884 #define XPATH_MAX 512
885 
886 /*!
887  * \internal
888  * \brief Check whether a node has a specific attribute name/value
889  *
890  * \param[in] node    Name of node to check
891  * \param[in] name    Name of an attribute to look for
892  * \param[in] value   The value the named attribute needs to be set to in order to be considered a match
893  *
894  * \return TRUE if the locally cached CIB has the specified node attribute
895  */
896 gboolean
node_has_attr(const char * node,const char * name,const char * value)897 node_has_attr(const char *node, const char *name, const char *value)
898 {
899     char xpath[XPATH_MAX];
900     xmlNode *match;
901     int n;
902 
903     CRM_CHECK(local_cib != NULL, return FALSE);
904 
905     /* Search for the node's attributes in the CIB. While the schema allows
906      * multiple sets of instance attributes, and allows instance attributes to
907      * use id-ref to reference values elsewhere, that is intended for resources,
908      * so we ignore that here.
909      */
910     n = snprintf(xpath, XPATH_MAX, "//" XML_CIB_TAG_NODES
911                  "/" XML_CIB_TAG_NODE "[@uname='%s']/" XML_TAG_ATTR_SETS
912                  "/" XML_CIB_TAG_NVPAIR "[@name='%s' and @value='%s']",
913                  node, name, value);
914     match = get_xpath_object(xpath, local_cib, LOG_TRACE);
915 
916     CRM_CHECK(n < XPATH_MAX, return FALSE);
917     return (match != NULL);
918 }
919 
920 static void
update_fencing_topology(const char * event,xmlNode * msg)921 update_fencing_topology(const char *event, xmlNode * msg)
922 {
923     int format = 1;
924     const char *xpath;
925     xmlXPathObjectPtr xpathObj = NULL;
926     xmlNode *patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
927 
928     CRM_ASSERT(patchset);
929     crm_element_value_int(patchset, "format", &format);
930 
931     if(format == 1) {
932         /* Process deletions (only) */
933         xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_REMOVED "//" XML_TAG_FENCING_LEVEL;
934         xpathObj = xpath_search(msg, xpath);
935 
936         remove_fencing_topology(xpathObj);
937         freeXpathObject(xpathObj);
938 
939         /* Process additions and changes */
940         xpath = "//" F_CIB_UPDATE_RESULT "//" XML_TAG_DIFF_ADDED "//" XML_TAG_FENCING_LEVEL;
941         xpathObj = xpath_search(msg, xpath);
942 
943         register_fencing_topology(xpathObj);
944         freeXpathObject(xpathObj);
945 
946     } else if(format == 2) {
947         xmlNode *change = NULL;
948         int add[] = { 0, 0, 0 };
949         int del[] = { 0, 0, 0 };
950 
951         xml_patch_versions(patchset, add, del);
952 
953         for (change = __xml_first_child(patchset); change != NULL; change = __xml_next(change)) {
954             const char *op = crm_element_value(change, XML_DIFF_OP);
955             const char *xpath = crm_element_value(change, XML_DIFF_PATH);
956 
957             if(op == NULL) {
958                 continue;
959 
960             } else if(strstr(xpath, "/" XML_TAG_FENCING_LEVEL) != NULL) {
961                 /* Change to a specific entry */
962 
963                 crm_trace("Handling %s operation %d.%d.%d for %s", op, add[0], add[1], add[2], xpath);
964                 if(strcmp(op, "move") == 0) {
965                     continue;
966 
967                 } else if(strcmp(op, "create") == 0) {
968                     handle_topology_change(change->children, FALSE);
969 
970                 } else if(strcmp(op, "modify") == 0) {
971                     xmlNode *match = first_named_child(change, XML_DIFF_RESULT);
972 
973                     if(match) {
974                         handle_topology_change(match->children, TRUE);
975                     }
976 
977                 } else if(strcmp(op, "delete") == 0) {
978                     /* Nuclear option, all we have is the path and an id... not enough to remove a specific entry */
979                     crm_info("Re-initializing fencing topology after %s operation %d.%d.%d for %s",
980                              op, add[0], add[1], add[2], xpath);
981                     fencing_topology_init();
982                     return;
983                 }
984 
985             } else if (strstr(xpath, "/" XML_TAG_FENCING_TOPOLOGY) != NULL) {
986                 /* Change to the topology in general */
987                 crm_info("Re-initializing fencing topology after top-level %s operation  %d.%d.%d for %s",
988                          op, add[0], add[1], add[2], xpath);
989                 fencing_topology_init();
990                 return;
991 
992             } else if (strstr(xpath, "/" XML_CIB_TAG_CONFIGURATION)) {
993                 /* Changes to the whole config section, possibly including the topology as a whild */
994                 if(first_named_child(change, XML_TAG_FENCING_TOPOLOGY) == NULL) {
995                     crm_trace("Nothing for us in %s operation %d.%d.%d for %s.",
996                               op, add[0], add[1], add[2], xpath);
997 
998                 } else if(strcmp(op, "delete") == 0 || strcmp(op, "create") == 0) {
999                     crm_info("Re-initializing fencing topology after top-level %s operation %d.%d.%d for %s.",
1000                              op, add[0], add[1], add[2], xpath);
1001                     fencing_topology_init();
1002                     return;
1003                 }
1004 
1005             } else {
1006                 crm_trace("Nothing for us in %s operation %d.%d.%d for %s",
1007                           op, add[0], add[1], add[2], xpath);
1008             }
1009         }
1010 
1011     } else {
1012         crm_warn("Unknown patch format: %d", format);
1013     }
1014 }
1015 static bool have_cib_devices = FALSE;
1016 
1017 static void
update_cib_cache_cb(const char * event,xmlNode * msg)1018 update_cib_cache_cb(const char *event, xmlNode * msg)
1019 {
1020     int rc = pcmk_ok;
1021     xmlNode *stonith_enabled_xml = NULL;
1022     xmlNode *stonith_watchdog_xml = NULL;
1023     const char *stonith_enabled_s = NULL;
1024     static gboolean stonith_enabled_saved = TRUE;
1025 
1026     if(!have_cib_devices) {
1027         crm_trace("Skipping updates until we get a full dump");
1028         return;
1029 
1030     } else if(msg == NULL) {
1031         crm_trace("Missing %s update", event);
1032         return;
1033     }
1034 
1035     /* Maintain a local copy of the CIB so that we have full access
1036      * to device definitions, location constraints, and node attributes
1037      */
1038     if (local_cib != NULL) {
1039         int rc = pcmk_ok;
1040         xmlNode *patchset = NULL;
1041 
1042         crm_element_value_int(msg, F_CIB_RC, &rc);
1043         if (rc != pcmk_ok) {
1044             return;
1045         }
1046 
1047         patchset = get_message_xml(msg, F_CIB_UPDATE_RESULT);
1048         xml_log_patchset(LOG_TRACE, "Config update", patchset);
1049         rc = xml_apply_patchset(local_cib, patchset, TRUE);
1050         switch (rc) {
1051             case pcmk_ok:
1052             case -pcmk_err_old_data:
1053                 break;
1054             case -pcmk_err_diff_resync:
1055             case -pcmk_err_diff_failed:
1056                 crm_notice("[%s] Patch aborted: %s (%d)", event, pcmk_strerror(rc), rc);
1057                 free_xml(local_cib);
1058                 local_cib = NULL;
1059                 break;
1060             default:
1061                 crm_warn("[%s] ABORTED: %s (%d)", event, pcmk_strerror(rc), rc);
1062                 free_xml(local_cib);
1063                 local_cib = NULL;
1064         }
1065     }
1066 
1067     if (local_cib == NULL) {
1068         crm_trace("Re-requesting the full cib");
1069         rc = cib_api->cmds->query(cib_api, NULL, &local_cib, cib_scope_local | cib_sync_call);
1070         if(rc != pcmk_ok) {
1071             crm_err("Couldn't retrieve the CIB: %s (%d)", pcmk_strerror(rc), rc);
1072             return;
1073         }
1074         CRM_ASSERT(local_cib != NULL);
1075         stonith_enabled_saved = FALSE; /* Trigger a full refresh below */
1076     }
1077 
1078     crm_peer_caches_refresh(local_cib);
1079 
1080     stonith_enabled_xml = get_xpath_object("//nvpair[@name='stonith-enabled']", local_cib, LOG_TRACE);
1081     if (stonith_enabled_xml) {
1082         stonith_enabled_s = crm_element_value(stonith_enabled_xml, XML_NVPAIR_ATTR_VALUE);
1083     }
1084 
1085     if (stonith_enabled_s == NULL || crm_is_true(stonith_enabled_s)) {
1086         long timeout_ms = 0;
1087         const char *value = NULL;
1088 
1089         stonith_watchdog_xml = get_xpath_object("//nvpair[@name='stonith-watchdog-timeout']", local_cib, LOG_TRACE);
1090         if (stonith_watchdog_xml) {
1091             value = crm_element_value(stonith_watchdog_xml, XML_NVPAIR_ATTR_VALUE);
1092         }
1093 
1094         if(value) {
1095             timeout_ms = crm_get_msec(value);
1096         }
1097 
1098         if(timeout_ms != stonith_watchdog_timeout_ms) {
1099             crm_notice("New watchdog timeout %lds (was %lds)", timeout_ms/1000, stonith_watchdog_timeout_ms/1000);
1100             stonith_watchdog_timeout_ms = timeout_ms;
1101         }
1102 
1103     } else {
1104         stonith_watchdog_timeout_ms = 0;
1105     }
1106 
1107     if (stonith_enabled_s && crm_is_true(stonith_enabled_s) == FALSE) {
1108         crm_trace("Ignoring cib updates while stonith is disabled");
1109         stonith_enabled_saved = FALSE;
1110         return;
1111 
1112     } else if (stonith_enabled_saved == FALSE) {
1113         crm_info("Updating stonith device and topology lists now that stonith is enabled");
1114         stonith_enabled_saved = TRUE;
1115         fencing_topology_init();
1116         cib_devices_update();
1117 
1118     } else {
1119         update_fencing_topology(event, msg);
1120         update_cib_stonith_devices(event, msg);
1121     }
1122 }
1123 
1124 static void
init_cib_cache_cb(xmlNode * msg,int call_id,int rc,xmlNode * output,void * user_data)1125 init_cib_cache_cb(xmlNode * msg, int call_id, int rc, xmlNode * output, void *user_data)
1126 {
1127     crm_info("Updating device list from the cib: init");
1128     have_cib_devices = TRUE;
1129     local_cib = copy_xml(output);
1130 
1131     crm_peer_caches_refresh(local_cib);
1132 
1133     fencing_topology_init();
1134     cib_devices_update();
1135 }
1136 
1137 static void
stonith_shutdown(int nsig)1138 stonith_shutdown(int nsig)
1139 {
1140     stonith_shutdown_flag = TRUE;
1141     crm_info("Terminating with %d clients",
1142              crm_hash_table_size(client_connections));
1143     if (mainloop != NULL && g_main_is_running(mainloop)) {
1144         g_main_quit(mainloop);
1145     } else {
1146         stonith_cleanup();
1147         crm_exit(pcmk_ok);
1148     }
1149 }
1150 
1151 static void
cib_connection_destroy(gpointer user_data)1152 cib_connection_destroy(gpointer user_data)
1153 {
1154     if (stonith_shutdown_flag) {
1155         crm_info("Connection to the CIB closed.");
1156         return;
1157     } else {
1158         crm_notice("Connection to the CIB terminated. Shutting down.");
1159     }
1160     if (cib_api) {
1161         cib_api->cmds->signoff(cib_api);
1162     }
1163     stonith_shutdown(0);
1164 }
1165 
1166 static void
stonith_cleanup(void)1167 stonith_cleanup(void)
1168 {
1169     if (cib_api) {
1170         cib_api->cmds->signoff(cib_api);
1171     }
1172 
1173     if (ipcs) {
1174         qb_ipcs_destroy(ipcs);
1175     }
1176 
1177     crm_peer_destroy();
1178     crm_client_cleanup();
1179     free_stonith_remote_op_list();
1180     free(stonith_our_uname);
1181     free_xml(local_cib);
1182 }
1183 
1184 /* *INDENT-OFF* */
1185 static struct crm_option long_options[] = {
1186     {"stand-alone",         0, 0, 's'},
1187     {"stand-alone-w-cpg",   0, 0, 'c'},
1188     {"logfile",             1, 0, 'l'},
1189     {"verbose",     0, 0, 'V'},
1190     {"version",     0, 0, '$'},
1191     {"help",        0, 0, '?'},
1192 
1193     {0, 0, 0, 0}
1194 };
1195 /* *INDENT-ON* */
1196 
1197 static void
setup_cib(void)1198 setup_cib(void)
1199 {
1200     int rc, retries = 0;
1201     static cib_t *(*cib_new_fn) (void) = NULL;
1202 
1203     if (cib_new_fn == NULL) {
1204         cib_new_fn = find_library_function(&cib_library, CIB_LIBRARY, "cib_new", TRUE);
1205     }
1206 
1207     if (cib_new_fn != NULL) {
1208         cib_api = (*cib_new_fn) ();
1209     }
1210 
1211     if (cib_api == NULL) {
1212         crm_err("No connection to the CIB");
1213         return;
1214     }
1215 
1216     do {
1217         sleep(retries);
1218         rc = cib_api->cmds->signon(cib_api, CRM_SYSTEM_STONITHD, cib_command);
1219     } while (rc == -ENOTCONN && ++retries < 5);
1220 
1221     if (rc != pcmk_ok) {
1222         crm_err("Could not connect to the CIB service: %s (%d)", pcmk_strerror(rc), rc);
1223 
1224     } else if (pcmk_ok !=
1225                cib_api->cmds->add_notify_callback(cib_api, T_CIB_DIFF_NOTIFY, update_cib_cache_cb)) {
1226         crm_err("Could not set CIB notification callback");
1227 
1228     } else {
1229         rc = cib_api->cmds->query(cib_api, NULL, NULL, cib_scope_local);
1230         cib_api->cmds->register_callback(cib_api, rc, 120, FALSE, NULL, "init_cib_cache_cb",
1231                                          init_cib_cache_cb);
1232         cib_api->cmds->set_connection_dnotify(cib_api, cib_connection_destroy);
1233         crm_info("Watching for stonith topology changes");
1234     }
1235 }
1236 
1237 struct qb_ipcs_service_handlers ipc_callbacks = {
1238     .connection_accept = st_ipc_accept,
1239     .connection_created = st_ipc_created,
1240     .msg_process = st_ipc_dispatch,
1241     .connection_closed = st_ipc_closed,
1242     .connection_destroyed = st_ipc_destroy
1243 };
1244 
1245 /*!
1246  * \internal
1247  * \brief Callback for peer status changes
1248  *
1249  * \param[in] type  What changed
1250  * \param[in] node  What peer had the change
1251  * \param[in] data  Previous value of what changed
1252  */
1253 static void
st_peer_update_callback(enum crm_status_type type,crm_node_t * node,const void * data)1254 st_peer_update_callback(enum crm_status_type type, crm_node_t * node, const void *data)
1255 {
1256     if ((type != crm_status_processes) && !is_set(node->flags, crm_remote_node)) {
1257         /*
1258          * This is a hack until we can send to a nodeid and/or we fix node name lookups
1259          * These messages are ignored in stonith_peer_callback()
1260          */
1261         xmlNode *query = create_xml_node(NULL, "stonith_command");
1262 
1263         crm_xml_add(query, F_XML_TAGNAME, "stonith_command");
1264         crm_xml_add(query, F_TYPE, T_STONITH_NG);
1265         crm_xml_add(query, F_STONITH_OPERATION, "poke");
1266 
1267         crm_debug("Broadcasting our uname because of node %u", node->id);
1268         send_cluster_message(NULL, crm_msg_stonith_ng, query, FALSE);
1269 
1270         free_xml(query);
1271     }
1272 }
1273 
1274 int
main(int argc,char ** argv)1275 main(int argc, char **argv)
1276 {
1277     int flag;
1278     int rc = 0;
1279     int lpc = 0;
1280     int argerr = 0;
1281     int option_index = 0;
1282     crm_cluster_t cluster;
1283     const char *actions[] = { "reboot", "off", "on", "list", "monitor", "status" };
1284     crm_ipc_t *old_instance = NULL;
1285 
1286     crm_log_preinit("stonith-ng", argc, argv);
1287     crm_set_options(NULL, "mode [options]", long_options,
1288                     "Provides a summary of cluster's current state."
1289                     "\n\nOutputs varying levels of detail in a number of different formats.\n");
1290 
1291     while (1) {
1292         flag = crm_get_option(argc, argv, &option_index);
1293         if (flag == -1) {
1294             break;
1295         }
1296 
1297         switch (flag) {
1298             case 'V':
1299                 crm_bump_log_level(argc, argv);
1300                 break;
1301             case 'l':
1302                 crm_add_logfile(optarg);
1303                 break;
1304             case 's':
1305                 stand_alone = TRUE;
1306                 break;
1307             case 'c':
1308                 stand_alone = FALSE;
1309                 no_cib_connect = TRUE;
1310                 break;
1311             case '$':
1312             case '?':
1313                 crm_help(flag, EX_OK);
1314                 break;
1315             default:
1316                 ++argerr;
1317                 break;
1318         }
1319     }
1320 
1321     if (argc - optind == 1 && safe_str_eq("metadata", argv[optind])) {
1322         printf("<?xml version=\"1.0\"?><!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n");
1323         printf("<resource-agent name=\"stonithd\">\n");
1324         printf(" <version>1.0</version>\n");
1325         printf
1326             (" <longdesc lang=\"en\">This is a fake resource that details the instance attributes handled by stonithd.</longdesc>\n");
1327         printf(" <shortdesc lang=\"en\">Options available for all stonith resources</shortdesc>\n");
1328         printf(" <parameters>\n");
1329 
1330 #if 0
1331         // priority is not implemented yet
1332         printf("  <parameter name=\"priority\" unique=\"0\">\n");
1333         printf("    <shortdesc lang=\"en\">Devices that are not in a topology "
1334                "are tried in order of highest to lowest integer priority</shortdesc>\n");
1335         printf("    <content type=\"integer\" default=\"0\"/>\n");
1336         printf("  </parameter>\n");
1337 #endif
1338 
1339         printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_HOSTARG);
1340         printf
1341             ("    <shortdesc lang=\"en\">Advanced use only: An alternate parameter to supply instead of 'port'</shortdesc>\n");
1342         printf
1343             ("    <longdesc lang=\"en\">Some devices do not support the standard 'port' parameter or may provide additional ones.\n"
1344              "Use this to specify an alternate, device-specific, parameter that should indicate the machine to be fenced.\n"
1345              "A value of 'none' can be used to tell the cluster not to supply any additional parameters.\n"
1346              "     </longdesc>\n");
1347         printf("    <content type=\"string\" default=\"port\"/>\n");
1348         printf("  </parameter>\n");
1349 
1350         printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_HOSTMAP);
1351         printf
1352             ("    <shortdesc lang=\"en\">A mapping of host names to ports numbers for devices that do not support host names.</shortdesc>\n");
1353         printf
1354             ("    <longdesc lang=\"en\">Eg. node1:1;node2:2,3 would tell the cluster to use port 1 for node1 and ports 2 and 3 for node2</longdesc>\n");
1355         printf("    <content type=\"string\" default=\"\"/>\n");
1356         printf("  </parameter>\n");
1357 
1358         printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_HOSTLIST);
1359         printf
1360             ("    <shortdesc lang=\"en\">A list of machines controlled by this device (Optional unless %s=static-list).</shortdesc>\n",
1361              STONITH_ATTR_HOSTCHECK);
1362         printf("    <content type=\"string\" default=\"\"/>\n");
1363         printf("  </parameter>\n");
1364 
1365         printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_HOSTCHECK);
1366         printf
1367             ("    <shortdesc lang=\"en\">How to determine which machines are controlled by the device.</shortdesc>\n");
1368         printf("    <longdesc lang=\"en\">Allowed values: dynamic-list "
1369                "(query the device via the 'list' command), static-list "
1370                "(check the " STONITH_ATTR_HOSTLIST " attribute), status "
1371                "(query the device via the 'status' command), none (assume "
1372                "every device can fence every machine)</longdesc>\n");
1373         printf("    <content type=\"string\" default=\"dynamic-list\"/>\n");
1374         printf("  </parameter>\n");
1375 
1376         printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_DELAY_MAX);
1377         printf
1378             ("    <shortdesc lang=\"en\">Enable a random delay for stonith actions and specify the maximum of random delay.</shortdesc>\n");
1379         printf
1380             ("    <longdesc lang=\"en\">This prevents double fencing when using slow devices such as sbd.\n"
1381              "Use this to enable a random delay for stonith actions.\n"
1382              "The overall delay is derived from this random delay value adding a static delay so that the sum is kept below the maximum delay.</longdesc>\n");
1383         printf("    <content type=\"time\" default=\"0s\"/>\n");
1384         printf("  </parameter>\n");
1385 
1386         printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_DELAY_BASE);
1387         printf
1388             ("    <shortdesc lang=\"en\">Enable a base delay for stonith actions and specify base delay value.</shortdesc>\n");
1389         printf
1390             ("    <longdesc lang=\"en\">This prevents double fencing when different delays are configured on the nodes.\n"
1391              "Use this to enable a static delay for stonith actions.\n"
1392              "The overall delay is derived from a random delay value adding this static delay so that the sum is kept below the maximum delay.</longdesc>\n");
1393         printf("    <content type=\"time\" default=\"0s\"/>\n");
1394         printf("  </parameter>\n");
1395 
1396         printf("  <parameter name=\"%s\" unique=\"0\">\n", STONITH_ATTR_ACTION_LIMIT);
1397         printf
1398             ("    <shortdesc lang=\"en\">The maximum number of actions can be performed in parallel on this device</shortdesc>\n");
1399         printf
1400             ("    <longdesc lang=\"en\">Pengine property concurrent-fencing=true needs to be configured first.\n"
1401              "Then use this to specify the maximum number of actions can be performed in parallel on this device. -1 is unlimited.</longdesc>\n");
1402         printf("    <content type=\"integer\" default=\"1\"/>\n");
1403         printf("  </parameter>\n");
1404 
1405 
1406         for (lpc = 0; lpc < DIMOF(actions); lpc++) {
1407             printf("  <parameter name=\"pcmk_%s_action\" unique=\"0\">\n", actions[lpc]);
1408             printf
1409                 ("    <shortdesc lang=\"en\">Advanced use only: An alternate command to run instead of '%s'</shortdesc>\n",
1410                  actions[lpc]);
1411             printf
1412                 ("    <longdesc lang=\"en\">Some devices do not support the standard commands or may provide additional ones.\n"
1413                  "Use this to specify an alternate, device-specific, command that implements the '%s' action.</longdesc>\n",
1414                  actions[lpc]);
1415             printf("    <content type=\"string\" default=\"%s\"/>\n", actions[lpc]);
1416             printf("  </parameter>\n");
1417 
1418             printf("  <parameter name=\"pcmk_%s_timeout\" unique=\"0\">\n", actions[lpc]);
1419             printf
1420                 ("    <shortdesc lang=\"en\">Advanced use only: Specify an alternate timeout to use for %s actions instead of stonith-timeout</shortdesc>\n",
1421                  actions[lpc]);
1422             printf
1423                 ("    <longdesc lang=\"en\">Some devices need much more/less time to complete than normal.\n"
1424                  "Use this to specify an alternate, device-specific, timeout for '%s' actions.</longdesc>\n",
1425                  actions[lpc]);
1426             printf("    <content type=\"time\" default=\"60s\"/>\n");
1427             printf("  </parameter>\n");
1428 
1429             printf("  <parameter name=\"pcmk_%s_retries\" unique=\"0\">\n", actions[lpc]);
1430             printf
1431                 ("    <shortdesc lang=\"en\">Advanced use only: The maximum number of times to retry the '%s' command within the timeout period</shortdesc>\n",
1432                  actions[lpc]);
1433             printf("    <longdesc lang=\"en\">Some devices do not support multiple connections."
1434                    " Operations may 'fail' if the device is busy with another task so Pacemaker will automatically retry the operation, if there is time remaining."
1435                    " Use this option to alter the number of times Pacemaker retries '%s' actions before giving up."
1436                    "</longdesc>\n", actions[lpc]);
1437             printf("    <content type=\"integer\" default=\"2\"/>\n");
1438             printf("  </parameter>\n");
1439         }
1440 
1441         printf(" </parameters>\n");
1442         printf("</resource-agent>\n");
1443         return 0;
1444     }
1445 
1446     if (optind != argc) {
1447         ++argerr;
1448     }
1449 
1450     if (argerr) {
1451         crm_help('?', EX_USAGE);
1452     }
1453 
1454     crm_log_init("stonith-ng", LOG_INFO, TRUE, FALSE, argc, argv, FALSE);
1455 
1456     old_instance = crm_ipc_new("stonith-ng", 0);
1457     if (crm_ipc_connect(old_instance)) {
1458         /* IPC end-point already up */
1459         crm_ipc_close(old_instance);
1460         crm_ipc_destroy(old_instance);
1461         crm_err("stonithd is already active, aborting startup");
1462         crm_exit(EX_OK);
1463     } else {
1464         /* not up or not authentic, we'll proceed either way */
1465         crm_ipc_destroy(old_instance);
1466         old_instance = NULL;
1467     }
1468 
1469     mainloop_add_signal(SIGTERM, stonith_shutdown);
1470 
1471     crm_peer_init();
1472 
1473     fenced_data_set = pe_new_working_set();
1474     CRM_ASSERT(fenced_data_set != NULL);
1475     set_bit(fenced_data_set->flags, pe_flag_no_counts);
1476     set_bit(fenced_data_set->flags, pe_flag_no_compat);
1477 
1478     if (stand_alone == FALSE) {
1479 #if SUPPORT_HEARTBEAT
1480         cluster.hb_conn = NULL;
1481         cluster.hb_dispatch = stonith_peer_hb_callback;
1482         cluster.destroy = stonith_peer_hb_destroy;
1483 #endif
1484 
1485         if (is_openais_cluster()) {
1486 #if SUPPORT_COROSYNC
1487             cluster.destroy = stonith_peer_cs_destroy;
1488             cluster.cpg.cpg_deliver_fn = stonith_peer_ais_callback;
1489             cluster.cpg.cpg_confchg_fn = pcmk_cpg_membership;
1490 #endif
1491         }
1492 
1493         crm_set_status_callback(&st_peer_update_callback);
1494 
1495         if (crm_cluster_connect(&cluster) == FALSE) {
1496             crm_crit("Cannot sign in to the cluster... terminating");
1497             crm_exit(DAEMON_RESPAWN_STOP);
1498         }
1499         stonith_our_uname = cluster.uname;
1500         stonith_our_uuid = cluster.uuid;
1501 
1502 #if SUPPORT_HEARTBEAT
1503         if (is_heartbeat_cluster()) {
1504             /* crm_cluster_connect() registered us for crm_system_name, which
1505              * usually is the only F_TYPE used by the respective sub system.
1506              * Stonith needs to register two additional F_TYPE callbacks,
1507              * because it can :-/ */
1508             if (HA_OK !=
1509                 cluster.hb_conn->llc_ops->set_msg_callback(cluster.hb_conn, T_STONITH_NOTIFY,
1510                                                             cluster.hb_dispatch, cluster.hb_conn)) {
1511                 crm_crit("Cannot set msg callback %s: %s", T_STONITH_NOTIFY, cluster.hb_conn->llc_ops->errmsg(cluster.hb_conn));
1512                 crm_exit(DAEMON_RESPAWN_STOP);
1513             }
1514             if (HA_OK !=
1515                 cluster.hb_conn->llc_ops->set_msg_callback(cluster.hb_conn, T_STONITH_TIMEOUT_VALUE,
1516                                                             cluster.hb_dispatch, cluster.hb_conn)) {
1517                 crm_crit("Cannot set msg callback %s: %s", T_STONITH_TIMEOUT_VALUE, cluster.hb_conn->llc_ops->errmsg(cluster.hb_conn));
1518                 crm_exit(DAEMON_RESPAWN_STOP);
1519             }
1520         }
1521 #endif
1522 
1523         if (no_cib_connect == FALSE) {
1524             setup_cib();
1525         }
1526 
1527     } else {
1528         stonith_our_uname = strdup("localhost");
1529     }
1530 
1531 
1532     device_list = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_device);
1533 
1534     topology = g_hash_table_new_full(crm_str_hash, g_str_equal, NULL, free_topology_entry);
1535 
1536     if(stonith_watchdog_timeout_ms > 0) {
1537         xmlNode *xml;
1538         stonith_key_value_t *params = NULL;
1539 
1540         params = stonith_key_value_add(params, STONITH_ATTR_HOSTLIST, stonith_our_uname);
1541 
1542         xml = create_device_registration_xml("watchdog", st_namespace_internal,
1543                                              STONITH_WATCHDOG_AGENT, params,
1544                                              NULL);
1545         stonith_device_register(xml, NULL, FALSE);
1546 
1547         stonith_key_value_freeall(params, 1, 1);
1548         free_xml(xml);
1549     }
1550 
1551     stonith_ipc_server_init(&ipcs, &ipc_callbacks);
1552 
1553 #if SUPPORT_STONITH_CONFIG
1554     if (((stand_alone == TRUE)) && !(standalone_cfg_read_file(STONITH_NG_CONF_FILE))) {
1555         standalone_cfg_commit();
1556     }
1557 #endif
1558 
1559     /* Create the mainloop and run it... */
1560     mainloop = g_main_new(FALSE);
1561     crm_info("Starting %s mainloop", crm_system_name);
1562 
1563     g_main_run(mainloop);
1564     stonith_cleanup();
1565 
1566 #if SUPPORT_HEARTBEAT
1567     if (cluster.hb_conn) {
1568         cluster.hb_conn->llc_ops->delete(cluster.hb_conn);
1569     }
1570 #endif
1571 
1572 
1573     pe_free_working_set(fenced_data_set);
1574     return crm_exit(rc);
1575 }
1576