pacemaker  2.1.1-52dc28db4
Scalable High-Availability cluster resource manager
upstart.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2010 Senko Rasic <senko.rasic@dobarkod.hr>
3  * Copyright (c) 2010 Ante Karamatic <ivoks@init.hr>
4  *
5  * This source code is licensed under the GNU Lesser General Public License
6  * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
7  */
8 
9 #include <crm_internal.h>
10 
11 #include <stdio.h>
12 
13 #include <crm/crm.h>
14 #include <crm/services.h>
15 #include <crm/common/mainloop.h>
16 
17 #include <services_private.h>
18 #include <upstart.h>
19 #include <dbus/dbus.h>
20 #include <pcmk-dbus.h>
21 
22 #include <glib.h>
23 #include <gio/gio.h>
24 
25 #define BUS_NAME "com.ubuntu.Upstart"
26 #define BUS_PATH "/com/ubuntu/Upstart"
27 
28 #define UPSTART_06_API BUS_NAME"0_6"
29 #define UPSTART_JOB_IFACE UPSTART_06_API".Job"
30 #define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties"
31 
32 /*
33  http://upstart.ubuntu.com/wiki/DBusInterface
34 */
35 static DBusConnection *upstart_proxy = NULL;
36 
37 static gboolean
38 upstart_init(void)
39 {
40  static int need_init = 1;
41 
42  if (need_init) {
43  need_init = 0;
44  upstart_proxy = pcmk_dbus_connect();
45  }
46  if (upstart_proxy == NULL) {
47  return FALSE;
48  }
49  return TRUE;
50 }
51 
52 void
54 {
55  if (upstart_proxy) {
56  pcmk_dbus_disconnect(upstart_proxy);
57  upstart_proxy = NULL;
58  }
59 }
60 
61 static gboolean
62 upstart_job_by_name(const gchar * arg_name, gchar ** out_unit, int timeout)
63 {
64 /*
65  com.ubuntu.Upstart0_6.GetJobByName (in String name, out ObjectPath job)
66 */
67  DBusError error;
68  DBusMessage *msg;
69  DBusMessage *reply = NULL;
70  const char *method = "GetJobByName";
71 
72  if(upstart_init() == FALSE) {
73  return FALSE;
74  }
75  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
76  BUS_PATH, // object to call on
77  UPSTART_06_API, // interface to call on
78  method); // method name
79 
80  dbus_error_init(&error);
81  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &arg_name, DBUS_TYPE_INVALID));
82  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, timeout);
83  dbus_message_unref(msg);
84 
85  if (dbus_error_is_set(&error)) {
86  crm_err("Could not issue %s for %s: %s", method, arg_name, error.message);
87  dbus_error_free(&error);
88 
89  } else if(!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
90  crm_err("Invalid return type for %s", method);
91 
92  } else {
93  if(out_unit) {
94  char *path = NULL;
95 
96  dbus_message_get_args (reply, NULL,
97  DBUS_TYPE_OBJECT_PATH, &path,
98  DBUS_TYPE_INVALID);
99 
100  *out_unit = strdup(path);
101  }
102  dbus_message_unref(reply);
103  return TRUE;
104  }
105 
106  if(reply) {
107  dbus_message_unref(reply);
108  }
109  return FALSE;
110 }
111 
112 static void
113 fix(char *input, const char *search, char replace)
114 {
115  char *match = NULL;
116  int shuffle = strlen(search) - 1;
117 
118  while (TRUE) {
119  int len, lpc;
120 
121  match = strstr(input, search);
122  if (match == NULL) {
123  break;
124  }
125  crm_trace("Found: %s", match);
126  match[0] = replace;
127  len = strlen(match) - shuffle;
128  for (lpc = 1; lpc <= len; lpc++) {
129  match[lpc] = match[lpc + shuffle];
130  }
131  }
132 }
133 
134 static char *
135 fix_upstart_name(const char *input)
136 {
137  char *output = strdup(input);
138 
139  fix(output, "_2b", '+');
140  fix(output, "_2c", ',');
141  fix(output, "_2d", '-');
142  fix(output, "_2e", '.');
143  fix(output, "_40", '@');
144  fix(output, "_5f", '_');
145  return output;
146 }
147 
148 GList *
150 {
151  GList *units = NULL;
152  DBusMessageIter args;
153  DBusMessageIter unit;
154  DBusMessage *msg = NULL;
155  DBusMessage *reply = NULL;
156  const char *method = "GetAllJobs";
157  DBusError error;
158  int lpc = 0;
159 
160  if (upstart_init() == FALSE) {
161  return NULL;
162  }
163 
164 /*
165  com.ubuntu.Upstart0_6.GetAllJobs (out <Array of ObjectPath> jobs)
166 */
167 
168  dbus_error_init(&error);
169  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
170  BUS_PATH, // object to call on
171  UPSTART_06_API, // interface to call on
172  method); // method name
173  CRM_ASSERT(msg != NULL);
174 
175  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, DBUS_TIMEOUT_USE_DEFAULT);
176  dbus_message_unref(msg);
177 
178  if (dbus_error_is_set(&error)) {
179  crm_err("Call to %s failed: %s", method, error.message);
180  dbus_error_free(&error);
181  return NULL;
182 
183  } else if (!dbus_message_iter_init(reply, &args)) {
184  crm_err("Call to %s failed: Message has no arguments", method);
185  dbus_message_unref(reply);
186  return NULL;
187  }
188 
189  if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __func__, __LINE__)) {
190  crm_err("Call to %s failed: Message has invalid arguments", method);
191  dbus_message_unref(reply);
192  return NULL;
193  }
194 
195  dbus_message_iter_recurse(&args, &unit);
196  while (dbus_message_iter_get_arg_type (&unit) != DBUS_TYPE_INVALID) {
197  DBusBasicValue value;
198  const char *job = NULL;
199  char *path = NULL;
200 
201  if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
202  crm_warn("Skipping Upstart reply argument with unexpected type");
203  continue;
204  }
205 
206  dbus_message_iter_get_basic(&unit, &value);
207 
208  if(value.str) {
209  int llpc = 0;
210  path = value.str;
211  job = value.str;
212  while (path[llpc] != 0) {
213  if (path[llpc] == '/') {
214  job = path + llpc + 1;
215  }
216  llpc++;
217  }
218  lpc++;
219  crm_trace("%s -> %s", path, job);
220  units = g_list_append(units, fix_upstart_name(job));
221  }
222  dbus_message_iter_next (&unit);
223  }
224 
225  dbus_message_unref(reply);
226  crm_trace("Found %d upstart jobs", lpc);
227  return units;
228 }
229 
230 gboolean
232 {
233  return upstart_job_by_name(name, NULL, DBUS_TIMEOUT_USE_DEFAULT);
234 }
235 
236 static char *
237 get_first_instance(const gchar * job, int timeout)
238 {
239  char *instance = NULL;
240  const char *method = "GetAllInstances";
241  DBusError error;
242  DBusMessage *msg;
243  DBusMessage *reply;
244  DBusMessageIter args;
245  DBusMessageIter unit;
246 
247  dbus_error_init(&error);
248  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
249  job, // object to call on
250  UPSTART_JOB_IFACE, // interface to call on
251  method); // method name
252  CRM_ASSERT(msg != NULL);
253 
254  dbus_message_append_args(msg, DBUS_TYPE_INVALID);
255  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, timeout);
256  dbus_message_unref(msg);
257 
258  if (dbus_error_is_set(&error)) {
259  crm_err("Call to %s failed: %s", method, error.message);
260  dbus_error_free(&error);
261  goto done;
262 
263  } else if(reply == NULL) {
264  crm_err("Call to %s failed: no reply", method);
265  goto done;
266 
267  } else if (!dbus_message_iter_init(reply, &args)) {
268  crm_err("Call to %s failed: Message has no arguments", method);
269  goto done;
270  }
271 
272  if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __func__, __LINE__)) {
273  crm_err("Call to %s failed: Message has invalid arguments", method);
274  goto done;
275  }
276 
277  dbus_message_iter_recurse(&args, &unit);
278  if(pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
279  DBusBasicValue value;
280 
281  dbus_message_iter_get_basic(&unit, &value);
282 
283  if(value.str) {
284  instance = strdup(value.str);
285  crm_trace("Result: %s", instance);
286  }
287  }
288 
289  done:
290  if(reply) {
291  dbus_message_unref(reply);
292  }
293  return instance;
294 }
295 
296 static void
297 upstart_job_check(const char *name, const char *state, void *userdata)
298 {
299  svc_action_t * op = userdata;
300 
301  if (state && g_strcmp0(state, "running") == 0) {
302  op->rc = PCMK_OCF_OK;
303  /* } else if (g_strcmp0(state, "activating") == 0) { */
304  /* op->rc = PCMK_OCF_PENDING; */
305  } else {
306  op->rc = PCMK_OCF_NOT_RUNNING;
307  }
308 
309  if (op->synchronous == FALSE) {
310  services_set_op_pending(op, NULL);
311  operation_finalize(op);
312  }
313 }
314 
315 static char *
316 upstart_job_metadata(const char *name)
317 {
318  return crm_strdup_printf("<?xml version=\"1.0\"?>\n"
319  "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
320  "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n"
321  " <version>1.0</version>\n"
322  " <longdesc lang=\"en\">\n"
323  " Upstart agent for controlling the system %s service\n"
324  " </longdesc>\n"
325  " <shortdesc lang=\"en\">%s upstart agent</shortdesc>\n"
326  " <parameters>\n"
327  " </parameters>\n"
328  " <actions>\n"
329  " <action name=\"start\" timeout=\"15\" />\n"
330  " <action name=\"stop\" timeout=\"15\" />\n"
331  " <action name=\"status\" timeout=\"15\" />\n"
332  " <action name=\"restart\" timeout=\"15\" />\n"
333  " <action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"
334  " <action name=\"meta-data\" timeout=\"5\" />\n"
335  " </actions>\n"
336  " <special tag=\"upstart\">\n"
337  " </special>\n" "</resource-agent>\n", name, name, name);
338 }
339 
340 static bool
341 upstart_mask_error(svc_action_t *op, const char *error)
342 {
343  crm_trace("Could not issue %s for %s: %s", op->action, op->rsc, error);
344  if(strstr(error, UPSTART_06_API ".Error.UnknownInstance")) {
345  if(pcmk__str_eq(op->action, "stop", pcmk__str_casei)) {
346  crm_trace("Masking %s failure for %s: unknown services are stopped", op->action, op->rsc);
347  op->rc = PCMK_OCF_OK;
348 
349  } else if(pcmk__str_eq(op->action, "start", pcmk__str_casei)) {
350  crm_trace("Mapping %s failure for %s: unknown services are not installed", op->action, op->rsc);
353  }
354  return TRUE;
355 
356  } else if (pcmk__str_eq(op->action, "start", pcmk__str_casei)
357  && strstr(error, UPSTART_06_API ".Error.AlreadyStarted")) {
358  crm_trace("Mapping %s failure for %s: starting a started resource is allowed", op->action, op->rsc);
359  op->rc = PCMK_OCF_OK;
360  return TRUE;
361  }
362 
363  return FALSE;
364 }
365 
366 static void
367 upstart_async_dispatch(DBusPendingCall *pending, void *user_data)
368 {
369  DBusError error;
370  DBusMessage *reply = NULL;
371  svc_action_t *op = user_data;
372 
373  dbus_error_init(&error);
374  if(pending) {
375  reply = dbus_pending_call_steal_reply(pending);
376  }
377 
378  if (pcmk_dbus_find_error(pending, reply, &error)) {
379 
380  /* ignore "already started" or "not running" errors */
381  if (!upstart_mask_error(op, error.name)) {
382  crm_err("%s for %s: %s", op->action, op->rsc, error.message);
383  }
384  dbus_error_free(&error);
385 
386  } else if (!g_strcmp0(op->action, "stop")) {
387  /* No return vaue */
388  op->rc = PCMK_OCF_OK;
389 
390  } else {
391  if(!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
392  crm_warn("Call to %s passed but return type was unexpected", op->action);
393  op->rc = PCMK_OCF_OK;
394 
395  } else {
396  const char *path = NULL;
397 
398  dbus_message_get_args (reply, NULL,
399  DBUS_TYPE_OBJECT_PATH, &path,
400  DBUS_TYPE_INVALID);
401  crm_info("Call to %s passed: %s", op->action, path);
402  op->rc = PCMK_OCF_OK;
403  }
404  }
405 
406  CRM_LOG_ASSERT(pending == op->opaque->pending);
407  services_set_op_pending(op, NULL);
408  operation_finalize(op);
409 
410  if(reply) {
411  dbus_message_unref(reply);
412  }
413 }
414 
415 /* For an asynchronous 'op', returns FALSE if 'op' should be free'd by the caller */
416 /* For a synchronous 'op', returns FALSE if 'op' fails */
417 gboolean
419 {
420  char *job = NULL;
421  int arg_wait = TRUE;
422  const char *arg_env = "pacemaker=1";
423  const char *action = op->action;
424 
425  DBusError error;
426  DBusMessage *msg = NULL;
427  DBusMessage *reply = NULL;
428  DBusMessageIter iter, array_iter;
429 
431  CRM_ASSERT(upstart_init());
432 
433  if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
434  op->stdout_data = upstart_job_metadata(op->agent);
435  op->rc = PCMK_OCF_OK;
436  goto cleanup;
437  }
438 
439  if(!upstart_job_by_name(op->agent, &job, op->timeout)) {
440  crm_debug("Could not obtain job named '%s' to %s", op->agent, action);
441  if (!g_strcmp0(action, "stop")) {
442  op->rc = PCMK_OCF_OK;
443 
444  } else {
447  }
448  goto cleanup;
449  }
450 
451  if (pcmk__strcase_any_of(op->action, "monitor", "status", NULL)) {
452  char *path = get_first_instance(job, op->timeout);
453 
454  op->rc = PCMK_OCF_NOT_RUNNING;
455  if(path) {
456  DBusPendingCall *pending = NULL;
457  char *state = pcmk_dbus_get_property(
458  upstart_proxy, BUS_NAME, path, UPSTART_06_API ".Instance", "state",
459  op->synchronous?NULL:upstart_job_check, op,
460  op->synchronous?NULL:&pending, op->timeout);
461 
462  free(job);
463  free(path);
464 
465  if(op->synchronous) {
466  upstart_job_check("state", state, op);
467  free(state);
468  return op->rc == PCMK_OCF_OK;
469  } else if (pending) {
470  services_set_op_pending(op, pending);
472  return TRUE;
473  }
474  return FALSE;
475  }
476  goto cleanup;
477 
478  } else if (!g_strcmp0(action, "start")) {
479  action = "Start";
480  } else if (!g_strcmp0(action, "stop")) {
481  action = "Stop";
482  } else if (!g_strcmp0(action, "restart")) {
483  action = "Restart";
484  } else {
486  goto cleanup;
487  }
488 
489  crm_debug("Calling %s for %s on %s", action, op->rsc, job);
490 
491  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
492  job, // object to call on
493  UPSTART_JOB_IFACE, // interface to call on
494  action); // method name
495  CRM_ASSERT(msg != NULL);
496 
497  dbus_message_iter_init_append (msg, &iter);
498 
499  CRM_LOG_ASSERT(dbus_message_iter_open_container (&iter,
500  DBUS_TYPE_ARRAY,
501  DBUS_TYPE_STRING_AS_STRING,
502  &array_iter));
503 
504  CRM_LOG_ASSERT(dbus_message_iter_append_basic (&array_iter, DBUS_TYPE_STRING, &arg_env));
505  CRM_LOG_ASSERT(dbus_message_iter_close_container (&iter, &array_iter));
506 
507  CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &arg_wait, DBUS_TYPE_INVALID));
508 
509  if (op->synchronous == FALSE) {
510  DBusPendingCall* pending = pcmk_dbus_send(msg, upstart_proxy, upstart_async_dispatch, op, op->timeout);
511  free(job);
512 
513  if(pending) {
514  services_set_op_pending(op, pending);
516  return TRUE;
517  }
518  return FALSE;
519  }
520 
521  dbus_error_init(&error);
522  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, op->timeout);
523 
524  if (dbus_error_is_set(&error)) {
525  if(!upstart_mask_error(op, error.name)) {
526  crm_err("Could not issue %s for %s: %s (%s)",
527  action, op->rsc, error.message, job);
528  }
529  dbus_error_free(&error);
530 
531  } else if (!g_strcmp0(op->action, "stop")) {
532  /* No return vaue */
533  op->rc = PCMK_OCF_OK;
534 
535  } else if(!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
536  crm_warn("Call to %s passed but return type was unexpected", op->action);
537  op->rc = PCMK_OCF_OK;
538 
539  } else {
540  const char *path = NULL;
541 
542  dbus_message_get_args (reply, NULL,
543  DBUS_TYPE_OBJECT_PATH, &path,
544  DBUS_TYPE_INVALID);
545  crm_info("Call to %s passed: %s", op->action, path);
546  op->rc = PCMK_OCF_OK;
547  }
548 
549 
550  cleanup:
551  free(job);
552  if(msg) {
553  dbus_message_unref(msg);
554  }
555 
556  if(reply) {
557  dbus_message_unref(reply);
558  }
559 
560  if (op->synchronous == FALSE) {
561  return operation_finalize(op);
562  }
563  return op->rc == PCMK_OCF_OK;
564 }
Services API.
A dumping ground.
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:955
gboolean upstart_job_exists(const char *name)
Definition: upstart.c:231
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition: dbus.c:295
#define DBUS_TIMEOUT_USE_DEFAULT
Definition: pcmk-dbus.h:16
char * rsc
Definition: services.h:121
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:202
gboolean upstart_job_exec(svc_action_t *op)
Definition: upstart.c:418
void upstart_cleanup(void)
Definition: upstart.c:53
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:30
#define crm_warn(fmt, args...)
Definition: logging.h:351
svc_action_private_t * opaque
Definition: services.h:155
GList * upstart_job_listall(void)
Definition: upstart.c:149
#define crm_debug(fmt, args...)
Definition: logging.h:355
gboolean operation_finalize(svc_action_t *op)
char * stdout_data
Definition: services.h:145
#define crm_trace(fmt, args...)
Definition: logging.h:356
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
char * agent
Definition: services.h:127
int synchronous
Definition: services.h:141
#define BUS_PATH
Definition: upstart.c:26
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:749
char * action
Definition: services.h:122
#define crm_err(fmt, args...)
Definition: logging.h:350
#define CRM_ASSERT(expr)
Definition: results.h:42
#define UPSTART_JOB_IFACE
Definition: upstart.c:29
#define PCMK_DEFAULT_AGENT_VERSION
Definition: services.h:58
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition: dbus.c:516
#define UPSTART_06_API
Definition: upstart.c:28
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
char * name
Definition: pcmk_fence.c:31
unsigned int timeout
Definition: pcmk_fence.c:32
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition: dbus.c:412
#define crm_info(fmt, args...)
Definition: logging.h:353
#define BUS_NAME
Definition: upstart.c:25