This source file includes following definitions.
- upstart_init
- upstart_cleanup
- upstart_job_by_name
- fix
- fix_upstart_name
- upstart_job_listall
- upstart_job_exists
- get_first_instance
- upstart_job_check
- upstart_job_metadata
- upstart_mask_error
- upstart_async_dispatch
- upstart_job_exec
1
2
3
4
5
6
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
34
35 static DBusConnection *upstart_proxy = NULL;
36
37 static gboolean
38 upstart_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
52 void
53 upstart_cleanup(void)
54 {
55 if (upstart_proxy) {
56 pcmk_dbus_disconnect(upstart_proxy);
57 upstart_proxy = NULL;
58 }
59 }
60
61 static gboolean
62 upstart_job_by_name(const gchar * arg_name, gchar ** out_unit, int timeout)
63 {
64
65
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,
76 BUS_PATH,
77 UPSTART_06_API,
78 method);
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
112 static void
113 fix(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
134 static char *
135 fix_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
148 GList *
149 upstart_job_listall(void)
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
166
167
168 dbus_error_init(&error);
169 msg = dbus_message_new_method_call(BUS_NAME,
170 BUS_PATH,
171 UPSTART_06_API,
172 method);
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
230 gboolean
231 upstart_job_exists(const char *name)
232 {
233 return upstart_job_by_name(name, NULL, DBUS_TIMEOUT_USE_DEFAULT);
234 }
235
236 static char *
237 get_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,
249 job,
250 UPSTART_JOB_IFACE,
251 method);
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
296 static void
297 upstart_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
304
305 } else {
306 op->rc = PCMK_OCF_NOT_RUNNING;
307 }
308
309 if (op->synchronous == FALSE) {
310 services_set_op_pending(op, NULL);
311 operation_finalize(op);
312 }
313 }
314
315 static char *
316 upstart_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
340 static bool
341 upstart_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);
351 op->rc = PCMK_OCF_NOT_INSTALLED;
352 op->status = PCMK_LRM_OP_NOT_INSTALLED;
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
366 static void
367 upstart_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
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
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);
408 operation_finalize(op);
409
410 if(reply) {
411 dbus_message_unref(reply);
412 }
413 }
414
415
416
417 gboolean
418 upstart_job_exec(svc_action_t * op)
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
430 op->rc = PCMK_OCF_UNKNOWN_ERROR;
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 {
445 op->rc = PCMK_OCF_NOT_INSTALLED;
446 op->status = PCMK_LRM_OP_NOT_INSTALLED;
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
454 op->rc = PCMK_OCF_NOT_RUNNING;
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);
471 services_add_inflight_op(op);
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 {
485 op->rc = PCMK_OCF_UNIMPLEMENT_FEATURE;
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,
492 job,
493 UPSTART_JOB_IFACE,
494 action);
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);
515 services_add_inflight_op(op);
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
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 }