pacemaker 2.1.1-77db578727
Scalable High-Availability cluster resource manager
Loading...
Searching...
No Matches
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*/
35static DBusConnection *upstart_proxy = NULL;
36
37static gboolean
38upstart_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
52void
54{
55 if (upstart_proxy) {
56 pcmk_dbus_disconnect(upstart_proxy);
57 upstart_proxy = NULL;
58 }
59}
60
61static gboolean
62upstart_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
112static void
113fix(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
134static char *
135fix_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
148GList *
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
230gboolean
232{
233 return upstart_job_by_name(name, NULL, DBUS_TIMEOUT_USE_DEFAULT);
234}
235
236static char *
237get_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
296static void
297upstart_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 {
307 }
308
309 if (op->synchronous == FALSE) {
310 services_set_op_pending(op, NULL);
312 }
313}
314
315static char *
316upstart_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
340static bool
341upstart_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
366static void
367upstart_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);
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 */
417gboolean
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
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}
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
A dumping ground.
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
DBusConnection * pcmk_dbus_connect(void)
Definition dbus.c:259
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition dbus.c:412
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
Definition dbus.c:476
bool pcmk_dbus_find_error(DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
Definition dbus.c:330
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition dbus.c:516
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition dbus.c:295
#define crm_info(fmt, args...)
Definition logging.h:353
#define crm_warn(fmt, args...)
Definition logging.h:351
#define CRM_LOG_ASSERT(expr)
Definition logging.h:202
#define crm_debug(fmt, args...)
Definition logging.h:355
#define crm_err(fmt, args...)
Definition logging.h:350
#define crm_trace(fmt, args...)
Definition logging.h:356
Wrappers for and extensions to glib mainloop.
#define DBUS_TIMEOUT_USE_DEFAULT
Definition pcmk-dbus.h:16
unsigned int timeout
Definition pcmk_fence.c:32
char * name
Definition pcmk_fence.c:31
const char * action
Definition pcmk_fence.c:30
#define CRM_ASSERT(expr)
Definition results.h:42
@ PCMK_OCF_UNIMPLEMENT_FEATURE
Definition results.h:154
@ PCMK_OCF_NOT_INSTALLED
Definition results.h:156
@ PCMK_OCF_UNKNOWN_ERROR
Definition results.h:152
@ PCMK_OCF_NOT_RUNNING
Definition results.h:158
@ PCMK_OCF_OK
Definition results.h:151
void services_add_inflight_op(svc_action_t *op)
Definition services.c:749
Services API.
@ PCMK_LRM_OP_NOT_INSTALLED
Definition services.h:96
#define PCMK_DEFAULT_AGENT_VERSION
Definition services.h:58
gboolean operation_finalize(svc_action_t *op)
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition strings.c:955
@ pcmk__str_casei
char * agent
Definition services.h:127
char * rsc
Definition services.h:121
char * action
Definition services.h:122
int synchronous
Definition services.h:141
char * stdout_data
Definition services.h:145
svc_action_private_t * opaque
Definition services.h:155
GList * upstart_job_listall(void)
Definition upstart.c:149
#define BUS_PATH
Definition upstart.c:26
gboolean upstart_job_exec(svc_action_t *op)
Definition upstart.c:418
#define UPSTART_JOB_IFACE
Definition upstart.c:29
void upstart_cleanup(void)
Definition upstart.c:53
#define BUS_NAME
Definition upstart.c:25
gboolean upstart_job_exists(const char *name)
Definition upstart.c:231
#define UPSTART_06_API
Definition upstart.c:28