* Systemd is now standard for Resources. It uses systemctl command to list services and invoke actions. * List of supported standards has been moved from agents to services library. It's now function instead of global list. * Make DBus invoke method asynchronous to ensure same behaviour as QMF agent. * Make standard and provider case insensitive * Disable interval for invoke method in DBus agent because dbus can't emit events (might use signal in the future).
Signed-off-by: Radek Novacek rnovacek@redhat.com --- src/include/matahari/services.h | 11 ++++++ src/lib/mh_dbus_common.c | 2 + src/lib/services.c | 40 ++++++++++++++++++--- src/lib/services_linux.c | 39 ++++++++++++++++++++- src/lib/services_private.h | 5 ++- src/service/service-dbus.c | 73 +++++++++++++++++++++++---------------- src/service/service-qmf.cpp | 44 ++++++++++------------- 7 files changed, 151 insertions(+), 63 deletions(-)
diff --git a/src/include/matahari/services.h b/src/include/matahari/services.h index 82d0aa9..6e61b4d 100644 --- a/src/include/matahari/services.h +++ b/src/include/matahari/services.h @@ -33,6 +33,8 @@ #define OCF_ROOT "/usr/lib/ocf" #define LSB_ROOT "/etc/init.d"
+#define SYSTEMCTL "/bin/systemctl" + enum lsb_exitcode { LSB_OK = 0, LSB_UNKNOWN_ERROR = 1, @@ -180,6 +182,15 @@ resources_list_providers(const char *standard); GList * resources_list_agents(const char *standard, const char *provider);
+/** + * Get list of available standards + * + * \return a list of resource standards. The list items are char *. This list _must_ + * be destroyed using g_list_free_full(list, free). + */ +GList * +resources_list_standards(void); + svc_action_t * services_action_create(const char *name, const char *action, int interval /* ms */, int timeout /* ms */); diff --git a/src/lib/mh_dbus_common.c b/src/lib/mh_dbus_common.c index be58428..874b207 100644 --- a/src/lib/mh_dbus_common.c +++ b/src/lib/mh_dbus_common.c @@ -24,6 +24,7 @@
#include "matahari/dbus_common.h" #include "matahari/logging.h" +#include "matahari/mainloop.h" #include "matahari/errors.h" #include <glib/gi18n.h>
@@ -207,6 +208,7 @@ run_dbus_server(char *bus_name, char *object_path) return 1; }
+ mainloop_track_children(G_PRIORITY_DEFAULT); g_main_loop_run(loop); g_main_loop_unref(loop); g_object_unref(obj); diff --git a/src/lib/services.c b/src/lib/services.c index 6ab5a66..3241002 100644 --- a/src/lib/services.c +++ b/src/lib/services.c @@ -96,7 +96,7 @@ svc_action_t *resources_action_create( op->sequence = ++operations; asprintf(&op->id, "%s_%s_%d", name, action, interval);
- if(strcmp(standard, "ocf") == 0) { + if(strcasecmp(standard, "ocf") == 0) { op->provider = strdup(provider); op->params = params;
@@ -105,9 +105,16 @@ svc_action_t *resources_action_create( op->opaque->args[0] = strdup(op->opaque->exec); op->opaque->args[1] = strdup(action);
- } else if(strcmp(standard, "lsb") == 0 || strcmp(standard, "windows") == 0) { + } else if(strcasecmp(standard, "lsb") == 0 || strcasecmp(standard, "windows") == 0) { services_os_set_exec(op);
+ } else if(strcasecmp(standard, "systemd") == 0) { + char *service; + op->opaque->exec = strdup(SYSTEMCTL); + op->opaque->args[0] = strdup(SYSTEMCTL); + op->opaque->args[1] = strdup(action); + asprintf(&service, "%s.service", agent); + op->opaque->args[2] = service; } else { mh_err("Unknown resource standard: %s", standard); services_action_free(op); @@ -253,9 +260,25 @@ services_list(void) }
GList * +resources_list_standards(void) +{ + GList *standards = NULL; +#ifdef __linux__ + standards = g_list_append(standards, strdup("ocf")); + standards = g_list_append(standards, strdup("lsb")); + if (g_file_test(SYSTEMCTL, G_FILE_TEST_IS_REGULAR)) + standards = g_list_append(standards, strdup("systemd")); +#endif +#ifdef WIN32 + standards = g_list_append(standards, strdup("windows")); +#endif + return standards; +} + +GList * resources_list_providers(const char *standard) { - if(strcmp(standard, "ocf") == 0) { + if (strcasecmp(standard, "ocf") == 0) { return resources_os_list_ocf_providers(); }
@@ -265,12 +288,17 @@ resources_list_providers(const char *standard) GList * resources_list_agents(const char *standard, const char *provider) { - if(strcmp(standard, "ocf") == 0) { + if (strcasecmp(standard, "ocf") == 0) { return resources_os_list_ocf_agents(provider);
- } else if(strcmp(standard, "lsb") == 0 - || strcmp(standard, "windows") == 0) { + } else if (strcasecmp(standard, "lsb") == 0 + || strcasecmp(standard, "windows") == 0) { return services_os_list(); + +#ifdef __linux__ + } else if (strcasecmp(standard, "systemd") == 0) { + return resources_os_list_systemd_services(); +#endif }
return NULL; diff --git a/src/lib/services_linux.c b/src/lib/services_linux.c index 7760913..d2f8135 100644 --- a/src/lib/services_linux.c +++ b/src/lib/services_linux.c @@ -142,7 +142,7 @@ set_ocf_env_with_prefix(gpointer key, gpointer value, gpointer user_data) static void add_OCF_env_vars(svc_action_t *op) { - if (!op->standard || strcmp("ocf", op->standard) != 0) { + if (!op->standard || strcasecmp("ocf", op->standard) != 0) { return; }
@@ -503,3 +503,40 @@ resources_os_list_ocf_agents(const char *provider) } 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 = mh_services_action_create_generic(SYSTEMCTL, args))) { + return NULL; + } + 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 = malloc(sizeof(char) * (len + 1)); + service = mh_string_copy(service, ptr, len + 1); + list = g_list_append(list, service); + } + services_action_free(action); + return list; +} diff --git a/src/lib/services_private.h b/src/lib/services_private.h index d7931dc..dada879 100644 --- a/src/lib/services_private.h +++ b/src/lib/services_private.h @@ -21,7 +21,7 @@
struct svc_action_private_s { char *exec; - char *args[4]; + char *args[7];
guint repeat_timer; void (*callback)(svc_action_t *op); @@ -51,4 +51,7 @@ resources_os_list_ocf_providers(void); GList * resources_os_list_ocf_agents(const char *provider);
+GList * +resources_os_list_systemd_services(void); + #endif /* __MH_SERVICES_PRIVATE_H__ */ diff --git a/src/service/service-dbus.c b/src/service/service-dbus.c index aefb842..e22e427 100644 --- a/src/service/service-dbus.c +++ b/src/service/service-dbus.c @@ -229,22 +229,14 @@ Services_describe(Matahari *matahari, const char *name, return TRUE; }
-// TODO: This and same code in service-qmf.cpp should go to the library -const char *standards[] = { -#ifdef __linux__ - "ocf", -#endif - "lsb", -#ifndef WIN32 - "windows", -#endif - NULL}; - - gboolean Resources_list_standards(Matahari *matahari, DBusGMethodInvocation *context) { GError* error = NULL; + gchar **list; + int i = 0; + GList *standards; + if (!check_authorization(RESOURCES_INTERFACE_NAME ".list_standards", &error, context)) { dbus_g_method_return_error(context, error); @@ -252,7 +244,17 @@ Resources_list_standards(Matahari *matahari, DBusGMethodInvocation *context) return FALSE; }
- dbus_g_method_return(context, standards); + standards = resources_list_standards(); + + // Convert GList to (char **) + list = g_new(char *, g_list_length(standards) + 1); + for (; standards != NULL; standards = standards->next) + list[i++] = strdup(standards->data); + list[i] = NULL; // Sentinel + + dbus_g_method_return(context, list); + g_strfreev(list); + g_list_free_full(standards, free); return TRUE; }
@@ -343,6 +345,19 @@ Resources_describe(Matahari *matahari, const char *standard, return TRUE; }
+struct invoke_cb_data { + DBusGMethodInvocation *context; + char *userdata; +}; + +void invoke_cb(svc_action_t *op) +{ + struct invoke_cb_data *cb_data = op->cb_data; + dbus_g_method_return(cb_data->context, op->rc, op->sequence, cb_data->userdata); + free(cb_data->userdata); + free(cb_data); +} + gboolean Resources_invoke(Matahari *matahari, const char *name, const char *standard, const char *provider, const char *agent, const char *action, @@ -350,10 +365,9 @@ Resources_invoke(Matahari *matahari, const char *name, const char *standard, unsigned int timeout, unsigned int expected_rc, const char *userdata_in, DBusGMethodInvocation *context) { - int i = 0; GError* error = NULL; svc_action_t *op = NULL; - gboolean valid_standard = FALSE; + GList *standards;
if (!check_authorization(RESOURCES_INTERFACE_NAME ".invoke", &error, context)) { @@ -363,31 +377,30 @@ Resources_invoke(Matahari *matahari, const char *name, const char *standard, }
// Check if standard is valid - while (standards[i] != NULL) - { - if (strcmp(standard, standards[i]) == 0) - { - valid_standard = TRUE; - break; - } - i++; - } - if (!valid_standard) { + standards = resources_list_standards(); + + if (g_list_find_custom(standards, standard, (GCompareFunc) strcasecmp) == NULL) { mh_err("%s is not a known resource standard", standard); error = g_error_new(MATAHARI_ERROR, MH_RES_NOT_IMPLEMENTED, "%s is not a known resource standard", standard); dbus_g_method_return_error(context, error); g_error_free(error); + g_list_free_full(standards, free); return FALSE; } + g_list_free_full(standards, free);
op = resources_action_create(name, standard, provider, agent, action, - interval, timeout, parameters); + 0, timeout, g_hash_table_ref(parameters)); op->expected_rc = expected_rc;
- services_action_sync(op); //TODO: maybe async - dbus_g_method_return(context, op->rc, op->sequence, userdata_in); - return TRUE; + struct invoke_cb_data *data = malloc(1 * sizeof(struct invoke_cb_data)); + data->context = context; + data->userdata = strdup(userdata_in); + op->cb_data = data; + + services_action_async(op, invoke_cb); + return FALSE; }
gboolean @@ -403,7 +416,7 @@ Resources_cancel(Matahari *matahari, const char *name, const char *action, return FALSE; }
- services_action_cancel(name, action, interval); + services_action_cancel(name, action, 0); dbus_g_method_return(context); return TRUE; } diff --git a/src/service/service-qmf.cpp b/src/service/service-qmf.cpp index 63f5db2..6aa4904 100644 --- a/src/service/service-qmf.cpp +++ b/src/service/service-qmf.cpp @@ -55,8 +55,6 @@ private: qmf::Data _resources; static const char RESOURCES_NAME[];
- _qtype::Variant::List standards; - gboolean invoke_services(qmf::AgentSession session, qmf::AgentEvent event, gpointer user_data); gboolean invoke_resources(qmf::AgentSession session, @@ -219,14 +217,6 @@ SrvAgent::raiseEvent(svc_action_t *op, enum service_id service, const std::strin int SrvAgent::setup(qmf::AgentSession session) { -#ifdef __linux__ - standards.push_back("ocf"); - standards.push_back("lsb"); -#endif -#ifndef WIN32 - standards.push_back("windows"); -#endif - _package.configure(session);
_services = qmf::Data(_package.data_Services); @@ -347,7 +337,17 @@ SrvAgent::invoke_resources(qmf::AgentSession session, qmf::AgentEvent event, qpid::types::Variant::Map& args = event.getArguments();
if (methodName == "list_standards") { - event.addReturnArgument("standards", standards); + GList *gIter = NULL; + GList *standards = NULL; + _qtype::Variant::List s_list; + + standards = resources_list_standards(); + for (gIter = standards; gIter != NULL; gIter = gIter->next) { + s_list.push_back((const char *) gIter->data); + } + g_list_free_full(standards, free); + + event.addReturnArgument("standards", s_list);
} else if (methodName == "list_providers") { GList *gIter = NULL; @@ -387,7 +387,6 @@ SrvAgent::invoke_resources(qmf::AgentSession session, qmf::AgentEvent event,
} else if (methodName == "invoke") { svc_action_t *op = NULL; - bool valid_standard = false; _qtype::Variant::List::iterator iter; _qtype::Variant::Map map;
@@ -403,19 +402,6 @@ SrvAgent::invoke_resources(qmf::AgentSession session, qmf::AgentEvent event, std::string standard("ocf"); std::string provider("heartbeat");
- for ( iter=standards.begin() ; iter != standards.end(); iter++ ) { - if(args["standard"].asString() == (*iter).asString()) { - valid_standard = true; - break; - } - } - - if(valid_standard == false) { - mh_err("%s is not a known resource standard", args["standard"].asString().c_str()); - session.raiseException(event, mh_result_to_str(MH_RES_NOT_IMPLEMENTED)); - return TRUE; - } - if (args.count("standard")) { standard = args["standard"].asString(); } @@ -435,6 +421,14 @@ SrvAgent::invoke_resources(qmf::AgentSession session, qmf::AgentEvent event, timeout = args["timeout"].asInt32(); }
+ GList *standards = resources_list_standards(); + + if (g_list_find_custom(standards, standard.c_str(), (GCompareFunc) strcasecmp) == NULL) { + mh_err("%s is not a known resource standard", standard.c_str()); + session.raiseException(event, mh_result_to_str(MH_RES_NOT_IMPLEMENTED)); + return TRUE; + } + op = resources_action_create( args["name"].asString().c_str(), standard.c_str(), provider.c_str(), agent.c_str(),
I dug into the Resources API more thoroughly today and it makes sense to me now. I tried to at least add some documentation but I'm still missing few bits, so if anyone can fill in (and check what I wrote), it would be great.
One more suggestion: how about some automated way to show agents' API on wiki page? The page API on wiki only shows API of libraries, not the agents themselves. We can link the schema.xml from the wiki directly or transform it to the wiki page. What do you think?
--- src/service/schema.xml | 44 +++++++++++++++++++++++++------------------- 1 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/src/service/schema.xml b/src/service/schema.xml index 0790eb8..b74b69a 100644 --- a/src/service/schema.xml +++ b/src/service/schema.xml @@ -24,7 +24,7 @@ <!-- Services API Simple API for accessing system services - + For more advanced functionality, use the Resources API with standard="lsb" -->
@@ -71,8 +71,8 @@
Fully featured API for controlling and monitoring services conforming to a standardized specification. - - Currently supported standards: windows, lsb (Linux Standard Base), ocf (Open Cluster Framework) + + Currently supported standards: windows, lsb (Linux Standard Base), ocf (Open Cluster Framework), systemd On Windows, 'lsb' is an alias for 'windows' -->
@@ -80,7 +80,7 @@ <property name="uuid" type="sstr" access="RO" desc="Host UUID" /> <property name="hostname" type="sstr" access="RO" desc="Hostname" index="y"/>
- <method name="list_standards" desc="List known resource standards (OCF, LSB, Upstart, etc)"> + <method name="list_standards" desc="List known resource standards (OCF, LSB, systemd, etc)"> <arg name="standards" dir="O" type="list" /> </method> <method name="list_providers" desc="List known providers (if supported) for a given standard"> @@ -178,24 +178,30 @@ </table> --> <method name="invoke" desc="Perform a custom action on a resource"> - <arg name="name" dir="I" type="sstr" /> - <arg name="standard" dir="I" type="sstr" /> - <arg name="provider" dir="I" type="sstr" /> - <arg name="agent" dir="I" type="sstr" /> - <arg name="action" dir="I" type="sstr" /> - <arg name="interval" dir="I" type="uint32" /> - <arg name="parameters" dir="I" type="map" /> - <arg name="timeout" dir="I" type="uint32" /> + <arg name="name" dir="I" type="sstr" desc="Identification of the action" /> + <arg name="standard" dir="I" type="sstr" desc="Resource standard to be used, see list_standards method" /> + <arg name="provider" dir="I" type="sstr" desc="Provider of ocf standard, see list_providers method" /> + <arg name="agent" dir="I" type="sstr" desc="Name of the resource affected by action" /> + <arg name="action" dir="I" type="sstr" desc="Action that will be executed on resource" /> + <!-- +<para>Actions depends on the standard/provider/agent +OCF: see <ulink url="http://www.opencf.org/cgi-bin/viewcvs.cgi/specs/ra/resource-agent-api.txt?rev=HEAD">ocf specification</ulink> +LSB: enable/disable/start/stop/status +systemd: see <ulink url="http://www.linuxcertif.com/man/1/systemctl/">man systemctl</ulink></para> + --> + <arg name="interval" dir="I" type="uint32" desc="Event resource_op will be risen each '''interval''' miliseconds (not supported by dbus agent)" /> + <arg name="parameters" dir="I" type="map" desc="Additional parameters for the action (enviromental variables for OCF)" /> + <arg name="timeout" dir="I" type="uint32" desc="Timeout for the action in miliseconds" /> <arg name="expected-rc" dir="I" type="uint32" /> - <arg name="rc" dir="O" type="uint32" /> + <arg name="rc" dir="O" type="uint32" desc="Return code of the action" /> <arg name="sequence" dir="O" type="uint32" /> - <arg name="userdata" dir="IO" type="sstr" /> + <arg name="userdata" dir="IO" type="sstr" /> </method> - <method name="cancel" desc="Cancel a pending or running action on a resource"> - <arg name="name" dir="I" type="sstr" /> - <arg name="action" dir="I" type="sstr" /> - <arg name="interval" dir="I" type="uint32" /> - <arg name="timeout" dir="I" type="uint32" /> + <method name="cancel" desc="Cancel a pending or running action on a resource. name, action and interval must be the same as for invoke method"> + <arg name="name" dir="I" type="sstr" desc="Identification of the action" /> + <arg name="action" dir="I" type="sstr" desc="Action that is running or pending" /> + <arg name="interval" dir="I" type="uint32" desc="Interval in miliseconds" /> + <arg name="timeout" dir="I" type="uint32" desc="Timeout for cancelling in miliseconds" /> </method> <method name="fail" desc="Indicate a resource has failed"> <arg name="name" dir="I" type="sstr" />
ACK on the patch, comments inline
On 10/20/2011 08:59 AM, Radek Novacek wrote:
I dug into the Resources API more thoroughly today and it makes sense to me now. I tried to at least add some documentation but I'm still missing few bits, so if anyone can fill in (and check what I wrote), it would be great.
One more suggestion: how about some automated way to show agents' API on wiki page? The page API on wiki only shows API of libraries, not the agents themselves. We can link the schema.xml from the wiki directly or transform it to the wiki page. What do you think?
We actually have something like this already. Check out:
http://matahariproject.org/manual/
The "Matahari APIs" chapter is automatically generated from the schemas. The code that does it is in the same repository as the manual. See gen_manual.sh.
https://github.com/matahari/matahari-manual
If you have any ideas for making it better, that would be welcome of course. :-)
src/service/schema.xml | 44 +++++++++++++++++++++++++------------------- 1 files changed, 25 insertions(+), 19 deletions(-)
diff --git a/src/service/schema.xml b/src/service/schema.xml index 0790eb8..b74b69a 100644 --- a/src/service/schema.xml +++ b/src/service/schema.xml @@ -24,7 +24,7 @@
<!-- Services API Simple API for accessing system services - + For more advanced functionality, use the Resources API with standard="lsb" -->
@@ -71,8 +71,8 @@
Fully featured API for controlling and monitoring services conforming to a standardized specification.
Currently supported standards: windows, lsb (Linux Standard Base), ocf (Open Cluster Framework)
-->Currently supported standards: windows, lsb (Linux Standard Base), ocf (Open Cluster Framework), systemd On Windows, 'lsb' is an alias for 'windows'
@@ -80,7 +80,7 @@ <property name="uuid" type="sstr" access="RO" desc="Host UUID" /> <property name="hostname" type="sstr" access="RO" desc="Hostname" index="y"/>
-<method name="list_standards" desc="List known resource standards (OCF, LSB, Upstart, etc)"> +<method name="list_standards" desc="List known resource standards (OCF, LSB, systemd, etc)"> <arg name="standards" dir="O" type="list" /> </method> <method name="list_providers" desc="List known providers (if supported) for a given standard"> @@ -178,24 +178,30 @@ </table> --> <method name="invoke" desc="Perform a custom action on a resource"> -<arg name="name" dir="I" type="sstr" /> -<arg name="standard" dir="I" type="sstr" /> -<arg name="provider" dir="I" type="sstr" /> -<arg name="agent" dir="I" type="sstr" /> -<arg name="action" dir="I" type="sstr" /> -<arg name="interval" dir="I" type="uint32" /> -<arg name="parameters" dir="I" type="map" /> -<arg name="timeout" dir="I" type="uint32" /> +<arg name="name" dir="I" type="sstr" desc="Identification of the action" /> +<arg name="standard" dir="I" type="sstr" desc="Resource standard to be used, see list_standards method" /> +<arg name="provider" dir="I" type="sstr" desc="Provider of ocf standard, see list_providers method" /> +<arg name="agent" dir="I" type="sstr" desc="Name of the resource affected by action" /> +<arg name="action" dir="I" type="sstr" desc="Action that will be executed on resource" /> +<!-- +<para>Actions depends on the standard/provider/agent +OCF: see<ulink url="http://www.opencf.org/cgi-bin/viewcvs.cgi/specs/ra/resource-agent-api.txt?rev=HEAD">ocf specification</ulink> +LSB: enable/disable/start/stop/status +systemd: see<ulink url="http://www.linuxcertif.com/man/1/systemctl/">man systemctl</ulink></para>
- -->
I don't think the current tool for coverting the schema into docbook documentation supports a comment in this spot. I think you have to include additional documentation for the method and its arguments above the method.
+<arg name="interval" dir="I" type="uint32" desc="Event resource_op will be risen each '''interval''' miliseconds (not supported by dbus agent)" /> +<arg name="parameters" dir="I" type="map" desc="Additional parameters for the action (enviromental variables for OCF)" /> +<arg name="timeout" dir="I" type="uint32" desc="Timeout for the action in miliseconds" /> <arg name="expected-rc" dir="I" type="uint32" /> -<arg name="rc" dir="O" type="uint32" /> +<arg name="rc" dir="O" type="uint32" desc="Return code of the action" /> <arg name="sequence" dir="O" type="uint32" /> -<arg name="userdata" dir="IO" type="sstr" /> +<arg name="userdata" dir="IO" type="sstr" /> </method> -<method name="cancel" desc="Cancel a pending or running action on a resource"> -<arg name="name" dir="I" type="sstr" /> -<arg name="action" dir="I" type="sstr" /> -<arg name="interval" dir="I" type="uint32" /> -<arg name="timeout" dir="I" type="uint32" /> +<method name="cancel" desc="Cancel a pending or running action on a resource. name, action and interval must be the same as for invoke method"> +<arg name="name" dir="I" type="sstr" desc="Identification of the action" /> +<arg name="action" dir="I" type="sstr" desc="Action that is running or pending" /> +<arg name="interval" dir="I" type="uint32" desc="Interval in miliseconds" /> +<arg name="timeout" dir="I" type="uint32" desc="Timeout for cancelling in miliseconds" /> </method> <method name="fail" desc="Indicate a resource has failed"> <arg name="name" dir="I" type="sstr" />
ACK.
Nice work!
On 10/20/2011 08:59 AM, Radek Novacek wrote:
- Systemd is now standard for Resources. It uses systemctl command to list services and invoke actions.
- List of supported standards has been moved from agents to services library. It's now function instead of global list.
- Make DBus invoke method asynchronous to ensure same behaviour as QMF agent.
- Make standard and provider case insensitive
- Disable interval for invoke method in DBus agent because dbus can't emit events (might use signal in the future).
Signed-off-by: Radek Novacekrnovacek@redhat.com
src/include/matahari/services.h | 11 ++++++ src/lib/mh_dbus_common.c | 2 + src/lib/services.c | 40 ++++++++++++++++++--- src/lib/services_linux.c | 39 ++++++++++++++++++++- src/lib/services_private.h | 5 ++- src/service/service-dbus.c | 73 +++++++++++++++++++++++---------------- src/service/service-qmf.cpp | 44 ++++++++++------------- 7 files changed, 151 insertions(+), 63 deletions(-)
diff --git a/src/include/matahari/services.h b/src/include/matahari/services.h index 82d0aa9..6e61b4d 100644 --- a/src/include/matahari/services.h +++ b/src/include/matahari/services.h @@ -33,6 +33,8 @@ #define OCF_ROOT "/usr/lib/ocf" #define LSB_ROOT "/etc/init.d"
+#define SYSTEMCTL "/bin/systemctl"
- enum lsb_exitcode { LSB_OK = 0, LSB_UNKNOWN_ERROR = 1,
@@ -180,6 +182,15 @@ resources_list_providers(const char *standard); GList * resources_list_agents(const char *standard, const char *provider);
+/**
- Get list of available standards
- \return a list of resource standards. The list items are char *. This list _must_
be destroyed using g_list_free_full(list, free).
- */
+GList * +resources_list_standards(void);
- svc_action_t * services_action_create(const char *name, const char *action, int interval /* ms */, int timeout /* ms */);
diff --git a/src/lib/mh_dbus_common.c b/src/lib/mh_dbus_common.c index be58428..874b207 100644 --- a/src/lib/mh_dbus_common.c +++ b/src/lib/mh_dbus_common.c @@ -24,6 +24,7 @@
#include "matahari/dbus_common.h" #include "matahari/logging.h" +#include "matahari/mainloop.h" #include "matahari/errors.h" #include<glib/gi18n.h>
@@ -207,6 +208,7 @@ run_dbus_server(char *bus_name, char *object_path) return 1; }
- mainloop_track_children(G_PRIORITY_DEFAULT); g_main_loop_run(loop); g_main_loop_unref(loop); g_object_unref(obj);
diff --git a/src/lib/services.c b/src/lib/services.c index 6ab5a66..3241002 100644 --- a/src/lib/services.c +++ b/src/lib/services.c @@ -96,7 +96,7 @@ svc_action_t *resources_action_create( op->sequence = ++operations; asprintf(&op->id, "%s_%s_%d", name, action, interval);
- if(strcmp(standard, "ocf") == 0) {
- if(strcasecmp(standard, "ocf") == 0) { op->provider = strdup(provider); op->params = params;
@@ -105,9 +105,16 @@ svc_action_t *resources_action_create( op->opaque->args[0] = strdup(op->opaque->exec); op->opaque->args[1] = strdup(action);
- } else if(strcmp(standard, "lsb") == 0 || strcmp(standard, "windows") == 0) {
} else if(strcasecmp(standard, "lsb") == 0 || strcasecmp(standard, "windows") == 0) { services_os_set_exec(op);
} else if(strcasecmp(standard, "systemd") == 0) {
char *service;
op->opaque->exec = strdup(SYSTEMCTL);
op->opaque->args[0] = strdup(SYSTEMCTL);
op->opaque->args[1] = strdup(action);
asprintf(&service, "%s.service", agent);
op->opaque->args[2] = service; } else { mh_err("Unknown resource standard: %s", standard); services_action_free(op);
@@ -253,9 +260,25 @@ services_list(void) }
GList * +resources_list_standards(void) +{
- GList *standards = NULL;
+#ifdef __linux__
- standards = g_list_append(standards, strdup("ocf"));
- standards = g_list_append(standards, strdup("lsb"));
- if (g_file_test(SYSTEMCTL, G_FILE_TEST_IS_REGULAR))
standards = g_list_append(standards, strdup("systemd"));
+#endif +#ifdef WIN32
- standards = g_list_append(standards, strdup("windows"));
+#endif
- return standards;
+}
+GList * resources_list_providers(const char *standard) {
- if(strcmp(standard, "ocf") == 0) {
- if (strcasecmp(standard, "ocf") == 0) { return resources_os_list_ocf_providers(); }
@@ -265,12 +288,17 @@ resources_list_providers(const char *standard) GList * resources_list_agents(const char *standard, const char *provider) {
- if(strcmp(standard, "ocf") == 0) {
- if (strcasecmp(standard, "ocf") == 0) { return resources_os_list_ocf_agents(provider);
- } else if(strcmp(standard, "lsb") == 0
|| strcmp(standard, "windows") == 0) {
- } else if (strcasecmp(standard, "lsb") == 0
|| strcasecmp(standard, "windows") == 0) { return services_os_list();
+#ifdef __linux__
- } else if (strcasecmp(standard, "systemd") == 0) {
return resources_os_list_systemd_services();
+#endif }
return NULL;
diff --git a/src/lib/services_linux.c b/src/lib/services_linux.c index 7760913..d2f8135 100644 --- a/src/lib/services_linux.c +++ b/src/lib/services_linux.c @@ -142,7 +142,7 @@ set_ocf_env_with_prefix(gpointer key, gpointer value, gpointer user_data) static void add_OCF_env_vars(svc_action_t *op) {
- if (!op->standard || strcmp("ocf", op->standard) != 0) {
- if (!op->standard || strcasecmp("ocf", op->standard) != 0) { return; }
@@ -503,3 +503,40 @@ resources_os_list_ocf_agents(const char *provider) } 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 = mh_services_action_create_generic(SYSTEMCTL, args))) {
return NULL;
- }
- 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 = malloc(sizeof(char) * (len + 1));
service = mh_string_copy(service, ptr, len + 1);
list = g_list_append(list, service);
- }
- services_action_free(action);
- return list;
+} diff --git a/src/lib/services_private.h b/src/lib/services_private.h index d7931dc..dada879 100644 --- a/src/lib/services_private.h +++ b/src/lib/services_private.h @@ -21,7 +21,7 @@
struct svc_action_private_s { char *exec;
- char *args[4];
char *args[7];
guint repeat_timer; void (*callback)(svc_action_t *op);
@@ -51,4 +51,7 @@ resources_os_list_ocf_providers(void); GList * resources_os_list_ocf_agents(const char *provider);
+GList * +resources_os_list_systemd_services(void);
- #endif /* __MH_SERVICES_PRIVATE_H__ */
diff --git a/src/service/service-dbus.c b/src/service/service-dbus.c index aefb842..e22e427 100644 --- a/src/service/service-dbus.c +++ b/src/service/service-dbus.c @@ -229,22 +229,14 @@ Services_describe(Matahari *matahari, const char *name, return TRUE; }
-// TODO: This and same code in service-qmf.cpp should go to the library -const char *standards[] = { -#ifdef __linux__
- "ocf",
-#endif
- "lsb",
-#ifndef WIN32
- "windows",
-#endif
- NULL};
- gboolean Resources_list_standards(Matahari *matahari, DBusGMethodInvocation *context) { GError* error = NULL;
- gchar **list;
- int i = 0;
- GList *standards;
if (!check_authorization(RESOURCES_INTERFACE_NAME ".list_standards", &error, context)) { dbus_g_method_return_error(context, error);
@@ -252,7 +244,17 @@ Resources_list_standards(Matahari *matahari, DBusGMethodInvocation *context) return FALSE; }
- dbus_g_method_return(context, standards);
- standards = resources_list_standards();
- // Convert GList to (char **)
- list = g_new(char *, g_list_length(standards) + 1);
- for (; standards != NULL; standards = standards->next)
list[i++] = strdup(standards->data);
- list[i] = NULL; // Sentinel
- dbus_g_method_return(context, list);
- g_strfreev(list);
- g_list_free_full(standards, free); return TRUE; }
@@ -343,6 +345,19 @@ Resources_describe(Matahari *matahari, const char *standard, return TRUE; }
+struct invoke_cb_data {
- DBusGMethodInvocation *context;
- char *userdata;
+};
+void invoke_cb(svc_action_t *op) +{
- struct invoke_cb_data *cb_data = op->cb_data;
- dbus_g_method_return(cb_data->context, op->rc, op->sequence, cb_data->userdata);
- free(cb_data->userdata);
- free(cb_data);
+}
- gboolean Resources_invoke(Matahari *matahari, const char *name, const char *standard, const char *provider, const char *agent, const char *action,
@@ -350,10 +365,9 @@ Resources_invoke(Matahari *matahari, const char *name, const char *standard, unsigned int timeout, unsigned int expected_rc, const char *userdata_in, DBusGMethodInvocation *context) {
- int i = 0; GError* error = NULL; svc_action_t *op = NULL;
- gboolean valid_standard = FALSE;
GList *standards;
if (!check_authorization(RESOURCES_INTERFACE_NAME ".invoke", &error, context)) {
@@ -363,31 +377,30 @@ Resources_invoke(Matahari *matahari, const char *name, const char *standard, }
// Check if standard is valid
- while (standards[i] != NULL)
- {
if (strcmp(standard, standards[i]) == 0)
{
valid_standard = TRUE;
break;
}
i++;
- }
- if (!valid_standard) {
standards = resources_list_standards();
if (g_list_find_custom(standards, standard, (GCompareFunc) strcasecmp) == NULL) { mh_err("%s is not a known resource standard", standard); error = g_error_new(MATAHARI_ERROR, MH_RES_NOT_IMPLEMENTED, "%s is not a known resource standard", standard); dbus_g_method_return_error(context, error); g_error_free(error);
g_list_free_full(standards, free); return FALSE; }
g_list_free_full(standards, free);
op = resources_action_create(name, standard, provider, agent, action,
interval, timeout, parameters);
0, timeout, g_hash_table_ref(parameters)); op->expected_rc = expected_rc;
- services_action_sync(op); //TODO: maybe async
- dbus_g_method_return(context, op->rc, op->sequence, userdata_in);
- return TRUE;
struct invoke_cb_data *data = malloc(1 * sizeof(struct invoke_cb_data));
data->context = context;
data->userdata = strdup(userdata_in);
op->cb_data = data;
services_action_async(op, invoke_cb);
return FALSE; }
gboolean
@@ -403,7 +416,7 @@ Resources_cancel(Matahari *matahari, const char *name, const char *action, return FALSE; }
- services_action_cancel(name, action, interval);
- services_action_cancel(name, action, 0); dbus_g_method_return(context); return TRUE; }
diff --git a/src/service/service-qmf.cpp b/src/service/service-qmf.cpp index 63f5db2..6aa4904 100644 --- a/src/service/service-qmf.cpp +++ b/src/service/service-qmf.cpp @@ -55,8 +55,6 @@ private: qmf::Data _resources; static const char RESOURCES_NAME[];
- _qtype::Variant::List standards;
gboolean invoke_services(qmf::AgentSession session, qmf::AgentEvent event, gpointer user_data); gboolean invoke_resources(qmf::AgentSession session,
@@ -219,14 +217,6 @@ SrvAgent::raiseEvent(svc_action_t *op, enum service_id service, const std::strin int SrvAgent::setup(qmf::AgentSession session) { -#ifdef __linux__
- standards.push_back("ocf");
- standards.push_back("lsb");
-#endif -#ifndef WIN32
- standards.push_back("windows");
-#endif
_package.configure(session); _services = qmf::Data(_package.data_Services);
@@ -347,7 +337,17 @@ SrvAgent::invoke_resources(qmf::AgentSession session, qmf::AgentEvent event, qpid::types::Variant::Map& args = event.getArguments();
if (methodName == "list_standards") {
event.addReturnArgument("standards", standards);
GList *gIter = NULL;
GList *standards = NULL;
_qtype::Variant::List s_list;
standards = resources_list_standards();
for (gIter = standards; gIter != NULL; gIter = gIter->next) {
s_list.push_back((const char *) gIter->data);
}
g_list_free_full(standards, free);
event.addReturnArgument("standards", s_list); } else if (methodName == "list_providers") { GList *gIter = NULL;
@@ -387,7 +387,6 @@ SrvAgent::invoke_resources(qmf::AgentSession session, qmf::AgentEvent event,
} else if (methodName == "invoke") { svc_action_t *op = NULL;
bool valid_standard = false; _qtype::Variant::List::iterator iter; _qtype::Variant::Map map;
@@ -403,19 +402,6 @@ SrvAgent::invoke_resources(qmf::AgentSession session, qmf::AgentEvent event, std::string standard("ocf"); std::string provider("heartbeat");
for ( iter=standards.begin() ; iter != standards.end(); iter++ ) {
if(args["standard"].asString() == (*iter).asString()) {
valid_standard = true;
break;
}
}
if(valid_standard == false) {
mh_err("%s is not a known resource standard", args["standard"].asString().c_str());
session.raiseException(event, mh_result_to_str(MH_RES_NOT_IMPLEMENTED));
return TRUE;
}
if (args.count("standard")) { standard = args["standard"].asString(); }
@@ -435,6 +421,14 @@ SrvAgent::invoke_resources(qmf::AgentSession session, qmf::AgentEvent event, timeout = args["timeout"].asInt32(); }
GList *standards = resources_list_standards();
if (g_list_find_custom(standards, standard.c_str(), (GCompareFunc) strcasecmp) == NULL) {
mh_err("%s is not a known resource standard", standard.c_str());
session.raiseException(event, mh_result_to_str(MH_RES_NOT_IMPLEMENTED));
return TRUE;
}
op = resources_action_create( args["name"].asString().c_str(), standard.c_str(), provider.c_str(), agent.c_str(),
matahari@lists.fedorahosted.org