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, __FUNCTION__, __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, __FUNCTION__, __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, __FUNCTION__, __LINE__)) {
202 continue;
203 }
204
205 dbus_message_iter_get_basic(&unit, &value);
206
207 if(value.str) {
208 int llpc = 0;
209 path = value.str;
210 job = value.str;
211 while (path[llpc] != 0) {
212 if (path[llpc] == '/') {
213 job = path + llpc + 1;
214 }
215 llpc++;
216 }
217 lpc++;
218 crm_trace("%s -> %s", path, job);
219 units = g_list_append(units, fix_upstart_name(job));
220 }
221 dbus_message_iter_next (&unit);
222 }
223
224 dbus_message_unref(reply);
225 crm_trace("Found %d upstart jobs", lpc);
226 return units;
227 }
228
229 gboolean
230 upstart_job_exists(const char *name)
231 {
232 return upstart_job_by_name(name, NULL, DBUS_TIMEOUT_USE_DEFAULT);
233 }
234
235 static char *
236 get_first_instance(const gchar * job, int timeout)
237 {
238 char *instance = NULL;
239 const char *method = "GetAllInstances";
240 DBusError error;
241 DBusMessage *msg;
242 DBusMessage *reply;
243 DBusMessageIter args;
244 DBusMessageIter unit;
245
246 dbus_error_init(&error);
247 msg = dbus_message_new_method_call(BUS_NAME,
248 job,
249 UPSTART_JOB_IFACE,
250 method);
251 CRM_ASSERT(msg != NULL);
252
253 dbus_message_append_args(msg, DBUS_TYPE_INVALID);
254 reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, timeout);
255 dbus_message_unref(msg);
256
257 if (dbus_error_is_set(&error)) {
258 crm_err("Call to %s failed: %s", method, error.message);
259 dbus_error_free(&error);
260 goto done;
261
262 } else if(reply == NULL) {
263 crm_err("Call to %s failed: no reply", method);
264 goto done;
265
266 } else if (!dbus_message_iter_init(reply, &args)) {
267 crm_err("Call to %s failed: Message has no arguments", method);
268 goto done;
269 }
270
271 if(!pcmk_dbus_type_check(reply, &args, DBUS_TYPE_ARRAY, __FUNCTION__, __LINE__)) {
272 crm_err("Call to %s failed: Message has invalid arguments", method);
273 goto done;
274 }
275
276 dbus_message_iter_recurse(&args, &unit);
277 if(pcmk_dbus_type_check(reply, &unit, DBUS_TYPE_OBJECT_PATH, __FUNCTION__, __LINE__)) {
278 DBusBasicValue value;
279
280 dbus_message_iter_get_basic(&unit, &value);
281
282 if(value.str) {
283 instance = strdup(value.str);
284 crm_trace("Result: %s", instance);
285 }
286 }
287
288 done:
289 if(reply) {
290 dbus_message_unref(reply);
291 }
292 return instance;
293 }
294
295 static void
296 upstart_job_check(const char *name, const char *state, void *userdata)
297 {
298 svc_action_t * op = userdata;
299
300 if (state && g_strcmp0(state, "running") == 0) {
301 op->rc = PCMK_OCF_OK;
302
303
304 } else {
305 op->rc = PCMK_OCF_NOT_RUNNING;
306 }
307
308 if (op->synchronous == FALSE) {
309 services_set_op_pending(op, NULL);
310 operation_finalize(op);
311 }
312 }
313
314 static char *
315 upstart_job_metadata(const char *name)
316 {
317 return crm_strdup_printf("<?xml version=\"1.0\"?>\n"
318 "<!DOCTYPE resource-agent SYSTEM \"ra-api-1.dtd\">\n"
319 "<resource-agent name=\"%s\" version=\"" PCMK_DEFAULT_AGENT_VERSION "\">\n"
320 " <version>1.0</version>\n"
321 " <longdesc lang=\"en\">\n"
322 " Upstart agent for controlling the system %s service\n"
323 " </longdesc>\n"
324 " <shortdesc lang=\"en\">%s upstart agent</shortdesc>\n"
325 " <parameters>\n"
326 " </parameters>\n"
327 " <actions>\n"
328 " <action name=\"start\" timeout=\"15\" />\n"
329 " <action name=\"stop\" timeout=\"15\" />\n"
330 " <action name=\"status\" timeout=\"15\" />\n"
331 " <action name=\"restart\" timeout=\"15\" />\n"
332 " <action name=\"monitor\" timeout=\"15\" interval=\"15\" start-delay=\"15\" />\n"
333 " <action name=\"meta-data\" timeout=\"5\" />\n"
334 " </actions>\n"
335 " <special tag=\"upstart\">\n"
336 " </special>\n" "</resource-agent>\n", name, name, name);
337 }
338
339 static bool
340 upstart_mask_error(svc_action_t *op, const char *error)
341 {
342 crm_trace("Could not issue %s for %s: %s", op->action, op->rsc, error);
343 if(strstr(error, UPSTART_06_API ".Error.UnknownInstance")) {
344 if(safe_str_eq(op->action, "stop")) {
345 crm_trace("Masking %s failure for %s: unknown services are stopped", op->action, op->rsc);
346 op->rc = PCMK_OCF_OK;
347
348 } else if(safe_str_eq(op->action, "start")) {
349 crm_trace("Mapping %s failure for %s: unknown services are not installed", op->action, op->rsc);
350 op->rc = PCMK_OCF_NOT_INSTALLED;
351 op->status = PCMK_LRM_OP_NOT_INSTALLED;
352 }
353 return TRUE;
354
355 } else if (safe_str_eq(op->action, "start")
356 && strstr(error, UPSTART_06_API ".Error.AlreadyStarted")) {
357 crm_trace("Mapping %s failure for %s: starting a started resource is allowed", op->action, op->rsc);
358 op->rc = PCMK_OCF_OK;
359 return TRUE;
360 }
361
362 return FALSE;
363 }
364
365 static void
366 upstart_async_dispatch(DBusPendingCall *pending, void *user_data)
367 {
368 DBusError error;
369 DBusMessage *reply = NULL;
370 svc_action_t *op = user_data;
371
372 dbus_error_init(&error);
373 if(pending) {
374 reply = dbus_pending_call_steal_reply(pending);
375 }
376
377 if (pcmk_dbus_find_error(pending, reply, &error)) {
378
379
380 if (!upstart_mask_error(op, error.name)) {
381 crm_err("%s for %s: %s", op->action, op->rsc, error.message);
382 }
383 dbus_error_free(&error);
384
385 } else if (!g_strcmp0(op->action, "stop")) {
386
387 op->rc = PCMK_OCF_OK;
388
389 } else {
390 if(!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH, __FUNCTION__, __LINE__)) {
391 crm_warn("Call to %s passed but return type was unexpected", op->action);
392 op->rc = PCMK_OCF_OK;
393
394 } else {
395 const char *path = NULL;
396
397 dbus_message_get_args (reply, NULL,
398 DBUS_TYPE_OBJECT_PATH, &path,
399 DBUS_TYPE_INVALID);
400 crm_info("Call to %s passed: %s", op->action, path);
401 op->rc = PCMK_OCF_OK;
402 }
403 }
404
405 CRM_LOG_ASSERT(pending == op->opaque->pending);
406 services_set_op_pending(op, NULL);
407 operation_finalize(op);
408
409 if(reply) {
410 dbus_message_unref(reply);
411 }
412 }
413
414
415
416 gboolean
417 upstart_job_exec(svc_action_t * op)
418 {
419 char *job = NULL;
420 int arg_wait = TRUE;
421 const char *arg_env = "pacemaker=1";
422 const char *action = op->action;
423
424 DBusError error;
425 DBusMessage *msg = NULL;
426 DBusMessage *reply = NULL;
427 DBusMessageIter iter, array_iter;
428
429 op->rc = PCMK_OCF_UNKNOWN_ERROR;
430 CRM_ASSERT(upstart_init());
431
432 if (safe_str_eq(op->action, "meta-data")) {
433 op->stdout_data = upstart_job_metadata(op->agent);
434 op->rc = PCMK_OCF_OK;
435 goto cleanup;
436 }
437
438 if(!upstart_job_by_name(op->agent, &job, op->timeout)) {
439 crm_debug("Could not obtain job named '%s' to %s", op->agent, action);
440 if (!g_strcmp0(action, "stop")) {
441 op->rc = PCMK_OCF_OK;
442
443 } else {
444 op->rc = PCMK_OCF_NOT_INSTALLED;
445 op->status = PCMK_LRM_OP_NOT_INSTALLED;
446 }
447 goto cleanup;
448 }
449
450 if (safe_str_eq(op->action, "monitor") || safe_str_eq(action, "status")) {
451
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, __FUNCTION__, __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 }