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