This source file includes following definitions.
- services__upstart_prepare
- services__upstart2ocf
- upstart_init
- upstart_cleanup
- object_path_for_job
- fix
- fix_upstart_name
- upstart_job_listall
- upstart_job_exists
- get_first_instance
- parse_status_result
- upstart_job_metadata
- set_result_from_method_error
- job_method_complete
- services__execute_upstart
1
2
3
4
5
6
7
8
9
10
11
12 #include <crm_internal.h>
13
14 #include <stdio.h>
15
16 #include <crm/crm.h>
17 #include <crm/common/xml.h>
18 #include <crm/services.h>
19 #include <crm/common/mainloop.h>
20
21 #include <services_private.h>
22 #include <upstart.h>
23 #include <dbus/dbus.h>
24 #include <pcmk-dbus.h>
25
26 #include <glib.h>
27 #include <gio/gio.h>
28
29 #define BUS_NAME "com.ubuntu.Upstart"
30 #define BUS_PATH "/com/ubuntu/Upstart"
31
32 #define UPSTART_06_API BUS_NAME"0_6"
33 #define UPSTART_JOB_IFACE UPSTART_06_API".Job"
34 #define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties"
35
36
37
38
39 static DBusConnection *upstart_proxy = NULL;
40
41
42
43
44
45
46
47
48
49 int
50 services__upstart_prepare(svc_action_t *op)
51 {
52 op->opaque->exec = strdup("upstart-dbus");
53 if (op->opaque->exec == NULL) {
54 return ENOMEM;
55 }
56 return pcmk_rc_ok;
57 }
58
59
60
61
62
63
64
65
66
67 enum ocf_exitcode
68 services__upstart2ocf(int exit_status)
69 {
70
71 return (enum ocf_exitcode) exit_status;
72 }
73
74 static gboolean
75 upstart_init(void)
76 {
77 static int need_init = 1;
78
79 if (need_init) {
80 need_init = 0;
81 upstart_proxy = pcmk_dbus_connect();
82 }
83 if (upstart_proxy == NULL) {
84 return FALSE;
85 }
86 return TRUE;
87 }
88
89 void
90 upstart_cleanup(void)
91 {
92 if (upstart_proxy) {
93 pcmk_dbus_disconnect(upstart_proxy);
94 upstart_proxy = NULL;
95 }
96 }
97
98
99
100
101
102
103
104
105
106
107
108
109 static bool
110 object_path_for_job(const gchar *arg_name, char **path, int timeout)
111 {
112
113
114
115 DBusError error;
116 DBusMessage *msg;
117 DBusMessage *reply = NULL;
118 bool rc = false;
119
120 if (path != NULL) {
121 *path = NULL;
122 }
123
124 if (!upstart_init()) {
125 return false;
126 }
127 msg = dbus_message_new_method_call(BUS_NAME,
128 BUS_PATH,
129 UPSTART_06_API,
130 "GetJobByName");
131
132 dbus_error_init(&error);
133 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_STRING, &arg_name,
134 DBUS_TYPE_INVALID));
135 reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, timeout);
136 dbus_message_unref(msg);
137
138 if (dbus_error_is_set(&error)) {
139 crm_err("Could not get DBus object path for %s: %s",
140 arg_name, error.message);
141 dbus_error_free(&error);
142
143 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
144 __func__, __LINE__)) {
145 crm_err("Could not get DBus object path for %s: Invalid return type",
146 arg_name);
147
148 } else {
149 if (path != NULL) {
150 dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, path,
151 DBUS_TYPE_INVALID);
152 if (*path != NULL) {
153 *path = strdup(*path);
154 }
155 }
156 rc = true;
157 }
158
159 if (reply != NULL) {
160 dbus_message_unref(reply);
161 }
162 return rc;
163 }
164
165 static void
166 fix(char *input, const char *search, char replace)
167 {
168 char *match = NULL;
169 int shuffle = strlen(search) - 1;
170
171 while (TRUE) {
172 int len, lpc;
173
174 match = strstr(input, search);
175 if (match == NULL) {
176 break;
177 }
178 crm_trace("Found: %s", match);
179 match[0] = replace;
180 len = strlen(match) - shuffle;
181 for (lpc = 1; lpc <= len; lpc++) {
182 match[lpc] = match[lpc + shuffle];
183 }
184 }
185 }
186
187 static char *
188 fix_upstart_name(const char *input)
189 {
190 char *output = strdup(input);
191
192 fix(output, "_2b", '+');
193 fix(output, "_2c", ',');
194 fix(output, "_2d", '-');
195 fix(output, "_2e", '.');
196 fix(output, "_40", '@');
197 fix(output, "_5f", '_');
198 return output;
199 }
200
201 GList *
202 upstart_job_listall(void)
203 {
204 GList *units = NULL;
205 DBusMessageIter args;
206 DBusMessageIter unit;
207 DBusMessage *msg = NULL;
208 DBusMessage *reply = NULL;
209 const char *method = "GetAllJobs";
210 DBusError error;
211 int lpc = 0;
212
213 if (upstart_init() == FALSE) {
214 return NULL;
215 }
216
217
218
219
220
221 dbus_error_init(&error);
222 msg = dbus_message_new_method_call(BUS_NAME,
223 BUS_PATH,
224 UPSTART_06_API,
225 method);
226 pcmk__assert(msg != NULL);
227
228 reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, DBUS_TIMEOUT_USE_DEFAULT);
229 dbus_message_unref(msg);
230
231 if (dbus_error_is_set(&error)) {
232 crm_err("Call to %s failed: %s", method, error.message);
233 dbus_error_free(&error);
234 return NULL;
235
236 } else if (!dbus_message_iter_init(reply, &args)) {
237 crm_err("Call to %s failed: Message has no arguments", method);
238 dbus_message_unref(reply);
239 return NULL;
240 }
241
242 if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __func__, __LINE__)) {
243 crm_err("Call to %s failed: Message has invalid arguments", method);
244 dbus_message_unref(reply);
245 return NULL;
246 }
247
248 dbus_message_iter_recurse(&args, &unit);
249 while (dbus_message_iter_get_arg_type (&unit) != DBUS_TYPE_INVALID) {
250 DBusBasicValue value;
251 const char *job = NULL;
252 char *path = NULL;
253
254 if(!pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
255 crm_warn("Skipping Upstart reply argument with unexpected type");
256 continue;
257 }
258
259 dbus_message_iter_get_basic(&unit, &value);
260
261 if(value.str) {
262 int llpc = 0;
263 path = value.str;
264 job = value.str;
265 while (path[llpc] != 0) {
266 if (path[llpc] == '/') {
267 job = path + llpc + 1;
268 }
269 llpc++;
270 }
271 lpc++;
272 crm_trace("%s -> %s", path, job);
273 units = g_list_append(units, fix_upstart_name(job));
274 }
275 dbus_message_iter_next (&unit);
276 }
277
278 dbus_message_unref(reply);
279 crm_trace("Found %d upstart jobs", lpc);
280 return units;
281 }
282
283 gboolean
284 upstart_job_exists(const char *name)
285 {
286 return object_path_for_job(name, NULL, DBUS_TIMEOUT_USE_DEFAULT);
287 }
288
289 static char *
290 get_first_instance(const gchar * job, int timeout)
291 {
292 char *instance = NULL;
293 const char *method = "GetAllInstances";
294 DBusError error;
295 DBusMessage *msg;
296 DBusMessage *reply;
297 DBusMessageIter args;
298 DBusMessageIter unit;
299
300 dbus_error_init(&error);
301 msg = dbus_message_new_method_call(BUS_NAME,
302 job,
303 UPSTART_JOB_IFACE,
304 method);
305 pcmk__assert(msg != NULL);
306
307 dbus_message_append_args(msg, DBUS_TYPE_INVALID);
308 reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, timeout);
309 dbus_message_unref(msg);
310
311 if (dbus_error_is_set(&error)) {
312 crm_info("Call to %s failed: %s", method, error.message);
313 dbus_error_free(&error);
314 goto done;
315
316 } else if(reply == NULL) {
317 crm_info("Call to %s failed: no reply", method);
318 goto done;
319
320 } else if (!dbus_message_iter_init(reply, &args)) {
321 crm_info("Call to %s failed: Message has no arguments", method);
322 goto done;
323 }
324
325 if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __func__, __LINE__)) {
326 crm_info("Call to %s failed: Message has invalid arguments", method);
327 goto done;
328 }
329
330 dbus_message_iter_recurse(&args, &unit);
331 if(pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_OBJECT_PATH, __func__, __LINE__)) {
332 DBusBasicValue value;
333
334 dbus_message_iter_get_basic(&unit, &value);
335
336 if(value.str) {
337 instance = strdup(value.str);
338 crm_trace("Result: %s", instance);
339 }
340 }
341
342 done:
343 if(reply) {
344 dbus_message_unref(reply);
345 }
346 return instance;
347 }
348
349
350
351
352
353
354
355
356
357 static void
358 parse_status_result(const char *name, const char *state, void *userdata)
359 {
360 svc_action_t *op = userdata;
361
362 if (pcmk__str_eq(state, "running", pcmk__str_none)) {
363 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
364 } else {
365 services__set_result(op, PCMK_OCF_NOT_RUNNING, PCMK_EXEC_DONE, state);
366 }
367
368 if (!(op->synchronous)) {
369 services_set_op_pending(op, NULL);
370 services__finalize_async_op(op);
371 }
372 }
373
374
375 #define METADATA_FORMAT \
376 "<?xml " PCMK_XA_VERSION "=\"1.0\"?>\n" \
377 "<" PCMK_XE_RESOURCE_AGENT " " \
378 PCMK_XA_NAME "=\"%s\" " \
379 PCMK_XA_VERSION "=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n" \
380 " <" PCMK_XE_VERSION ">1.1</" PCMK_XE_VERSION ">\n" \
381 " <" PCMK_XE_LONGDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">\n" \
382 " Upstart agent for controlling the system %s service\n" \
383 " </" PCMK_XE_LONGDESC ">\n" \
384 " <" PCMK_XE_SHORTDESC " " PCMK_XA_LANG "=\"" PCMK__VALUE_EN "\">" \
385 "Upstart job for %s" \
386 "</" PCMK_XE_SHORTDESC ">\n" \
387 " <" PCMK_XE_PARAMETERS "/>\n" \
388 " <" PCMK_XE_ACTIONS ">\n" \
389 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_START "\"" \
390 " " PCMK_META_TIMEOUT "=\"15s\" />\n" \
391 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STOP "\"" \
392 " " PCMK_META_TIMEOUT "=\"15s\" />\n" \
393 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_STATUS "\"" \
394 " " PCMK_META_TIMEOUT "=\"15s\" />\n" \
395 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"restart\"" \
396 " " PCMK_META_TIMEOUT "=\"15s\" />\n" \
397 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_MONITOR "\"" \
398 " " PCMK_META_TIMEOUT "=\"15s\"" \
399 " " PCMK_META_INTERVAL "=\"15s\"" \
400 " " PCMK_META_START_DELAY "=\"15s\" />\n" \
401 " <" PCMK_XE_ACTION " " PCMK_XA_NAME "=\"" PCMK_ACTION_META_DATA "\"" \
402 " " PCMK_META_TIMEOUT "=\"5s\" />\n" \
403 " </" PCMK_XE_ACTIONS ">\n" \
404 " <" PCMK_XE_SPECIAL " " PCMK_XA_TAG "=\"upstart\"/>\n" \
405 "</" PCMK_XE_RESOURCE_AGENT ">\n"
406
407 static char *
408 upstart_job_metadata(const char *name)
409 {
410 return crm_strdup_printf(METADATA_FORMAT, name, name, name);
411 }
412
413
414
415
416
417
418
419
420 static void
421 set_result_from_method_error(svc_action_t *op, const DBusError *error)
422 {
423 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
424 "Unable to invoke Upstart DBus method");
425
426 if (strstr(error->name, UPSTART_06_API ".Error.UnknownInstance")) {
427
428 if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_casei)) {
429 crm_trace("Masking stop failure (%s) for %s "
430 "because unknown service can be considered stopped",
431 error->name, pcmk__s(op->rsc, "unknown resource"));
432 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
433 return;
434 }
435
436 services__set_result(op, PCMK_OCF_NOT_INSTALLED,
437 PCMK_EXEC_NOT_INSTALLED, "Upstart job not found");
438
439 } else if (pcmk__str_eq(op->action, PCMK_ACTION_START, pcmk__str_casei)
440 && strstr(error->name, UPSTART_06_API ".Error.AlreadyStarted")) {
441 crm_trace("Masking start failure (%s) for %s "
442 "because already started resource is OK",
443 error->name, pcmk__s(op->rsc, "unknown resource"));
444 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
445 return;
446 }
447
448 crm_info("DBus request for %s of Upstart job %s for resource %s failed: %s",
449 op->action, op->agent, pcmk__s(op->rsc, "with unknown name"),
450 error->message);
451 }
452
453
454
455
456
457
458
459
460 static void
461 job_method_complete(DBusPendingCall *pending, void *user_data)
462 {
463 DBusError error;
464 DBusMessage *reply = NULL;
465 svc_action_t *op = user_data;
466
467
468 if (pending != NULL) {
469 reply = dbus_pending_call_steal_reply(pending);
470 }
471
472
473 dbus_error_init(&error);
474 if (pcmk_dbus_find_error(pending, reply, &error)) {
475 set_result_from_method_error(op, &error);
476 dbus_error_free(&error);
477
478 } else if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_none)) {
479
480 crm_debug("DBus request for stop of %s succeeded",
481 pcmk__s(op->rsc, "unknown resource"));
482 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
483
484 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
485 __func__, __LINE__)) {
486 crm_info("DBus request for %s of %s succeeded but "
487 "return type was unexpected", op->action,
488 pcmk__s(op->rsc, "unknown resource"));
489 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
490
491 } else {
492 const char *path = NULL;
493
494 dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
495 DBUS_TYPE_INVALID);
496 crm_debug("DBus request for %s of %s using %s succeeded",
497 op->action, pcmk__s(op->rsc, "unknown resource"), path);
498 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
499 }
500
501
502 CRM_LOG_ASSERT(pending == op->opaque->pending);
503 services_set_op_pending(op, NULL);
504
505
506 services__finalize_async_op(op);
507 if (reply != NULL) {
508 dbus_message_unref(reply);
509 }
510 }
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528 int
529 services__execute_upstart(svc_action_t *op)
530 {
531 char *job = NULL;
532 int arg_wait = TRUE;
533 const char *arg_env = "pacemaker=1";
534 const char *action = op->action;
535
536 DBusError error;
537 DBusMessage *msg = NULL;
538 DBusMessage *reply = NULL;
539 DBusMessageIter iter, array_iter;
540
541 pcmk__assert(op != NULL);
542
543 if ((op->action == NULL) || (op->agent == NULL)) {
544 services__set_result(op, PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR_FATAL,
545 "Bug in action caller");
546 goto cleanup;
547 }
548
549 if (!upstart_init()) {
550 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
551 "No DBus connection");
552 goto cleanup;
553 }
554
555 if (pcmk__str_eq(op->action, PCMK_ACTION_META_DATA, pcmk__str_casei)) {
556 op->stdout_data = upstart_job_metadata(op->agent);
557 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
558 goto cleanup;
559 }
560
561 if (!object_path_for_job(op->agent, &job, op->timeout)) {
562 if (pcmk__str_eq(action, PCMK_ACTION_STOP, pcmk__str_none)) {
563 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
564 } else {
565 services__set_result(op, PCMK_OCF_NOT_INSTALLED,
566 PCMK_EXEC_NOT_INSTALLED,
567 "Upstart job not found");
568 }
569 goto cleanup;
570 }
571
572 if (job == NULL) {
573
574 op->rc = PCMK_OCF_UNKNOWN_ERROR;
575 op->status = PCMK_EXEC_ERROR;
576 goto cleanup;
577 }
578
579 if (pcmk__strcase_any_of(op->action, PCMK_ACTION_MONITOR,
580 PCMK_ACTION_STATUS, NULL)) {
581 DBusPendingCall *pending = NULL;
582 char *state = NULL;
583 char *path = get_first_instance(job, op->timeout);
584
585 services__set_result(op, PCMK_OCF_NOT_RUNNING, PCMK_EXEC_DONE,
586 "No Upstart job instances found");
587 if (path == NULL) {
588 goto cleanup;
589 }
590 state = pcmk_dbus_get_property(upstart_proxy, BUS_NAME, path,
591 UPSTART_06_API ".Instance", "state",
592 op->synchronous? NULL : parse_status_result,
593 op,
594 op->synchronous? NULL : &pending,
595 op->timeout);
596 free(path);
597
598 if (op->synchronous) {
599 parse_status_result("state", state, op);
600 free(state);
601
602 } else if (pending == NULL) {
603 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
604 "Could not get job state from DBus");
605
606 } else {
607 free(job);
608 services_set_op_pending(op, pending);
609 services_add_inflight_op(op);
610 return pcmk_rc_ok;
611 }
612
613 goto cleanup;
614
615 } else if (pcmk__str_eq(action, PCMK_ACTION_START, pcmk__str_none)) {
616 action = "Start";
617
618 } else if (pcmk__str_eq(action, PCMK_ACTION_STOP, pcmk__str_none)) {
619 action = "Stop";
620
621 } else if (pcmk__str_eq(action, "restart", pcmk__str_none)) {
622 action = "Restart";
623
624 } else {
625 services__set_result(op, PCMK_OCF_UNIMPLEMENT_FEATURE,
626 PCMK_EXEC_ERROR_HARD,
627 "Action not implemented for Upstart resources");
628 goto cleanup;
629 }
630
631
632 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_DONE,
633 "Bug in service library");
634
635 crm_debug("Calling %s for %s on %s",
636 action, pcmk__s(op->rsc, "unknown resource"), job);
637
638 msg = dbus_message_new_method_call(BUS_NAME,
639 job,
640 UPSTART_JOB_IFACE,
641 action);
642 pcmk__assert(msg != NULL);
643
644 dbus_message_iter_init_append (msg, &iter);
645 CRM_LOG_ASSERT(dbus_message_iter_open_container(&iter,
646 DBUS_TYPE_ARRAY,
647 DBUS_TYPE_STRING_AS_STRING,
648 &array_iter));
649 CRM_LOG_ASSERT(dbus_message_iter_append_basic(&array_iter,
650 DBUS_TYPE_STRING, &arg_env));
651 CRM_LOG_ASSERT(dbus_message_iter_close_container(&iter, &array_iter));
652 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &arg_wait,
653 DBUS_TYPE_INVALID));
654
655 if (!(op->synchronous)) {
656 DBusPendingCall *pending = pcmk_dbus_send(msg, upstart_proxy,
657 job_method_complete, op,
658 op->timeout);
659
660 if (pending == NULL) {
661 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
662 "Unable to send DBus message");
663 goto cleanup;
664
665 } else {
666 free(job);
667 services_set_op_pending(op, pending);
668 services_add_inflight_op(op);
669 return pcmk_rc_ok;
670 }
671 }
672
673
674
675 dbus_error_init(&error);
676 reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, op->timeout);
677
678 if (dbus_error_is_set(&error)) {
679 set_result_from_method_error(op, &error);
680 dbus_error_free(&error);
681
682 } else if (pcmk__str_eq(op->action, PCMK_ACTION_STOP, pcmk__str_none)) {
683
684 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
685
686 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
687 __func__, __LINE__)) {
688 crm_info("Call to %s passed but return type was unexpected",
689 op->action);
690 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
691
692 } else {
693 const char *path = NULL;
694
695 dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
696 DBUS_TYPE_INVALID);
697 crm_debug("Call to %s passed: %s", op->action, path);
698 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
699 }
700
701 cleanup:
702 free(job);
703 if (msg != NULL) {
704 dbus_message_unref(msg);
705 }
706 if (reply != NULL) {
707 dbus_message_unref(reply);
708 }
709
710 if (op->synchronous) {
711 return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
712 } else {
713 return services__finalize_async_op(op);
714 }
715 }