pacemaker 2.1.5-a3f44794f94
Scalable High-Availability cluster resource manager
upstart.c
Go to the documentation of this file.
1/*
2 * Original copyright 2010 Senko Rasic <senko.rasic@dobarkod.hr>
3 * and Ante Karamatic <ivoks@init.hr>
4 * Later changes copyright 2012-2022 the Pacemaker project contributors
5 *
6 * The version control history for this file may have further details.
7 *
8 * This source code is licensed under the GNU Lesser General Public License
9 * version 2.1 or later (LGPLv2.1+) WITHOUT ANY WARRANTY.
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 http://upstart.ubuntu.com/wiki/DBusInterface
37*/
38static DBusConnection *upstart_proxy = NULL;
39
48int
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
66enum ocf_exitcode
67services__upstart2ocf(int exit_status)
68{
69 // This library uses OCF codes for Upstart actions
70 return (enum ocf_exitcode) exit_status;
71}
72
73static gboolean
74upstart_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
88void
90{
91 if (upstart_proxy) {
92 pcmk_dbus_disconnect(upstart_proxy);
93 upstart_proxy = NULL;
94 }
95}
96
108static bool
109object_path_for_job(const gchar *arg_name, char **path, int timeout)
110{
111 /*
112 com.ubuntu.Upstart0_6.GetJobByName (in String name, out ObjectPath job)
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, // target for the method call
127 BUS_PATH, // object to call on
128 UPSTART_06_API, // interface to call on
129 "GetJobByName"); // method name
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
164static void
165fix(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
186static char *
187fix_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
200GList *
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 com.ubuntu.Upstart0_6.GetAllJobs (out <Array of ObjectPath> jobs)
218*/
219
220 dbus_error_init(&error);
221 msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
222 BUS_PATH, // object to call on
223 UPSTART_06_API, // interface to call on
224 method); // method name
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
282gboolean
284{
285 return object_path_for_job(name, NULL, DBUS_TIMEOUT_USE_DEFAULT);
286}
287
288static char *
289get_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, // target for the method call
301 job, // object to call on
302 UPSTART_JOB_IFACE, // interface to call on
303 method); // method name
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
356static void
357parse_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)) {
363 } else {
365 }
366
367 if (!(op->synchronous)) {
368 services_set_op_pending(op, NULL);
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
394static char *
395upstart_job_metadata(const char *name)
396{
398}
399
407static void
408set_result_from_method_error(svc_action_t *op, const DBusError *error)
409{
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, pcmk__s(op->rsc, "unknown resource"));
420 return;
421 }
422
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, pcmk__s(op->rsc, "unknown resource"));
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
447static void
448job_method_complete(DBusPendingCall *pending, void *user_data)
449{
450 DBusError error;
451 DBusMessage *reply = NULL;
452 svc_action_t *op = user_data;
453
454 // Grab the reply
455 if (pending != NULL) {
456 reply = dbus_pending_call_steal_reply(pending);
457 }
458
459 // Determine result
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, "stop", pcmk__str_none)) {
466 // Call has no return value
467 crm_debug("DBus request for stop of %s succeeded",
468 pcmk__s(op->rsc, "unknown resource"));
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"));
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);
486 }
487
488 // The call is no longer pending
489 CRM_LOG_ASSERT(pending == op->opaque->pending);
490 services_set_op_pending(op, NULL);
491
492 // Finalize action
494 if (reply != NULL) {
495 dbus_message_unref(reply);
496 }
497}
498
515int
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)) {
532 "Bug in action caller");
533 goto cleanup;
534 }
535
536 if (!upstart_init()) {
538 "No DBus connection");
539 goto cleanup;
540 }
541
542 if (pcmk__str_eq(op->action, "meta-data", pcmk__str_casei)) {
543 op->stdout_data = upstart_job_metadata(op->agent);
545 goto cleanup;
546 }
547
548 if (!object_path_for_job(op->agent, &job, op->timeout)) {
549 if (pcmk__str_eq(action, "stop", pcmk__str_none)) {
551 } else {
554 "Upstart job not found");
555 }
556 goto cleanup;
557 }
558
559 if (job == NULL) {
560 // Shouldn't normally be possible -- maybe a memory error
563 goto cleanup;
564 }
565
566 if (pcmk__strcase_any_of(op->action, "monitor", "status", NULL)) {
567 DBusPendingCall *pending = NULL;
568 char *state = NULL;
569 char *path = get_first_instance(job, op->timeout);
570
572 "No Upstart job instances found");
573 if (path == NULL) {
574 goto cleanup;
575 }
576 state = pcmk_dbus_get_property(upstart_proxy, BUS_NAME, path,
577 UPSTART_06_API ".Instance", "state",
578 op->synchronous? NULL : parse_status_result,
579 op,
580 op->synchronous? NULL : &pending,
581 op->timeout);
582 free(path);
583
584 if (op->synchronous) {
585 parse_status_result("state", state, op);
586 free(state);
587
588 } else if (pending == NULL) {
590 "Could not get job state from DBus");
591
592 } else { // Successfully initiated async op
593 free(job);
594 services_set_op_pending(op, pending);
596 return pcmk_rc_ok;
597 }
598
599 goto cleanup;
600
601 } else if (pcmk__str_eq(action, "start", pcmk__str_none)) {
602 action = "Start";
603
604 } else if (pcmk__str_eq(action, "stop", pcmk__str_none)) {
605 action = "Stop";
606
607 } else if (pcmk__str_eq(action, "restart", pcmk__str_none)) {
608 action = "Restart";
609
610 } else {
613 "Action not implemented for Upstart resources");
614 goto cleanup;
615 }
616
617 // Initialize rc/status in case called functions don't set them
619 "Bug in service library");
620
621 crm_debug("Calling %s for %s on %s",
622 action, pcmk__s(op->rsc, "unknown resource"), job);
623
624 msg = dbus_message_new_method_call(BUS_NAME, // target for the method call
625 job, // object to call on
626 UPSTART_JOB_IFACE, // interface to call on
627 action); // method name
628 CRM_ASSERT(msg != NULL);
629
630 dbus_message_iter_init_append (msg, &iter);
631 CRM_LOG_ASSERT(dbus_message_iter_open_container(&iter,
632 DBUS_TYPE_ARRAY,
633 DBUS_TYPE_STRING_AS_STRING,
634 &array_iter));
635 CRM_LOG_ASSERT(dbus_message_iter_append_basic(&array_iter,
636 DBUS_TYPE_STRING, &arg_env));
637 CRM_LOG_ASSERT(dbus_message_iter_close_container(&iter, &array_iter));
638 CRM_LOG_ASSERT(dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &arg_wait,
639 DBUS_TYPE_INVALID));
640
641 if (!(op->synchronous)) {
642 DBusPendingCall *pending = pcmk_dbus_send(msg, upstart_proxy,
643 job_method_complete, op,
644 op->timeout);
645
646 if (pending == NULL) {
648 "Unable to send DBus message");
649 goto cleanup;
650
651 } else { // Successfully initiated async op
652 free(job);
653 services_set_op_pending(op, pending);
655 return pcmk_rc_ok;
656 }
657 }
658
659 // Synchronous call
660
661 dbus_error_init(&error);
662 reply = pcmk_dbus_send_recv(msg, upstart_proxy, &error, op->timeout);
663
664 if (dbus_error_is_set(&error)) {
665 set_result_from_method_error(op, &error);
666 dbus_error_free(&error);
667
668 } else if (pcmk__str_eq(op->action, "stop", pcmk__str_none)) {
669 // DBus call does not return a value
671
672 } else if (!pcmk_dbus_type_check(reply, NULL, DBUS_TYPE_OBJECT_PATH,
673 __func__, __LINE__)) {
674 crm_info("Call to %s passed but return type was unexpected",
675 op->action);
677
678 } else {
679 const char *path = NULL;
680
681 dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path,
682 DBUS_TYPE_INVALID);
683 crm_debug("Call to %s passed: %s", op->action, path);
685 }
686
687cleanup:
688 free(job);
689 if (msg != NULL) {
690 dbus_message_unref(msg);
691 }
692 if (reply != NULL) {
693 dbus_message_unref(reply);
694 }
695
696 if (op->synchronous) {
697 return (op->rc == PCMK_OCF_OK)? pcmk_rc_ok : pcmk_rc_error;
698 } else {
700 }
701}
const char * path
Definition: cib.c:26
const char * name
Definition: cib.c:24
char * crm_strdup_printf(char const *format,...) G_GNUC_PRINTF(1
A dumping ground.
char * pcmk_dbus_get_property(DBusConnection *connection, const char *target, const char *obj, const gchar *iface, const char *name, property_callback_func callback, void *userdata, DBusPendingCall **pending, int timeout)
Definition: dbus.c:693
DBusConnection * pcmk_dbus_connect(void)
Definition: dbus.c:259
DBusMessage * pcmk_dbus_send_recv(DBusMessage *msg, DBusConnection *connection, DBusError *error, int timeout)
Definition: dbus.c:412
DBusPendingCall * pcmk_dbus_send(DBusMessage *msg, DBusConnection *connection, void(*done)(DBusPendingCall *pending, void *user_data), void *user_data, int timeout)
Definition: dbus.c:476
bool pcmk_dbus_find_error(DBusPendingCall *pending, DBusMessage *reply, DBusError *ret)
Definition: dbus.c:330
bool pcmk_dbus_type_check(DBusMessage *msg, DBusMessageIter *field, int expected, const char *function, int line)
Definition: dbus.c:516
void pcmk_dbus_disconnect(DBusConnection *connection)
Definition: dbus.c:295
#define crm_info(fmt, args...)
Definition: logging.h:362
#define crm_warn(fmt, args...)
Definition: logging.h:360
#define CRM_LOG_ASSERT(expr)
Definition: logging.h:211
#define crm_debug(fmt, args...)
Definition: logging.h:364
#define crm_err(fmt, args...)
Definition: logging.h:359
#define crm_trace(fmt, args...)
Definition: logging.h:365
Wrappers for and extensions to glib mainloop.
xmlNode * input
#define DBUS_TIMEOUT_USE_DEFAULT
Definition: pcmk-dbus.h:16
unsigned int timeout
Definition: pcmk_fence.c:32
const char * action
Definition: pcmk_fence.c:30
#define CRM_ASSERT(expr)
Definition: results.h:42
ocf_exitcode
Exit status codes for resource agents.
Definition: results.h:163
@ PCMK_OCF_UNIMPLEMENT_FEATURE
Requested action not implemented.
Definition: results.h:167
@ PCMK_OCF_NOT_CONFIGURED
Parameter invalid (inherently)
Definition: results.h:170
@ PCMK_OCF_NOT_INSTALLED
Dependencies not available locally.
Definition: results.h:169
@ PCMK_OCF_UNKNOWN_ERROR
Unspecified error.
Definition: results.h:165
@ PCMK_OCF_NOT_RUNNING
Service safely stopped.
Definition: results.h:171
@ PCMK_OCF_OK
Success.
Definition: results.h:164
@ pcmk_rc_ok
Definition: results.h:148
@ pcmk_rc_error
Definition: results.h:144
@ PCMK_EXEC_ERROR_FATAL
Execution failed, do not retry anywhere.
Definition: results.h:318
@ PCMK_EXEC_NOT_INSTALLED
Agent or dependency not available locally.
Definition: results.h:319
@ PCMK_EXEC_DONE
Action completed, result is known.
Definition: results.h:312
@ PCMK_EXEC_ERROR
Execution failed, may be retried.
Definition: results.h:316
@ PCMK_EXEC_ERROR_HARD
Execution failed, do not retry on node.
Definition: results.h:317
void services_add_inflight_op(svc_action_t *op)
Definition: services.c:835
Services API.
void services__set_result(svc_action_t *action, int agent_status, enum pcmk_exec_status exec_status, const char *exit_reason)
Definition: services.c:1271
int services__finalize_async_op(svc_action_t *op)
bool pcmk__strcase_any_of(const char *s,...) G_GNUC_NULL_TERMINATED
Definition: strings.c:928
@ pcmk__str_none
@ pcmk__str_casei
Object for executing external actions.
Definition: services.h:112
char * agent
Resource agent name for resource actions, otherwise NULL.
Definition: services.h:134
int rc
Exit status of action (set by library upon completion)
Definition: services.h:145
char * rsc
XML ID of resource being executed for resource actions, otherwise NULL.
Definition: services.h:119
char * action
Name of action being executed for resource actions, otherwise NULL.
Definition: services.h:122
int synchronous
Definition: services.h:163
int timeout
Action timeout (in milliseconds)
Definition: services.h:136
char * stdout_data
Action stdout (set by library)
Definition: services.h:168
int status
Execution status (enum pcmk_exec_status set by library)
Definition: services.h:153
svc_action_private_t * opaque
This field should be treated as internal to Pacemaker.
Definition: services.h:172
GList * upstart_job_listall(void)
Definition: upstart.c:201
#define BUS_PATH
Definition: upstart.c:29
#define METADATA_FORMAT
Definition: upstart.c:373
#define UPSTART_JOB_IFACE
Definition: upstart.c:32
void upstart_cleanup(void)
Definition: upstart.c:89
#define BUS_NAME
Definition: upstart.c:28
int services__upstart_prepare(svc_action_t *op)
Definition: upstart.c:49
int services__execute_upstart(svc_action_t *op)
Definition: upstart.c:516
gboolean upstart_job_exists(const char *name)
Definition: upstart.c:283
#define UPSTART_06_API
Definition: upstart.c:31
enum ocf_exitcode services__upstart2ocf(int exit_status)
Definition: upstart.c:67