pacemaker  2.1.4-dc6eb4362
Scalable High-Availability cluster resource manager
upstart.c
Go to the documentation of this file.
1 /*
2  * Original copyright 2010 Senko Rasic <senko.rasic@dobarkod.hr>
3  * and Ante Karamatic <ivoks@init.hr>
4  * Later changes copyright 2012-2022 the Pacemaker project contributors
5  *
6  * The version control history for this file may have further details.
7  *
8  * This source code is licensed under the GNU Lesser General Public License
9  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
10  */
11 
12 #include <crm_internal.h>
13 
14 #include <stdio.h>
15 
16 #include <crm/crm.h>
17 #include <crm/services.h>
18 #include <crm/common/mainloop.h>
19 
20 #include <services_private.h>
21 #include <upstart.h>
22 #include <dbus/dbus.h>
23 #include <pcmk-dbus.h>
24 
25 #include <glib.h>
26 #include <gio/gio.h>
27 
28 #define BUS_NAME "com.ubuntu.Upstart"
29 #define BUS_PATH "/com/ubuntu/Upstart"
30 
31 #define UPSTART_06_API BUS_NAME"0_6"
32 #define UPSTART_JOB_IFACE UPSTART_06_API".Job"
33 #define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties"
34 
35 /*
36  http://upstart.ubuntu.com/wiki/DBusInterface
37 */
38 static DBusConnection *upstart_proxy = NULL;
39 
48 int
50 {
51  op->opaque->exec = strdup("upstart-dbus");
52  if (op->opaque->exec == NULL) {
53  return ENOMEM;
54  }
55  return pcmk_rc_ok;
56 }
57 
66 enum ocf_exitcode
67 services__upstart2ocf(int exit_status)
68 {
69  // This library uses OCF codes for Upstart actions
70  return (enum ocf_exitcode) exit_status;
71 }
72 
73 static gboolean
74 upstart_init(void)
75 {
76  static int need_init = 1;
77 
78  if (need_init) {
79  need_init = 0;
80  upstart_proxy = pcmk_dbus_connect();
81  }
82  if (upstart_proxy == NULL) {
83  return FALSE;
84  }
85  return TRUE;
86 }
87 
88 void
90 {
91  if (upstart_proxy) {
92  pcmk_dbus_disconnect(upstart_proxy);
93  upstart_proxy = NULL;
94  }
95 }
96 
108 static bool
109 object_path_for_job(const gchar *arg_name, char **path, int timeout)
110 {
111  /*
112  com.ubuntu.Upstart0_6.GetJobByName (in String name, out ObjectPath job)
113  */
114  DBusError error;
115  DBusMessage *msg;
116  DBusMessage *reply = NULL;
117  bool rc = false;
118 
119  if (path != NULL) {
120  *path = NULL;
121  }
122 
123  if (!upstart_init()) {
124  return false;
125  }
126  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
127  BUS_PATH, // object to call on
128  UPSTART_06_API, // interface to call on
129  "GetJobByName"); // method name
130 
131  dbus_error_init(&error);
132  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &arg_name,
133  DBUS_TYPE_INVALID));
134  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, timeout);
135  dbus_message_unref(msg);
136 
137  if (dbus_error_is_set(&error)) {
138  crm_err("Could not get DBus object path for %s: %s",
139  arg_name, error.message);
140  dbus_error_free(&error);
141 
142  } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
143  __func__, __LINE__)) {
144  crm_err("Could not get DBus object path for %s: Invalid return type",
145  arg_name);
146 
147  } else {
148  if (path != NULL) {
149  dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, path,
150  DBUS_TYPE_INVALID);
151  if (*path != NULL) {
152  *path = strdup(*path);
153  }
154  }
155  rc = true;
156  }
157 
158  if (reply != NULL) {
159  dbus_message_unref(reply);
160  }
161  return rc;
162 }
163 
164 static void
165 fix(char *input, const char *search, char replace)
166 {
167  char *match = NULL;
168  int shuffle = strlen(search) - 1;
169 
170  while (TRUE) {
171  int len, lpc;
172 
173  match = strstr(input, search);
174  if (match == NULL) {
175  break;
176  }
177  crm_trace("Found: %s", match);
178  match[0] = replace;
179  len = strlen(match) - shuffle;
180  for (lpc = 1; lpc <= len; lpc++) {
181  match[lpc] = match[lpc + shuffle];
182  }
183  }
184 }
185 
186 static char *
187 fix_upstart_name(const char *input)
188 {
189  char *output = strdup(input);
190 
191  fix(output, "_2b", '+');
192  fix(output, "_2c", ',');
193  fix(output, "_2d", '-');
194  fix(output, "_2e", '.');
195  fix(output, "_40", '@');
196  fix(output, "_5f", '_');
197  return output;
198 }
199 
200 GList *
202 {
203  GList *units = NULL;
204  DBusMessageIter args;
205  DBusMessageIter unit;
206  DBusMessage *msg = NULL;
207  DBusMessage *reply = NULL;
208  const char *method = "GetAllJobs";
209  DBusError error;
210  int lpc = 0;
211 
212  if (upstart_init() == FALSE) {
213  return NULL;
214  }
215 
216 /*
217  com.ubuntu.Upstart0_6.GetAllJobs (out <Array of ObjectPath> jobs)
218 */
219 
220  dbus_error_init(&error);
221  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
222  BUS_PATH, // object to call on
223  UPSTART_06_API, // interface to call on
224  method); // method name
225  CRM_ASSERT(msg != NULL);
226 
227  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, DBUS_TIMEOUT_USE_DEFAULT);
228  dbus_message_unref(msg);
229 
230  if (dbus_error_is_set(&error)) {
231  crm_err("Call to %s failed: %s", method, error.message);
232  dbus_error_free(&error);
233  return NULL;
234 
235  } else if (!dbus_message_iter_init(reply, &args)) {
236  crm_err("Call to %s failed: Message has no arguments", method);
237  dbus_message_unref(reply);
238  return NULL;
239  }
240 
241  if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __func__, __LINE__)) {
242  crm_err("Call to %s failed: Message has invalid arguments", method);
243  dbus_message_unref(reply);
244  return NULL;
245  }
246 
247  dbus_message_iter_recurse(&args, &unit);
248  while (dbus_message_iter_get_arg_type (&unit) != DBUS_TYPE_INVALID) {
249  DBusBasicValue value;
250  const char *job = NULL;
251  char *path = NULL;
252 
253  if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
254  crm_warn("Skipping Upstart reply argument with unexpected type");
255  continue;
256  }
257 
258  dbus_message_iter_get_basic(&unit, &value);
259 
260  if(value.str) {
261  int llpc = 0;
262  path = value.str;
263  job = value.str;
264  while (path[llpc] != 0) {
265  if (path[llpc] == '/') {
266  job = path + llpc + 1;
267  }
268  llpc++;
269  }
270  lpc++;
271  crm_trace("%s -> %s", path, job);
272  units = g_list_append(units, fix_upstart_name(job));
273  }
274  dbus_message_iter_next (&unit);
275  }
276 
277  dbus_message_unref(reply);
278  crm_trace("Found %d upstart jobs", lpc);
279  return units;
280 }
281 
282 gboolean
284 {
285  return object_path_for_job(name, NULL, DBUS_TIMEOUT_USE_DEFAULT);
286 }
287 
288 static char *
289 get_first_instance(const gchar * job, int timeout)
290 {
291  char *instance = NULL;
292  const char *method = "GetAllInstances";
293  DBusError error;
294  DBusMessage *msg;
295  DBusMessage *reply;
296  DBusMessageIter args;
297  DBusMessageIter unit;
298 
299  dbus_error_init(&error);
300  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
301  job, // object to call on
302  UPSTART_JOB_IFACE, // interface to call on
303  method); // method name
304  CRM_ASSERT(msg != NULL);
305 
306  dbus_message_append_args(msg, DBUS_TYPE_INVALID);
307  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, timeout);
308  dbus_message_unref(msg);
309 
310  if (dbus_error_is_set(&error)) {
311  crm_info("Call to %s failed: %s", method, error.message);
312  dbus_error_free(&error);
313  goto done;
314 
315  } else if(reply == NULL) {
316  crm_info("Call to %s failed: no reply", method);
317  goto done;
318 
319  } else if (!dbus_message_iter_init(reply, &args)) {
320  crm_info("Call to %s failed: Message has no arguments", method);
321  goto done;
322  }
323 
324  if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __func__, __LINE__)) {
325  crm_info("Call to %s failed: Message has invalid arguments", method);
326  goto done;
327  }
328 
329  dbus_message_iter_recurse(&args, &unit);
330  if(pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
331  DBusBasicValue value;
332 
333  dbus_message_iter_get_basic(&unit, &value);
334 
335  if(value.str) {
336  instance = strdup(value.str);
337  crm_trace("Result: %s", instance);
338  }
339  }
340 
341  done:
342  if(reply) {
343  dbus_message_unref(reply);
344  }
345  return instance;
346 }
347 
356 static void
357 parse_status_result(const char *name, const char *state, void *userdata)
358 {
359  svc_action_t *op = userdata;
360 
361  if (pcmk__str_eq(state, "running", pcmk__str_none)) {
363  } else {
365  }
366 
367  if (!(op->synchronous)) {
368  services_set_op_pending(op, NULL);
370  }
371 }
372 
373 #define METADATA_FORMAT \
374  "<?xml version=\"1.0\"?>\n" \
375  "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n" \
376  "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \
377  " <version>1.1</version>\n" \
378  " <longdesc lang=\"en\">\n" \
379  " Upstart agent for controlling the system %s service\n" \
380  " </longdesc>\n" \
381  " <shortdesc lang=\"en\">Upstart job for %s</shortdesc>\n" \
382  " <parameters/>\n" \
383  " <actions>\n" \
384  " <action name=\"start\" timeout=\"15\" />\n" \
385  " <action name=\"stop\" timeout=\"15\" />\n" \
386  " <action name=\"status\" timeout=\"15\" />\n" \
387  " <action name=\"restart\" timeout=\"15\" />\n" \
388  " <action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n" \
389  " <action name=\"meta-data\" timeout=\"5\" />\n" \
390  " </actions>\n" \
391  " <special tag=\"upstart\"/>\n" \
392  "</resource-agent>\n"
393 
394 static char *
395 upstart_job_metadata(const char *name)
396 {
398 }
399 
407 static void
408 set_result_from_method_error(svc_action_t *op, const DBusError *error)
409 {
411  "Unable to invoke Upstart DBus method");
412 
413  if (strstr(error->name, UPSTART_06_API ".Error.UnknownInstance")) {
414 
415  if (pcmk__str_eq(op->action, "stop", pcmk__str_casei)) {
416  crm_trace("Masking stop failure (%s) for %s "
417  "because unknown service can be considered stopped",
418  error->name, crm_str(op->rsc));
420  return;
421  }
422 
424  PCMK_EXEC_NOT_INSTALLED, "Upstart job not found");
425 
426  } else if (pcmk__str_eq(op->action, "start", pcmk__str_casei)
427  && strstr(error->name, UPSTART_06_API ".Error.AlreadyStarted")) {
428  crm_trace("Masking start failure (%s) for %s "
429  "because already started resource is OK",
430  error->name, crm_str(op->rsc));
432  return;
433  }
434 
435  crm_info("DBus request for %s of Upstart job %s for resource %s failed: %s",
436  op->action, op->agent, crm_str(op->rsc), error->message);
437 }
438 
446 static void
447 job_method_complete(DBusPendingCall *pending, void *user_data)
448 {
449  DBusError error;
450  DBusMessage *reply = NULL;
451  svc_action_t *op = user_data;
452 
453  // Grab the reply
454  if (pending != NULL) {
455  reply = dbus_pending_call_steal_reply(pending);
456  }
457 
458  // Determine result
459  dbus_error_init(&error);
460  if (pcmk_dbus_find_error(pending, reply, &error)) {
461  set_result_from_method_error(op, &error);
462  dbus_error_free(&error);
463 
464  } else if (pcmk__str_eq(op->action, "stop", pcmk__str_none)) {
465  // Call has no return value
466  crm_debug("DBus request for stop of %s succeeded", crm_str(op->rsc));
468 
469  } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
470  __func__, __LINE__)) {
471  crm_info("DBus request for %s of %s succeeded but "
472  "return type was unexpected", op->action, crm_str(op->rsc));
474 
475  } else {
476  const char *path = NULL;
477 
478  dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
479  DBUS_TYPE_INVALID);
480  crm_debug("DBus request for %s of %s using %s succeeded",
481  op->action, crm_str(op->rsc), path);
483  }
484 
485  // The call is no longer pending
486  CRM_LOG_ASSERT(pending == op->opaque->pending);
487  services_set_op_pending(op, NULL);
488 
489  // Finalize action
491  if (reply != NULL) {
492  dbus_message_unref(reply);
493  }
494 }
495 
512 int
514 {
515  char *job = NULL;
516  int arg_wait = TRUE;
517  const char *arg_env = "pacemaker=1";
518  const char *action = op->action;
519 
520  DBusError error;
521  DBusMessage *msg = NULL;
522  DBusMessage *reply = NULL;
523  DBusMessageIter iter, array_iter;
524 
525  CRM_ASSERT(op != NULL);
526 
527  if ((op->action == NULL) || (op->agent == NULL)) {
529  "Bug in action caller");
530  goto cleanup;
531  }
532 
533  if (!upstart_init()) {
535  "No DBus connection");
536  goto cleanup;
537  }
538 
539  if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
540  op->stdout_data = upstart_job_metadata(op->agent);
542  goto cleanup;
543  }
544 
545  if (!object_path_for_job(op->agent, &job, op->timeout)) {
546  if (pcmk__str_eq(action, "stop", pcmk__str_none)) {
548  } else {
551  "Upstart job not found");
552  }
553  goto cleanup;
554  }
555 
556  if (job == NULL) {
557  // Shouldn't normally be possible -- maybe a memory error
559  op->status = PCMK_EXEC_ERROR;
560  goto cleanup;
561  }
562 
563  if (pcmk__strcase_any_of(op->action, "monitor", "status", NULL)) {
564  DBusPendingCall *pending = NULL;
565  char *state = NULL;
566  char *path = get_first_instance(job, op->timeout);
567 
569  "No Upstart job instances found");
570  if (path == NULL) {
571  goto cleanup;
572  }
573  state = pcmk_dbus_get_property(upstart_proxy, BUS_NAME, path,
574  UPSTART_06_API ".Instance", "state",
575  op->synchronous? NULL : parse_status_result,
576  op,
577  op->synchronous? NULL : &pending,
578  op->timeout);
579  free(path);
580 
581  if (op->synchronous) {
582  parse_status_result("state", state, op);
583  free(state);
584 
585  } else if (pending == NULL) {
587  "Could not get job state from DBus");
588 
589  } else { // Successfully initiated async op
590  free(job);
591  services_set_op_pending(op, pending);
593  return pcmk_rc_ok;
594  }
595 
596  goto cleanup;
597 
598  } else if (pcmk__str_eq(action, "start", pcmk__str_none)) {
599  action = "Start";
600 
601  } else if (pcmk__str_eq(action, "stop", pcmk__str_none)) {
602  action = "Stop";
603 
604  } else if (pcmk__str_eq(action, "restart", pcmk__str_none)) {
605  action = "Restart";
606 
607  } else {
610  "Action not implemented for Upstart resources");
611  goto cleanup;
612  }
613 
614  // Initialize rc/status in case called functions don't set them
616  "Bug in service library");
617 
618  crm_debug("Calling %s for %s on %s", action, crm_str(op->rsc), job);
619 
620  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
621  job, // object to call on
622  UPSTART_JOB_IFACE, // interface to call on
623  action); // method name
624  CRM_ASSERT(msg != NULL);
625 
626  dbus_message_iter_init_append (msg, &iter);
627  CRM_LOG_ASSERT(dbus_message_iter_open_container(&iter,
628  DBUS_TYPE_ARRAY,
629  DBUS_TYPE_STRING_AS_STRING,
630  &array_iter));
631  CRM_LOG_ASSERT(dbus_message_iter_append_basic(&array_iter,
632  DBUS_TYPE_STRING, &arg_env));
633  CRM_LOG_ASSERT(dbus_message_iter_close_container(&iter, &array_iter));
634  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &arg_wait,
635  DBUS_TYPE_INVALID));
636 
637  if (!(op->synchronous)) {
638  DBusPendingCall *pending = pcmk_dbus_send(msg, upstart_proxy,
639  job_method_complete, op,
640  op->timeout);
641 
642  if (pending == NULL) {
644  "Unable to send DBus message");
645  goto cleanup;
646 
647  } else { // Successfully initiated async op
648  free(job);
649  services_set_op_pending(op, pending);
651  return pcmk_rc_ok;
652  }
653  }
654 
655  // Synchronous call
656 
657  dbus_error_init(&error);
658  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, op->timeout);
659 
660  if (dbus_error_is_set(&error)) {
661  set_result_from_method_error(op, &error);
662  dbus_error_free(&error);
663 
664  } else if (pcmk__str_eq(op->action, "stop", pcmk__str_none)) {
665  // DBus call does not return a value
667 
668  } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
669  __func__, __LINE__)) {
670  crm_info("Call to %s passed but return type was unexpected",
671  op->action);
673 
674  } else {
675  const char *path = NULL;
676 
677  dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
678  DBUS_TYPE_INVALID);
679  crm_debug("Call to %s passed: %s", op->action, path);
681  }
682 
683 cleanup:
684  free(job);
685  if (msg != NULL) {
686  dbus_message_unref(msg);
687  }
688  if (reply != NULL) {
689  dbus_message_unref(reply);
690  }
691 
692  if (op->synchronous) {
693  return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
694  } else {
695  return services__finalize_async_op(op);
696  }
697 }
Services API.
int rc
Exit status of action (set by library upon completion)
Definition: services.h:154
A dumping ground.
ocf_exitcode
Exit status codes for resource agents.
Definition: results.h:161
const char * name
Definition: cib.c:24
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:931
gboolean upstart_job_exists(const char *name)
Definition: upstart.c:283
enum ocf_exitcode services__upstart2ocf(int exit_status)
Definition: upstart.c:67
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition: dbus.c:295
#define DBUS_TIMEOUT_USE_DEFAULT
Definition: pcmk-dbus.h:16
Service safely stopped.
Definition: results.h:169
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
Definition: services.h:128
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:210
int timeout
Action timeout (in milliseconds)
Definition: services.h:145
Execution failed, do not retry on node.
Definition: results.h:313
void upstart_cleanup(void)
Definition: upstart.c:89
Wrappers for and extensions to glib mainloop.
bool pcmk_dbus_find_error(DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
Definition: dbus.c:330
const char * action
Definition: pcmk_fence.c:29
#define crm_warn(fmt, args...)
Definition: logging.h:359
svc_action_private_t * opaque
This field should be treated as internal to Pacemaker.
Definition: services.h:181
GList * upstart_job_listall(void)
Definition: upstart.c:201
#define crm_debug(fmt, args...)
Definition: logging.h:363
char * stdout_data
Action stdout (set by library)
Definition: services.h:177
Parameter invalid (inherently)
Definition: results.h:168
#define crm_trace(fmt, args...)
Definition: logging.h:364
int services__finalize_async_op(svc_action_t *op)
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
Object for executing external actions.
Definition: services.h:121
char * agent
Resource agent name for resource actions, otherwise NULL.
Definition: services.h:143
void services__set_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *exit_reason)
Definition: services.c:1271
int synchronous
Definition: services.h:172
Action completed, result is known.
Definition: results.h:308
Execution failed, do not retry anywhere.
Definition: results.h:314
#define BUS_PATH
Definition: upstart.c:29
Dependencies not available locally.
Definition: results.h:167
int services__upstart_prepare(svc_action_t *op)
Definition: upstart.c:49
int services__execute_upstart(svc_action_t *op)
Definition: upstart.c:513
Unspecified error.
Definition: results.h:163
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
Definition: dbus.c:476
DBusConnection * pcmk_dbus_connect(void)
Definition: dbus.c:259
void services_add_inflight_op(svc_action_t *op)
Definition: services.c:835
Requested action not implemented.
Definition: results.h:165
char * action
Name of action being executed for resource actions, otherwise NULL.
Definition: services.h:131
const char * path
Definition: cib.c:26
#define crm_err(fmt, args...)
Definition: logging.h:358
#define CRM_ASSERT(expr)
Definition: results.h:42
Success.
Definition: results.h:162
xmlNode * input
int status
Execution status (enum pcmk_exec_status set by library)
Definition: services.h:162
#define UPSTART_JOB_IFACE
Definition: upstart.c:32
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition: dbus.c:516
#define crm_str(x)
Definition: logging.h:384
Agent or dependency not available locally.
Definition: results.h:315
#define UPSTART_06_API
Definition: upstart.c:31
char * pcmk_dbus_get_property(DBusConnection *connection, const char *target, const char *obj, const gchar *iface, const char *name, property_callback_func callback, void *userdata, DBusPendingCall **pending, int timeout)
Definition: dbus.c:693
unsigned int timeout
Definition: pcmk_fence.c:31
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition: dbus.c:412
Execution failed, may be retried.
Definition: results.h:312
#define crm_info(fmt, args...)
Definition: logging.h:361
#define BUS_NAME
Definition: upstart.c:28
#define METADATA_FORMAT
Definition: upstart.c:373