diff --git a/lib/services/systemd.c b/lib/services/systemd.c index 6bbf0bd539..11e5b1aaf2 100644 --- a/lib/services/systemd.c +++ b/lib/services/systemd.c @@ -1,503 +1,505 @@ /* * 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 #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) { static int need_init = 1; if(need_init) { need_init = 0; systemd_proxy = get_proxy(NULL, BUS_NAME".Manager"); } if(systemd_proxy == NULL) { return FALSE; } return TRUE; } void systemd_cleanup(void) { if (systemd_proxy) { 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 void systemd_daemon_reload (GDBusProxy *proxy, GError **error) { GVariant *_ret = g_dbus_proxy_call_sync ( proxy, "Reload", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); if (_ret) { g_variant_unref (_ret); } } static gboolean systemd_unit_by_name ( GDBusProxy *proxy, const gchar *arg_name, gchar **out_unit, GCancellable *cancellable, GError **error) { GError *reload_error = NULL; GVariant *_ret = NULL; char *name = NULL; int retry = 0; /* " \n" \ " \n" \ " \n" \ " \n" \ */ name = systemd_service_name(arg_name); crm_debug("Calling GetUnit"); _ret = g_dbus_proxy_call_sync ( proxy, "GetUnit", g_variant_new ("(s)", name), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error); if (_ret) { crm_debug("Checking output"); g_variant_get (_ret, "(o)", out_unit); crm_debug("%s = %s", arg_name, *out_unit); g_variant_unref (_ret); goto done; } crm_debug("Reloading the systemd manager configuration"); systemd_daemon_reload (proxy, &reload_error); retry++; if (reload_error) { crm_err("Cannot reload the systemd manager configuration: %s", reload_error->message); g_error_free(reload_error); goto done; } if(*error) { crm_debug("Cannot find %s: %s", name, (*error)->message); g_error_free(*error); *error = NULL; } /* */ crm_debug("Calling LoadUnit"); _ret = g_dbus_proxy_call_sync ( proxy, "LoadUnit", g_variant_new ("(s)", name), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error); if (_ret) { crm_debug("Checking output"); g_variant_get (_ret, "(o)", out_unit); crm_debug("%s = %s", arg_name, *out_unit); g_variant_unref (_ret); } done: 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); if (!proxy) { return NULL; } _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_debug("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); g_variant_unref(_ret); 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; if(systemd_init() == FALSE) { return NULL; } /* " \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_info("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] = %s", u.id, u.active_state, 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; if(systemd_init() == FALSE) { return FALSE; } 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; char *desc = NULL; GError *error = NULL; CRM_ASSERT(systemd_init()); if(systemd_unit_by_name(systemd_proxy, name, &path, NULL, &error)) { desc = systemd_unit_property(path, BUS_NAME".Unit", "Description"); } else { desc = g_strdup_printf("systemd unit file for %s", name); } 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); if (_ret) { g_variant_unref(_ret); } } 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->agent); op->rc = PCMK_EXECRA_UNKNOWN_ERROR; CRM_ASSERT(systemd_init()); - crm_debug("Performing %s op on systemd unit %s named '%s'", op->action, op->agent, op->rsc); + crm_debug("Performing %ssynchronous %s op on systemd unit %s named '%s'", synchronous?"":"a", op->action, op->agent, op->rsc); + if (safe_str_eq(op->action, "meta-data")) { op->stdout_data = systemd_unit_metadata(op->agent); op->rc = PCMK_EXECRA_OK; goto cleanup; } pass = systemd_unit_by_name (systemd_proxy, op->agent, &unit, NULL, &error); if (error || pass == FALSE) { crm_debug("Could not obtain unit named '%s': %s", op->agent, error->message); if(strstr(error->message, "systemd1.NoSuchUnit")) { op->rc = PCMK_EXECRA_NOT_INSTALLED; } g_error_free(error); - free(name); - return FALSE; + goto cleanup; } if (safe_str_eq(op->action, "monitor") || safe_str_eq(action, "status")) { char *state = systemd_unit_property(unit, BUS_NAME".Unit", "ActiveState"); if ( !g_strcmp0(state, "active")) { op->rc = PCMK_EXECRA_OK; } else { op->rc = PCMK_EXECRA_NOT_RUNNING; } - if(synchronous == FALSE) { - operation_finalize(op); - } 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; + op->rc = PCMK_EXECRA_UNIMPLEMENT_FEATURE; + goto cleanup; } 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, op->timeout, NULL, &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)", 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); if (_ret) { g_variant_unref(_ret); } + if(synchronous == FALSE) { + operation_finalize(op); + return TRUE; + } return op->rc == PCMK_EXECRA_OK; } diff --git a/lib/services/upstart.c b/lib/services/upstart.c index d7aea07bfe..6d423d0dc8 100644 --- a/lib/services/upstart.c +++ b/lib/services/upstart.c @@ -1,508 +1,511 @@ /* * 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 * * File: upstart-dbus.c * Copyright (C) 2010 Senko Rasic * Copyright (c) 2010 Ante Karamatic * * * Each exported function is standalone, and creates a new connection to * the upstart daemon. This is because lrmd plugins fork off for exec, * and if we try and share the connection, the whole thing blocks * indefinitely. */ #include #include #include #include #include #include #include #include #include #define BUS_NAME "com.ubuntu.Upstart" #define BUS_PATH "/com/ubuntu/Upstart" #define BUS_MANAGER_IFACE BUS_NAME"0_6" #define BUS_PROPERTY_IFACE "org.freedesktop.DBus.Properties" /* http://upstart.ubuntu.com/wiki/DBusInterface */ static GDBusProxy *upstart_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 upstart_init(void) { static int need_init = 1; if(need_init) { need_init = 0; upstart_proxy = get_proxy(NULL, BUS_MANAGER_IFACE); } if(upstart_proxy == NULL) { return FALSE; } return TRUE; } void upstart_cleanup(void) { if (upstart_proxy) { g_object_unref(upstart_proxy); upstart_proxy = NULL; } } static gboolean upstart_job_by_name ( GDBusProxy *proxy, const gchar *arg_name, gchar **out_unit, GCancellable *cancellable, GError **error) { /* com.ubuntu.Upstart0_6.GetJobByName (in String name, out ObjectPath job) */ GVariant *_ret = g_dbus_proxy_call_sync ( proxy, "GetJobByName", g_variant_new ("(s)", arg_name), G_DBUS_CALL_FLAGS_NONE, -1, cancellable, error); if (_ret) { g_variant_get (_ret, "(o)", out_unit); g_variant_unref (_ret); } return _ret != NULL; } static void fix(char *input, const char *search, char replace) { char *match = NULL; int shuffle = strlen(search) - 1; while(TRUE) { int len, lpc; match = strstr(input, search); if(match == NULL) { break; } crm_err("Found: %s", match); match[0] = replace; len = strlen(match) - shuffle; for(lpc = 1; lpc <= len; lpc++) { match[lpc] = match[lpc+shuffle]; } } } static char * fix_upstart_name(const char *input) { char *output = strdup(input); fix(output, "_2b", '+'); fix(output, "_2c", ','); fix(output, "_2d", '-'); fix(output, "_2e", '.'); fix(output, "_40", '@'); fix(output, "_5f", '_'); return output; } GList * upstart_job_listall(void) { GList *units = NULL; GError *error = NULL; GVariantIter *iter; char *path = NULL; GVariant *_ret = NULL; int lpc = 0; if(upstart_init() == FALSE) { return NULL; } /* com.ubuntu.Upstart0_6.GetAllJobs (out jobs) */ _ret = g_dbus_proxy_call_sync ( upstart_proxy, "GetAllJobs", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { crm_info("Call to GetAllJobs failed: %s", error->message); g_error_free(error); return NULL; } g_variant_get (_ret, "(ao)", &iter); while (g_variant_iter_loop (iter, "o", &path)) { int llpc = 0; const char *job = path; while(path[llpc] != 0) { if(path[llpc] == '/') { job = path+llpc+1; } llpc++; } lpc++; crm_trace("%s\n", path); units = g_list_append(units, fix_upstart_name(job)); } crm_info("Call to GetAllJobs passed: type '%s', count %d", g_variant_get_type_string (_ret), lpc); g_variant_iter_free(iter); g_variant_unref(_ret); return units; } gboolean upstart_job_exists(const char *name) { char *path = NULL; GError *error = NULL; gboolean pass = FALSE; if(upstart_init() == FALSE) { return FALSE; } pass = upstart_job_by_name (upstart_proxy, name, &path, NULL, &error); if (error || pass == FALSE) { crm_trace("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 * upstart_job_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); if (!proxy) { return NULL; } _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); g_variant_unref(_ret); return output; } static char * get_first_instance(const gchar *job) { char *instance = NULL; GError *error = NULL; GDBusProxy *proxy = get_proxy(job, BUS_MANAGER_IFACE".Job"); GVariant *_ret = g_dbus_proxy_call_sync ( proxy, "GetAllInstances", g_variant_new ("()"), G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { crm_err("Cannot call GetAllInstances for %s: %s", job, error->message); g_error_free(error); return NULL; } crm_trace("Call to GetAllInstances passed: type '%s' %d\n", g_variant_get_type_string (_ret), g_variant_n_children (_ret)); if(g_variant_n_children (_ret)) { GVariant *tmp1 = g_variant_get_child_value(_ret, 0); if(g_variant_n_children (tmp1)) { GVariant *tmp2 = g_variant_get_child_value(tmp1, 0); instance = g_variant_dup_string(tmp2, NULL); } } crm_info("Result: %s", instance); g_variant_unref(_ret); return instance; } gboolean upstart_job_running(const gchar *name) { char *job = NULL; GError *error = NULL; gboolean pass = FALSE; pass = upstart_job_by_name (upstart_proxy, name, &job, NULL, &error); if (error || pass == FALSE) { crm_err("Call to ListUnits failed: %s", error->message); g_error_free(error); } else { char *instance = get_first_instance(job); pass = FALSE; if(instance) { if (instance) { char *state = upstart_job_property(instance, BUS_MANAGER_IFACE".Instance", "state"); crm_info("State of %s: %s", name, state); if(state) { pass = !g_strcmp0(state, "running"); } free(state); } } free(instance); } crm_info("%s is%s running", name, pass?"":" not"); return pass; } static char * upstart_job_metadata(const char *name) { return g_strdup_printf( "\n" "\n" "\n" " 1.0\n" " \n" " Upstart agent for controlling the system %s service\n" " \n" " %s upstart agent\n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" " \n" "\n", name, name, name); } static void upstart_job_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, "start") && strstr(error->message, BUS_MANAGER_IFACE".Error.AlreadyStarted")) { crm_trace("Masking Start failure for %s: already started", op->rsc); op->rc = PCMK_EXECRA_OK; } else if (safe_str_eq(op->action, "stop") && strstr(error->message, BUS_MANAGER_IFACE".Error.UnknownInstance")) { 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", 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); g_object_unref(proxy); if (_ret) { g_variant_unref(_ret); } } gboolean upstart_job_exec(svc_action_t* op, gboolean synchronous) { char *job = NULL; GError *error = NULL; gboolean pass = FALSE; gchar *no_args[] = { NULL }; const char *action = op->action; GVariant *_ret = NULL; GDBusProxy *job_proxy = NULL; op->rc = PCMK_EXECRA_UNKNOWN_ERROR; CRM_ASSERT(upstart_init()); if (safe_str_eq(op->action, "meta-data")) { op->stdout_data = upstart_job_metadata(op->agent); op->rc = PCMK_EXECRA_OK; goto cleanup; } pass = upstart_job_by_name (upstart_proxy, op->agent, &job, NULL, &error); if (error || pass == FALSE) { crm_debug("Could not obtain job named '%s': %s", op->agent, error->message); if (!g_strcmp0(action, "stop")) { op->rc = PCMK_EXECRA_OK; } else { op->rc = PCMK_EXECRA_NOT_INSTALLED; } goto cleanup; } if (safe_str_eq(op->action, "monitor") || safe_str_eq(action, "status")) { if (upstart_job_running (op->agent)) { op->rc = PCMK_EXECRA_OK; } else { op->rc = PCMK_EXECRA_NOT_RUNNING; } - - if(synchronous == FALSE) { - operation_finalize(op); - } goto cleanup; } else if (!g_strcmp0(action, "start")) { action = "Start"; } else if (!g_strcmp0(action, "stop")) { action = "Stop"; } else if (!g_strcmp0(action, "restart")) { action = "Restart"; } else { - return PCMK_EXECRA_UNIMPLEMENT_FEATURE; + op->rc = PCMK_EXECRA_UNIMPLEMENT_FEATURE; + goto cleanup; } job_proxy = get_proxy(job, BUS_MANAGER_IFACE".Job"); crm_debug("Calling %s for %s: %s", action, op->rsc, job); if(synchronous == FALSE) { g_dbus_proxy_call( job_proxy, action, g_variant_new ("(^asb)", no_args, TRUE), G_DBUS_CALL_FLAGS_NONE, op->timeout, NULL, upstart_job_exec_done, op); free(job); return TRUE; } _ret = g_dbus_proxy_call_sync ( job_proxy, action, g_variant_new ("(^asb)", no_args, TRUE), 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, BUS_MANAGER_IFACE".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, BUS_MANAGER_IFACE".Error.UnknownInstance")) { 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, job); } - 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(job); + if(error) { + g_error_free(error); + } if(job_proxy) { g_object_unref(job_proxy); } if (_ret) { g_variant_unref(_ret); } + if(synchronous == FALSE) { + operation_finalize(op); + return TRUE; + } return op->rc == PCMK_EXECRA_OK; } diff --git a/lrmd/lrmd.c b/lrmd/lrmd.c index 3bb2d35d16..324493fd60 100644 --- a/lrmd/lrmd.c +++ b/lrmd/lrmd.c @@ -1,985 +1,991 @@ /* * Copyright (c) 2012 David Vossel * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser 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 library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_TIMEB_H # include #endif GHashTable *rsc_list = NULL; GHashTable *client_list = NULL; typedef struct lrmd_cmd_s { int timeout; int interval; int start_delay; int call_id; int exec_rc; int lrmd_op_status; /* Timer ids, must be removed on cmd destruction. */ int delay_id; int stonith_recurring_id; int rsc_deleted; char *only_notify_client; char *origin; char *rsc_id; char *action; char *output; char *userdata_str; #ifdef HAVE_SYS_TIMEB_H /* Timestamp of when op ran */ struct timeb t_run; /* Timestamp of when op was queued */ struct timeb t_queue; /* Timestamp of last rc change */ struct timeb t_rcchange; #endif GHashTable *params; } lrmd_cmd_t; static void cmd_finalize(lrmd_cmd_t * cmd, lrmd_rsc_t * rsc); static gboolean lrmd_rsc_dispatch(gpointer user_data); static lrmd_rsc_t * build_rsc_from_xml(xmlNode * msg) { xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, msg, LOG_ERR); lrmd_rsc_t *rsc = NULL; rsc = calloc(1, sizeof(lrmd_rsc_t)); rsc->rsc_id = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ID); rsc->class = crm_element_value_copy(rsc_xml, F_LRMD_CLASS); rsc->provider = crm_element_value_copy(rsc_xml, F_LRMD_PROVIDER); rsc->type = crm_element_value_copy(rsc_xml, F_LRMD_TYPE); rsc->work = mainloop_add_trigger(G_PRIORITY_HIGH, lrmd_rsc_dispatch, rsc); return rsc; } static lrmd_cmd_t * create_lrmd_cmd(xmlNode * msg, lrmd_client_t * client) { int call_options = 0; xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, msg, LOG_ERR); lrmd_cmd_t *cmd = NULL; cmd = calloc(1, sizeof(lrmd_cmd_t)); crm_element_value_int(msg, F_LRMD_CALLOPTS, &call_options); if (call_options & lrmd_opt_notify_orig_only) { cmd->only_notify_client = strdup(client->id); } crm_element_value_int(msg, F_LRMD_CALLID, &cmd->call_id); crm_element_value_int(rsc_xml, F_LRMD_RSC_INTERVAL, &cmd->interval); crm_element_value_int(rsc_xml, F_LRMD_TIMEOUT, &cmd->timeout); crm_element_value_int(rsc_xml, F_LRMD_RSC_START_DELAY, &cmd->start_delay); cmd->origin = crm_element_value_copy(rsc_xml, F_LRMD_ORIGIN); cmd->action = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ACTION); cmd->userdata_str = crm_element_value_copy(rsc_xml, F_LRMD_RSC_USERDATA_STR); cmd->rsc_id = crm_element_value_copy(rsc_xml, F_LRMD_RSC_ID); cmd->params = xml2list(rsc_xml); return cmd; } static void free_lrmd_cmd(lrmd_cmd_t * cmd) { if (cmd->stonith_recurring_id) { g_source_remove(cmd->stonith_recurring_id); } if (cmd->delay_id) { g_source_remove(cmd->delay_id); } if (cmd->params) { g_hash_table_destroy(cmd->params); } free(cmd->origin); free(cmd->action); free(cmd->userdata_str); free(cmd->rsc_id); free(cmd->output); free(cmd->only_notify_client); free(cmd); } static gboolean stonith_recurring_op_helper(gpointer data) { lrmd_cmd_t *cmd = data; lrmd_rsc_t *rsc; cmd->stonith_recurring_id = 0; if (!cmd->rsc_id) { return FALSE; } rsc = g_hash_table_lookup(rsc_list, cmd->rsc_id); CRM_ASSERT(rsc != NULL); /* take it out of recurring_ops list, and put it in the pending ops * to be executed */ rsc->recurring_ops = g_list_remove(rsc->recurring_ops, cmd); rsc->pending_ops = g_list_append(rsc->pending_ops, cmd); #ifdef HAVE_SYS_TIMEB_H ftime(&cmd->t_queue); #endif mainloop_set_trigger(rsc->work); return FALSE; } static gboolean start_delay_helper(gpointer data) { lrmd_cmd_t *cmd = data; lrmd_rsc_t *rsc = NULL; cmd->delay_id = 0; rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL; if (rsc) { mainloop_set_trigger(rsc->work); } return FALSE; } static void schedule_lrmd_cmd(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd) { CRM_CHECK(cmd != NULL, return); CRM_CHECK(rsc != NULL, return); crm_trace("Scheduling %s on %s", cmd->action, rsc->rsc_id); rsc->pending_ops = g_list_append(rsc->pending_ops, cmd); #ifdef HAVE_SYS_TIMEB_H ftime(&cmd->t_queue); #endif mainloop_set_trigger(rsc->work); if (cmd->start_delay) { cmd->delay_id = g_timeout_add(cmd->start_delay, start_delay_helper, cmd); } } static void send_reply(lrmd_client_t * client, int rc, int call_id) { int send_rc = 0; xmlNode *reply = NULL; reply = create_xml_node(NULL, T_LRMD_REPLY); crm_xml_add(reply, F_LRMD_ORIGIN, __FUNCTION__); crm_xml_add_int(reply, F_LRMD_RC, rc); crm_xml_add_int(reply, F_LRMD_CALLID, call_id); send_rc = crm_ipcs_send(client->channel, reply, FALSE); free_xml(reply); if (send_rc < 0) { crm_warn("LRMD reply to %s failed: %d", client->name, send_rc); } } static void send_client_notify(gpointer key, gpointer value, gpointer user_data) { xmlNode *update_msg = user_data; lrmd_client_t *client = value; if (client == NULL) { crm_err("Asked to send event to NULL client"); return; } else if (client->channel == NULL) { crm_trace("Asked to send event to disconnected client"); return; } else if (client->name == NULL) { crm_trace("Asked to send event to client with no name"); return; } if (crm_ipcs_send(client->channel, update_msg, TRUE) <= 0) { crm_warn("Notification of client %s/%s failed", client->name, client->id); } } #ifdef HAVE_SYS_TIMEB_H static int time_diff_ms(struct timeb *now, struct timeb *old) { int sec = difftime(now->time, old->time); int ms = now->millitm - old->millitm; if (old->time == 0) { return 0; } return (sec * 1000) + ms; } #endif static void send_cmd_complete_notify(lrmd_cmd_t * cmd) { #ifdef HAVE_SYS_TIMEB_H struct timeb now = { 0, }; #endif xmlNode *notify = NULL; notify = create_xml_node(NULL, T_LRMD_NOTIFY); crm_xml_add(notify, F_LRMD_ORIGIN, __FUNCTION__); crm_xml_add_int(notify, F_LRMD_TIMEOUT, cmd->timeout); crm_xml_add_int(notify, F_LRMD_RSC_INTERVAL, cmd->interval); crm_xml_add_int(notify, F_LRMD_RSC_START_DELAY, cmd->start_delay); crm_xml_add_int(notify, F_LRMD_EXEC_RC, cmd->exec_rc); crm_xml_add_int(notify, F_LRMD_OP_STATUS, cmd->lrmd_op_status); crm_xml_add_int(notify, F_LRMD_CALLID, cmd->call_id); crm_xml_add_int(notify, F_LRMD_RSC_DELETED, cmd->rsc_deleted); #ifdef HAVE_SYS_TIMEB_H ftime(&now); crm_xml_add_int(notify, F_LRMD_RSC_RUN_TIME, cmd->t_run.time); crm_xml_add_int(notify, F_LRMD_RSC_RCCHANGE_TIME, cmd->t_rcchange.time); crm_xml_add_int(notify, F_LRMD_RSC_EXEC_TIME, time_diff_ms(&now, &cmd->t_run)); crm_xml_add_int(notify, F_LRMD_RSC_QUEUE_TIME, time_diff_ms(&cmd->t_run, &cmd->t_queue)); #endif crm_xml_add(notify, F_LRMD_OPERATION, LRMD_OP_RSC_EXEC); crm_xml_add(notify, F_LRMD_RSC_ID, cmd->rsc_id); crm_xml_add(notify, F_LRMD_RSC_ACTION, cmd->action); crm_xml_add(notify, F_LRMD_RSC_USERDATA_STR, cmd->userdata_str); crm_xml_add(notify, F_LRMD_RSC_OUTPUT, cmd->output); if (cmd->params) { char *key = NULL; char *value = NULL; GHashTableIter iter; xmlNode *args = create_xml_node(notify, XML_TAG_ATTRS); g_hash_table_iter_init(&iter, cmd->params); while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) { hash2field((gpointer) key, (gpointer) value, args); } } if (cmd->only_notify_client) { lrmd_client_t *client = g_hash_table_lookup(client_list, cmd->only_notify_client); if (client) { send_client_notify(client->id, client, notify); } } else { g_hash_table_foreach(client_list, send_client_notify, notify); } free_xml(notify); } static void send_generic_notify(int rc, xmlNode * request) { int call_id = 0; xmlNode *notify = NULL; xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR); const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); const char *op = crm_element_value(request, F_LRMD_OPERATION); crm_element_value_int(request, F_LRMD_CALLID, &call_id); notify = create_xml_node(NULL, T_LRMD_NOTIFY); crm_xml_add(notify, F_LRMD_ORIGIN, __FUNCTION__); crm_xml_add_int(notify, F_LRMD_RC, rc); crm_xml_add_int(notify, F_LRMD_CALLID, call_id); crm_xml_add(notify, F_LRMD_OPERATION, op); crm_xml_add(notify, F_LRMD_RSC_ID, rsc_id); g_hash_table_foreach(client_list, send_client_notify, notify); free_xml(notify); } static void cmd_finalize(lrmd_cmd_t * cmd, lrmd_rsc_t * rsc) { - crm_trace("Resource operation rsc:%s action:%s completed", cmd->rsc_id, cmd->action); + crm_trace("Resource operation rsc:%s action:%s completed (%p %p)", cmd->rsc_id, cmd->action, rsc?rsc->active:NULL, cmd); if (rsc && (rsc->active == cmd)) { rsc->active = NULL; mainloop_set_trigger(rsc->work); } if (!rsc) { cmd->rsc_deleted = 1; } send_cmd_complete_notify(cmd); if (cmd->interval && (cmd->lrmd_op_status == PCMK_LRM_OP_CANCELLED)) { if (rsc) { rsc->recurring_ops = g_list_remove(rsc->recurring_ops, cmd); rsc->pending_ops = g_list_remove(rsc->pending_ops, cmd); } free_lrmd_cmd(cmd); } else if (cmd->interval == 0) { if (rsc) { rsc->pending_ops = g_list_remove(rsc->pending_ops, cmd); } free_lrmd_cmd(cmd); } else { /* Clear all the values pertaining just to the last iteration of a recurring op. */ cmd->lrmd_op_status = 0; memset(&cmd->t_run, 0, sizeof(cmd->t_run)); memset(&cmd->t_queue, 0, sizeof(cmd->t_queue)); free(cmd->output); cmd->output = NULL; } } static int lsb2uniform_rc(const char *action, int rc) { if (rc < 0) { return PCMK_EXECRA_UNKNOWN_ERROR; } /* status has different return codes that everything else. */ if (!safe_str_eq(action, "status") && !safe_str_eq(action, "monitor")) { if (rc > PCMK_LSB_NOT_RUNNING) { return PCMK_EXECRA_UNKNOWN_ERROR; } return rc; } switch (rc) { case PCMK_LSB_STATUS_OK: return PCMK_EXECRA_OK; case PCMK_LSB_STATUS_NOT_INSTALLED: return PCMK_EXECRA_NOT_INSTALLED; case PCMK_LSB_STATUS_VAR_PID: case PCMK_LSB_STATUS_VAR_LOCK: case PCMK_LSB_STATUS_NOT_RUNNING: return PCMK_EXECRA_NOT_RUNNING; default: return PCMK_EXECRA_UNKNOWN_ERROR; } return PCMK_EXECRA_UNKNOWN_ERROR; } static int ocf2uniform_rc(int rc) { if (rc < 0 || rc > PCMK_OCF_FAILED_MASTER) { return PCMK_EXECRA_UNKNOWN_ERROR; } return rc; } static int stonith2uniform_rc(const char *action, int rc) { if (rc == -ENODEV) { if (safe_str_eq(action, "stop")) { rc = PCMK_EXECRA_OK; } else if (safe_str_eq(action, "start")) { rc = PCMK_EXECRA_NOT_INSTALLED; } else { rc = PCMK_EXECRA_NOT_RUNNING; } } else if (rc != 0) { rc = PCMK_EXECRA_UNKNOWN_ERROR; } return rc; } static int get_uniform_rc(const char *standard, const char *action, int rc) { if (safe_str_eq(standard, "ocf")) { return ocf2uniform_rc(rc); } else if (safe_str_eq(standard, "stonith")) { return stonith2uniform_rc(action, rc); } else if (safe_str_eq(standard, "systemd")) { - return lsb2uniform_rc(action, rc); + return rc; } else if (safe_str_eq(standard, "upstart")) { return rc; } else { return lsb2uniform_rc(action, rc); } } static void action_complete(svc_action_t * action) { lrmd_rsc_t *rsc; lrmd_cmd_t *cmd = action->cb_data; if (!cmd) { crm_err("LRMD action (%s) completed does not match any known operations.", action->id); return; } #ifdef HAVE_SYS_TIMEB_H if (cmd->exec_rc != action->rc) { ftime(&cmd->t_rcchange); } #endif cmd->exec_rc = get_uniform_rc(action->standard, cmd->action, action->rc); cmd->lrmd_op_status = action->status; rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL; if (action->stdout_data) { cmd->output = strdup(action->stdout_data); } cmd_finalize(cmd, rsc); } static int lrmd_rsc_execute_stonith(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd) { int rc = 0; stonith_t *stonith_api = get_stonith_connection(); if (!stonith_api) { cmd->exec_rc = get_uniform_rc("stonith", cmd->action, -ENOTCONN); cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; cmd_finalize(cmd, rsc); return -EUNATCH; } if (safe_str_eq(cmd->action, "start")) { char *key = NULL; char *value = NULL; stonith_key_value_t *device_params = NULL; if (cmd->params) { GHashTableIter iter; g_hash_table_iter_init(&iter, cmd->params); while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & value)) { device_params = stonith_key_value_add(device_params, key, value); } } rc = stonith_api->cmds->register_device(stonith_api, st_opt_sync_call, cmd->rsc_id, rsc->provider, rsc->type, device_params); stonith_key_value_freeall(device_params, 1, 1); if (rc == 0) { rc = stonith_api->cmds->call(stonith_api, st_opt_sync_call, cmd->rsc_id, "monitor", NULL, cmd->timeout); } } else if (safe_str_eq(cmd->action, "stop")) { rc = stonith_api->cmds->remove_device(stonith_api, st_opt_sync_call, cmd->rsc_id); } else if (safe_str_eq(cmd->action, "monitor")) { rc = stonith_api->cmds->call(stonith_api, st_opt_sync_call, cmd->rsc_id, cmd->action, NULL, cmd->timeout); } cmd->exec_rc = get_uniform_rc("stonith", cmd->action, rc); /* Attempt to map return codes to op status if possible */ if (rc) { switch (rc) { case -EPROTONOSUPPORT: cmd->lrmd_op_status = PCMK_LRM_OP_NOTSUPPORTED; break; case -ETIME: cmd->lrmd_op_status = PCMK_LRM_OP_TIMEOUT; break; default: cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; } } else { cmd->lrmd_op_status = PCMK_LRM_OP_DONE; } if (cmd->interval > 0) { rsc->recurring_ops = g_list_append(rsc->recurring_ops, cmd); cmd->stonith_recurring_id = g_timeout_add(cmd->interval, stonith_recurring_op_helper, cmd); } cmd_finalize(cmd, rsc); return rc; } static const char * normalize_action_name(lrmd_rsc_t * rsc, const char *action) { if (safe_str_eq(action, "monitor") && (safe_str_eq(rsc->class, "lsb") || safe_str_eq(rsc->class, "service") || safe_str_eq(rsc->class, "systemd"))) { return "status"; } return action; } static void dup_attr(gpointer key, gpointer value, gpointer user_data) { g_hash_table_replace(user_data, strdup(key), strdup(value)); } static int lrmd_rsc_execute_service_lib(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd) { svc_action_t *action = NULL; GHashTable *params_copy = NULL; crm_trace("Creating action, resource:%s action:%s class:%s provider:%s agent:%s", rsc->rsc_id, cmd->action, rsc->class, rsc->provider, rsc->type); if (cmd->params) { params_copy = g_hash_table_new_full(crm_str_hash, g_str_equal, g_hash_destroy_str, g_hash_destroy_str); if (params_copy != NULL) { g_hash_table_foreach(cmd->params, dup_attr, params_copy); } } action = resources_action_create(rsc->rsc_id, rsc->class, rsc->provider, rsc->type, normalize_action_name(rsc, cmd->action), cmd->interval, cmd->timeout, params_copy); if (!action) { crm_err("Failed to create action, action:%s on resource %s", cmd->action, rsc->rsc_id); cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; goto exec_done; } action->cb_data = cmd; - if (!services_action_async(action, action_complete)) { - services_action_free(action); - action = NULL; - cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; - goto exec_done; - } - + /* The cmd will be finalized by the action_complete callback after + * the service library is done with it */ + rsc->active = cmd; /* only one op at a time for a rsc */ if (cmd->interval) { rsc->recurring_ops = g_list_append(rsc->recurring_ops, cmd); } - /* The cmd will be finalized by the action_complete callback after - * the service library is done with it */ - rsc->active = cmd; /* only one op at a time for a rsc */ - cmd = NULL; + /* 'cmd' may not be valid after this point + * + * Upstart and systemd both synchronously determine monitor/status + * results and call action_complete (which may free 'cmd') if necessary + */ + if (services_action_async(action, action_complete)) { + return TRUE; + } + + cmd->exec_rc = action->rc; + cmd->lrmd_op_status = PCMK_LRM_OP_ERROR; + services_action_free(action); + action = NULL; exec_done: if (cmd) { cmd_finalize(cmd, rsc); } return TRUE; } static gboolean lrmd_rsc_execute(lrmd_rsc_t * rsc) { lrmd_cmd_t *cmd = NULL; CRM_CHECK(rsc != NULL, return FALSE); if (rsc->active) { crm_trace("%s is still active", rsc->rsc_id); return TRUE; } if (rsc->pending_ops) { GList *first = rsc->pending_ops; cmd = first->data; if (cmd->delay_id) { crm_trace ("Command %s %s was asked to run too early, waiting for start_delay timeout of %dms", cmd->rsc_id, cmd->action, cmd->start_delay); return TRUE; } rsc->pending_ops = g_list_remove_link(rsc->pending_ops, first); g_list_free_1(first); #ifdef HAVE_SYS_TIMEB_H ftime(&cmd->t_run); } #endif if (!cmd) { crm_trace("Nothing further to do for %s", rsc->rsc_id); return TRUE; } if (safe_str_eq(rsc->class, "stonith")) { lrmd_rsc_execute_stonith(rsc, cmd); } else { lrmd_rsc_execute_service_lib(rsc, cmd); } return TRUE; } static gboolean lrmd_rsc_dispatch(gpointer user_data) { return lrmd_rsc_execute(user_data); } void free_rsc(gpointer data) { GListPtr gIter = NULL; lrmd_rsc_t *rsc = data; int is_stonith = safe_str_eq(rsc->class, "stonith"); for (gIter = rsc->pending_ops; gIter != NULL; gIter = gIter->next) { lrmd_cmd_t *cmd = gIter->data; /* command was never executed */ cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; cmd_finalize(cmd, NULL); } /* frees list, but not list elements. */ g_list_free(rsc->pending_ops); for (gIter = rsc->recurring_ops; gIter != NULL; gIter = gIter->next) { lrmd_cmd_t *cmd = gIter->data; if (is_stonith) { cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; cmd_finalize(cmd, NULL); } else { /* This command is already handed off to service library, * let service library cancel it and tell us via the callback * when it is cancelled. The rsc can be safely destroyed * even if we are waiting for the cancel result */ services_action_cancel(rsc->rsc_id, cmd->action, cmd->interval); } } /* frees list, but not list elements. */ g_list_free(rsc->recurring_ops); free(rsc->rsc_id); free(rsc->class); free(rsc->provider); free(rsc->type); mainloop_destroy_trigger(rsc->work); free(rsc); } static int process_lrmd_signon(lrmd_client_t * client, xmlNode * request) { xmlNode *reply = create_xml_node(NULL, "reply"); crm_xml_add(reply, F_LRMD_OPERATION, CRM_OP_REGISTER); crm_xml_add(reply, F_LRMD_CLIENTID, client->id); crm_ipcs_send(client->channel, reply, FALSE); free_xml(reply); return pcmk_ok; } static int process_lrmd_rsc_register(lrmd_client_t * client, xmlNode * request) { int rc = pcmk_ok; lrmd_rsc_t *rsc = build_rsc_from_xml(request); lrmd_rsc_t *dup = g_hash_table_lookup(rsc_list, rsc->rsc_id); if (dup && safe_str_eq(rsc->class, dup->class) && safe_str_eq(rsc->provider, dup->provider) && safe_str_eq(rsc->type, dup->type)) { crm_warn("Can't add, RSC '%s' already present in the rsc list (%d active resources)", rsc->rsc_id, g_hash_table_size(rsc_list)); free_rsc(rsc); return rc; } g_hash_table_replace(rsc_list, rsc->rsc_id, rsc); crm_info("Added '%s' to the rsc list (%d active resources)", rsc->rsc_id, g_hash_table_size(rsc_list)); return rc; } static void process_lrmd_get_rsc_info(lrmd_client_t * client, xmlNode * request) { int rc = pcmk_ok; int send_rc = 0; int call_id = 0; xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR); const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); xmlNode *reply = NULL; lrmd_rsc_t *rsc = NULL; crm_element_value_int(request, F_LRMD_CALLID, &call_id); if (!rsc_id) { rc = -ENODEV; goto get_rsc_done; } if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) { crm_info("Resource '%s' not found (%d active resources)", rsc_id, g_hash_table_size(rsc_list)); rc = -ENODEV; goto get_rsc_done; } get_rsc_done: reply = create_xml_node(NULL, T_LRMD_REPLY); crm_xml_add(reply, F_LRMD_ORIGIN, __FUNCTION__); crm_xml_add_int(reply, F_LRMD_RC, rc); crm_xml_add_int(reply, F_LRMD_CALLID, call_id); if (rsc) { crm_xml_add(reply, F_LRMD_RSC_ID, rsc->rsc_id); crm_xml_add(reply, F_LRMD_CLASS, rsc->class); crm_xml_add(reply, F_LRMD_PROVIDER, rsc->provider); crm_xml_add(reply, F_LRMD_TYPE, rsc->type); } send_rc = crm_ipcs_send(client->channel, reply, FALSE); if (send_rc < 0) { crm_warn("LRMD reply to %s failed: %d", client->name, send_rc); } free_xml(reply); } static int process_lrmd_rsc_unregister(lrmd_client_t * client, xmlNode * request) { int rc = pcmk_ok; lrmd_rsc_t *rsc = NULL; xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR); const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); if (!rsc_id) { return -ENODEV; } if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) { crm_info("Resource '%s' not found (%d active resources)", rsc_id, g_hash_table_size(rsc_list)); - return -ENODEV; + return pcmk_ok; } if (rsc->active) { /* let the caller know there are still active ops on this rsc to watch for */ + crm_trace("Operation still in progress: %p", rsc->active); rc = -EINPROGRESS; } g_hash_table_remove(rsc_list, rsc_id); return rc; } static int process_lrmd_rsc_exec(lrmd_client_t * client, xmlNode * request) { lrmd_rsc_t *rsc = NULL; lrmd_cmd_t *cmd = NULL; xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR); const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); if (!rsc_id) { return -EINVAL; } if (!(rsc = g_hash_table_lookup(rsc_list, rsc_id))) { crm_info("Resource '%s' not found (%d active resources)", rsc_id, g_hash_table_size(rsc_list)); return -ENODEV; } cmd = create_lrmd_cmd(request, client); schedule_lrmd_cmd(rsc, cmd); return cmd->call_id; } static int cancel_op(const char *rsc_id, const char *action, int interval) { GListPtr gIter = NULL; lrmd_rsc_t *rsc = g_hash_table_lookup(rsc_list, rsc_id); /* How to cancel an action. * 1. Check pending ops list, if it hasn't been handed off * to the service library or stonith recurring list remove * it there and that will stop it. * 2. If it isn't in the pending ops list, then its either a * recurring op in the stonith recurring list, or the service * library's recurring list. Stop it there * 3. If not found in any lists, then this operation has either * been executed already and is not a recurring operation, or * never existed. */ if (!rsc) { return -ENODEV; } for (gIter = rsc->pending_ops; gIter != NULL; gIter = gIter->next) { lrmd_cmd_t *cmd = gIter->data; if (safe_str_eq(cmd->action, action) && cmd->interval == interval) { cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; cmd_finalize(cmd, rsc); return pcmk_ok; } } if (safe_str_eq(rsc->class, "stonith")) { /* The service library does not handle stonith operations. * We have to handle recurring stonith opereations ourselves. */ for (gIter = rsc->recurring_ops; gIter != NULL; gIter = gIter->next) { lrmd_cmd_t *cmd = gIter->data; if (safe_str_eq(cmd->action, action) && cmd->interval == interval) { cmd->lrmd_op_status = PCMK_LRM_OP_CANCELLED; cmd_finalize(cmd, rsc); return pcmk_ok; } } } else if (services_action_cancel(rsc_id, normalize_action_name(rsc, action), interval) == TRUE) { /* The service library will tell the action_complete callback function * this action was cancelled, which will destroy the cmd and remove * it from the recurring_op list. Do not do that in this function * if the service library says it cancelled it. */ return pcmk_ok; } return -EOPNOTSUPP; } static int process_lrmd_rsc_cancel(lrmd_client_t * client, xmlNode * request) { xmlNode *rsc_xml = get_xpath_object("//" F_LRMD_RSC, request, LOG_ERR); const char *rsc_id = crm_element_value(rsc_xml, F_LRMD_RSC_ID); const char *action = crm_element_value(rsc_xml, F_LRMD_RSC_ACTION); int interval = 0; crm_element_value_int(rsc_xml, F_LRMD_RSC_INTERVAL, &interval); if (!rsc_id || !action) { return -EINVAL; } return cancel_op(rsc_id, action, interval); } void process_lrmd_message(lrmd_client_t * client, xmlNode * request) { int rc = pcmk_ok; int call_id = 0; const char *op = crm_element_value(request, F_LRMD_OPERATION); int do_reply = 0; int do_notify = 0; int exit = 0; crm_trace("Processing %s operation from %s", op, client->id); crm_element_value_int(request, F_LRMD_CALLID, &call_id); if (crm_str_eq(op, CRM_OP_REGISTER, TRUE)) { rc = process_lrmd_signon(client, request); } else if (crm_str_eq(op, LRMD_OP_RSC_REG, TRUE)) { rc = process_lrmd_rsc_register(client, request); do_notify = 1; do_reply = 1; } else if (crm_str_eq(op, LRMD_OP_RSC_INFO, TRUE)) { process_lrmd_get_rsc_info(client, request); } else if (crm_str_eq(op, LRMD_OP_RSC_UNREG, TRUE)) { rc = process_lrmd_rsc_unregister(client, request); /* don't notify anyone about failed un-registers */ if (rc == pcmk_ok || rc == -EINPROGRESS) { do_notify = 1; } do_reply = 1; } else if (crm_str_eq(op, LRMD_OP_RSC_EXEC, TRUE)) { rc = process_lrmd_rsc_exec(client, request); do_reply = 1; } else if (crm_str_eq(op, LRMD_OP_RSC_CANCEL, TRUE)) { rc = process_lrmd_rsc_cancel(client, request); do_reply = 1; } else if (crm_str_eq(op, CRM_OP_QUIT, TRUE)) { do_reply = 1; exit = 1; } else { rc = -EOPNOTSUPP; do_reply = 1; crm_err("Unknown %s from %s", op, client->name); crm_log_xml_warn(request, "UnknownOp"); } crm_debug("Processed %s operation from %s: rc=%d, reply=%d, notify=%d, exit=%d", op, client->id, rc, do_reply, do_notify, exit); if (do_reply) { send_reply(client, rc, call_id); } if (do_notify) { send_generic_notify(rc, request); } if (exit) { lrmd_shutdown(0); } }