pacemaker  1.1.15-e174ec8
Scalable High-Availability cluster resource manager
lrmd_client.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2012 David Vossel <davidvossel@gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17  *
18  */
19 
20 #include <crm_internal.h>
21 
22 #include <unistd.h>
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <stdarg.h>
26 #include <string.h>
27 #include <ctype.h>
28 
29 #include <sys/types.h>
30 #include <sys/wait.h>
31 
32 #include <glib.h>
33 #include <dirent.h>
34 
35 #include <crm/crm.h>
36 #include <crm/lrmd.h>
37 #include <crm/services.h>
38 #include <crm/common/mainloop.h>
39 #include <crm/common/ipcs.h>
40 #include <crm/msg_xml.h>
41 
42 #include <crm/stonith-ng.h>
43 
44 #ifdef HAVE_GNUTLS_GNUTLS_H
45 # undef KEYFILE
46 # include <gnutls/gnutls.h>
47 #endif
48 
49 #include <sys/socket.h>
50 #include <netinet/in.h>
51 #include <netinet/ip.h>
52 #include <arpa/inet.h>
53 #include <netdb.h>
54 
55 #define MAX_TLS_RECV_WAIT 10000
56 
58 
59 static int lrmd_api_disconnect(lrmd_t * lrmd);
60 static int lrmd_api_is_connected(lrmd_t * lrmd);
61 
62 /* IPC proxy functions */
63 int lrmd_internal_proxy_send(lrmd_t * lrmd, xmlNode *msg);
64 static void lrmd_internal_proxy_dispatch(lrmd_t *lrmd, xmlNode *msg);
65 void lrmd_internal_set_proxy_callback(lrmd_t * lrmd, void *userdata, void (*callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg));
66 
67 #ifdef HAVE_GNUTLS_GNUTLS_H
68 # define LRMD_CLIENT_HANDSHAKE_TIMEOUT 5000 /* 5 seconds */
69 gnutls_psk_client_credentials_t psk_cred_s;
70 int lrmd_tls_set_key(gnutls_datum_t * key);
71 static void lrmd_tls_disconnect(lrmd_t * lrmd);
72 static int global_remote_msg_id = 0;
73 int lrmd_tls_send_msg(crm_remote_t * session, xmlNode * msg, uint32_t id, const char *msg_type);
74 static void lrmd_tls_connection_destroy(gpointer userdata);
75 #endif
76 
77 typedef struct lrmd_private_s {
78  enum client_type type;
79  char *token;
80  mainloop_io_t *source;
81 
82  /* IPC parameters */
83  crm_ipc_t *ipc;
84 
85  crm_remote_t *remote;
86 
87  /* Extra TLS parameters */
88  char *remote_nodename;
89 #ifdef HAVE_GNUTLS_GNUTLS_H
90  char *server;
91  int port;
92  gnutls_psk_client_credentials_t psk_cred_c;
93 
94  /* while the async connection is occuring, this is the id
95  * of the connection timeout timer. */
96  int async_timer;
97  int sock;
98  /* since tls requires a round trip across the network for a
99  * request/reply, there are times where we just want to be able
100  * to send a request from the client and not wait around (or even care
101  * about) what the reply is. */
102  int expected_late_replies;
103  GList *pending_notify;
104  crm_trigger_t *process_notify;
105 #endif
106 
107  lrmd_event_callback callback;
108 
109  /* Internal IPC proxy msg passing for remote guests */
110  void (*proxy_callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg);
111  void *proxy_callback_userdata;
112  char *peer_version;
114 
115 static lrmd_list_t *
116 lrmd_list_add(lrmd_list_t * head, const char *value)
117 {
118  lrmd_list_t *p, *end;
119 
120  p = calloc(1, sizeof(lrmd_list_t));
121  p->val = strdup(value);
122 
123  end = head;
124  while (end && end->next) {
125  end = end->next;
126  }
127 
128  if (end) {
129  end->next = p;
130  } else {
131  head = p;
132  }
133 
134  return head;
135 }
136 
137 void
139 {
140  lrmd_list_t *p;
141 
142  while (head) {
143  char *val = (char *)head->val;
144 
145  p = head->next;
146  free(val);
147  free(head);
148  head = p;
149  }
150 }
151 
153 lrmd_key_value_add(lrmd_key_value_t * head, const char *key, const char *value)
154 {
155  lrmd_key_value_t *p, *end;
156 
157  p = calloc(1, sizeof(lrmd_key_value_t));
158  p->key = strdup(key);
159  p->value = strdup(value);
160 
161  end = head;
162  while (end && end->next) {
163  end = end->next;
164  }
165 
166  if (end) {
167  end->next = p;
168  } else {
169  head = p;
170  }
171 
172  return head;
173 }
174 
175 void
177 {
178  lrmd_key_value_t *p;
179 
180  while (head) {
181  p = head->next;
182  free(head->key);
183  free(head->value);
184  free(head);
185  head = p;
186  }
187 }
188 
189 static void
190 dup_attr(gpointer key, gpointer value, gpointer user_data)
191 {
192  g_hash_table_replace(user_data, strdup(key), strdup(value));
193 }
194 
197 {
198  lrmd_event_data_t *copy = NULL;
199 
200  copy = calloc(1, sizeof(lrmd_event_data_t));
201 
202  /* This will get all the int values.
203  * we just have to be careful not to leave any
204  * dangling pointers to strings. */
205  memcpy(copy, event, sizeof(lrmd_event_data_t));
206 
207  copy->rsc_id = event->rsc_id ? strdup(event->rsc_id) : NULL;
208  copy->op_type = event->op_type ? strdup(event->op_type) : NULL;
209  copy->user_data = event->user_data ? strdup(event->user_data) : NULL;
210  copy->output = event->output ? strdup(event->output) : NULL;
211  copy->exit_reason = event->exit_reason ? strdup(event->exit_reason) : NULL;
212  copy->remote_nodename = event->remote_nodename ? strdup(event->remote_nodename) : NULL;
213 
214  if (event->params) {
215  copy->params = g_hash_table_new_full(crm_str_hash,
216  g_str_equal, g_hash_destroy_str, g_hash_destroy_str);
217 
218  if (copy->params != NULL) {
219  g_hash_table_foreach(event->params, dup_attr, copy->params);
220  }
221  }
222 
223  return copy;
224 }
225 
226 void
228 {
229  if (!event) {
230  return;
231  }
232 
233  /* free gives me grief if i try to cast */
234  free((char *)event->rsc_id);
235  free((char *)event->op_type);
236  free((char *)event->user_data);
237  free((char *)event->output);
238  free((char *)event->exit_reason);
239  free((char *)event->remote_nodename);
240  if (event->params) {
241  g_hash_table_destroy(event->params);
242  }
243  free(event);
244 }
245 
246 static int
247 lrmd_dispatch_internal(lrmd_t * lrmd, xmlNode * msg)
248 {
249  const char *type;
250  const char *proxy_session = crm_element_value(msg, F_LRMD_IPC_SESSION);
251  lrmd_private_t *native = lrmd->private;
252  lrmd_event_data_t event = { 0, };
253 
254  if (proxy_session != NULL) {
255  /* this is proxy business */
256  lrmd_internal_proxy_dispatch(lrmd, msg);
257  return 1;
258  } else if (!native->callback) {
259  /* no callback set */
260  crm_trace("notify event received but client has not set callback");
261  return 1;
262  }
263 
264  event.remote_nodename = native->remote_nodename;
265  type = crm_element_value(msg, F_LRMD_OPERATION);
266  crm_element_value_int(msg, F_LRMD_CALLID, &event.call_id);
267  event.rsc_id = crm_element_value(msg, F_LRMD_RSC_ID);
268 
269  if (crm_str_eq(type, LRMD_OP_RSC_REG, TRUE)) {
270  event.type = lrmd_event_register;
271  } else if (crm_str_eq(type, LRMD_OP_RSC_UNREG, TRUE)) {
272  event.type = lrmd_event_unregister;
273  } else if (crm_str_eq(type, LRMD_OP_RSC_EXEC, TRUE)) {
274  crm_element_value_int(msg, F_LRMD_TIMEOUT, &event.timeout);
275  crm_element_value_int(msg, F_LRMD_RSC_INTERVAL, &event.interval);
276  crm_element_value_int(msg, F_LRMD_RSC_START_DELAY, &event.start_delay);
277  crm_element_value_int(msg, F_LRMD_EXEC_RC, (int *)&event.rc);
278  crm_element_value_int(msg, F_LRMD_OP_STATUS, &event.op_status);
279  crm_element_value_int(msg, F_LRMD_RSC_DELETED, &event.rsc_deleted);
280 
281  crm_element_value_int(msg, F_LRMD_RSC_RUN_TIME, (int *)&event.t_run);
282  crm_element_value_int(msg, F_LRMD_RSC_RCCHANGE_TIME, (int *)&event.t_rcchange);
283  crm_element_value_int(msg, F_LRMD_RSC_EXEC_TIME, (int *)&event.exec_time);
284  crm_element_value_int(msg, F_LRMD_RSC_QUEUE_TIME, (int *)&event.queue_time);
285 
286  event.op_type = crm_element_value(msg, F_LRMD_RSC_ACTION);
287  event.user_data = crm_element_value(msg, F_LRMD_RSC_USERDATA_STR);
288  event.output = crm_element_value(msg, F_LRMD_RSC_OUTPUT);
289  event.exit_reason = crm_element_value(msg, F_LRMD_RSC_EXIT_REASON);
290  event.type = lrmd_event_exec_complete;
291 
292  event.params = xml2list(msg);
293  } else if (crm_str_eq(type, LRMD_OP_NEW_CLIENT, TRUE)) {
294  event.type = lrmd_event_new_client;
295  } else if (crm_str_eq(type, LRMD_OP_POKE, TRUE)) {
296  event.type = lrmd_event_poke;
297  } else {
298  return 1;
299  }
300 
301  crm_trace("op %s notify event received", type);
302  native->callback(&event);
303 
304  if (event.params) {
305  g_hash_table_destroy(event.params);
306  }
307  return 1;
308 }
309 
310 static int
311 lrmd_ipc_dispatch(const char *buffer, ssize_t length, gpointer userdata)
312 {
313  lrmd_t *lrmd = userdata;
314  lrmd_private_t *native = lrmd->private;
315  xmlNode *msg;
316  int rc;
317 
318  if (!native->callback) {
319  /* no callback set */
320  return 1;
321  }
322 
323  msg = string2xml(buffer);
324  rc = lrmd_dispatch_internal(lrmd, msg);
325  free_xml(msg);
326  return rc;
327 }
328 
329 #ifdef HAVE_GNUTLS_GNUTLS_H
330 static void
331 lrmd_free_xml(gpointer userdata)
332 {
333  free_xml((xmlNode *) userdata);
334 }
335 
336 static int
337 lrmd_tls_connected(lrmd_t * lrmd)
338 {
339  lrmd_private_t *native = lrmd->private;
340 
341  if (native->remote->tls_session) {
342  return TRUE;
343  }
344 
345  return FALSE;
346 }
347 
348 static int
349 lrmd_tls_dispatch(gpointer userdata)
350 {
351  lrmd_t *lrmd = userdata;
352  lrmd_private_t *native = lrmd->private;
353  xmlNode *xml = NULL;
354  int rc = 0;
355  int disconnected = 0;
356 
357  if (lrmd_tls_connected(lrmd) == FALSE) {
358  crm_trace("tls dispatch triggered after disconnect");
359  return 0;
360  }
361 
362  crm_trace("tls_dispatch triggered");
363 
364  /* First check if there are any pending notifies to process that came
365  * while we were waiting for replies earlier. */
366  if (native->pending_notify) {
367  GList *iter = NULL;
368 
369  crm_trace("Processing pending notifies");
370  for (iter = native->pending_notify; iter; iter = iter->next) {
371  lrmd_dispatch_internal(lrmd, iter->data);
372  }
373  g_list_free_full(native->pending_notify, lrmd_free_xml);
374  native->pending_notify = NULL;
375  }
376 
377  /* Next read the current buffer and see if there are any messages to handle. */
378  rc = crm_remote_ready(native->remote, 0);
379  if (rc == 0) {
380  /* nothing to read, see if any full messages are already in buffer. */
381  xml = crm_remote_parse_buffer(native->remote);
382  } else if (rc < 0) {
383  disconnected = 1;
384  } else {
385  crm_remote_recv(native->remote, -1, &disconnected);
386  xml = crm_remote_parse_buffer(native->remote);
387  }
388  while (xml) {
389  const char *msg_type = crm_element_value(xml, F_LRMD_REMOTE_MSG_TYPE);
390  if (safe_str_eq(msg_type, "notify")) {
391  lrmd_dispatch_internal(lrmd, xml);
392  } else if (safe_str_eq(msg_type, "reply")) {
393  if (native->expected_late_replies > 0) {
394  native->expected_late_replies--;
395  } else {
396  int reply_id = 0;
397  crm_element_value_int(xml, F_LRMD_CALLID, &reply_id);
398  /* if this happens, we want to know about it */
399  crm_err("Got outdated reply %d", reply_id);
400  }
401  }
402  free_xml(xml);
403  xml = crm_remote_parse_buffer(native->remote);
404  }
405 
406  if (disconnected) {
407  crm_info("Server disconnected while reading remote server msg.");
408  lrmd_tls_disconnect(lrmd);
409  return 0;
410  }
411  return 1;
412 }
413 #endif
414 
415 /* Not used with mainloop */
416 int
417 lrmd_poll(lrmd_t * lrmd, int timeout)
418 {
419  lrmd_private_t *native = lrmd->private;
420 
421  switch (native->type) {
422  case CRM_CLIENT_IPC:
423  return crm_ipc_ready(native->ipc);
424 
425 #ifdef HAVE_GNUTLS_GNUTLS_H
426  case CRM_CLIENT_TLS:
427  if (native->pending_notify) {
428  return 1;
429  }
430 
431  return crm_remote_ready(native->remote, 0);
432 #endif
433  default:
434  crm_err("Unsupported connection type: %d", native->type);
435  }
436 
437  return 0;
438 }
439 
440 /* Not used with mainloop */
441 bool
443 {
444  lrmd_private_t *private = NULL;
445 
446  CRM_ASSERT(lrmd != NULL);
447 
448  private = lrmd->private;
449  switch (private->type) {
450  case CRM_CLIENT_IPC:
451  while (crm_ipc_ready(private->ipc)) {
452  if (crm_ipc_read(private->ipc) > 0) {
453  const char *msg = crm_ipc_buffer(private->ipc);
454 
455  lrmd_ipc_dispatch(msg, strlen(msg), lrmd);
456  }
457  }
458  break;
459 #ifdef HAVE_GNUTLS_GNUTLS_H
460  case CRM_CLIENT_TLS:
461  lrmd_tls_dispatch(lrmd);
462  break;
463 #endif
464  default:
465  crm_err("Unsupported connection type: %d", private->type);
466  }
467 
468  if (lrmd_api_is_connected(lrmd) == FALSE) {
469  crm_err("Connection closed");
470  return FALSE;
471  }
472 
473  return TRUE;
474 }
475 
476 static xmlNode *
477 lrmd_create_op(const char *token, const char *op, xmlNode * data, enum lrmd_call_options options)
478 {
479  xmlNode *op_msg = create_xml_node(NULL, "lrmd_command");
480 
481  CRM_CHECK(op_msg != NULL, return NULL);
482  CRM_CHECK(token != NULL, return NULL);
483 
484  crm_xml_add(op_msg, F_XML_TAGNAME, "lrmd_command");
485 
486  crm_xml_add(op_msg, F_TYPE, T_LRMD);
487  crm_xml_add(op_msg, F_LRMD_CALLBACK_TOKEN, token);
488  crm_xml_add(op_msg, F_LRMD_OPERATION, op);
489  crm_trace("Sending call options: %.8lx, %d", (long)options, options);
490  crm_xml_add_int(op_msg, F_LRMD_CALLOPTS, options);
491 
492  if (data != NULL) {
493  add_message_xml(op_msg, F_LRMD_CALLDATA, data);
494  }
495 
496  return op_msg;
497 }
498 
499 static void
500 lrmd_ipc_connection_destroy(gpointer userdata)
501 {
502  lrmd_t *lrmd = userdata;
503  lrmd_private_t *native = lrmd->private;
504 
505  crm_info("IPC connection destroyed");
506 
507  /* Prevent these from being cleaned up in lrmd_api_disconnect() */
508  native->ipc = NULL;
509  native->source = NULL;
510 
511  if (native->callback) {
512  lrmd_event_data_t event = { 0, };
513  event.type = lrmd_event_disconnect;
514  event.remote_nodename = native->remote_nodename;
515  native->callback(&event);
516  }
517 }
518 
519 #ifdef HAVE_GNUTLS_GNUTLS_H
520 static void
521 lrmd_tls_connection_destroy(gpointer userdata)
522 {
523  lrmd_t *lrmd = userdata;
524  lrmd_private_t *native = lrmd->private;
525 
526  crm_info("TLS connection destroyed");
527 
528  if (native->remote->tls_session) {
529  gnutls_bye(*native->remote->tls_session, GNUTLS_SHUT_RDWR);
530  gnutls_deinit(*native->remote->tls_session);
531  gnutls_free(native->remote->tls_session);
532  }
533  if (native->psk_cred_c) {
534  gnutls_psk_free_client_credentials(native->psk_cred_c);
535  }
536  if (native->sock) {
537  close(native->sock);
538  }
539  if (native->process_notify) {
540  mainloop_destroy_trigger(native->process_notify);
541  native->process_notify = NULL;
542  }
543  if (native->pending_notify) {
544  g_list_free_full(native->pending_notify, lrmd_free_xml);
545  native->pending_notify = NULL;
546  }
547 
548  free(native->remote->buffer);
549  native->remote->buffer = NULL;
550  native->source = 0;
551  native->sock = 0;
552  native->psk_cred_c = NULL;
553  native->remote->tls_session = NULL;
554  native->sock = 0;
555 
556  if (native->callback) {
557  lrmd_event_data_t event = { 0, };
558  event.remote_nodename = native->remote_nodename;
559  event.type = lrmd_event_disconnect;
560  native->callback(&event);
561  }
562  return;
563 }
564 
565 int
566 lrmd_tls_send_msg(crm_remote_t * session, xmlNode * msg, uint32_t id, const char *msg_type)
567 {
568  int rc = -1;
569 
571  crm_xml_add(msg, F_LRMD_REMOTE_MSG_TYPE, msg_type);
572 
573  rc = crm_remote_send(session, msg);
574 
575  if (rc < 0) {
576  crm_err("Failed to send remote lrmd tls msg, rc = %d", rc);
577  return rc;
578  }
579 
580  return rc;
581 }
582 
583 static xmlNode *
584 lrmd_tls_recv_reply(lrmd_t * lrmd, int total_timeout, int expected_reply_id, int *disconnected)
585 {
586  lrmd_private_t *native = lrmd->private;
587  xmlNode *xml = NULL;
588  time_t start = time(NULL);
589  const char *msg_type = NULL;
590  int reply_id = 0;
591  int remaining_timeout = 0;
592 
593  /* A timeout of 0 here makes no sense. We have to wait a period of time
594  * for the response to come back. If -1 or 0, default to 10 seconds. */
595  if (total_timeout <= 0 || total_timeout > MAX_TLS_RECV_WAIT) {
596  total_timeout = MAX_TLS_RECV_WAIT;
597  }
598 
599  while (!xml) {
600 
601  xml = crm_remote_parse_buffer(native->remote);
602  if (!xml) {
603  /* read some more off the tls buffer if we still have time left. */
604  if (remaining_timeout) {
605  remaining_timeout = remaining_timeout - ((time(NULL) - start) * 1000);
606  } else {
607  remaining_timeout = total_timeout;
608  }
609  if (remaining_timeout <= 0) {
610  crm_err("Never received the expected reply during the timeout period, disconnecting.");
611  *disconnected = TRUE;
612  return NULL;
613  }
614 
615  crm_remote_recv(native->remote, remaining_timeout, disconnected);
616  xml = crm_remote_parse_buffer(native->remote);
617  if (!xml) {
618  crm_err("Unable to receive expected reply, disconnecting.");
619  *disconnected = TRUE;
620  return NULL;
621  } else if (*disconnected) {
622  return NULL;
623  }
624  }
625 
626  CRM_ASSERT(xml != NULL);
627 
629  msg_type = crm_element_value(xml, F_LRMD_REMOTE_MSG_TYPE);
630 
631  if (!msg_type) {
632  crm_err("Empty msg type received while waiting for reply");
633  free_xml(xml);
634  xml = NULL;
635  } else if (safe_str_eq(msg_type, "notify")) {
636  /* got a notify while waiting for reply, trigger the notify to be processed later */
637  crm_info("queueing notify");
638  native->pending_notify = g_list_append(native->pending_notify, xml);
639  if (native->process_notify) {
640  crm_info("notify trigger set.");
641  mainloop_set_trigger(native->process_notify);
642  }
643  xml = NULL;
644  } else if (safe_str_neq(msg_type, "reply")) {
645  /* msg isn't a reply, make some noise */
646  crm_err("Expected a reply, got %s", msg_type);
647  free_xml(xml);
648  xml = NULL;
649  } else if (reply_id != expected_reply_id) {
650  if (native->expected_late_replies > 0) {
651  native->expected_late_replies--;
652  } else {
653  crm_err("Got outdated reply, expected id %d got id %d", expected_reply_id, reply_id);
654  }
655  free_xml(xml);
656  xml = NULL;
657  }
658  }
659 
660  if (native->remote->buffer && native->process_notify) {
661  mainloop_set_trigger(native->process_notify);
662  }
663 
664  return xml;
665 }
666 
667 static int
668 lrmd_tls_send(lrmd_t * lrmd, xmlNode * msg)
669 {
670  int rc = 0;
671  lrmd_private_t *native = lrmd->private;
672 
673  global_remote_msg_id++;
674  if (global_remote_msg_id <= 0) {
675  global_remote_msg_id = 1;
676  }
677 
678  rc = lrmd_tls_send_msg(native->remote, msg, global_remote_msg_id, "request");
679  if (rc <= 0) {
680  crm_err("Remote lrmd send failed, disconnecting");
681  lrmd_tls_disconnect(lrmd);
682  return -ENOTCONN;
683  }
684  return pcmk_ok;
685 }
686 
687 static int
688 lrmd_tls_send_recv(lrmd_t * lrmd, xmlNode * msg, int timeout, xmlNode ** reply)
689 {
690  int rc = 0;
691  int disconnected = 0;
692  xmlNode *xml = NULL;
693 
694  if (lrmd_tls_connected(lrmd) == FALSE) {
695  return -1;
696  }
697 
698  rc = lrmd_tls_send(lrmd, msg);
699  if (rc < 0) {
700  return rc;
701  }
702 
703  xml = lrmd_tls_recv_reply(lrmd, timeout, global_remote_msg_id, &disconnected);
704 
705  if (disconnected) {
706  crm_err("Remote lrmd server disconnected while waiting for reply with id %d. ",
707  global_remote_msg_id);
708  lrmd_tls_disconnect(lrmd);
709  rc = -ENOTCONN;
710  } else if (!xml) {
711  crm_err("Remote lrmd never received reply for request id %d. timeout: %dms ",
712  global_remote_msg_id, timeout);
713  rc = -ECOMM;
714  }
715 
716  if (reply) {
717  *reply = xml;
718  } else {
719  free_xml(xml);
720  }
721 
722  return rc;
723 }
724 #endif
725 
726 static int
727 lrmd_send_xml(lrmd_t * lrmd, xmlNode * msg, int timeout, xmlNode ** reply)
728 {
729  int rc = -1;
730  lrmd_private_t *native = lrmd->private;
731 
732  switch (native->type) {
733  case CRM_CLIENT_IPC:
734  rc = crm_ipc_send(native->ipc, msg, crm_ipc_client_response, timeout, reply);
735  break;
736 #ifdef HAVE_GNUTLS_GNUTLS_H
737  case CRM_CLIENT_TLS:
738  rc = lrmd_tls_send_recv(lrmd, msg, timeout, reply);
739  break;
740 #endif
741  default:
742  crm_err("Unsupported connection type: %d", native->type);
743  }
744 
745  return rc;
746 }
747 
748 static int
749 lrmd_send_xml_no_reply(lrmd_t * lrmd, xmlNode * msg)
750 {
751  int rc = -1;
752  lrmd_private_t *native = lrmd->private;
753 
754  switch (native->type) {
755  case CRM_CLIENT_IPC:
756  rc = crm_ipc_send(native->ipc, msg, crm_ipc_flags_none, 0, NULL);
757  break;
758 #ifdef HAVE_GNUTLS_GNUTLS_H
759  case CRM_CLIENT_TLS:
760  rc = lrmd_tls_send(lrmd, msg);
761  if (rc == pcmk_ok) {
762  /* we don't want to wait around for the reply, but
763  * since the request/reply protocol needs to behave the same
764  * as libqb, a reply will eventually come later anyway. */
765  native->expected_late_replies++;
766  }
767  break;
768 #endif
769  default:
770  crm_err("Unsupported connection type: %d", native->type);
771  }
772 
773  return rc;
774 }
775 
776 static int
777 lrmd_api_is_connected(lrmd_t * lrmd)
778 {
779  lrmd_private_t *native = lrmd->private;
780 
781  switch (native->type) {
782  case CRM_CLIENT_IPC:
783  return crm_ipc_connected(native->ipc);
784  break;
785 #ifdef HAVE_GNUTLS_GNUTLS_H
786  case CRM_CLIENT_TLS:
787  return lrmd_tls_connected(lrmd);
788  break;
789 #endif
790  default:
791  crm_err("Unsupported connection type: %d", native->type);
792  }
793 
794  return 0;
795 }
796 
797 static int
798 lrmd_send_command(lrmd_t * lrmd, const char *op, xmlNode * data, xmlNode ** output_data, int timeout, /* ms. defaults to 1000 if set to 0 */
799  enum lrmd_call_options options, gboolean expect_reply)
800 { /* TODO we need to reduce usage of this boolean */
801  int rc = pcmk_ok;
802  int reply_id = -1;
803  lrmd_private_t *native = lrmd->private;
804  xmlNode *op_msg = NULL;
805  xmlNode *op_reply = NULL;
806 
807  if (!lrmd_api_is_connected(lrmd)) {
808  return -ENOTCONN;
809  }
810 
811  if (op == NULL) {
812  crm_err("No operation specified");
813  return -EINVAL;
814  }
815 
816  CRM_CHECK(native->token != NULL,;
817  );
818  crm_trace("sending %s op to lrmd", op);
819 
820  op_msg = lrmd_create_op(native->token, op, data, options);
821 
822  if (op_msg == NULL) {
823  return -EINVAL;
824  }
825 
826  crm_xml_add_int(op_msg, F_LRMD_TIMEOUT, timeout);
827 
828  if (expect_reply) {
829  rc = lrmd_send_xml(lrmd, op_msg, timeout, &op_reply);
830  } else {
831  rc = lrmd_send_xml_no_reply(lrmd, op_msg);
832  goto done;
833  }
834 
835  if (rc < 0) {
836  crm_perror(LOG_ERR, "Couldn't perform %s operation (timeout=%d): %d", op, timeout, rc);
837  rc = -ECOMM;
838  goto done;
839 
840  } else if(op_reply == NULL) {
841  rc = -ENOMSG;
842  goto done;
843  }
844 
845  rc = pcmk_ok;
846  crm_element_value_int(op_reply, F_LRMD_CALLID, &reply_id);
847  crm_trace("%s op reply received", op);
848  if (crm_element_value_int(op_reply, F_LRMD_RC, &rc) != 0) {
849  rc = -ENOMSG;
850  goto done;
851  }
852 
853  crm_log_xml_trace(op_reply, "Reply");
854 
855  if (output_data) {
856  *output_data = op_reply;
857  op_reply = NULL; /* Prevent subsequent free */
858  }
859 
860  done:
861  if (lrmd_api_is_connected(lrmd) == FALSE) {
862  crm_err("LRMD disconnected");
863  }
864 
865  free_xml(op_msg);
866  free_xml(op_reply);
867  return rc;
868 }
869 
870 static int
871 lrmd_api_poke_connection(lrmd_t * lrmd)
872 {
873  int rc;
874  lrmd_private_t *native = lrmd->private;
875  xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
876 
877  crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
878  rc = lrmd_send_command(lrmd, LRMD_OP_POKE, data, NULL, 0, 0, native->type == CRM_CLIENT_IPC ? TRUE : FALSE);
879  free_xml(data);
880 
881  return rc < 0 ? rc : pcmk_ok;
882 }
883 
884 int
885 remote_proxy_check(lrmd_t * lrmd, GHashTable *hash)
886 {
887  int rc;
888  const char *value;
889  lrmd_private_t *native = lrmd->private;
890  xmlNode *data = create_xml_node(NULL, F_LRMD_OPERATION);
891 
892  crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
893 
894  value = g_hash_table_lookup(hash, "stonith-watchdog-timeout");
895  crm_xml_add(data, F_LRMD_WATCHDOG, value);
896 
897  rc = lrmd_send_command(lrmd, LRMD_OP_CHECK, data, NULL, 0, 0, native->type == CRM_CLIENT_IPC ? TRUE : FALSE);
898  free_xml(data);
899 
900  return rc < 0 ? rc : pcmk_ok;
901 }
902 
903 static int
904 lrmd_handshake(lrmd_t * lrmd, const char *name)
905 {
906  int rc = pcmk_ok;
907  lrmd_private_t *native = lrmd->private;
908  xmlNode *reply = NULL;
909  xmlNode *hello = create_xml_node(NULL, "lrmd_command");
910 
911  crm_xml_add(hello, F_TYPE, T_LRMD);
913  crm_xml_add(hello, F_LRMD_CLIENTNAME, name);
915 
916  /* advertise that we are a proxy provider */
917  if (native->proxy_callback) {
918  crm_xml_add(hello, F_LRMD_IS_IPC_PROVIDER, "true");
919  }
920 
921  rc = lrmd_send_xml(lrmd, hello, -1, &reply);
922 
923  if (rc < 0) {
924  crm_perror(LOG_DEBUG, "Couldn't complete registration with the lrmd API: %d", rc);
925  rc = -ECOMM;
926  } else if (reply == NULL) {
927  crm_err("Did not receive registration reply");
928  rc = -EPROTO;
929  } else {
930  const char *version = crm_element_value(reply, F_LRMD_PROTOCOL_VERSION);
931  const char *msg_type = crm_element_value(reply, F_LRMD_OPERATION);
932  const char *tmp_ticket = crm_element_value(reply, F_LRMD_CLIENTID);
933 
934  crm_element_value_int(reply, F_LRMD_RC, &rc);
935 
936  if (rc == -EPROTO) {
937  crm_err("LRMD protocol mismatch client version %s, server version %s",
938  LRMD_PROTOCOL_VERSION, version);
939  crm_log_xml_err(reply, "Protocol Error");
940 
941  } else if (safe_str_neq(msg_type, CRM_OP_REGISTER)) {
942  crm_err("Invalid registration message: %s", msg_type);
943  crm_log_xml_err(reply, "Bad reply");
944  rc = -EPROTO;
945  } else if (tmp_ticket == NULL) {
946  crm_err("No registration token provided");
947  crm_log_xml_err(reply, "Bad reply");
948  rc = -EPROTO;
949  } else {
950  crm_trace("Obtained registration token: %s", tmp_ticket);
951  native->token = strdup(tmp_ticket);
952  native->peer_version = strdup(version?version:"1.0"); /* Included since 1.1 */
953  rc = pcmk_ok;
954  }
955  }
956 
957  free_xml(reply);
958  free_xml(hello);
959 
960  if (rc != pcmk_ok) {
961  lrmd_api_disconnect(lrmd);
962  }
963  return rc;
964 }
965 
966 static int
967 lrmd_ipc_connect(lrmd_t * lrmd, int *fd)
968 {
969  int rc = pcmk_ok;
970  lrmd_private_t *native = lrmd->private;
971 
972  static struct ipc_client_callbacks lrmd_callbacks = {
973  .dispatch = lrmd_ipc_dispatch,
974  .destroy = lrmd_ipc_connection_destroy
975  };
976 
977  crm_info("Connecting to lrmd");
978 
979  if (fd) {
980  /* No mainloop */
981  native->ipc = crm_ipc_new(CRM_SYSTEM_LRMD, 0);
982  if (native->ipc && crm_ipc_connect(native->ipc)) {
983  *fd = crm_ipc_get_fd(native->ipc);
984  } else if (native->ipc) {
985  crm_perror(LOG_ERR, "Connection to local resource manager failed");
986  rc = -ENOTCONN;
987  }
988  } else {
989  native->source = mainloop_add_ipc_client(CRM_SYSTEM_LRMD, G_PRIORITY_HIGH, 0, lrmd, &lrmd_callbacks);
990  native->ipc = mainloop_get_ipc_client(native->source);
991  }
992 
993  if (native->ipc == NULL) {
994  crm_debug("Could not connect to the LRMD API");
995  rc = -ENOTCONN;
996  }
997 
998  return rc;
999 }
1000 
1001 #ifdef HAVE_GNUTLS_GNUTLS_H
1002 static int
1003 set_key(gnutls_datum_t * key, const char *location)
1004 {
1005  FILE *stream;
1006  int read_len = 256;
1007  int cur_len = 0;
1008  int buf_len = read_len;
1009  static char *key_cache = NULL;
1010  static size_t key_cache_len = 0;
1011  static time_t key_cache_updated;
1012 
1013  if (location == NULL) {
1014  return -1;
1015  }
1016 
1017  if (key_cache) {
1018  time_t now = time(NULL);
1019 
1020  if ((now - key_cache_updated) < 60) {
1021  key->data = gnutls_malloc(key_cache_len + 1);
1022  key->size = key_cache_len;
1023  memcpy(key->data, key_cache, key_cache_len);
1024 
1025  crm_debug("using cached LRMD key");
1026  return 0;
1027  } else {
1028  key_cache_len = 0;
1029  key_cache_updated = 0;
1030  free(key_cache);
1031  key_cache = NULL;
1032  crm_debug("clearing lrmd key cache");
1033  }
1034  }
1035 
1036  stream = fopen(location, "r");
1037  if (!stream) {
1038  return -1;
1039  }
1040 
1041  key->data = gnutls_malloc(read_len);
1042  while (!feof(stream)) {
1043  int next;
1044 
1045  if (cur_len == buf_len) {
1046  buf_len = cur_len + read_len;
1047  key->data = gnutls_realloc(key->data, buf_len);
1048  }
1049  next = fgetc(stream);
1050  if (next == EOF && feof(stream)) {
1051  break;
1052  }
1053 
1054  key->data[cur_len] = next;
1055  cur_len++;
1056  }
1057  fclose(stream);
1058 
1059  key->size = cur_len;
1060  if (!cur_len) {
1061  gnutls_free(key->data);
1062  key->data = 0;
1063  return -1;
1064  }
1065 
1066  if (!key_cache) {
1067  key_cache = calloc(1, key->size + 1);
1068  memcpy(key_cache, key->data, key->size);
1069 
1070  key_cache_len = key->size;
1071  key_cache_updated = time(NULL);
1072  }
1073 
1074  return 0;
1075 }
1076 
1077 int
1078 lrmd_tls_set_key(gnutls_datum_t * key)
1079 {
1080  int rc = 0;
1081  const char *specific_location = getenv("PCMK_authkey_location");
1082 
1083  if (set_key(key, specific_location) == 0) {
1084  crm_debug("Using custom authkey location %s", specific_location);
1085  return 0;
1086 
1087  } else if (specific_location) {
1088  crm_err("No valid lrmd remote key found at %s, trying default location", specific_location);
1089  }
1090 
1091  if (set_key(key, DEFAULT_REMOTE_KEY_LOCATION) != 0) {
1092  rc = set_key(key, ALT_REMOTE_KEY_LOCATION);
1093  }
1094 
1095  if (rc) {
1096  crm_err("No valid lrmd remote key found at %s", DEFAULT_REMOTE_KEY_LOCATION);
1097  return -1;
1098  }
1099 
1100  return rc;
1101 }
1102 
1103 static void
1104 lrmd_gnutls_global_init(void)
1105 {
1106  static int gnutls_init = 0;
1107 
1108  if (!gnutls_init) {
1109  crm_gnutls_global_init();
1110  }
1111  gnutls_init = 1;
1112 }
1113 #endif
1114 
1115 static void
1116 report_async_connection_result(lrmd_t * lrmd, int rc)
1117 {
1118  lrmd_private_t *native = lrmd->private;
1119 
1120  if (native->callback) {
1121  lrmd_event_data_t event = { 0, };
1122  event.type = lrmd_event_connect;
1123  event.remote_nodename = native->remote_nodename;
1124  event.connection_rc = rc;
1125  native->callback(&event);
1126  }
1127 }
1128 
1129 #ifdef HAVE_GNUTLS_GNUTLS_H
1130 static void
1131 lrmd_tcp_connect_cb(void *userdata, int sock)
1132 {
1133  lrmd_t *lrmd = userdata;
1134  lrmd_private_t *native = lrmd->private;
1135  char name[256] = { 0, };
1136  static struct mainloop_fd_callbacks lrmd_tls_callbacks = {
1137  .dispatch = lrmd_tls_dispatch,
1138  .destroy = lrmd_tls_connection_destroy,
1139  };
1140  int rc = sock;
1141  gnutls_datum_t psk_key = { NULL, 0 };
1142 
1143  native->async_timer = 0;
1144 
1145  if (rc < 0) {
1146  lrmd_tls_connection_destroy(lrmd);
1147  crm_info("remote lrmd connect to %s at port %d failed", native->server, native->port);
1148  report_async_connection_result(lrmd, rc);
1149  return;
1150  }
1151 
1152  /* TODO continue with tls stuff now that tcp connect passed. make this async as well soon
1153  * to avoid all blocking code in the client. */
1154  native->sock = sock;
1155 
1156  if (lrmd_tls_set_key(&psk_key) != 0) {
1157  lrmd_tls_connection_destroy(lrmd);
1158  return;
1159  }
1160 
1161  gnutls_psk_allocate_client_credentials(&native->psk_cred_c);
1162  gnutls_psk_set_client_credentials(native->psk_cred_c, DEFAULT_REMOTE_USERNAME, &psk_key, GNUTLS_PSK_KEY_RAW);
1163  gnutls_free(psk_key.data);
1164 
1165  native->remote->tls_session = create_psk_tls_session(sock, GNUTLS_CLIENT, native->psk_cred_c);
1166 
1167  if (crm_initiate_client_tls_handshake(native->remote, LRMD_CLIENT_HANDSHAKE_TIMEOUT) != 0) {
1168  crm_warn("Client tls handshake failed for server %s:%d. Disconnecting", native->server,
1169  native->port);
1170  gnutls_deinit(*native->remote->tls_session);
1171  gnutls_free(native->remote->tls_session);
1172  native->remote->tls_session = NULL;
1173  lrmd_tls_connection_destroy(lrmd);
1174  report_async_connection_result(lrmd, -1);
1175  return;
1176  }
1177 
1178  crm_info("Remote lrmd client TLS connection established with server %s:%d", native->server,
1179  native->port);
1180 
1181  snprintf(name, 128, "remote-lrmd-%s:%d", native->server, native->port);
1182 
1183  native->process_notify = mainloop_add_trigger(G_PRIORITY_HIGH, lrmd_tls_dispatch, lrmd);
1184  native->source =
1185  mainloop_add_fd(name, G_PRIORITY_HIGH, native->sock, lrmd, &lrmd_tls_callbacks);
1186 
1187  rc = lrmd_handshake(lrmd, name);
1188  report_async_connection_result(lrmd, rc);
1189 
1190  return;
1191 }
1192 
1193 static int
1194 lrmd_tls_connect_async(lrmd_t * lrmd, int timeout /*ms */ )
1195 {
1196  int rc = -1;
1197  int sock = 0;
1198  int timer_id = 0;
1199 
1200  lrmd_private_t *native = lrmd->private;
1201 
1202  lrmd_gnutls_global_init();
1203 
1204  sock = crm_remote_tcp_connect_async(native->server, native->port, timeout, &timer_id, lrmd,
1205  lrmd_tcp_connect_cb);
1206 
1207  if (sock != -1) {
1208  native->sock = sock;
1209  rc = 0;
1210  native->async_timer = timer_id;
1211  }
1212 
1213  return rc;
1214 }
1215 
1216 static int
1217 lrmd_tls_connect(lrmd_t * lrmd, int *fd)
1218 {
1219  static struct mainloop_fd_callbacks lrmd_tls_callbacks = {
1220  .dispatch = lrmd_tls_dispatch,
1221  .destroy = lrmd_tls_connection_destroy,
1222  };
1223 
1224  lrmd_private_t *native = lrmd->private;
1225  int sock;
1226  gnutls_datum_t psk_key = { NULL, 0 };
1227 
1228  lrmd_gnutls_global_init();
1229 
1230  sock = crm_remote_tcp_connect(native->server, native->port);
1231  if (sock < 0) {
1232  crm_warn("Could not establish remote lrmd connection to %s", native->server);
1233  lrmd_tls_connection_destroy(lrmd);
1234  return -ENOTCONN;
1235  }
1236 
1237  native->sock = sock;
1238 
1239  if (lrmd_tls_set_key(&psk_key) != 0) {
1240  lrmd_tls_connection_destroy(lrmd);
1241  return -1;
1242  }
1243 
1244  gnutls_psk_allocate_client_credentials(&native->psk_cred_c);
1245  gnutls_psk_set_client_credentials(native->psk_cred_c, DEFAULT_REMOTE_USERNAME, &psk_key, GNUTLS_PSK_KEY_RAW);
1246  gnutls_free(psk_key.data);
1247 
1248  native->remote->tls_session = create_psk_tls_session(sock, GNUTLS_CLIENT, native->psk_cred_c);
1249 
1250  if (crm_initiate_client_tls_handshake(native->remote, LRMD_CLIENT_HANDSHAKE_TIMEOUT) != 0) {
1251  crm_err("Session creation for %s:%d failed", native->server, native->port);
1252  gnutls_deinit(*native->remote->tls_session);
1253  gnutls_free(native->remote->tls_session);
1254  native->remote->tls_session = NULL;
1255  lrmd_tls_connection_destroy(lrmd);
1256  return -1;
1257  }
1258 
1259  crm_info("Remote lrmd client TLS connection established with server %s:%d", native->server,
1260  native->port);
1261 
1262  if (fd) {
1263  *fd = sock;
1264  } else {
1265  char name[256] = { 0, };
1266  snprintf(name, 128, "remote-lrmd-%s:%d", native->server, native->port);
1267 
1268  native->process_notify = mainloop_add_trigger(G_PRIORITY_HIGH, lrmd_tls_dispatch, lrmd);
1269  native->source =
1270  mainloop_add_fd(name, G_PRIORITY_HIGH, native->sock, lrmd, &lrmd_tls_callbacks);
1271  }
1272  return pcmk_ok;
1273 }
1274 #endif
1275 
1276 static int
1277 lrmd_api_connect(lrmd_t * lrmd, const char *name, int *fd)
1278 {
1279  int rc = -ENOTCONN;
1280  lrmd_private_t *native = lrmd->private;
1281 
1282  switch (native->type) {
1283  case CRM_CLIENT_IPC:
1284  rc = lrmd_ipc_connect(lrmd, fd);
1285  break;
1286 #ifdef HAVE_GNUTLS_GNUTLS_H
1287  case CRM_CLIENT_TLS:
1288  rc = lrmd_tls_connect(lrmd, fd);
1289  break;
1290 #endif
1291  default:
1292  crm_err("Unsupported connection type: %d", native->type);
1293  }
1294 
1295  if (rc == pcmk_ok) {
1296  rc = lrmd_handshake(lrmd, name);
1297  }
1298 
1299  return rc;
1300 }
1301 
1302 static int
1303 lrmd_api_connect_async(lrmd_t * lrmd, const char *name, int timeout)
1304 {
1305  int rc = 0;
1306  lrmd_private_t *native = lrmd->private;
1307 
1308  if (!native->callback) {
1309  crm_err("Async connect not possible, no lrmd client callback set.");
1310  return -1;
1311  }
1312 
1313  switch (native->type) {
1314  case CRM_CLIENT_IPC:
1315  /* fake async connection with ipc. it should be fast
1316  * enough that we gain very little from async */
1317  rc = lrmd_api_connect(lrmd, name, NULL);
1318  if (!rc) {
1319  report_async_connection_result(lrmd, rc);
1320  }
1321  break;
1322 #ifdef HAVE_GNUTLS_GNUTLS_H
1323  case CRM_CLIENT_TLS:
1324  rc = lrmd_tls_connect_async(lrmd, timeout);
1325  if (rc) {
1326  /* connection failed, report rc now */
1327  report_async_connection_result(lrmd, rc);
1328  }
1329  break;
1330 #endif
1331  default:
1332  crm_err("Unsupported connection type: %d", native->type);
1333  }
1334 
1335  return rc;
1336 }
1337 
1338 static void
1339 lrmd_ipc_disconnect(lrmd_t * lrmd)
1340 {
1341  lrmd_private_t *native = lrmd->private;
1342 
1343  if (native->source != NULL) {
1344  /* Attached to mainloop */
1345  mainloop_del_ipc_client(native->source);
1346  native->source = NULL;
1347  native->ipc = NULL;
1348 
1349  } else if (native->ipc) {
1350  /* Not attached to mainloop */
1351  crm_ipc_t *ipc = native->ipc;
1352 
1353  native->ipc = NULL;
1354  crm_ipc_close(ipc);
1355  crm_ipc_destroy(ipc);
1356  }
1357 }
1358 
1359 #ifdef HAVE_GNUTLS_GNUTLS_H
1360 static void
1361 lrmd_tls_disconnect(lrmd_t * lrmd)
1362 {
1363  lrmd_private_t *native = lrmd->private;
1364 
1365  if (native->remote->tls_session) {
1366  gnutls_bye(*native->remote->tls_session, GNUTLS_SHUT_RDWR);
1367  gnutls_deinit(*native->remote->tls_session);
1368  gnutls_free(native->remote->tls_session);
1369  native->remote->tls_session = 0;
1370  }
1371 
1372  if (native->async_timer) {
1373  g_source_remove(native->async_timer);
1374  native->async_timer = 0;
1375  }
1376 
1377  if (native->source != NULL) {
1378  /* Attached to mainloop */
1379  mainloop_del_ipc_client(native->source);
1380  native->source = NULL;
1381 
1382  } else if (native->sock) {
1383  close(native->sock);
1384  native->sock = 0;
1385  }
1386 
1387  if (native->pending_notify) {
1388  g_list_free_full(native->pending_notify, lrmd_free_xml);
1389  native->pending_notify = NULL;
1390  }
1391 }
1392 #endif
1393 
1394 static int
1395 lrmd_api_disconnect(lrmd_t * lrmd)
1396 {
1397  lrmd_private_t *native = lrmd->private;
1398 
1399  crm_info("Disconnecting from %d lrmd service", native->type);
1400  switch (native->type) {
1401  case CRM_CLIENT_IPC:
1402  lrmd_ipc_disconnect(lrmd);
1403  break;
1404 #ifdef HAVE_GNUTLS_GNUTLS_H
1405  case CRM_CLIENT_TLS:
1406  lrmd_tls_disconnect(lrmd);
1407  break;
1408 #endif
1409  default:
1410  crm_err("Unsupported connection type: %d", native->type);
1411  }
1412 
1413  free(native->token);
1414  native->token = NULL;
1415 
1416  free(native->peer_version);
1417  native->peer_version = NULL;
1418  return 0;
1419 }
1420 
1421 static int
1422 lrmd_api_register_rsc(lrmd_t * lrmd,
1423  const char *rsc_id,
1424  const char *class,
1425  const char *provider, const char *type, enum lrmd_call_options options)
1426 {
1427  int rc = pcmk_ok;
1428  xmlNode *data = NULL;
1429 
1430  if (!class || !type || !rsc_id) {
1431  return -EINVAL;
1432  }
1433  if (safe_str_eq(class, "ocf") && !provider) {
1434  return -EINVAL;
1435  }
1436 
1437  data = create_xml_node(NULL, F_LRMD_RSC);
1438 
1439  crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
1440  crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
1441  crm_xml_add(data, F_LRMD_CLASS, class);
1442  crm_xml_add(data, F_LRMD_PROVIDER, provider);
1443  crm_xml_add(data, F_LRMD_TYPE, type);
1444  rc = lrmd_send_command(lrmd, LRMD_OP_RSC_REG, data, NULL, 0, options, TRUE);
1445  free_xml(data);
1446 
1447  return rc;
1448 }
1449 
1450 static int
1451 lrmd_api_unregister_rsc(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options)
1452 {
1453  int rc = pcmk_ok;
1454  xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
1455 
1456  crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
1457  crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
1458  rc = lrmd_send_command(lrmd, LRMD_OP_RSC_UNREG, data, NULL, 0, options, TRUE);
1459  free_xml(data);
1460 
1461  return rc;
1462 }
1463 
1466 {
1467  lrmd_rsc_info_t *copy = NULL;
1468 
1469  copy = calloc(1, sizeof(lrmd_rsc_info_t));
1470 
1471  copy->id = strdup(rsc_info->id);
1472  copy->type = strdup(rsc_info->type);
1473  copy->class = strdup(rsc_info->class);
1474  if (rsc_info->provider) {
1475  copy->provider = strdup(rsc_info->provider);
1476  }
1477 
1478  return copy;
1479 }
1480 
1481 void
1483 {
1484  if (!rsc_info) {
1485  return;
1486  }
1487  free(rsc_info->id);
1488  free(rsc_info->type);
1489  free(rsc_info->class);
1490  free(rsc_info->provider);
1491  free(rsc_info);
1492 }
1493 
1494 static lrmd_rsc_info_t *
1495 lrmd_api_get_rsc_info(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options options)
1496 {
1497  lrmd_rsc_info_t *rsc_info = NULL;
1498  xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
1499  xmlNode *output = NULL;
1500  const char *class = NULL;
1501  const char *provider = NULL;
1502  const char *type = NULL;
1503 
1504  crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
1505  crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
1506  lrmd_send_command(lrmd, LRMD_OP_RSC_INFO, data, &output, 0, options, TRUE);
1507  free_xml(data);
1508 
1509  if (!output) {
1510  return NULL;
1511  }
1512 
1513  class = crm_element_value(output, F_LRMD_CLASS);
1514  provider = crm_element_value(output, F_LRMD_PROVIDER);
1515  type = crm_element_value(output, F_LRMD_TYPE);
1516 
1517  if (!class || !type) {
1518  free_xml(output);
1519  return NULL;
1520  } else if (safe_str_eq(class, "ocf") && !provider) {
1521  free_xml(output);
1522  return NULL;
1523  }
1524 
1525  rsc_info = calloc(1, sizeof(lrmd_rsc_info_t));
1526  rsc_info->id = strdup(rsc_id);
1527  rsc_info->class = strdup(class);
1528  if (provider) {
1529  rsc_info->provider = strdup(provider);
1530  }
1531  rsc_info->type = strdup(type);
1532 
1533  free_xml(output);
1534  return rsc_info;
1535 }
1536 
1537 static void
1538 lrmd_api_set_callback(lrmd_t * lrmd, lrmd_event_callback callback)
1539 {
1540  lrmd_private_t *native = lrmd->private;
1541 
1542  native->callback = callback;
1543 }
1544 
1545 void
1546 lrmd_internal_set_proxy_callback(lrmd_t * lrmd, void *userdata, void (*callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg))
1547 {
1548  lrmd_private_t *native = lrmd->private;
1549 
1550  native->proxy_callback = callback;
1551  native->proxy_callback_userdata = userdata;
1552 }
1553 
1554 void
1555 lrmd_internal_proxy_dispatch(lrmd_t *lrmd, xmlNode *msg)
1556 {
1557  lrmd_private_t *native = lrmd->private;
1558 
1559  if (native->proxy_callback) {
1560  crm_log_xml_trace(msg, "PROXY_INBOUND");
1561  native->proxy_callback(lrmd, native->proxy_callback_userdata, msg);
1562  }
1563 }
1564 
1565 int
1566 lrmd_internal_proxy_send(lrmd_t * lrmd, xmlNode *msg)
1567 {
1568  if (lrmd == NULL) {
1569  return -ENOTCONN;
1570  }
1572 
1573  crm_log_xml_trace(msg, "PROXY_OUTBOUND");
1574  return lrmd_send_xml_no_reply(lrmd, msg);
1575 }
1576 
1577 static int
1578 stonith_get_metadata(const char *provider, const char *type, char **output)
1579 {
1580  int rc = pcmk_ok;
1581  stonith_t *stonith_api = stonith_api_new();
1582 
1583  if(stonith_api) {
1584  stonith_api->cmds->metadata(stonith_api, st_opt_sync_call, type, provider, output, 0);
1585  stonith_api->cmds->free(stonith_api);
1586  }
1587  if (*output == NULL) {
1588  rc = -EIO;
1589  }
1590  return rc;
1591 }
1592 
1593 #define lsb_metadata_template \
1594  "<?xml version='1.0'?>\n" \
1595  "<!DOCTYPE resource-agent SYSTEM 'ra-api-1.dtd'>\n" \
1596  "<resource-agent name='%s' version='0.1'>\n" \
1597  " <version>1.0</version>\n" \
1598  " <longdesc lang='en'>\n" \
1599  " %s\n" \
1600  " </longdesc>\n" \
1601  " <shortdesc lang='en'>%s</shortdesc>\n" \
1602  " <parameters>\n" \
1603  " </parameters>\n" \
1604  " <actions>\n" \
1605  " <action name='meta-data' timeout='5' />\n" \
1606  " <action name='start' timeout='15' />\n" \
1607  " <action name='stop' timeout='15' />\n" \
1608  " <action name='status' timeout='15' />\n" \
1609  " <action name='restart' timeout='15' />\n" \
1610  " <action name='force-reload' timeout='15' />\n" \
1611  " <action name='monitor' timeout='15' interval='15' />\n" \
1612  " </actions>\n" \
1613  " <special tag='LSB'>\n" \
1614  " <Provides>%s</Provides>\n" \
1615  " <Required-Start>%s</Required-Start>\n" \
1616  " <Required-Stop>%s</Required-Stop>\n" \
1617  " <Should-Start>%s</Should-Start>\n" \
1618  " <Should-Stop>%s</Should-Stop>\n" \
1619  " <Default-Start>%s</Default-Start>\n" \
1620  " <Default-Stop>%s</Default-Stop>\n" \
1621  " </special>\n" \
1622  "</resource-agent>\n"
1623 
1624 #define LSB_INITSCRIPT_INFOBEGIN_TAG "### BEGIN INIT INFO"
1625 #define LSB_INITSCRIPT_INFOEND_TAG "### END INIT INFO"
1626 #define PROVIDES "# Provides:"
1627 #define REQ_START "# Required-Start:"
1628 #define REQ_STOP "# Required-Stop:"
1629 #define SHLD_START "# Should-Start:"
1630 #define SHLD_STOP "# Should-Stop:"
1631 #define DFLT_START "# Default-Start:"
1632 #define DFLT_STOP "# Default-Stop:"
1633 #define SHORT_DSCR "# Short-Description:"
1634 #define DESCRIPTION "# Description:"
1635 
1636 #define lsb_meta_helper_free_value(m) \
1637  do { \
1638  if ((m) != NULL) { \
1639  xmlFree(m); \
1640  (m) = NULL; \
1641  } \
1642  } while(0)
1643 
1644 /*
1645  * \internal
1646  * \brief Grab an LSB header value
1647  *
1648  * \param[in] line Line read from LSB init script
1649  * \param[in/out] value If not set, will be set to XML-safe copy of value
1650  * \param[in] prefix Set value if line starts with this pattern
1651  *
1652  * \return TRUE if value was set, FALSE otherwise
1653  */
1654 static inline gboolean
1655 lsb_meta_helper_get_value(const char *line, char **value, const char *prefix)
1656 {
1657  if (!*value && !strncasecmp(line, prefix, strlen(prefix))) {
1658  *value = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST line+strlen(prefix));
1659  return TRUE;
1660  }
1661  return FALSE;
1662 }
1663 
1664 static int
1665 lsb_get_metadata(const char *type, char **output)
1666 {
1667  char ra_pathname[PATH_MAX] = { 0, };
1668  FILE *fp;
1669  char buffer[1024];
1670  char *provides = NULL;
1671  char *req_start = NULL;
1672  char *req_stop = NULL;
1673  char *shld_start = NULL;
1674  char *shld_stop = NULL;
1675  char *dflt_start = NULL;
1676  char *dflt_stop = NULL;
1677  char *s_dscrpt = NULL;
1678  char *xml_l_dscrpt = NULL;
1679  int offset = 0;
1680  int max = 2048;
1681  char description[max];
1682 
1683  if(type[0] == '/') {
1684  snprintf(ra_pathname, sizeof(ra_pathname), "%s", type);
1685  } else {
1686  snprintf(ra_pathname, sizeof(ra_pathname), "%s/%s", LSB_ROOT_DIR, type);
1687  }
1688 
1689  crm_trace("Looking into %s", ra_pathname);
1690  if (!(fp = fopen(ra_pathname, "r"))) {
1691  return -errno;
1692  }
1693 
1694  /* Enter into the lsb-compliant comment block */
1695  while (fgets(buffer, sizeof(buffer), fp)) {
1696 
1697  /* Now suppose each of the following eight arguments contain only one line */
1698  if (lsb_meta_helper_get_value(buffer, &provides, PROVIDES)) {
1699  continue;
1700  }
1701  if (lsb_meta_helper_get_value(buffer, &req_start, REQ_START)) {
1702  continue;
1703  }
1704  if (lsb_meta_helper_get_value(buffer, &req_stop, REQ_STOP)) {
1705  continue;
1706  }
1707  if (lsb_meta_helper_get_value(buffer, &shld_start, SHLD_START)) {
1708  continue;
1709  }
1710  if (lsb_meta_helper_get_value(buffer, &shld_stop, SHLD_STOP)) {
1711  continue;
1712  }
1713  if (lsb_meta_helper_get_value(buffer, &dflt_start, DFLT_START)) {
1714  continue;
1715  }
1716  if (lsb_meta_helper_get_value(buffer, &dflt_stop, DFLT_STOP)) {
1717  continue;
1718  }
1719  if (lsb_meta_helper_get_value(buffer, &s_dscrpt, SHORT_DSCR)) {
1720  continue;
1721  }
1722 
1723  /* Long description may cross multiple lines */
1724  if (offset == 0 && (0 == strncasecmp(buffer, DESCRIPTION, strlen(DESCRIPTION)))) {
1725  /* Between # and keyword, more than one space, or a tab
1726  * character, indicates the continuation line.
1727  *
1728  * Extracted from LSB init script standard
1729  */
1730  while (fgets(buffer, sizeof(buffer), fp)) {
1731  if (!strncmp(buffer, "# ", 3) || !strncmp(buffer, "#\t", 2)) {
1732  buffer[0] = ' ';
1733  offset += snprintf(description+offset, max-offset, "%s", buffer);
1734 
1735  } else {
1736  fputs(buffer, fp);
1737  break; /* Long description ends */
1738  }
1739  }
1740  continue;
1741  }
1742 
1743  if (xml_l_dscrpt == NULL && offset > 0) {
1744  xml_l_dscrpt = (char *)xmlEncodeEntitiesReentrant(NULL, BAD_CAST(description));
1745  }
1746 
1747  if (!strncasecmp(buffer, LSB_INITSCRIPT_INFOEND_TAG, strlen(LSB_INITSCRIPT_INFOEND_TAG))) {
1748  /* Get to the out border of LSB comment block */
1749  break;
1750  }
1751  if (buffer[0] != '#') {
1752  break; /* Out of comment block in the beginning */
1753  }
1754  }
1755  fclose(fp);
1756 
1757  *output = crm_strdup_printf(lsb_metadata_template, type,
1758  (xml_l_dscrpt == NULL) ? type : xml_l_dscrpt,
1759  (s_dscrpt == NULL) ? type : s_dscrpt, (provides == NULL) ? "" : provides,
1760  (req_start == NULL) ? "" : req_start, (req_stop == NULL) ? "" : req_stop,
1761  (shld_start == NULL) ? "" : shld_start, (shld_stop == NULL) ? "" : shld_stop,
1762  (dflt_start == NULL) ? "" : dflt_start, (dflt_stop == NULL) ? "" : dflt_stop);
1763 
1764  lsb_meta_helper_free_value(xml_l_dscrpt);
1765  lsb_meta_helper_free_value(s_dscrpt);
1766  lsb_meta_helper_free_value(provides);
1767  lsb_meta_helper_free_value(req_start);
1768  lsb_meta_helper_free_value(req_stop);
1769  lsb_meta_helper_free_value(shld_start);
1770  lsb_meta_helper_free_value(shld_stop);
1771  lsb_meta_helper_free_value(dflt_start);
1772  lsb_meta_helper_free_value(dflt_stop);
1773 
1774  crm_trace("Created fake metadata: %llu",
1775  (unsigned long long) strlen(*output));
1776  return pcmk_ok;
1777 }
1778 
1779 #if SUPPORT_NAGIOS
1780 static int
1781 nagios_get_metadata(const char *type, char **output)
1782 {
1783  int rc = pcmk_ok;
1784  FILE *file_strm = NULL;
1785  int start = 0, length = 0, read_len = 0;
1786  char *metadata_file = NULL;
1787  int len = 36;
1788 
1789  len += strlen(NAGIOS_METADATA_DIR);
1790  len += strlen(type);
1791  metadata_file = calloc(1, len);
1792  CRM_CHECK(metadata_file != NULL, return -ENOMEM);
1793 
1794  sprintf(metadata_file, "%s/%s.xml", NAGIOS_METADATA_DIR, type);
1795  file_strm = fopen(metadata_file, "r");
1796  if (file_strm == NULL) {
1797  crm_err("Metadata file %s does not exist", metadata_file);
1798  free(metadata_file);
1799  return -EIO;
1800  }
1801 
1802  /* see how big the file is */
1803  start = ftell(file_strm);
1804  fseek(file_strm, 0L, SEEK_END);
1805  length = ftell(file_strm);
1806  fseek(file_strm, 0L, start);
1807 
1808  CRM_ASSERT(length >= 0);
1809  CRM_ASSERT(start == ftell(file_strm));
1810 
1811  if (length <= 0) {
1812  crm_info("%s was not valid", metadata_file);
1813  free(*output);
1814  *output = NULL;
1815  rc = -EIO;
1816 
1817  } else {
1818  crm_trace("Reading %d bytes from file", length);
1819  *output = calloc(1, (length + 1));
1820  read_len = fread(*output, 1, length, file_strm);
1821  if (read_len != length) {
1822  crm_err("Calculated and read bytes differ: %d vs. %d", length, read_len);
1823  free(*output);
1824  *output = NULL;
1825  rc = -EIO;
1826  }
1827  }
1828 
1829  fclose(file_strm);
1830  free(metadata_file);
1831  return rc;
1832 }
1833 #endif
1834 
1835 #if SUPPORT_HEARTBEAT
1836 /* strictly speaking, support for class=heartbeat style scripts
1837  * does not require "heartbeat support" to be enabled.
1838  * But since those scripts are part of the "heartbeat" package usually,
1839  * and are very unlikely to be present in any other deployment,
1840  * I leave it inside this ifdef.
1841  *
1842  * Yes, I know, these are legacy and should die,
1843  * or at least be rewritten to be a proper OCF style agent.
1844  * But they exist, and custom scripts following these rules do, too.
1845  *
1846  * Taken from the old "glue" lrmd, see
1847  * http://hg.linux-ha.org/glue/file/0a7add1d9996/lib/plugins/lrm/raexechb.c#l49
1848  * http://hg.linux-ha.org/glue/file/0a7add1d9996/lib/plugins/lrm/raexechb.c#l393
1849  */
1850 
1851 static const char hb_metadata_template[] =
1852 "<?xml version='1.0'?>\n"
1853 "<!DOCTYPE resource-agent SYSTEM 'ra-api-1.dtd'>\n"
1854 "<resource-agent name='%s' version='0.1'>\n"
1855 "<version>1.0</version>\n"
1856 "<longdesc lang='en'>\n"
1857 "%s"
1858 "</longdesc>\n"
1859 "<shortdesc lang='en'>%s</shortdesc>\n"
1860 "<parameters>\n"
1861 "<parameter name='1' unique='1' required='0'>\n"
1862 "<longdesc lang='en'>\n"
1863 "This argument will be passed as the first argument to the "
1864 "heartbeat resource agent (assuming it supports one)\n"
1865 "</longdesc>\n"
1866 "<shortdesc lang='en'>argv[1]</shortdesc>\n"
1867 "<content type='string' default=' ' />\n"
1868 "</parameter>\n"
1869 "<parameter name='2' unique='1' required='0'>\n"
1870 "<longdesc lang='en'>\n"
1871 "This argument will be passed as the second argument to the "
1872 "heartbeat resource agent (assuming it supports one)\n"
1873 "</longdesc>\n"
1874 "<shortdesc lang='en'>argv[2]</shortdesc>\n"
1875 "<content type='string' default=' ' />\n"
1876 "</parameter>\n"
1877 "<parameter name='3' unique='1' required='0'>\n"
1878 "<longdesc lang='en'>\n"
1879 "This argument will be passed as the third argument to the "
1880 "heartbeat resource agent (assuming it supports one)\n"
1881 "</longdesc>\n"
1882 "<shortdesc lang='en'>argv[3]</shortdesc>\n"
1883 "<content type='string' default=' ' />\n"
1884 "</parameter>\n"
1885 "<parameter name='4' unique='1' required='0'>\n"
1886 "<longdesc lang='en'>\n"
1887 "This argument will be passed as the fourth argument to the "
1888 "heartbeat resource agent (assuming it supports one)\n"
1889 "</longdesc>\n"
1890 "<shortdesc lang='en'>argv[4]</shortdesc>\n"
1891 "<content type='string' default=' ' />\n"
1892 "</parameter>\n"
1893 "<parameter name='5' unique='1' required='0'>\n"
1894 "<longdesc lang='en'>\n"
1895 "This argument will be passed as the fifth argument to the "
1896 "heartbeat resource agent (assuming it supports one)\n"
1897 "</longdesc>\n"
1898 "<shortdesc lang='en'>argv[5]</shortdesc>\n"
1899 "<content type='string' default=' ' />\n"
1900 "</parameter>\n"
1901 "</parameters>\n"
1902 "<actions>\n"
1903 "<action name='start' timeout='15' />\n"
1904 "<action name='stop' timeout='15' />\n"
1905 "<action name='status' timeout='15' />\n"
1906 "<action name='monitor' timeout='15' interval='15' start-delay='15' />\n"
1907 "<action name='meta-data' timeout='5' />\n"
1908 "</actions>\n"
1909 "<special tag='heartbeat'>\n"
1910 "</special>\n"
1911 "</resource-agent>\n";
1912 
1913 static int
1914 heartbeat_get_metadata(const char *type, char **output)
1915 {
1916  *output = crm_strdup_printf(hb_metadata_template, type, type, type);
1917  crm_trace("Created fake metadata: %llu",
1918  (unsigned long long) strlen(*output));
1919  return pcmk_ok;
1920 }
1921 #endif
1922 
1923 static int
1924 generic_get_metadata(const char *standard, const char *provider, const char *type, char **output)
1925 {
1926  svc_action_t *action;
1927 
1928  action = resources_action_create(type, standard, provider, type,
1929  "meta-data", 0, 30000, NULL, 0);
1930  if (action == NULL) {
1931  crm_err("Unable to retrieve meta-data for %s:%s:%s", standard, provider, type);
1932  services_action_free(action);
1933  return -EINVAL;
1934  }
1935 
1936  if (!(services_action_sync(action))) {
1937  crm_err("Failed to retrieve meta-data for %s:%s:%s", standard, provider, type);
1938  services_action_free(action);
1939  return -EIO;
1940  }
1941 
1942  if (!action->stdout_data) {
1943  crm_err("Failed to receive meta-data for %s:%s:%s", standard, provider, type);
1944  services_action_free(action);
1945  return -EIO;
1946  }
1947 
1948  *output = strdup(action->stdout_data);
1949  services_action_free(action);
1950 
1951  return pcmk_ok;
1952 }
1953 
1954 static int
1955 lrmd_api_get_metadata(lrmd_t * lrmd,
1956  const char *class,
1957  const char *provider,
1958  const char *type, char **output, enum lrmd_call_options options)
1959 {
1960  if (!class || !type) {
1961  return -EINVAL;
1962  }
1963 
1964  if (safe_str_eq(class, "service")) {
1965  class = resources_find_service_class(type);
1966  }
1967 
1968  if (safe_str_eq(class, "stonith")) {
1969  return stonith_get_metadata(provider, type, output);
1970  } else if (safe_str_eq(class, "lsb")) {
1971  return lsb_get_metadata(type, output);
1972 #if SUPPORT_NAGIOS
1973  } else if (safe_str_eq(class, "nagios")) {
1974  return nagios_get_metadata(type, output);
1975 #endif
1976 #if SUPPORT_HEARTBEAT
1977  } else if (safe_str_eq(class, "heartbeat")) {
1978  return heartbeat_get_metadata(type, output);
1979 #endif
1980  }
1981  return generic_get_metadata(class, provider, type, output);
1982 }
1983 
1984 static int
1985 lrmd_api_exec(lrmd_t * lrmd, const char *rsc_id, const char *action, const char *userdata, int interval, /* ms */
1986  int timeout, /* ms */
1987  int start_delay, /* ms */
1988  enum lrmd_call_options options, lrmd_key_value_t * params)
1989 {
1990  int rc = pcmk_ok;
1991  xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
1992  xmlNode *args = create_xml_node(data, XML_TAG_ATTRS);
1993  lrmd_key_value_t *tmp = NULL;
1994 
1995  crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
1996  crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
1997  crm_xml_add(data, F_LRMD_RSC_ACTION, action);
1998  crm_xml_add(data, F_LRMD_RSC_USERDATA_STR, userdata);
1999  crm_xml_add_int(data, F_LRMD_RSC_INTERVAL, interval);
2000  crm_xml_add_int(data, F_LRMD_TIMEOUT, timeout);
2001  crm_xml_add_int(data, F_LRMD_RSC_START_DELAY, start_delay);
2002 
2003  for (tmp = params; tmp; tmp = tmp->next) {
2004  hash2smartfield((gpointer) tmp->key, (gpointer) tmp->value, args);
2005  }
2006 
2007  rc = lrmd_send_command(lrmd, LRMD_OP_RSC_EXEC, data, NULL, timeout, options, TRUE);
2008  free_xml(data);
2009 
2010  lrmd_key_value_freeall(params);
2011  return rc;
2012 }
2013 
2014 static int
2015 lrmd_api_cancel(lrmd_t * lrmd, const char *rsc_id, const char *action, int interval)
2016 {
2017  int rc = pcmk_ok;
2018  xmlNode *data = create_xml_node(NULL, F_LRMD_RSC);
2019 
2020  crm_xml_add(data, F_LRMD_ORIGIN, __FUNCTION__);
2021  crm_xml_add(data, F_LRMD_RSC_ACTION, action);
2022  crm_xml_add(data, F_LRMD_RSC_ID, rsc_id);
2023  crm_xml_add_int(data, F_LRMD_RSC_INTERVAL, interval);
2024  rc = lrmd_send_command(lrmd, LRMD_OP_RSC_CANCEL, data, NULL, 0, 0, TRUE);
2025  free_xml(data);
2026  return rc;
2027 }
2028 
2029 static int
2030 list_stonith_agents(lrmd_list_t ** resources)
2031 {
2032  int rc = 0;
2033  stonith_t *stonith_api = stonith_api_new();
2034  stonith_key_value_t *stonith_resources = NULL;
2035  stonith_key_value_t *dIter = NULL;
2036 
2037  if(stonith_api) {
2038  stonith_api->cmds->list_agents(stonith_api, st_opt_sync_call, NULL, &stonith_resources, 0);
2039  stonith_api->cmds->free(stonith_api);
2040  }
2041 
2042  for (dIter = stonith_resources; dIter; dIter = dIter->next) {
2043  rc++;
2044  if (resources) {
2045  *resources = lrmd_list_add(*resources, dIter->value);
2046  }
2047  }
2048 
2049  stonith_key_value_freeall(stonith_resources, 1, 0);
2050  return rc;
2051 }
2052 
2053 static int
2054 lrmd_api_list_agents(lrmd_t * lrmd, lrmd_list_t ** resources, const char *class,
2055  const char *provider)
2056 {
2057  int rc = 0;
2058 
2059  if (safe_str_eq(class, "stonith")) {
2060  rc += list_stonith_agents(resources);
2061 
2062  } else {
2063  GListPtr gIter = NULL;
2064  GList *agents = resources_list_agents(class, provider);
2065 
2066  for (gIter = agents; gIter != NULL; gIter = gIter->next) {
2067  *resources = lrmd_list_add(*resources, (const char *)gIter->data);
2068  rc++;
2069  }
2070  g_list_free_full(agents, free);
2071 
2072  if (!class) {
2073  rc += list_stonith_agents(resources);
2074  }
2075  }
2076 
2077  if (rc == 0) {
2078  crm_notice("No agents found for class %s", class);
2079  rc = -EPROTONOSUPPORT;
2080  }
2081  return rc;
2082 }
2083 
2084 static int
2085 does_provider_have_agent(const char *agent, const char *provider, const char *class)
2086 {
2087  int found = 0;
2088  GList *agents = NULL;
2089  GListPtr gIter2 = NULL;
2090 
2091  agents = resources_list_agents(class, provider);
2092  for (gIter2 = agents; gIter2 != NULL; gIter2 = gIter2->next) {
2093  if (safe_str_eq(agent, gIter2->data)) {
2094  found = 1;
2095  }
2096  }
2097  g_list_free_full(agents, free);
2098 
2099  return found;
2100 }
2101 
2102 static int
2103 lrmd_api_list_ocf_providers(lrmd_t * lrmd, const char *agent, lrmd_list_t ** providers)
2104 {
2105  int rc = pcmk_ok;
2106  char *provider = NULL;
2107  GList *ocf_providers = NULL;
2108  GListPtr gIter = NULL;
2109 
2110  ocf_providers = resources_list_providers("ocf");
2111 
2112  for (gIter = ocf_providers; gIter != NULL; gIter = gIter->next) {
2113  provider = gIter->data;
2114  if (!agent || does_provider_have_agent(agent, provider, "ocf")) {
2115  *providers = lrmd_list_add(*providers, (const char *)gIter->data);
2116  rc++;
2117  }
2118  }
2119 
2120  g_list_free_full(ocf_providers, free);
2121  return rc;
2122 }
2123 
2124 static int
2125 lrmd_api_list_standards(lrmd_t * lrmd, lrmd_list_t ** supported)
2126 {
2127  int rc = 0;
2128  GList *standards = NULL;
2129  GListPtr gIter = NULL;
2130 
2131  standards = resources_list_standards();
2132 
2133  for (gIter = standards; gIter != NULL; gIter = gIter->next) {
2134  *supported = lrmd_list_add(*supported, (const char *)gIter->data);
2135  rc++;
2136  }
2137 
2138  if (list_stonith_agents(NULL) > 0) {
2139  *supported = lrmd_list_add(*supported, "stonith");
2140  rc++;
2141  }
2142 
2143  g_list_free_full(standards, free);
2144  return rc;
2145 }
2146 
2147 lrmd_t *
2149 {
2150  lrmd_t *new_lrmd = NULL;
2151  lrmd_private_t *pvt = NULL;
2152 
2153  new_lrmd = calloc(1, sizeof(lrmd_t));
2154  pvt = calloc(1, sizeof(lrmd_private_t));
2155  pvt->remote = calloc(1, sizeof(crm_remote_t));
2156  new_lrmd->cmds = calloc(1, sizeof(lrmd_api_operations_t));
2157 
2158  pvt->type = CRM_CLIENT_IPC;
2159  new_lrmd->private = pvt;
2160 
2161  new_lrmd->cmds->connect = lrmd_api_connect;
2162  new_lrmd->cmds->connect_async = lrmd_api_connect_async;
2163  new_lrmd->cmds->is_connected = lrmd_api_is_connected;
2164  new_lrmd->cmds->poke_connection = lrmd_api_poke_connection;
2165  new_lrmd->cmds->disconnect = lrmd_api_disconnect;
2166  new_lrmd->cmds->register_rsc = lrmd_api_register_rsc;
2167  new_lrmd->cmds->unregister_rsc = lrmd_api_unregister_rsc;
2168  new_lrmd->cmds->get_rsc_info = lrmd_api_get_rsc_info;
2169  new_lrmd->cmds->set_callback = lrmd_api_set_callback;
2170  new_lrmd->cmds->get_metadata = lrmd_api_get_metadata;
2171  new_lrmd->cmds->exec = lrmd_api_exec;
2172  new_lrmd->cmds->cancel = lrmd_api_cancel;
2173  new_lrmd->cmds->list_agents = lrmd_api_list_agents;
2174  new_lrmd->cmds->list_ocf_providers = lrmd_api_list_ocf_providers;
2175  new_lrmd->cmds->list_standards = lrmd_api_list_standards;
2176 
2177  return new_lrmd;
2178 }
2179 
2180 lrmd_t *
2181 lrmd_remote_api_new(const char *nodename, const char *server, int port)
2182 {
2183 #ifdef HAVE_GNUTLS_GNUTLS_H
2184  lrmd_t *new_lrmd = lrmd_api_new();
2185  lrmd_private_t *native = new_lrmd->private;
2186 
2187  if (!nodename && !server) {
2188  lrmd_api_delete(new_lrmd);
2189  return NULL;
2190  }
2191 
2192  native->type = CRM_CLIENT_TLS;
2193  native->remote_nodename = nodename ? strdup(nodename) : strdup(server);
2194  native->server = server ? strdup(server) : strdup(nodename);
2195  native->port = port;
2196  if (native->port == 0) {
2197  const char *remote_port_str = getenv("PCMK_remote_port");
2198  native->port = remote_port_str ? atoi(remote_port_str) : DEFAULT_REMOTE_PORT;
2199  }
2200 
2201  return new_lrmd;
2202 #else
2203  crm_err("GNUTLS is not enabled for this build, remote LRMD client can not be created");
2204  return NULL;
2205 #endif
2206 
2207 }
2208 
2209 void
2211 {
2212  if (!lrmd) {
2213  return;
2214  }
2215  lrmd->cmds->disconnect(lrmd); /* no-op if already disconnected */
2216  free(lrmd->cmds);
2217  if (lrmd->private) {
2218  lrmd_private_t *native = lrmd->private;
2219 
2220 #ifdef HAVE_GNUTLS_GNUTLS_H
2221  free(native->server);
2222 #endif
2223  free(native->remote_nodename);
2224  free(native->remote);
2225  free(native->token);
2226  free(native->peer_version);
2227  }
2228 
2229  free(lrmd->private);
2230  free(lrmd);
2231 }
Services API.
#define F_LRMD_RSC
Definition: lrmd.h:82
#define CRM_CHECK(expr, failure_action)
Definition: logging.h:164
bool crm_ipc_connect(crm_ipc_t *client)
Establish an IPC connection to a Pacemaker component.
Definition: ipc.c:806
A dumping ground.
client_type
Definition: ipcs.h:33
#define F_TYPE
Definition: msg_xml.h:34
#define crm_notice(fmt, args...)
Definition: logging.h:250
GHashTable * xml2list(xmlNode *parent)
Definition: xml.c:5045
lrmd_event_data_t * lrmd_copy_event(lrmd_event_data_t *event)
Definition: lrmd_client.c:196
gboolean safe_str_neq(const char *a, const char *b)
Definition: utils.c:696
void services_action_free(svc_action_t *op)
Definition: services.c:417
lrmd_call_options
Definition: lrmd.h:161
int crm_remote_ready(crm_remote_t *remote, int total_timeout)
Definition: remote.c:449
gboolean crm_remote_recv(crm_remote_t *remote, int total_timeout, int *disconnected)
Definition: remote.c:607
const char * user_data
Definition: lrmd.h:196
mainloop_io_t * mainloop_add_fd(const char *name, int priority, int fd, void *userdata, struct mainloop_fd_callbacks *callbacks)
Definition: mainloop.c:808
#define F_LRMD_IS_IPC_PROVIDER
Definition: lrmd.h:48
const char * rsc_id
Definition: lrmd.h:192
char * class
Definition: lrmd.h:248
#define F_LRMD_IPC_SESSION
Definition: lrmd.h:105
#define F_LRMD_RSC_EXEC_TIME
Definition: lrmd.h:70
int(* list_agents)(stonith_t *stonith, int call_options, const char *namespace, stonith_key_value_t **devices, int timeout)
Retrieve a list of installed stonith agents.
Definition: stonith-ng.h:211
#define F_LRMD_RSC_ACTION
Definition: lrmd.h:74
#define LRMD_OP_RSC_CANCEL
Definition: lrmd.h:87
int crm_ipc_get_fd(crm_ipc_t *client)
Definition: ipc.c:875
#define LRMD_OP_CHECK
Definition: lrmd.h:93
#define F_LRMD_ORIGIN
Definition: lrmd.h:66
#define F_LRMD_RSC_OUTPUT
Definition: lrmd.h:76
int(* connect_async)(lrmd_t *lrmd, const char *client_name, int timeout)
Establish an connection to lrmd, don&#39;t block while connecting.
Definition: lrmd.h:284
#define SHLD_START
Definition: lrmd_client.c:1629
void(* lrmd_event_callback)(lrmd_event_data_t *event)
Definition: lrmd.h:255
#define pcmk_ok
Definition: error.h:42
svc_action_t * resources_action_create(const char *name, const char *standard, const char *provider, const char *agent, const char *action, int interval, int timeout, GHashTable *params, enum svc_action_flags flags)
Create a new resource action.
Definition: services.c:103
struct stonith_key_value_s * next
Definition: stonith-ng.h:77
void lrmd_key_value_freeall(lrmd_key_value_t *head)
Definition: lrmd_client.c:176
char * id
Definition: lrmd.h:246
#define XML_TAG_ATTRS
Definition: msg_xml.h:178
struct mainloop_io_s mainloop_io_t
Definition: mainloop.h:35
void mainloop_set_trigger(crm_trigger_t *source)
Definition: mainloop.c:223
#define DEFAULT_REMOTE_USERNAME
Definition: lrmd.h:44
Local Resource Manager.
#define MAX_TLS_RECV_WAIT
Definition: lrmd_client.c:55
#define F_LRMD_EXEC_RC
Definition: lrmd.h:59
const char * output
Definition: lrmd.h:214
void g_hash_destroy_str(gpointer data)
Definition: utils.c:615
int lrmd_internal_proxy_send(lrmd_t *lrmd, xmlNode *msg)
Definition: lrmd_client.c:1566
#define F_LRMD_WATCHDOG
Definition: lrmd.h:62
#define SHLD_STOP
Definition: lrmd_client.c:1630
#define LRMD_OP_POKE
Definition: lrmd.h:91
lrmd_rsc_info_t * lrmd_copy_rsc_info(lrmd_rsc_info_t *rsc_info)
Definition: lrmd_client.c:1465
long crm_ipc_read(crm_ipc_t *client)
Definition: ipc.c:973
#define REQ_STOP
Definition: lrmd_client.c:1628
gboolean mainloop_destroy_trigger(crm_trigger_t *source)
Definition: mainloop.c:231
#define REQ_START
Definition: lrmd_client.c:1627
int(* exec)(lrmd_t *lrmd, const char *rsc_id, const char *action, const char *userdata, int interval, int timeout, int start_delay, enum lrmd_call_options options, lrmd_key_value_t *params)
Issue a command on a resource.
Definition: lrmd.h:369
int(* cancel)(lrmd_t *lrmd, const char *rsc_id, const char *action, int interval)
Cancel a recurring command.
Definition: lrmd.h:398
#define F_LRMD_RSC_ID
Definition: lrmd.h:73
int crm_remote_tcp_connect(const char *host, int port)
Definition: remote.c:927
Wrappers for and extensions to glib mainloop.
#define F_LRMD_PROTOCOL_VERSION
Definition: lrmd.h:50
#define F_LRMD_RC
Definition: lrmd.h:58
lrmd_t * lrmd_remote_api_new(const char *nodename, const char *server, int port)
Create a new remote lrmd connection using tls backend.
Definition: lrmd_client.c:2181
stonith_t * stonith_api_new(void)
Definition: st_client.c:2462
#define CRM_OP_REGISTER
Definition: crm.h:122
xmlNode * string2xml(const char *input)
Definition: xml.c:2960
char version[256]
Definition: plugin.c:84
const char * crm_ipc_buffer(crm_ipc_t *client)
Definition: ipc.c:1019
int remote_proxy_check(lrmd_t *lrmd, GHashTable *hash)
Definition: lrmd_client.c:885
const char * val
Definition: lrmd.h:258
#define LRMD_OP_RSC_UNREG
Definition: lrmd.h:88
#define F_LRMD_CLIENTID
Definition: lrmd.h:49
#define DEFAULT_REMOTE_PORT
Definition: lrmd.h:43
#define DEFAULT_REMOTE_KEY_LOCATION
Definition: lrmd.h:41
struct trigger_s crm_trigger_t
Definition: mainloop.h:34
void hash2smartfield(gpointer key, gpointer value, gpointer user_data)
Definition: xml.c:4988
struct lrmd_private_s lrmd_private_t
#define PROVIDES
Definition: lrmd_client.c:1626
int(* free)(stonith_t *st)
Destroy the stonith api structure.
Definition: stonith-ng.h:126
int(* dispatch)(gpointer userdata)
Definition: mainloop.h:90
#define DESCRIPTION
Definition: lrmd_client.c:1634
void * params
Definition: lrmd.h:229
#define crm_warn(fmt, args...)
Definition: logging.h:249
int(* poke_connection)(lrmd_t *lrmd)
Poke lrmd connection to verify it is still capable of serving requests.
Definition: lrmd.h:301
const char * exit_reason
Definition: lrmd.h:238
#define crm_debug(fmt, args...)
Definition: logging.h:253
int crm_initiate_client_tls_handshake(crm_remote_t *remote, int timeout_ms)
#define DFLT_START
Definition: lrmd_client.c:1631
#define lsb_metadata_template
Definition: lrmd_client.c:1593
struct crm_ipc_s crm_ipc_t
Definition: ipc.h:61
#define F_LRMD_RSC_EXIT_REASON
Definition: lrmd.h:77
#define ALT_REMOTE_KEY_LOCATION
Definition: lrmd.h:42
bool lrmd_dispatch(lrmd_t *lrmd)
Use after lrmd_poll returns 1 to read and dispatch a message.
Definition: lrmd_client.c:442
char * key
Definition: lrmd.h:33
struct lrmd_list_s * next
Definition: lrmd.h:259
gboolean services_action_sync(svc_action_t *op)
Definition: services.c:703
char * stdout_data
Definition: services.h:174
#define LRMD_PROTOCOL_VERSION
Definition: lrmd.h:38
xmlNode * crm_remote_parse_buffer(crm_remote_t *remote)
Definition: remote.c:379
int(* get_metadata)(lrmd_t *lrmd, const char *class, const char *provider, const char *agent, char **output, enum lrmd_call_options options)
Get the metadata documentation for a resource.
Definition: lrmd.h:408
#define F_LRMD_TIMEOUT
Definition: lrmd.h:61
lrmd_rsc_info_t *(* get_rsc_info)(lrmd_t *lrmd, const char *rsc_id, enum lrmd_call_options options)
Retrieve registration info for a rsc.
Definition: lrmd.h:330
void lrmd_list_freeall(lrmd_list_t *head)
Definition: lrmd_client.c:138
int(* connect)(lrmd_t *lrmd, const char *client_name, int *fd)
Connect from the lrmd.
Definition: lrmd.h:272
#define F_LRMD_CALLDATA
Definition: lrmd.h:57
#define crm_trace(fmt, args...)
Definition: logging.h:254
#define F_LRMD_TYPE
Definition: lrmd.h:65
#define F_LRMD_CLASS
Definition: lrmd.h:63
crm_trigger_t * mainloop_add_trigger(int priority, int(*dispatch)(gpointer user_data), gpointer userdata)
Definition: mainloop.c:211
#define LRMD_OP_NEW_CLIENT
Definition: lrmd.h:92
xmlNode * create_xml_node(xmlNode *parent, const char *name)
Definition: xml.c:2796
int crm_element_value_int(xmlNode *data, const char *name, int *dest)
Definition: xml.c:4009
const char * crm_element_value(xmlNode *data, const char *name)
Definition: xml.c:5842
void stonith_key_value_freeall(stonith_key_value_t *kvp, int keys, int values)
Definition: st_client.c:2541
#define ECOMM
Definition: portability.h:231
void mainloop_del_ipc_client(mainloop_io_t *client)
Definition: mainloop.c:793
void crm_ipc_destroy(crm_ipc_t *client)
Definition: ipc.c:852
GList * resources_list_providers(const char *standard)
Get a list of providers.
Definition: services.c:800
lrmd_t * lrmd_api_new(void)
Create a new local lrmd connection.
Definition: lrmd_client.c:2148
#define LSB_INITSCRIPT_INFOEND_TAG
Definition: lrmd_client.c:1625
struct lrmd_key_value_s * next
Definition: lrmd.h:35
#define F_LRMD_RSC_USERDATA_STR
Definition: lrmd.h:75
gboolean add_message_xml(xmlNode *msg, const char *field, xmlNode *xml)
Definition: xml.c:3335
void free_xml(xmlNode *child)
Definition: xml.c:2851
#define F_LRMD_RSC_INTERVAL
Definition: lrmd.h:79
void lrmd_free_rsc_info(lrmd_rsc_info_t *rsc_info)
Definition: lrmd_client.c:1482
int crm_remote_tcp_connect_async(const char *host, int port, int timeout, int *timer_id, void *userdata, void(*callback)(void *userdata, int sock))
Definition: remote.c:838
#define LSB_ROOT_DIR
Definition: services.h:42
CRM_TRACE_INIT_DATA(lrmd)
#define LRMD_OP_RSC_EXEC
Definition: lrmd.h:86
gboolean crm_str_eq(const char *a, const char *b, gboolean use_case)
Definition: utils.c:1441
#define F_LRMD_OP_STATUS
Definition: lrmd.h:60
int(* unregister_rsc)(lrmd_t *lrmd, const char *rsc_id, enum lrmd_call_options options)
Unregister a resource from the lrmd.
Definition: lrmd.h:347
#define F_LRMD_RSC_START_DELAY
Definition: lrmd.h:78
int(* disconnect)(lrmd_t *lrmd)
Disconnect from the lrmd.
Definition: lrmd.h:309
const char * op_type
Definition: lrmd.h:194
#define LRMD_OP_RSC_REG
Definition: lrmd.h:85
#define SHORT_DSCR
Definition: lrmd_client.c:1633
bool crm_ipc_connected(crm_ipc_t *client)
Definition: ipc.c:889
void * private
Definition: lrmd.h:451
int lrmd_poll(lrmd_t *lrmd, int timeout)
Poll for a specified timeout period to determine if a message is ready for dispatch.
Definition: lrmd_client.c:417
const char * crm_xml_add(xmlNode *node, const char *name, const char *value)
Definition: xml.c:2698
const char * crm_xml_add_int(xmlNode *node, const char *name, int value)
Definition: xml.c:2786
int crm_ipc_ready(crm_ipc_t *client)
Definition: ipc.c:914
#define F_LRMD_RSC_RCCHANGE_TIME
Definition: lrmd.h:69
int(* list_agents)(lrmd_t *lrmd, lrmd_list_t **agents, const char *class, const char *provider)
Retrieve a list of installed resource agents.
Definition: lrmd.h:422
#define lsb_meta_helper_free_value(m)
Definition: lrmd_client.c:1636
char * type
Definition: lrmd.h:247
#define crm_log_xml_err(xml, text)
Definition: logging.h:257
#define F_LRMD_PROVIDER
Definition: lrmd.h:64
void lrmd_api_delete(lrmd_t *lrmd)
Destroy lrmd object.
Definition: lrmd_client.c:2210
#define F_LRMD_REMOTE_MSG_ID
Definition: lrmd.h:52
#define crm_perror(level, fmt, args...)
Log a system error message.
Definition: logging.h:226
const char * remote_nodename
Definition: lrmd.h:235
lrmd_api_operations_t * cmds
Definition: lrmd.h:450
crm_ipc_t * mainloop_get_ipc_client(mainloop_io_t *client)
Definition: mainloop.c:799
GList * resources_list_standards(void)
Definition: services.c:755
#define crm_err(fmt, args...)
Definition: logging.h:248
#define T_LRMD
Definition: lrmd.h:113
enum lrmd_callback_event type
Definition: lrmd.h:189
lrmd_key_value_t * lrmd_key_value_add(lrmd_key_value_t *head, const char *key, const char *value)
Definition: lrmd_client.c:153
stonith_api_operations_t * cmds
Definition: stonith-ng.h:370
int crm_ipc_send(crm_ipc_t *client, xmlNode *message, enum crm_ipc_flags flags, int32_t ms_timeout, xmlNode **reply)
Definition: ipc.c:1122
Fencing aka. STONITH.
#define F_LRMD_CALLID
Definition: lrmd.h:54
int(* list_standards)(lrmd_t *lrmd, lrmd_list_t **standards)
Retrieve a list of standards supported by this machine/installation.
Definition: lrmd.h:445
crm_ipc_t * crm_ipc_new(const char *name, size_t max_size)
Definition: ipc.c:778
#define F_LRMD_CALLOPTS
Definition: lrmd.h:56
GList * resources_list_agents(const char *standard, const char *provider)
Get a list of resource agents.
Definition: services.c:810
#define CRM_SYSTEM_LRMD
Definition: crm.h:85
#define uint32_t
Definition: stdint.in.h:158
#define CRM_ASSERT(expr)
Definition: error.h:35
char data[0]
Definition: internal.h:58
void * create_psk_tls_session(int csock, int type, void *credentials)
#define F_LRMD_RSC_RUN_TIME
Definition: lrmd.h:68
char * provider
Definition: lrmd.h:249
Definition: lrmd.h:449
int(* register_rsc)(lrmd_t *lrmd, const char *rsc_id, const char *class, const char *provider, const char *agent, enum lrmd_call_options options)
Register a resource with the lrmd.
Definition: lrmd.h:319
int(* list_ocf_providers)(lrmd_t *lrmd, const char *agent, lrmd_list_t **providers)
Retrieve a list of resource agent providers.
Definition: lrmd.h:435
void lrmd_internal_set_proxy_callback(lrmd_t *lrmd, void *userdata, void(*callback)(lrmd_t *lrmd, void *userdata, xmlNode *msg))
Definition: lrmd_client.c:1546
#define crm_log_xml_trace(xml, text)
Definition: logging.h:262
#define F_LRMD_CLIENTNAME
Definition: lrmd.h:47
mainloop_io_t * mainloop_add_ipc_client(const char *name, int priority, size_t max_size, void *userdata, struct ipc_client_callbacks *callbacks)
Definition: mainloop.c:765
#define F_LRMD_RSC_QUEUE_TIME
Definition: lrmd.h:71
#define F_LRMD_RSC_DELETED
Definition: lrmd.h:81
#define F_LRMD_OPERATION
Definition: lrmd.h:46
#define CRM_OP_IPC_FWD
Definition: crm.h:123
#define safe_str_eq(a, b)
Definition: util.h:74
#define F_LRMD_CALLBACK_TOKEN
Definition: lrmd.h:53
int(* metadata)(stonith_t *st, int options, const char *device, const char *namespace, char **output, int timeout)
Get the metadata documentation for a resource.
Definition: stonith-ng.h:198
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
#define crm_str_hash
Definition: crm.h:198
#define F_XML_TAGNAME
Definition: msg_xml.h:42
void lrmd_free_event(lrmd_event_data_t *event)
Definition: lrmd_client.c:227
#define F_LRMD_REMOTE_MSG_TYPE
Definition: lrmd.h:51
void crm_ipc_close(crm_ipc_t *client)
Definition: ipc.c:837
GList * GListPtr
Definition: crm.h:192
char * value
Definition: lrmd.h:34
#define DFLT_STOP
Definition: lrmd_client.c:1632
int crm_remote_send(crm_remote_t *remote, xmlNode *msg)
Definition: remote.c:331
#define crm_info(fmt, args...)
Definition: logging.h:251
#define NAGIOS_METADATA_DIR
Definition: config.h:502
int(* is_connected)(lrmd_t *lrmd)
Is connected to lrmd daemon?
Definition: lrmd.h:292
int(* dispatch)(const char *buffer, ssize_t length, gpointer userdata)
Definition: mainloop.h:73
void(* set_callback)(lrmd_t *lrmd, lrmd_event_callback callback)
Sets the callback to receive lrmd events on.
Definition: lrmd.h:352
#define LRMD_OP_RSC_INFO
Definition: lrmd.h:89
enum crm_ais_msg_types type
Definition: internal.h:51