diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c index 09132afcaa..1cf5ca18d2 100644 --- a/lib/services/services_linux.c +++ b/lib/services/services_linux.c @@ -1,571 +1,576 @@ /* * Copyright (C) 2010 Andrew Beekhof * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif #include #include #include #include #include #include #include #include #include "crm/crm.h" #include "crm/common/mainloop.h" #include "crm/services.h" #include "services_private.h" static inline void set_fd_opts(int fd, int opts) { int flag; if ((flag = fcntl(fd, F_GETFL)) >= 0) { if (fcntl(fd, F_SETFL, flag | opts) < 0) { crm_err( "fcntl() write failed"); } } else { crm_err( "fcntl() read failed"); } } static gboolean read_output(int fd, svc_action_t *op) { char *data = NULL; int rc = 0, len = 0; gboolean is_err = FALSE; char buf[500]; static const size_t buf_read_len = sizeof(buf) - 1; crm_trace("%p", op); if (fd < 0) { return FALSE; } if (fd == op->opaque->stderr_fd) { is_err = TRUE; if (op->stderr_data) { len = strlen(op->stderr_data); data = op->stderr_data; } } else if (op->stdout_data) { len = strlen(op->stdout_data); data = op->stdout_data; } do { rc = read(fd, buf, buf_read_len); if (rc > 0) { buf[rc] = 0; crm_realloc(data, len + rc + 1); sprintf(data + len, "%s", buf); len += rc; } else if (errno != EINTR) { /* error or EOF * Cleanup happens in pipe_done() */ rc = FALSE; break; } } while (rc == buf_read_len || rc < 0); if (data != NULL && is_err) { op->stderr_data = data; } else if (data != NULL) { op->stdout_data = data; } return rc; } static int dispatch_stdout(gpointer userdata) { svc_action_t* op = (svc_action_t *) userdata; return read_output(op->opaque->stdout_fd, op); } static int dispatch_stderr(gpointer userdata) { svc_action_t* op = (svc_action_t *) userdata; return read_output(op->opaque->stderr_fd, op); } static void pipe_out_done(gpointer user_data) { svc_action_t* op = (svc_action_t *) user_data; crm_trace("%p", op); op->opaque->stdout_gsource = NULL; if (op->opaque->stdout_fd > STDOUT_FILENO) { close(op->opaque->stdout_fd); } op->opaque->stdout_fd = -1; } static void pipe_err_done(gpointer user_data) { svc_action_t* op = (svc_action_t *) user_data; op->opaque->stderr_gsource = NULL; if (op->opaque->stderr_fd > STDERR_FILENO) { close(op->opaque->stderr_fd); } op->opaque->stderr_fd = -1; } static struct mainloop_fd_callbacks stdout_callbacks = { .dispatch = dispatch_stdout, .destroy = pipe_out_done, }; static struct mainloop_fd_callbacks stderr_callbacks = { .dispatch = dispatch_stderr, .destroy = pipe_err_done, }; static void set_ocf_env(const char *key, const char *value, gpointer user_data) { if (setenv(key, value, 1) != 0) { crm_perror(LOG_ERR, "setenv failed for key:%s and value:%s", key, value); } } static void set_ocf_env_with_prefix(gpointer key, gpointer value, gpointer user_data) { char buffer[500]; snprintf(buffer, sizeof(buffer), "OCF_RESKEY_%s", (char *) key); set_ocf_env(buffer, value, user_data); } static void add_OCF_env_vars(svc_action_t *op) { if (!op->standard || strcasecmp("ocf", op->standard) != 0) { return; } if (op->params) { g_hash_table_foreach(op->params, set_ocf_env_with_prefix, NULL); } set_ocf_env("OCF_RA_VERSION_MAJOR", "1", NULL); set_ocf_env("OCF_RA_VERSION_MINOR", "0", NULL); set_ocf_env("OCF_ROOT", OCF_ROOT_DIR, NULL); if (op->rsc) { set_ocf_env("OCF_RESOURCE_INSTANCE", op->rsc, NULL); } if (op->agent != NULL) { set_ocf_env("OCF_RESOURCE_TYPE", op->agent, NULL); } /* Notes: this is not added to specification yet. Sept 10,2004 */ if (op->provider != NULL) { set_ocf_env("OCF_RESOURCE_PROVIDER", op->provider, NULL); } } -static gboolean recurring_action_timer(gpointer data) +gboolean recurring_action_timer(gpointer data) { svc_action_t *op = data; crm_debug("Scheduling another invokation of %s", op->id); /* Clean out the old result */ free(op->stdout_data); op->stdout_data = NULL; free(op->stderr_data); op->stderr_data = NULL; services_action_async(op, NULL); return FALSE; } +void +operation_finalize(svc_action_t *op) +{ + int recurring = 0; + if (op->interval) { + if (op->cancel) { + op->status = PCMK_LRM_OP_CANCELLED; + cancel_recurring_action(op); + } else { + recurring = 1; + op->opaque->repeat_timer = g_timeout_add(op->interval, + recurring_action_timer, + (void *) op); + } + } + + if (op->opaque->callback) { + op->opaque->callback(op); + } + + if (!recurring) { + /* + * If this is a recurring action, do not free explicitly. + * It will get freed whenever the action gets cancelled. + */ + services_action_free(op); + } +} + static void operation_finished(mainloop_child_t *p, int status, int signo, int exitcode) { char *next = NULL; char *offset = NULL; svc_action_t *op = mainloop_get_child_userdata(p); - int recurring = 0; pid_t pid = mainloop_get_child_pid(p); mainloop_clear_child_userdata(p); op->status = PCMK_LRM_OP_DONE; CRM_ASSERT(op->pid == pid); if (op->opaque->stderr_gsource) { /* Make sure we have read everything from the buffer. * Depending on the priority mainloop gives the fd, operation_finished * could occur before all the reads are done. Force the read now.*/ dispatch_stderr(op); } if (op->opaque->stdout_gsource) { /* Make sure we have read everything from the buffer. * Depending on the priority mainloop gives the fd, operation_finished * could occur before all the reads are done. Force the read now.*/ dispatch_stdout(op); } if (signo) { if (mainloop_get_child_timeout(p)) { crm_warn("%s:%d - timed out after %dms", op->id, op->pid, op->timeout); op->status = PCMK_LRM_OP_TIMEOUT; op->rc = PCMK_OCF_TIMEOUT; } else { crm_warn("%s:%d - terminated with signal %d", op->id, op->pid, signo); op->status = PCMK_LRM_OP_ERROR; op->rc = PCMK_OCF_SIGNAL; } } else { op->rc = exitcode; crm_debug("%s:%d - exited with rc=%d", op->id, op->pid, exitcode); if (op->stdout_data) { next = op->stdout_data; do { offset = next; next = strchrnul(offset, '\n'); crm_debug("%s:%d [ %.*s ]", op->id, op->pid, (int) (next - offset), offset); if (next[0] != 0) { next++; } } while (next != NULL && next[0] != 0); } if (op->stderr_data) { next = op->stderr_data; do { offset = next; next = strchrnul(offset, '\n'); crm_notice("%s:%d [ %.*s ]", op->id, op->pid, (int) (next - offset), offset); if (next[0] != 0) { next++; } } while (next != NULL && next[0] != 0); } } op->pid = 0; - - if (op->interval) { - if (op->cancel) { - op->status = PCMK_LRM_OP_CANCELLED; - cancel_recurring_action(op); - } else { - recurring = 1; - op->opaque->repeat_timer = g_timeout_add(op->interval, - recurring_action_timer, - (void *) op); - } - } - - if (op->opaque->callback) { - op->opaque->callback(op); - } - - if (!recurring) { - /* - * If this is a recurring action, do not free explicitly. - * It will get freed whenever the action gets cancelled. - */ - services_action_free(op); - } + operation_finalize(op); } gboolean services_os_action_execute(svc_action_t* op, gboolean synchronous) { int rc, lpc; int stdout_fd[2]; int stderr_fd[2]; if (pipe(stdout_fd) < 0) { crm_err( "pipe() failed"); } if (pipe(stderr_fd) < 0) { crm_err( "pipe() failed"); } op->pid = fork(); switch (op->pid) { case -1: crm_err( "fork() failed"); close(stdout_fd[0]); close(stdout_fd[1]); close(stderr_fd[0]); close(stderr_fd[1]); return FALSE; case 0: /* Child */ /* Man: The call setpgrp() is equivalent to setpgid(0,0) * _and_ compiles on BSD variants too * need to investigate if it works the same too. */ setpgid(0, 0); close(stdout_fd[0]); close(stderr_fd[0]); if (STDOUT_FILENO != stdout_fd[1]) { if (dup2(stdout_fd[1], STDOUT_FILENO) != STDOUT_FILENO) { crm_err( "dup2() failed (stdout)"); } close(stdout_fd[1]); } if (STDERR_FILENO != stderr_fd[1]) { if (dup2(stderr_fd[1], STDERR_FILENO) != STDERR_FILENO) { crm_err( "dup2() failed (stderr)"); } close(stderr_fd[1]); } /* close all descriptors except stdin/out/err and channels to logd */ for (lpc = getdtablesize() - 1; lpc > STDERR_FILENO; lpc--) { close(lpc); } /* Setup environment correctly */ add_OCF_env_vars(op); /* execute the RA */ execvp(op->opaque->exec, op->opaque->args); switch (errno) { /* see execve(2) */ case ENOENT: /* No such file or directory */ case EISDIR: /* Is a directory */ rc = PCMK_OCF_NOT_INSTALLED; break; case EACCES: /* permission denied (various errors) */ rc = PCMK_OCF_INSUFFICIENT_PRIV; break; default: rc = PCMK_OCF_UNKNOWN_ERROR; break; } _exit(rc); } /* Only the parent reaches here */ close(stdout_fd[1]); close(stderr_fd[1]); op->opaque->stdout_fd = stdout_fd[0]; set_fd_opts(op->opaque->stdout_fd, O_NONBLOCK); op->opaque->stderr_fd = stderr_fd[0]; set_fd_opts(op->opaque->stderr_fd, O_NONBLOCK); if (synchronous) { int status = 0; int timeout = (1 + op->timeout) / 1000; crm_trace("Waiting for %d", op->pid); while (timeout > 0 && waitpid(op->pid, &status, WNOHANG) <= 0) { sleep(1); read_output(op->opaque->stdout_fd, op); read_output(op->opaque->stderr_fd, op); timeout--; } crm_trace("Child done: %d", op->pid); if (timeout == 0) { int killrc = kill(op->pid, 9 /*SIGKILL*/); op->status = PCMK_LRM_OP_TIMEOUT; crm_warn("%s:%d - timed out after %dms", op->id, op->pid, op->timeout); if (killrc && errno != ESRCH) { crm_err("kill(%d, KILL) failed: %d", op->pid, errno); } } else if (WIFEXITED(status)) { op->status = PCMK_LRM_OP_DONE; op->rc = WEXITSTATUS(status); crm_info("Managed %s process %d exited with rc=%d", op->id, op->pid, op->rc); } else if (WIFSIGNALED(status)) { int signo = WTERMSIG(status); op->status = PCMK_LRM_OP_ERROR; crm_err("Managed %s process %d exited with signal=%d", op->id, op->pid, signo); } #ifdef WCOREDUMP if (WCOREDUMP(status)) { crm_err("Managed %s process %d dumped core", op->id, op->pid); } #endif read_output(op->opaque->stdout_fd, op); read_output(op->opaque->stderr_fd, op); } else { crm_trace("Async waiting for %d - %s", op->pid, op->opaque->exec); mainloop_add_child(op->pid, op->timeout, op->id, op, operation_finished); op->opaque->stdout_gsource = mainloop_add_fd(op->id, op->opaque->stdout_fd, op, &stdout_callbacks); op->opaque->stderr_gsource = mainloop_add_fd(op->id, op->opaque->stderr_fd, op, &stderr_callbacks); } return TRUE; } GList * services_os_get_directory_list(const char *root, gboolean files) { GList *list = NULL; struct dirent **namelist; int entries = 0, lpc = 0; char buffer[PATH_MAX]; entries = scandir(root, &namelist, NULL, alphasort); if (entries <= 0) { return list; } for (lpc = 0; lpc < entries; lpc++) { struct stat sb; if ('.' == namelist[lpc]->d_name[0]) { free(namelist[lpc]); continue; } snprintf(buffer, sizeof(buffer), "%s/%s", root, namelist[lpc]->d_name); stat(buffer, &sb); if (S_ISDIR(sb.st_mode)) { if (files) { free(namelist[lpc]); continue; } } else if (S_ISREG(sb.st_mode)) { if (files == FALSE) { free(namelist[lpc]); continue; } else if ((sb.st_mode & S_IXUSR) == 0 && (sb.st_mode & S_IXGRP) == 0 && (sb.st_mode & S_IXOTH) == 0) { free(namelist[lpc]); continue; } } list = g_list_append(list, strdup(namelist[lpc]->d_name)); free(namelist[lpc]); } free(namelist); return list; } GList * resources_os_list_lsb_agents(void) { return get_directory_list(LSB_ROOT_DIR, TRUE); } GList * resources_os_list_ocf_providers(void) { return get_directory_list(OCF_ROOT_DIR "/resource.d", FALSE); } GList * resources_os_list_ocf_agents(const char *provider) { if (provider) { char buffer[500]; snprintf(buffer, sizeof(buffer), "%s/resource.d/%s", OCF_ROOT_DIR, provider); return get_directory_list(buffer, TRUE); } return NULL; } GList * resources_os_list_systemd_services(void) { GList *list = NULL; char *ptr, *service, *end; svc_action_t *action; int len; const char *args[] = { "list-units", "--all", "--type=service", "--full", "--no-pager", NULL }; if (!(action = services_action_create_generic(SYSTEMCTL, args))) { return NULL; } action->timeout = 5000; if (!services_action_sync(action)) { services_action_free(action); return NULL; } ptr = action->stdout_data; // Skip first line with column labels while ((ptr = strchr(ptr, '\n')) != NULL) { // Skip the line break ptr++; // Read beggining of the line until ".service" if (!(end = strstr(ptr, ".service"))) break; // Length of service name len = end - ptr; // Append the name to the list of services service = calloc(1, sizeof(char) * (len + 1)); service = strncpy(service, ptr, len); list = g_list_append(list, service); } services_action_free(action); return list; } diff --git a/lib/services/services_private.h b/lib/services/services_private.h index f585d6e32e..f6e272db5e 100644 --- a/lib/services/services_private.h +++ b/lib/services/services_private.h @@ -1,57 +1,60 @@ /* * Copyright (C) 2010 - 2011, Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __MH_SERVICES_PRIVATE_H__ #define __MH_SERVICES_PRIVATE_H__ struct svc_action_private_s { char *exec; char *args[7]; guint repeat_timer; void (*callback)(svc_action_t *op); int stderr_fd; mainloop_io_t *stderr_gsource; int stdout_fd; mainloop_io_t *stdout_gsource; }; GList * services_os_get_directory_list(const char *root, gboolean files); gboolean services_os_action_execute(svc_action_t *op, gboolean synchronous); GList * resources_os_list_lsb_agents(void); GList * resources_os_list_ocf_providers(void); GList * resources_os_list_ocf_agents(const char *provider); GList * resources_os_list_systemd_services(void); gboolean cancel_recurring_action(svc_action_t *op); +gboolean recurring_action_timer(gpointer data); +void operation_finalize(svc_action_t *op); + #endif /* __MH_SERVICES_PRIVATE_H__ */ diff --git a/lib/services/systemd.c b/lib/services/systemd.c index c0cfbfa05d..cf99c1cb42 100644 --- a/lib/services/systemd.c +++ b/lib/services/systemd.c @@ -1,379 +1,424 @@ /* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Copyright (C) 2012 Andrew Beekhof */ #include #include #include +#include -#include #include +#include +#include #define BUS_NAME "org.freedesktop.systemd1" #define BUS_PATH "/org/freedesktop/systemd1" #define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties" /* /usr/share/dbus-1/interfaces/org.freedesktop.systemd1.Manager.xml */ struct unit_info { const char *id; const char *description; const char *load_state; const char *active_state; const char *sub_state; const char *following; const char *unit_path; uint32_t job_id; const char *job_type; const char *job_path; }; static GDBusProxy *systemd_proxy = NULL; static GDBusProxy * get_proxy(const char *path, const char *interface) { GError *error = NULL; GDBusProxy *proxy = NULL; g_type_init(); if(path == NULL) { path = BUS_PATH; } proxy = g_dbus_proxy_new_for_bus_sync ( G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, /* GDBusInterfaceInfo */ BUS_NAME, path, interface, NULL, /* GCancellable */ &error); if (error) { crm_err("Can't connect obtain proxy to %s interface: %s", interface, error->message); g_error_free(error); proxy = NULL; } return proxy; } static gboolean systemd_init(void) { if(systemd_proxy == NULL) { systemd_proxy = get_proxy(NULL, BUS_NAME".Manager"); } if(systemd_proxy == NULL) { return FALSE; } return TRUE; } void systemd_cleanup(void) { g_object_unref(systemd_proxy); systemd_proxy = NULL; } static char * systemd_service_name(const char *name) { if(name == NULL) { return NULL; } else if(strstr(name, ".service")) { return strdup(name); } return g_strdup_printf("%s.service", name); } static gboolean systemd_unit_by_name ( GDBusProxy *proxy, const gchar *arg_name, gchar **out_unit, GCancellable *cancellable, GError **error) { GVariant *_ret = NULL; char *name = NULL; /* " \n" \ " \n" \ " \n" \ " \n" \ */ name = systemd_service_name(arg_name); _ret = g_dbus_proxy_call_sync ( proxy, "GetUnit", g_variant_new ("(s)", name), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error); if (_ret) { g_variant_get (_ret, "(o)", out_unit); crm_info("%s = %s", arg_name, *out_unit); g_variant_unref (_ret); } free(name); return _ret != NULL; } static char * systemd_unit_property(const char *obj, const gchar *iface, const char *name) { GError *error = NULL; GDBusProxy *proxy; GVariant *asv = NULL; GVariant *value = NULL; GVariant *_ret = NULL; char *output = NULL; crm_info("Calling GetAll on %s", obj); proxy = get_proxy(obj, BUS_PROPERTY_IFACE); _ret = g_dbus_proxy_call_sync ( proxy, "GetAll", g_variant_new ("(s)", iface), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { crm_err("Cannot get properties for %s: %s", g_dbus_proxy_get_object_path(proxy), error->message); g_error_free(error); g_object_unref(proxy); return NULL; } crm_info("Call to GetAll passed: type '%s' %d\n", g_variant_get_type_string (_ret), g_variant_n_children (_ret)); asv = g_variant_get_child_value(_ret, 0); crm_trace("asv type '%s' %d\n", g_variant_get_type_string (asv), g_variant_n_children (asv)); value = g_variant_lookup_value(asv, name, NULL); if(value && g_variant_is_of_type(value, G_VARIANT_TYPE_STRING)) { crm_info("Got value '%s' for %s[%s]", g_variant_get_string(value, NULL), obj, name); output = g_variant_dup_string(value, NULL); } else { crm_info("No value for %s[%s]", obj, name); } g_object_unref(proxy); return output; } GList * systemd_unit_listall(void) { int lpc = 0; GList *units = NULL; GError *error = NULL; GVariant *out_units = NULL; GVariantIter iter; struct unit_info u; GVariant *_ret = NULL; CRM_ASSERT(systemd_init()); /* " \n" \ " \n" \ " \n" \ */ _ret = g_dbus_proxy_call_sync ( systemd_proxy, "ListUnits", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error || _ret == NULL) { crm_err("Call to ListUnits failed: %s", error->message); g_error_free(error); return NULL; } g_variant_get (_ret, "(@a(ssssssouso))", &out_units); g_variant_iter_init (&iter, out_units); while (g_variant_iter_loop (&iter, "(ssssssouso)", &u.id, &u.description, &u.load_state, &u.active_state, &u.sub_state, &u.following, &u.unit_path, &u.job_id, &u.job_type, &u.job_path)) { char *match = strstr(u.id, ".service"); if(match) { lpc++; match[0] = 0; crm_trace("Got %s = %s", u.id, u.description); units = g_list_append(units, strdup(u.id)); } } crm_info("Call to ListUnits passed: type '%s' count %d", g_variant_get_type_string (out_units), lpc); g_variant_unref (_ret); return units; } gboolean systemd_unit_exists(const char *name) { char *path = NULL; GError *error = NULL; gboolean pass = FALSE; CRM_ASSERT(systemd_init()); pass = systemd_unit_by_name(systemd_proxy, name, &path, NULL, &error); if (error || pass == FALSE) { crm_err("Call to ListUnits failed: %s", error->message); g_error_free(error); pass = FALSE; } else { crm_trace("Got %s", path); } /* free(path) */ return pass; } static char * systemd_unit_metadata(const char *name) { char *path = NULL; char *meta = NULL; GError *error = NULL; CRM_ASSERT(systemd_init()); if(systemd_unit_by_name(systemd_proxy, name, &path, NULL, &error)) { char *desc = systemd_unit_property(path, BUS_NAME".Unit", "Description"); meta = g_strdup_printf( "\n" "\n" "\n" " 1.0\n" " \n" " %s\n" " \n" " systemd unit file for %s\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n", name, desc, name); free(desc); } return meta; } +static void +systemd_unit_exec_done(GObject *source_object, GAsyncResult *res, gpointer user_data) +{ + GError *error = NULL; + GVariant *_ret = NULL; + svc_action_t* op = user_data; + GDBusProxy *proxy = G_DBUS_PROXY (source_object); + + /* Obtain rc and stderr/out */ + _ret = g_dbus_proxy_call_finish (proxy, res, &error); + + if (error) { + /* ignore "already started" or "not running" errors */ + if (safe_str_eq(op->action, "stop") + && strstr(error->message, "systemd1.InvalidName")) { + crm_trace("Masking Stop failure for %s: unknown services are stopped", op->rsc); + op->rc = PCMK_EXECRA_OK; + + } else { + crm_err("Could not issue %s for %s: %s (%s)", op->action, op->rsc, error->message, ""); + } + g_error_free(error); + + } else { + char *path = NULL; + g_variant_get(_ret, "(o)", &path); + crm_info("Call to %s passed: type '%s' %s", op->action, g_variant_get_type_string (_ret), path); + op->rc = PCMK_EXECRA_OK; + } + + operation_finalize(op); +} + + gboolean systemd_unit_exec(svc_action_t* op, gboolean synchronous) { char *unit = NULL; GError *error = NULL; gboolean pass = FALSE; GVariant *_ret = NULL; const char *action = op->action; char *name = systemd_service_name(op->rsc); op->rc = PCMK_EXECRA_UNKNOWN_ERROR; CRM_ASSERT(systemd_init()); pass = systemd_unit_by_name (systemd_proxy, op->rsc, &unit, NULL, &error); if (error || pass == FALSE) { - crm_err("Call to ListUnits failed: %s", error->message); + crm_debug("Could not obtain unit named '%s': %s", op->rsc, error->message); + if(strstr(error->message, "systemd1.NoSuchUnit")) { + op->rc = PCMK_EXECRA_NOT_INSTALLED; + } g_error_free(error); - op->rc = PCMK_EXECRA_NOT_INSTALLED; return FALSE; } if (safe_str_eq(op->action, "meta-data")) { op->stdout_data = systemd_unit_metadata(op->rsc); op->rc = PCMK_EXECRA_OK; goto cleanup; } if (safe_str_eq(op->action, "monitor") || safe_str_eq(action, "status")) { char *state = systemd_unit_property(unit, BUS_NAME".Unit", "ActiveState"); - gboolean running = !g_strcmp0(state, "active"); - crm_info("%s %s", state, running ? "running" : "stopped"); - - if (running) { + if ( !g_strcmp0(state, "active")) { op->rc = PCMK_EXECRA_OK; - goto cleanup; + } else { + op->rc = PCMK_EXECRA_NOT_RUNNING; + } + + if(synchronous == FALSE) { + operation_finalize(op); } - op->rc = PCMK_EXECRA_NOT_RUNNING; + free(state); goto cleanup; } else if (!g_strcmp0(action, "start")) { action = "StartUnit"; } else if (!g_strcmp0(action, "stop")) { action = "StopUnit"; } else if (!g_strcmp0(action, "restart")) { action = "RestartUnit"; } else { return PCMK_EXECRA_UNIMPLEMENT_FEATURE; } - crm_info("Calling %s for %s: %s", action, op->rsc, unit); + crm_debug("Calling %s for %s: %s", action, op->rsc, unit); + if(synchronous == FALSE) { + g_dbus_proxy_call( + systemd_proxy, action, g_variant_new ("(ss)", name, "replace"), + G_DBUS_CALL_FLAGS_NONE, op->timeout, NULL, systemd_unit_exec_done, op); + free(unit); + free(name); + return TRUE; + } + _ret = g_dbus_proxy_call_sync ( systemd_proxy, action, g_variant_new ("(ss)", name, "replace"), - G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); + G_DBUS_CALL_FLAGS_NONE, op->timeout, NULL, &error); if (error) { /* ignore "already started" or "not running" errors */ - if (safe_str_eq(action, "Start") - && strstr(error->message, "Error.AlreadyStarted")) { - crm_trace("Masking Start failure for %s: already started", op->rsc); - op->rc = PCMK_EXECRA_OK; - } else if (safe_str_eq(action, "Stop") - && strstr(error->message, "systemd1.InvalidName")) { + if (safe_str_eq(op->action, "stop") + && strstr(error->message, "systemd1.InvalidName")) { crm_trace("Masking Stop failure for %s: unknown services are stopped", op->rsc); op->rc = PCMK_EXECRA_OK; } else { crm_err("Could not issue %s for %s: %s (%s)", action, op->rsc, error->message, unit); } g_error_free(error); } else { char *path = NULL; g_variant_get(_ret, "(o)", &path); crm_info("Call to %s passed: type '%s' %s", action, g_variant_get_type_string (_ret), path); op->rc = PCMK_EXECRA_OK; } cleanup: free(unit); free(name); return op->rc == PCMK_EXECRA_OK; }