diff --git a/configure.ac b/configure.ac index 993589b9126..6cacfc081d8 100644 --- a/configure.ac +++ b/configure.ac @@ -485,6 +485,13 @@ AC_ARG_WITH([logdir], [ CRM_LOG_DIR="$withval" ] ) +DLOPEN_ROOT_DIR="" +AC_ARG_WITH([dlopen-root-dir], + [AS_HELP_STRING([--with-dlopen-root-dir=DIR], + [directory for dlopen plugins @<:@/usr/lib/dlopen@:>@])], + [ DLOPEN_ROOT_DIR="$withval" ] +) + CRM_BUNDLE_DIR="" AC_ARG_WITH([bundledir], [AS_HELP_STRING([--with-bundledir=DIR], @@ -693,6 +700,10 @@ expand_path_option CRM_LOG_DIR "${localstatedir}/log/pacemaker" AC_DEFINE_UNQUOTED(CRM_LOG_DIR,"$CRM_LOG_DIR", Location for Pacemaker log file) AC_SUBST(CRM_LOG_DIR) +expand_path_option DLOPEN_ROOT_DIR "/usr/lib/dlopen" +AC_DEFINE_UNQUOTED(DLOPEN_ROOT_DIR, "$DLOPEN_ROOT_DIR", Directory for dlopen plugins) +AC_SUBST(DLOPEN_ROOT_DIR) + expand_path_option CRM_BUNDLE_DIR "${localstatedir}/log/pacemaker/bundles" AC_DEFINE_UNQUOTED(CRM_BUNDLE_DIR,"$CRM_BUNDLE_DIR", Location for Pacemaker bundle logs) AC_SUBST(CRM_BUNDLE_DIR) diff --git a/daemons/execd/execd_commands.c b/daemons/execd/execd_commands.c index 096e9d53659..1ba85f0233a 100644 --- a/daemons/execd/execd_commands.c +++ b/daemons/execd/execd_commands.c @@ -902,6 +902,20 @@ action_complete(svc_action_t * action) } #endif + if (rsc && pcmk__str_eq(rsc->class, PCMK_RESOURCE_CLASS_DLOPEN, pcmk__str_casei)) { + if (action_matches(cmd, "monitor", 0) + && pcmk__result_ok(&(cmd->result))) { + /* Successfully executed --version for the nagios plugin */ + cmd->result.exit_status = PCMK_OCF_NOT_RUNNING; + + } else if (pcmk__str_eq(cmd->action, "start", pcmk__str_casei) + && !pcmk__result_ok(&(cmd->result))) { +#ifdef PCMK__TIME_USE_CGT + goagain = true; +#endif + } + } + #ifdef PCMK__TIME_USE_CGT if (goagain) { int time_sum = time_diff_ms(NULL, &(cmd->t_first_run)); diff --git a/include/crm/services.h b/include/crm/services.h index dcd28d348f4..4715cb885fe 100644 --- a/include/crm/services.h +++ b/include/crm/services.h @@ -44,6 +44,7 @@ extern "C" { #define PCMK_RESOURCE_CLASS_NAGIOS "nagios" #define PCMK_RESOURCE_CLASS_STONITH "stonith" #define PCMK_RESOURCE_CLASS_ALERT "alert" +#define PCMK_RESOURCE_CLASS_DLOPEN "dlopen" /* This is the string passed in the OCF_EXIT_REASON_PREFIX environment variable. * The stderr output that occurs after this prefix is encountered is considered diff --git a/lib/common/agents.c b/lib/common/agents.c index cde029f579e..14a80fde248 100644 --- a/lib/common/agents.c +++ b/lib/common/agents.c @@ -40,6 +40,9 @@ pcmk_get_ra_caps(const char *standard) return pcmk_ra_cap_provider | pcmk_ra_cap_params | pcmk_ra_cap_unique | pcmk_ra_cap_promotable; + } else if (!strcasecmp(standard, PCMK_RESOURCE_CLASS_DLOPEN)) { + return pcmk_ra_cap_params; + } else if (!strcasecmp(standard, PCMK_RESOURCE_CLASS_STONITH)) { /* @COMPAT Stonith resources can't really be unique clones, but we've * allowed it in the past and have it in some scheduler regression tests diff --git a/lib/pacemaker/pcmk_injections.c b/lib/pacemaker/pcmk_injections.c index 895003435e7..2186b3ad3d0 100644 --- a/lib/pacemaker/pcmk_injections.c +++ b/lib/pacemaker/pcmk_injections.c @@ -377,7 +377,8 @@ pcmk__inject_resource_history(pcmk__output_t *out, xmlNode *cib_node, PCMK_RESOURCE_CLASS_SERVICE, PCMK_RESOURCE_CLASS_UPSTART, PCMK_RESOURCE_CLASS_SYSTEMD, - PCMK_RESOURCE_CLASS_LSB, NULL)) { + PCMK_RESOURCE_CLASS_LSB, + PCMK_RESOURCE_CLASS_DLOPEN, NULL)) { out->err(out, "Invalid class for %s: %s", resource, rclass); return NULL; diff --git a/lib/services/Makefile.am b/lib/services/Makefile.am index 0c65b281a0c..ddcd8971618 100644 --- a/lib/services/Makefile.am +++ b/lib/services/Makefile.am @@ -15,6 +15,7 @@ lib_LTLIBRARIES = libcrmservice.la noinst_HEADERS = pcmk-dbus.h upstart.h systemd.h \ services_lsb.h services_nagios.h \ services_ocf.h \ + services_dlopen.h \ services_private.h libcrmservice_la_LDFLAGS = -version-info 31:0:3 @@ -29,6 +30,7 @@ libcrmservice_la_SOURCES = services.c libcrmservice_la_SOURCES += services_linux.c libcrmservice_la_SOURCES += services_lsb.c libcrmservice_la_SOURCES += services_ocf.c +libcrmservice_la_SOURCES += services_dlopen.c if BUILD_DBUS libcrmservice_la_SOURCES += dbus.c endif diff --git a/lib/services/services.c b/lib/services/services.c index 99d89091438..0f14bb78c1c 100644 --- a/lib/services/services.c +++ b/lib/services/services.c @@ -30,6 +30,7 @@ #include "services_private.h" #include "services_ocf.h" #include "services_lsb.h" +#include "services_dlopen.h" #if SUPPORT_UPSTART # include @@ -306,6 +307,9 @@ services__create_resource_action(const char *name, const char *standard, } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_LSB) == 0) { rc = services__lsb_prepare(op); + } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_DLOPEN) == 0) { + rc = services__dlopen_prepare(op); + #if SUPPORT_SYSTEMD } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) { rc = services__systemd_prepare(op); @@ -574,6 +578,10 @@ services_result2ocf(const char *standard, const char *action, int exit_status) pcmk__str_casei)) { return services__lsb2ocf(action, exit_status); + } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_DLOPEN, + pcmk__str_casei)) { + return services__dlopen2ocf(exit_status); + } else { crm_warn("Treating result from unknown standard '%s' as OCF", ((standard == NULL)? "unspecified" : standard)); @@ -814,6 +822,11 @@ handle_duplicate_recurring(svc_action_t * op) static int execute_action(svc_action_t *op) { + if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_DLOPEN, + pcmk__str_casei)) { + return services__execute_dlopen(op); + } + #if SUPPORT_UPSTART if (pcmk__str_eq(op->standard, PCMK_RESOURCE_CLASS_UPSTART, pcmk__str_casei)) { @@ -1063,6 +1076,7 @@ resources_list_standards(void) GList *standards = NULL; standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_OCF)); + standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_DLOPEN)); standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_LSB)); standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_SERVICE)); @@ -1154,6 +1168,8 @@ resources_list_agents(const char *standard, const char *provider) return resources_os_list_ocf_agents(provider); } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_LSB) == 0) { return services__list_lsb_agents(); + } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_DLOPEN) == 0) { + return services__list_dlopen_agents(); #if SUPPORT_SYSTEMD } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) { return systemd_unit_listall(); @@ -1233,6 +1249,9 @@ resources_agent_exists(const char *standard, const char *provider, const char *a } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_LSB, pcmk__str_casei)) { rc = services__lsb_agent_exists(agent); + } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_DLOPEN, pcmk__str_casei)) { + rc = services__dlopen_agent_exists(agent); + #if SUPPORT_SYSTEMD } else if (pcmk__str_eq(standard, PCMK_RESOURCE_CLASS_SYSTEMD, pcmk__str_casei)) { rc = systemd_unit_exists(agent); diff --git a/lib/services/services_dlopen.c b/lib/services/services_dlopen.c new file mode 100644 index 00000000000..e4fca6beac5 --- /dev/null +++ b/lib/services/services_dlopen.c @@ -0,0 +1,158 @@ +#include +#pragma GCC diagnostic ignored "-Waggregate-return" +#ifndef _GNU_SOURCE +# define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include "services_private.h" +#include "services_dlopen.h" + +typedef long long int go_int; +typedef struct{const char *p; go_int len;} go_str; +typedef struct{void *arr; go_int len; go_int cap;} go_slice; + +GList * +services__list_dlopen_agents(void) +{ + return services_os_get_directory_list(DLOPEN_ROOT_DIR, TRUE, FALSE); +} + +bool +services__dlopen_agent_exists(const char *agent) +{ + bool rc = FALSE; + struct stat st; + char *path = pcmk__full_path(agent, DLOPEN_ROOT_DIR); + + rc = (stat(path, &st) == 0); + free(path); + return rc; +} + +int +services__dlopen_prepare(svc_action_t *op) +{ + op->opaque->exec = pcmk__full_path(op->agent, DLOPEN_ROOT_DIR); + op->opaque->args[0] = strdup(op->opaque->exec); + op->opaque->args[1] = strdup(op->action); + if ((op->opaque->args[0] == NULL) || (op->opaque->args[1] == NULL)) { + return ENOMEM; + } + return pcmk_rc_ok; +} + +// TODO: +enum ocf_exitcode +services__dlopen2ocf(int exit_status) +{ + return (enum ocf_exitcode) exit_status; +} + +int +services__execute_dlopen(svc_action_t *op) { + if (strcasecmp(op->action, "meta-data") == 0) { + return services__execute_dlopen_metadata(op); + } + + return services__execute_dlopen_action(op); +} + +int +services__execute_dlopen_metadata(svc_action_t *op) { + void *lib; + char *lib_error; + go_str (*metadata)(); + go_str msg; + char *dst = pcmk__full_path(op->agent, DLOPEN_ROOT_DIR); + + lib = dlopen(dst, RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE); + + if (!lib) { + return pcmk_rc_error; + } + + metadata = dlsym(lib, "metadata"); + + if ((lib_error = dlerror()) != NULL){ + free(lib_error); + + return pcmk_rc_error; + } + + msg = metadata(); + op->rc = PCMK_OCF_OK; + op->status = PCMK_EXEC_DONE; + op->pid = 0; + op->stdout_data = strndup(msg.p, msg.len); + //op->stdout_data = strdup(metadata().p); + + if (op->opaque->callback) { + op->opaque->callback(op); + } + + dlclose(lib); + return pcmk_rc_ok; +} + +int +services__execute_dlopen_action(svc_action_t *op) { + void *lib; + char *lib_error; + go_str error; + go_int (*exec)(go_slice, go_str *); + go_str data[1] = {{op->rsc, strlen(op->rsc)}}; + go_slice params = {data, 1, 1}; + char *dst = pcmk__full_path(op->agent, DLOPEN_ROOT_DIR); + + // crm_info("Resource %s", op->rsc); + + lib = dlopen(dst, RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE); + + if (!lib) { + return pcmk_rc_error; + } + + exec = dlsym(lib, op->action); + + if ((lib_error = dlerror()) != NULL){ + free(lib_error); + + return pcmk_rc_error; + } + + op->rc = exec(params, &error); + op->status = PCMK_EXEC_DONE; + op->pid = 0; + if (op->rc) { + op->stderr_data = strndup(error.p, error.len); + } + + if (op->interval_ms != 0) { + // Recurring operations must be either cancelled or rescheduled + if (op->cancel) { + services__set_cancelled(op); + cancel_recurring_action(op); + } else { + op->opaque->repeat_timer = g_timeout_add(op->interval_ms, + recurring_action_timer, + (void *) op); + } + } + + if (op->opaque->callback) { + op->opaque->callback(op); + } + + // crm_info("Exit code: %d, error: %s", op->rc, error->p); + + dlclose(lib); + return pcmk_rc_ok; +} diff --git a/lib/services/services_dlopen.h b/lib/services/services_dlopen.h new file mode 100644 index 00000000000..3784db159b5 --- /dev/null +++ b/lib/services/services_dlopen.h @@ -0,0 +1,23 @@ +#ifndef SERVICES_DLOPEN__H +# define SERVICES_DLOPEN__H + +#include + +// G_GNUC_INTERNAL int services__get_dlopen_metadata(const char *type, char **output); +G_GNUC_INTERNAL GList *services__list_dlopen_agents(void); +G_GNUC_INTERNAL bool services__dlopen_agent_exists(const char *agent); +G_GNUC_INTERNAL int services__dlopen_prepare(svc_action_t *op); + +G_GNUC_INTERNAL +enum ocf_exitcode services__dlopen2ocf(int exit_status); + +G_GNUC_INTERNAL +int services__execute_dlopen(svc_action_t *op); + +G_GNUC_INTERNAL +int services__execute_dlopen_metadata(svc_action_t *op); + +G_GNUC_INTERNAL +int services__execute_dlopen_action(svc_action_t *op); + +#endif \ No newline at end of file diff --git a/xml/resources-3.7.rng b/xml/resources-3.7.rng index 9b58d346302..1a027a00895 100644 --- a/xml/resources-3.7.rng +++ b/xml/resources-3.7.rng @@ -421,6 +421,7 @@ service systemd nagios + dlopen