pacemaker  2.0.2-debe490
Scalable High-Availability cluster resource manager
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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, __FUNCTION__, __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, __FUNCTION__, __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, __FUNCTION__, __LINE__)) {
202  continue;
203  }
204 
205  dbus_message_iter_get_basic(&unit, &value);
206 
207  if(value.str) {
208  int llpc = 0;
209  path = value.str;
210  job = value.str;
211  while (path[llpc] != 0) {
212  if (path[llpc] == '/') {
213  job = path + llpc + 1;
214  }
215  llpc++;
216  }
217  lpc++;
218  crm_trace("%s -> %s", path, job);
219  units = g_list_append(units, fix_upstart_name(job));
220  }
221  dbus_message_iter_next (&unit);
222  }
223 
224  dbus_message_unref(reply);
225  crm_trace("Found %d upstart jobs", lpc);
226  return units;
227 }
228 
229 gboolean
230 upstart_job_exists(const char *name)
231 {
232  return upstart_job_by_name(name, NULL, DBUS_TIMEOUT_USE_DEFAULT);
233 }
234 
235 static char *
236 get_first_instance(const gchar * job, int timeout)
237 {
238  char *instance = NULL;
239  const char *method = "GetAllInstances";
240  DBusError error;
241  DBusMessage *msg;
242  DBusMessage *reply;
243  DBusMessageIter args;
244  DBusMessageIter unit;
245 
246  dbus_error_init(&error);
247  msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
248  job, // object to call on
249  UPSTART_JOB_IFACE, // interface to call on
250  method); // method name
251  CRM_ASSERT(msg != NULL);
252 
253  dbus_message_append_args(msg, DBUS_TYPE_INVALID);
254  reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, timeout);
255  dbus_message_unref(msg);
256 
257  if (dbus_error_is_set(&error)) {
258  crm_err("Call to %s failed: %s", method, error.message);
259  dbus_error_free(&error);
260  goto done;
261 
262  } else if(reply == NULL) {
263  crm_err("Call to %s failed: no reply", method);
264  goto done;
265 
266  } else if (!dbus_message_iter_init(reply, &args)) {
267  crm_err("Call to %s failed: Message has no arguments", method);
268  goto done;
269  }
270 
271  if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __FUNCTION__, __LINE__)) {
272  crm_err("Call to %s failed: Message has invalid arguments", method);
273  goto done;
274  }
275 
276  dbus_message_iter_recurse(&args, &unit);
277  if(pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_OBJECT_PATH, __FUNCTION__, __LINE__)) {
278  DBusBasicValue value;
279 
280  dbus_message_iter_get_basic(&unit, &value);
281 
282  if(value.str) {
283  instance = strdup(value.str);
284  crm_trace("Result: %s", instance);
285  }
286  }
287 
288  done:
289  if(reply) {
290  dbus_message_unref(reply);
291  }
292  return instance;
293 }
294 
295 static void
296 upstart_job_check(const char *name, const char *state, void *userdata)
297 {
298  svc_action_t * op = userdata;
299 
300  if (state && g_strcmp0(state, "running") == 0) {
301  op->rc = PCMK_OCF_OK;
302  /* } else if (g_strcmp0(state, "activating") == 0) { */
303  /* op->rc = PCMK_OCF_PENDING; */
304  } else {
305  op->rc = PCMK_OCF_NOT_RUNNING;
306  }
307 
308  if (op->synchronous == FALSE) {
309  services_set_op_pending(op, NULL);
310  operation_finalize(op);
311  }
312 }
313 
314 static char *
315 upstart_job_metadata(const char *name)
316 {
317  return crm_strdup_printf("<?xml version=\"1.0\"?>\n"
318  "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
319  "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n"
320  " <version>1.0</version>\n"
321  " <longdesc lang=\"en\">\n"
322  " Upstart agent for controlling the system %s service\n"
323  " </longdesc>\n"
324  " <shortdesc lang=\"en\">%s upstart agent</shortdesc>\n"
325  " <parameters>\n"
326  " </parameters>\n"
327  " <actions>\n"
328  " <action name=\"start\" timeout=\"15\" />\n"
329  " <action name=\"stop\" timeout=\"15\" />\n"
330  " <action name=\"status\" timeout=\"15\" />\n"
331  " <action name=\"restart\" timeout=\"15\" />\n"
332  " <action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"
333  " <action name=\"meta-data\" timeout=\"5\" />\n"
334  " </actions>\n"
335  " <special tag=\"upstart\">\n"
336  " </special>\n" "</resource-agent>\n", name, name, name);
337 }
338 
339 static bool
340 upstart_mask_error(svc_action_t *op, const char *error)
341 {
342  crm_trace("Could not issue %s for %s: %s", op->action, op->rsc, error);
343  if(strstr(error, UPSTART_06_API ".Error.UnknownInstance")) {
344  if(safe_str_eq(op->action, "stop")) {
345  crm_trace("Masking %s failure for %s: unknown services are stopped", op->action, op->rsc);
346  op->rc = PCMK_OCF_OK;
347 
348  } else if(safe_str_eq(op->action, "start")) {
349  crm_trace("Mapping %s failure for %s: unknown services are not installed", op->action, op->rsc);
352  }
353  return TRUE;
354 
355  } else if (safe_str_eq(op->action, "start")
356  && strstr(error, UPSTART_06_API ".Error.AlreadyStarted")) {
357  crm_trace("Mapping %s failure for %s: starting a started resource is allowed", op->action, op->rsc);
358  op->rc = PCMK_OCF_OK;
359  return TRUE;
360  }
361 
362  return FALSE;
363 }
364 
365 static void
366 upstart_async_dispatch(DBusPendingCall *pending, void *user_data)
367 {
368  DBusError error;
369  DBusMessage *reply = NULL;
370  svc_action_t *op = user_data;
371 
372  dbus_error_init(&error);
373  if(pending) {
374  reply = dbus_pending_call_steal_reply(pending);
375  }
376 
377  if (pcmk_dbus_find_error(pending, reply, &error)) {
378 
379  /* ignore "already started" or "not running" errors */
380  if (!upstart_mask_error(op, error.name)) {
381  crm_err("%s for %s: %s", op->action, op->rsc, error.message);
382  }
383  dbus_error_free(&error);
384 
385  } else if (!g_strcmp0(op->action, "stop")) {
386  /* No return vaue */
387  op->rc = PCMK_OCF_OK;
388 
389  } else {
390  if(!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, __FUNCTION__, __LINE__)) {
391  crm_warn("Call to %s passed but return type was unexpected", op->action);
392  op->rc = PCMK_OCF_OK;
393 
394  } else {
395  const char *path = NULL;
396 
397  dbus_message_get_args (reply, NULL,
398  DBUS_TYPE_OBJECT_PATH, &path,
399  DBUS_TYPE_INVALID);
400  crm_info("Call to %s passed: %s", op->action, path);
401  op->rc = PCMK_OCF_OK;
402  }
403  }
404 
405  CRM_LOG_ASSERT(pending == op->opaque->pending);
406  services_set_op_pending(op, NULL);
407  operation_finalize(op);
408 
409  if(reply) {
410  dbus_message_unref(reply);
411  }
412 }
413 
414 /* For an asynchronous 'op', returns FALSE if 'op' should be free'd by the caller */
415 /* For a synchronous 'op', returns FALSE if 'op' fails */
416 gboolean
418 {
419  char *job = NULL;
420  int arg_wait = TRUE;
421  const char *arg_env = "pacemaker=1";
422  const char *action = op->action;
423 
424  DBusError error;
425  DBusMessage *msg = NULL;
426  DBusMessage *reply = NULL;
427  DBusMessageIter iter, array_iter;
428 
430  CRM_ASSERT(upstart_init());
431 
432  if (safe_str_eq(op->action, "meta-data")) {
433  op->stdout_data = upstart_job_metadata(op->agent);
434  op->rc = PCMK_OCF_OK;
435  goto cleanup;
436  }
437 
438  if(!upstart_job_by_name(op->agent, &job, op->timeout)) {
439  crm_debug("Could not obtain job named '%s' to %s", op->agent, action);
440  if (!g_strcmp0(action, "stop")) {
441  op->rc = PCMK_OCF_OK;
442 
443  } else {
446  }
447  goto cleanup;
448  }
449 
450  if (safe_str_eq(op->action, "monitor") || safe_str_eq(action, "status")) {
451 
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, __FUNCTION__, __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.
gboolean upstart_job_exists(const char *name)
Definition: upstart.c:230
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition: dbus.c:55
#define DBUS_TIMEOUT_USE_DEFAULT
Definition: pcmk-dbus.h:14
char * rsc
Definition: services.h:150
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:142
gboolean upstart_job_exec(svc_action_t *op)
Definition: upstart.c:417
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:76
#define crm_warn(fmt, args...)
Definition: logging.h:241
svc_action_private_t * opaque
Definition: services.h:184
GList * upstart_job_listall(void)
Definition: upstart.c:149
#define crm_debug(fmt, args...)
Definition: logging.h:245
gboolean operation_finalize(svc_action_t *op)
char * stdout_data
Definition: services.h:174
#define crm_trace(fmt, args...)
Definition: logging.h:246
char * agent
Definition: services.h:156
int synchronous
Definition: services.h:170
#define BUS_PATH
Definition: upstart.c:26
char * pcmk_dbus_get_property(DBusConnection *connection, const char *target, const char *obj, const gchar *iface, const char *name, void(*callback)(const char *name, const char *value, void *userdata), void *userdata, DBusPendingCall **pending, int timeout)
Definition: dbus.c:408
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
Definition: dbus.c:223
DBusConnection * pcmk_dbus_connect(void)
Definition: dbus.c:36
void services_add_inflight_op(svc_action_t *op)
Definition: services.c:699
char * action
Definition: services.h:151
#define crm_err(fmt, args...)
Definition: logging.h:240
#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:270
#define safe_str_eq(a, b)
Definition: util.h:59
#define UPSTART_06_API
Definition: upstart.c:28
char * crm_strdup_printf(char const *format,...) __attribute__((__format__(__printf__
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition: dbus.c:159
#define crm_info(fmt, args...)
Definition: logging.h:243
#define BUS_NAME
Definition: upstart.c:25