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