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, "stop", pcmk__str_casei)) {
416 crm_trace("Masking stop failure (%s) for %s "
417 "because unknown service can be considered stopped",
418 error->name, crm_str(op->rsc));
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, "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, crm_str(op->rsc));
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, crm_str(op->rsc), error->message);
437 }
438
439
440
441
442
443
444
445
446 static void
447 job_method_complete(DBusPendingCall *pending, void *user_data)
448 {
449 DBusError error;
450 DBusMessage *reply = NULL;
451 svc_action_t *op = user_data;
452
453
454 if (pending != NULL) {
455 reply = dbus_pending_call_steal_reply(pending);
456 }
457
458
459 dbus_error_init(&error);
460 if (pcmk_dbus_find_error(pending, reply, &error)) {
461 set_result_from_method_error(op, &error);
462 dbus_error_free(&error);
463
464 } else if (pcmk__str_eq(op->action, "stop", pcmk__str_none)) {
465
466 crm_debug("DBus request for stop of %s succeeded", crm_str(op->rsc));
467 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
468
469 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
470 __func__, __LINE__)) {
471 crm_info("DBus request for %s of %s succeeded but "
472 "return type was unexpected", op->action, crm_str(op->rsc));
473 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
474
475 } else {
476 const char *path = NULL;
477
478 dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
479 DBUS_TYPE_INVALID);
480 crm_debug("DBus request for %s of %s using %s succeeded",
481 op->action, crm_str(op->rsc), path);
482 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
483 }
484
485
486 CRM_LOG_ASSERT(pending == op->opaque->pending);
487 services_set_op_pending(op, NULL);
488
489
490 services__finalize_async_op(op);
491 if (reply != NULL) {
492 dbus_message_unref(reply);
493 }
494 }
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512 int
513 services__execute_upstart(svc_action_t *op)
514 {
515 char *job = NULL;
516 int arg_wait = TRUE;
517 const char *arg_env = "pacemaker=1";
518 const char *action = op->action;
519
520 DBusError error;
521 DBusMessage *msg = NULL;
522 DBusMessage *reply = NULL;
523 DBusMessageIter iter, array_iter;
524
525 CRM_ASSERT(op != NULL);
526
527 if ((op->action == NULL) || (op->agent == NULL)) {
528 services__set_result(op, PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR_FATAL,
529 "Bug in action caller");
530 goto cleanup;
531 }
532
533 if (!upstart_init()) {
534 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
535 "No DBus connection");
536 goto cleanup;
537 }
538
539 if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
540 op->stdout_data = upstart_job_metadata(op->agent);
541 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
542 goto cleanup;
543 }
544
545 if (!object_path_for_job(op->agent, &job, op->timeout)) {
546 if (pcmk__str_eq(action, "stop", pcmk__str_none)) {
547 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
548 } else {
549 services__set_result(op, PCMK_OCF_NOT_INSTALLED,
550 PCMK_EXEC_NOT_INSTALLED,
551 "Upstart job not found");
552 }
553 goto cleanup;
554 }
555
556 if (job == NULL) {
557
558 op->rc = PCMK_OCF_UNKNOWN_ERROR;
559 op->status = PCMK_EXEC_ERROR;
560 goto cleanup;
561 }
562
563 if (pcmk__strcase_any_of(op->action, "monitor", "status", NULL)) {
564 DBusPendingCall *pending = NULL;
565 char *state = NULL;
566 char *path = get_first_instance(job, op->timeout);
567
568 services__set_result(op, PCMK_OCF_NOT_RUNNING, PCMK_EXEC_DONE,
569 "No Upstart job instances found");
570 if (path == NULL) {
571 goto cleanup;
572 }
573 state = pcmk_dbus_get_property(upstart_proxy, BUS_NAME, path,
574 UPSTART_06_API ".Instance", "state",
575 op->synchronous? NULL : parse_status_result,
576 op,
577 op->synchronous? NULL : &pending,
578 op->timeout);
579 free(path);
580
581 if (op->synchronous) {
582 parse_status_result("state", state, op);
583 free(state);
584
585 } else if (pending == NULL) {
586 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
587 "Could not get job state from DBus");
588
589 } else {
590 free(job);
591 services_set_op_pending(op, pending);
592 services_add_inflight_op(op);
593 return pcmk_rc_ok;
594 }
595
596 goto cleanup;
597
598 } else if (pcmk__str_eq(action, "start", pcmk__str_none)) {
599 action = "Start";
600
601 } else if (pcmk__str_eq(action, "stop", pcmk__str_none)) {
602 action = "Stop";
603
604 } else if (pcmk__str_eq(action, "restart", pcmk__str_none)) {
605 action = "Restart";
606
607 } else {
608 services__set_result(op, PCMK_OCF_UNIMPLEMENT_FEATURE,
609 PCMK_EXEC_ERROR_HARD,
610 "Action not implemented for Upstart resources");
611 goto cleanup;
612 }
613
614
615 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_DONE,
616 "Bug in service library");
617
618 crm_debug("Calling %s for %s on %s", action, crm_str(op->rsc), job);
619
620 msg = dbus_message_new_method_call(BUS_NAME,
621 job,
622 UPSTART_JOB_IFACE,
623 action);
624 CRM_ASSERT(msg != NULL);
625
626 dbus_message_iter_init_append (msg, &iter);
627 CRM_LOG_ASSERT(dbus_message_iter_open_container(&iter,
628 DBUS_TYPE_ARRAY,
629 DBUS_TYPE_STRING_AS_STRING,
630 &array_iter));
631 CRM_LOG_ASSERT(dbus_message_iter_append_basic(&array_iter,
632 DBUS_TYPE_STRING, &arg_env));
633 CRM_LOG_ASSERT(dbus_message_iter_close_container(&iter, &array_iter));
634 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &arg_wait,
635 DBUS_TYPE_INVALID));
636
637 if (!(op->synchronous)) {
638 DBusPendingCall *pending = pcmk_dbus_send(msg, upstart_proxy,
639 job_method_complete, op,
640 op->timeout);
641
642 if (pending == NULL) {
643 services__set_result(op, PCMK_OCF_UNKNOWN_ERROR, PCMK_EXEC_ERROR,
644 "Unable to send DBus message");
645 goto cleanup;
646
647 } else {
648 free(job);
649 services_set_op_pending(op, pending);
650 services_add_inflight_op(op);
651 return pcmk_rc_ok;
652 }
653 }
654
655
656
657 dbus_error_init(&error);
658 reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, op->timeout);
659
660 if (dbus_error_is_set(&error)) {
661 set_result_from_method_error(op, &error);
662 dbus_error_free(&error);
663
664 } else if (pcmk__str_eq(op->action, "stop", pcmk__str_none)) {
665
666 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
667
668 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
669 __func__, __LINE__)) {
670 crm_info("Call to %s passed but return type was unexpected",
671 op->action);
672 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
673
674 } else {
675 const char *path = NULL;
676
677 dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
678 DBUS_TYPE_INVALID);
679 crm_debug("Call to %s passed: %s", op->action, path);
680 services__set_result(op, PCMK_OCF_OK, PCMK_EXEC_DONE, NULL);
681 }
682
683 cleanup:
684 free(job);
685 if (msg != NULL) {
686 dbus_message_unref(msg);
687 }
688 if (reply != NULL) {
689 dbus_message_unref(reply);
690 }
691
692 if (op->synchronous) {
693 return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
694 } else {
695 return services__finalize_async_op(op);
696 }
697 }