Blob Blame History Raw
From 94a4c58f675d163085a055f59fd6c3a2c9f57c43 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Mon, 27 Feb 2017 14:17:01 -0600
Subject: [PATCH 01/10] Fix: libservices: ensure completed ops aren't on
 blocked ops list

This refactors removal of a completed operation from the in-flight ops list
into a new internal function services_untrack_op(), which allows the
in-flight ops list and handle_blocked_ops() to be static to services.c.

The new function also ensures the op isn't on the blocked ops list,
which could otherwise result in a use-after-free bug.
---
 lib/services/services.c         | 23 +++++++++++++++++++++--
 lib/services/services_linux.c   |  7 +------
 lib/services/services_private.h |  2 +-
 3 files changed, 23 insertions(+), 9 deletions(-)

diff --git a/lib/services/services.c b/lib/services/services.c
index 0b535e6..90a2181 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -44,7 +44,9 @@ static GHashTable *recurring_actions = NULL;
 static GList *blocked_ops = NULL;
 
 /* ops currently active (in-flight) */
-GList *inflight_ops = NULL;
+static GList *inflight_ops = NULL;
+
+static void handle_blocked_ops(void);
 
 svc_action_t *
 services_action_create(const char *name, const char *action, int interval, int timeout)
@@ -605,6 +607,23 @@ services_add_inflight_op(svc_action_t * op)
     }
 }
 
+/*!
+ * \internal
+ * \brief Stop tracking an operation that completed
+ *
+ * \param[in] op  Operation to stop tracking
+ */
+void
+services_untrack_op(svc_action_t *op)
+{
+    /* Op is no longer in-flight or blocked */
+    inflight_ops = g_list_remove(inflight_ops, op);
+    blocked_ops = g_list_remove(blocked_ops, op);
+
+    /* Op is no longer blocking other ops, so check if any need to run */
+    handle_blocked_ops();
+}
+
 gboolean
 services_action_async(svc_action_t * op, void (*action_callback) (svc_action_t *))
 {
@@ -649,7 +668,7 @@ is_op_blocked(const char *rsc)
     return FALSE;
 }
 
-void
+static void
 handle_blocked_ops(void)
 {
     GList *executed_ops = NULL;
diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c
index adfefaa..1fe3ec1 100644
--- a/lib/services/services_linux.c
+++ b/lib/services/services_linux.c
@@ -36,9 +36,6 @@
 #  include "crm/common/cib_secrets.h"
 #endif
 
-/* ops currently active (in-flight) */
-extern GList *inflight_ops;
-
 static inline void
 set_fd_opts(int fd, int opts)
 {
@@ -248,9 +245,7 @@ operation_finalize(svc_action_t * op)
 
     op->pid = 0;
 
-    inflight_ops = g_list_remove(inflight_ops, op);
-
-    handle_blocked_ops();
+    services_untrack_op(op);
 
     if (!recurring && op->synchronous == FALSE) {
         /*
diff --git a/lib/services/services_private.h b/lib/services/services_private.h
index ec9e1c4..a949592 100644
--- a/lib/services/services_private.h
+++ b/lib/services/services_private.h
@@ -76,7 +76,7 @@ G_GNUC_INTERNAL
 void services_add_inflight_op(svc_action_t *op);
 
 G_GNUC_INTERNAL
-void handle_blocked_ops(void);
+void services_untrack_op(svc_action_t *op);
 
 G_GNUC_INTERNAL
 gboolean is_op_blocked(const char *rsc);
-- 
1.8.3.1


From b938c47740a456d0057438e4f5e6029d0e084587 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Mon, 27 Feb 2017 14:20:05 -0600
Subject: [PATCH 02/10] Refactor: libservices: dynamically allocate operation
 key

reduces code duplication, and avoids memory over-allocation
---
 lib/services/services.c | 18 ++++++------------
 1 file changed, 6 insertions(+), 12 deletions(-)

diff --git a/lib/services/services.c b/lib/services/services.c
index 90a2181..4020b7d 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -149,10 +149,7 @@ resources_action_create(const char *name, const char *standard, const char *prov
     op->agent = strdup(agent);
     op->sequence = ++operations;
     op->flags = flags;
-
-    if (asprintf(&op->id, "%s_%s_%d", name, action, interval) == -1) {
-        goto return_error;
-    }
+    op->id = generate_op_key(name, action, interval);
 
     if (strcasecmp(op->standard, "service") == 0) {
         const char *expanded = resources_find_service_class(op->agent);
@@ -467,11 +464,10 @@ gboolean
 services_action_cancel(const char *name, const char *action, int interval)
 {
     svc_action_t *op = NULL;
-    char id[512];
-
-    snprintf(id, sizeof(id), "%s_%s_%d", name, action, interval);
+    char *id = generate_op_key(name, action, interval);
 
     if (!(op = g_hash_table_lookup(recurring_actions, id))) {
+        free(id);
         return FALSE;
     }
 
@@ -494,10 +490,12 @@ services_action_cancel(const char *name, const char *action, int interval)
             /* even though the early termination failed,
              * the op will be marked as cancelled once it completes. */
             crm_err("Termination of %s (pid=%d) failed", id, op->pid);
+            free(id);
             return FALSE;
         }
     }
 
+    free(id);
     return TRUE;
 }
 
@@ -505,11 +503,7 @@ gboolean
 services_action_kick(const char *name, const char *action, int interval /* ms */)
 {
     svc_action_t * op = NULL;
-    char *id = NULL;
-
-    if (asprintf(&id, "%s_%s_%d", name, action, interval) == -1) {
-        return FALSE;
-    }
+    char *id = generate_op_key(name, action, interval);
 
     op = g_hash_table_lookup(recurring_actions, id);
     free(id);
-- 
1.8.3.1


From 933d46ef20591757301784773a37e06b78906584 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Mon, 27 Feb 2017 14:28:36 -0600
Subject: [PATCH 03/10] Fix: libservices: prevent use-after-free when freeing
 an operation

---
 lib/services/services.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/lib/services/services.c b/lib/services/services.c
index 4020b7d..78a0781 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -410,6 +410,16 @@ services_action_free(svc_action_t * op)
         return;
     }
 
+    /* The operation should be removed from all tracking lists by this point.
+     * If it's not, we have a bug somewhere, so bail. That may lead to a
+     * memory leak, but it's better than a use-after-free segmentation fault.
+     */
+    CRM_CHECK(g_list_find(inflight_ops, op) == NULL, return);
+    CRM_CHECK(g_list_find(blocked_ops, op) == NULL, return);
+    CRM_CHECK((recurring_actions == NULL)
+              || (g_hash_table_lookup(recurring_actions, op->id) == NULL),
+              return);
+
     services_action_cleanup(op);
 
     if (op->opaque->repeat_timer) {
-- 
1.8.3.1


From dc36d4375c049024a6f9e4d2277a3e6444fad05b Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 28 Feb 2017 15:41:10 -0600
Subject: [PATCH 04/10] Refactor: libservices: handle in-flight case first when
 cancelling an operation

will make next bugfix easier
---
 lib/services/services.c | 65 ++++++++++++++++++++++++++++++++-----------------
 1 file changed, 42 insertions(+), 23 deletions(-)

diff --git a/lib/services/services.c b/lib/services/services.c
index 78a0781..dba4e66 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -470,43 +470,62 @@ cancel_recurring_action(svc_action_t * op)
     return TRUE;
 }
 
+/*!
+ * \brief Cancel a recurring action
+ *
+ * \param[in] name      Name of resource that operation is for
+ * \param[in] action    Name of operation to cancel
+ * \param[in] interval  Interval of operation to cancel
+ *
+ * \return TRUE if action was successfully cancelled, FALSE otherwise
+ */
 gboolean
 services_action_cancel(const char *name, const char *action, int interval)
 {
-    svc_action_t *op = NULL;
+    gboolean cancelled = FALSE;
     char *id = generate_op_key(name, action, interval);
+    svc_action_t *op = g_hash_table_lookup(recurring_actions, id);
 
-    if (!(op = g_hash_table_lookup(recurring_actions, id))) {
-        free(id);
-        return FALSE;
+    /* We can only cancel a recurring action */
+    if (op == NULL) {
+        goto done;
     }
 
-    /* Always kill the recurring timer */
+    /* Tell operation_finalize() not to reschedule the operation */
+    op->cancel = TRUE;
+
+    /* Stop tracking it as a recurring operation, and stop its timer */
     cancel_recurring_action(op);
 
-    if (op->pid == 0) {
-        op->status = PCMK_LRM_OP_CANCELLED;
-        if (op->opaque->callback) {
-            op->opaque->callback(op);
+    /* If the op has a PID, it's an in-flight child process, so kill it.
+     *
+     * Whether the kill succeeds or fails, the main loop will send the op to
+     * operation_finished() (and thus operation_finalize()) when the process
+     * goes away.
+     */
+    if (op->pid != 0) {
+        crm_info("Terminating in-flight op %s (pid %d) early because it was cancelled",
+                 id, op->pid);
+        cancelled = mainloop_child_kill(op->pid);
+        if (cancelled == FALSE) {
+            crm_err("Termination of %s (pid %d) failed", id, op->pid);
         }
+        goto done;
+    }
 
-        blocked_ops = g_list_remove(blocked_ops, op);
-        services_action_free(op);
-
-    } else {
-        crm_info("Cancelling in-flight op: performing early termination of %s (pid=%d)", id, op->pid);
-        op->cancel = 1;
-        if (mainloop_child_kill(op->pid) == FALSE) {
-            /* even though the early termination failed,
-             * the op will be marked as cancelled once it completes. */
-            crm_err("Termination of %s (pid=%d) failed", id, op->pid);
-            free(id);
-            return FALSE;
-        }
+    /* Otherwise, operation is not in-flight, just report as cancelled */
+    op->status = PCMK_LRM_OP_CANCELLED;
+    if (op->opaque->callback) {
+        op->opaque->callback(op);
     }
 
+    blocked_ops = g_list_remove(blocked_ops, op);
+    services_action_free(op);
+    cancelled = TRUE;
+
+done:
     free(id);
-    return TRUE;
+    return cancelled;
 }
 
 gboolean
-- 
1.8.3.1


From deabcc5a6aa93dadf0b20364715b559a5b9848ac Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 28 Feb 2017 15:48:11 -0600
Subject: [PATCH 05/10] Fix: libservices: properly cancel in-flight
 systemd/upstart op

---
 lib/services/services.c | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/lib/services/services.c b/lib/services/services.c
index dba4e66..4aa4c01 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -89,6 +89,21 @@ resources_find_service_class(const char *agent)
     return NULL;
 }
 
+/*!
+ * \internal
+ * \brief Check whether op is in-flight systemd or upstart op
+ *
+ * \param[in] op  Operation to check
+ *
+ * \return TRUE if op is in-flight systemd or upstart op
+ */
+static inline gboolean
+inflight_systemd_or_upstart(svc_action_t *op)
+{
+    return (safe_str_eq(op->standard, "systemd")
+            || safe_str_eq(op->standard, "upstart"))
+            && (g_list_find(inflight_ops, op) != NULL);
+}
 
 svc_action_t *
 resources_action_create(const char *name, const char *standard, const char *provider,
@@ -513,6 +528,18 @@ services_action_cancel(const char *name, const char *action, int interval)
         goto done;
     }
 
+    /* In-flight systemd and upstart ops don't have a pid. The relevant handlers
+     * will call operation_finalize() when the operation completes.
+     * @TODO: Can we request early termination, maybe using
+     * dbus_pending_call_cancel()?
+     */
+    if (inflight_systemd_or_upstart(op)) {
+        crm_info("Will cancel %s op %s when in-flight instance completes",
+                 op->standard, op->id);
+        cancelled = FALSE;
+        goto done;
+    }
+
     /* Otherwise, operation is not in-flight, just report as cancelled */
     op->status = PCMK_LRM_OP_CANCELLED;
     if (op->opaque->callback) {
-- 
1.8.3.1


From b85037b75255061a41d0ec3fd9b64f271351b43e Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 28 Feb 2017 16:43:52 -0600
Subject: [PATCH 06/10] Fix: libservices: properly detect in-flight
 systemd/upstart ops when kicking

---
 lib/services/services.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/services/services.c b/lib/services/services.c
index 4aa4c01..b41517d 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -568,7 +568,8 @@ services_action_kick(const char *name, const char *action, int interval /* ms */
         return FALSE;
     }
 
-    if (op->pid) {
+
+    if (op->pid || inflight_systemd_or_upstart(op)) {
         return TRUE;
     } else {
         if (op->opaque->repeat_timer) {
-- 
1.8.3.1


From 55be77356d9253c46a1ddebefd6f403366209281 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 28 Feb 2017 18:37:23 -0600
Subject: [PATCH 07/10] Log: libservices: improve error messages when creating
 operation

---
 lib/services/services.c | 21 +++++++++++----------
 1 file changed, 11 insertions(+), 10 deletions(-)

diff --git a/lib/services/services.c b/lib/services/services.c
index b41517d..e089717 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -118,27 +118,27 @@ resources_action_create(const char *name, const char *standard, const char *prov
      */
 
     if (crm_strlen_zero(name)) {
-        crm_err("A service or resource action must have a name.");
+        crm_err("Cannot create operation without resource name");
         goto return_error;
     }
 
     if (crm_strlen_zero(standard)) {
-        crm_err("A service action must have a valid standard.");
+        crm_err("Cannot create operation for %s without resource class", name);
         goto return_error;
     }
 
     if (!strcasecmp(standard, "ocf") && crm_strlen_zero(provider)) {
-        crm_err("An OCF resource action must have a provider.");
+        crm_err("Cannot create OCF operation for %s without provider", name);
         goto return_error;
     }
 
     if (crm_strlen_zero(agent)) {
-        crm_err("A service or resource action must have an agent.");
+        crm_err("Cannot create operation for %s without agent name", name);
         goto return_error;
     }
 
     if (crm_strlen_zero(action)) {
-        crm_err("A service or resource action must specify an action.");
+        crm_err("Cannot create operation for %s without operation name", name);
         goto return_error;
     }
 
@@ -170,12 +170,13 @@ resources_action_create(const char *name, const char *standard, const char *prov
         const char *expanded = resources_find_service_class(op->agent);
 
         if(expanded) {
-            crm_debug("Found a %s agent for %s/%s", expanded, op->rsc, op->agent);
+            crm_debug("Found %s agent %s for %s", expanded, op->agent, op->rsc);
             free(op->standard);
             op->standard = strdup(expanded);
 
         } else {
-            crm_info("Cannot determine the standard for %s (%s)", op->rsc, op->agent);
+            crm_info("Assuming resource class lsb for agent %s for %s",
+                     op->agent, op->rsc);
             free(op->standard);
             op->standard = strdup("lsb");
         }
@@ -226,7 +227,7 @@ resources_action_create(const char *name, const char *standard, const char *prov
         /* The "heartbeat" agent class only has positional arguments,
          * which we keyed by their decimal position number. */
         param_num = 1;
-	for (index = 1; index <= MAX_ARGC - 3; index++ ) {
+        for (index = 1; index <= MAX_ARGC - 3; index++ ) {
             snprintf(buf_tmp, sizeof(buf_tmp), "%d", index);
             value_tmp = g_hash_table_lookup(params, buf_tmp);
             if (value_tmp == NULL) {
@@ -237,8 +238,8 @@ resources_action_create(const char *name, const char *standard, const char *prov
             op->opaque->args[param_num++] = strdup(value_tmp);
         }
 
-	/* Add operation code as the last argument, */
-	/* and the teminating NULL pointer */
+        /* Add operation code as the last argument, */
+        /* and the teminating NULL pointer */
         op->opaque->args[param_num++] = strdup(op->action);
         op->opaque->args[param_num] = NULL;
 #endif
-- 
1.8.3.1


From 121a3f219ff5a9ea07e271cb03d9c55af2d2ad1f Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 28 Feb 2017 18:55:11 -0600
Subject: [PATCH 08/10] Refactor: libservices: functionize expanding resource
 class, and remove dead code

---
 include/crm/services.h  |  1 +
 lib/services/services.c | 75 ++++++++++++++++++++++++++++---------------------
 2 files changed, 44 insertions(+), 32 deletions(-)

diff --git a/include/crm/services.h b/include/crm/services.h
index 05c8ebf..28bb76a 100644
--- a/include/crm/services.h
+++ b/include/crm/services.h
@@ -47,6 +47,7 @@ extern "C" {
 #    define SYSTEMCTL "/bin/systemctl"
 #  endif
 
+/* Deprecated and unused by Pacemaker, kept for API backward compatibility */
 #  ifndef SERVICE_SCRIPT
 #    define SERVICE_SCRIPT "/sbin/service"
 #  endif
diff --git a/lib/services/services.c b/lib/services/services.c
index e089717..3ffaeb6 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -105,6 +105,41 @@ inflight_systemd_or_upstart(svc_action_t *op)
             && (g_list_find(inflight_ops, op) != NULL);
 }
 
+/*!
+ * \internal
+ * \brief Expand "service" alias to an actual resource class
+ *
+ * \param[in] rsc       Resource name (for logging only)
+ * \param[in] standard  Resource class as configured
+ * \param[in] agent     Agent name to look for
+ *
+ * \return Newly allocated string with actual resource class
+ *
+ * \note The caller is responsible for calling free() on the result.
+ */
+static char *
+expand_resource_class(const char *rsc, const char *standard, const char *agent)
+{
+    char *expanded_class = NULL;
+
+    if (strcasecmp(standard, "service") == 0) {
+        const char *found_class = resources_find_service_class(agent);
+
+        if (found_class) {
+            crm_debug("Found %s agent %s for %s", found_class, agent, rsc);
+            expanded_class = strdup(found_class);
+        } else {
+            crm_info("Assuming resource class lsb for agent %s for %s",
+                     agent, rsc);
+            expanded_class = strdup("lsb");
+        }
+    } else {
+        expanded_class = strdup(standard);
+    }
+    CRM_ASSERT(expanded_class);
+    return expanded_class;
+}
+
 svc_action_t *
 resources_action_create(const char *name, const char *standard, const char *provider,
                         const char *agent, const char *action, int interval, int timeout,
@@ -142,14 +177,6 @@ resources_action_create(const char *name, const char *standard, const char *prov
         goto return_error;
     }
 
-    if (safe_str_eq(action, "monitor") && (
-#if SUPPORT_HEARTBEAT
-        safe_str_eq(standard, "heartbeat") ||
-#endif
-        safe_str_eq(standard, "lsb") || safe_str_eq(standard, "service"))) {
-        action = "status";
-    }
-
     /*
      * Sanity checks passed, proceed!
      */
@@ -157,31 +184,22 @@ resources_action_create(const char *name, const char *standard, const char *prov
     op = calloc(1, sizeof(svc_action_t));
     op->opaque = calloc(1, sizeof(svc_action_private_t));
     op->rsc = strdup(name);
-    op->action = strdup(action);
     op->interval = interval;
     op->timeout = timeout;
-    op->standard = strdup(standard);
+    op->standard = expand_resource_class(name, standard, agent);
     op->agent = strdup(agent);
     op->sequence = ++operations;
     op->flags = flags;
     op->id = generate_op_key(name, action, interval);
 
-    if (strcasecmp(op->standard, "service") == 0) {
-        const char *expanded = resources_find_service_class(op->agent);
-
-        if(expanded) {
-            crm_debug("Found %s agent %s for %s", expanded, op->agent, op->rsc);
-            free(op->standard);
-            op->standard = strdup(expanded);
-
-        } else {
-            crm_info("Assuming resource class lsb for agent %s for %s",
-                     op->agent, op->rsc);
-            free(op->standard);
-            op->standard = strdup("lsb");
-        }
-        CRM_ASSERT(op->standard);
+    if (safe_str_eq(action, "monitor") && (
+#if SUPPORT_HEARTBEAT
+        safe_str_eq(op->standard, "heartbeat") ||
+#endif
+        safe_str_eq(op->standard, "lsb"))) {
+        action = "status";
     }
+    op->action = strdup(action);
 
     if (strcasecmp(op->standard, "ocf") == 0) {
         op->provider = strdup(provider);
@@ -251,12 +269,6 @@ resources_action_create(const char *name, const char *standard, const char *prov
     } else if (strcasecmp(op->standard, "upstart") == 0) {
         op->opaque->exec = strdup("upstart-dbus");
 #endif
-    } else if (strcasecmp(op->standard, "service") == 0) {
-        op->opaque->exec = strdup(SERVICE_SCRIPT);
-        op->opaque->args[0] = strdup(SERVICE_SCRIPT);
-        op->opaque->args[1] = strdup(agent);
-        op->opaque->args[2] = strdup(action);
-
 #if SUPPORT_NAGIOS
     } else if (strcasecmp(op->standard, "nagios") == 0) {
         int index = 0;
@@ -308,7 +320,6 @@ resources_action_create(const char *name, const char *standard, const char *prov
         }
         op->opaque->args[index] = NULL;
 #endif
-
     } else {
         crm_err("Unknown resource standard: %s", op->standard);
         services_action_free(op);
-- 
1.8.3.1


From 46c07e23d48806140cf12393b7867063630cdf30 Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Tue, 28 Feb 2017 19:30:16 -0600
Subject: [PATCH 09/10] Refactor: various: use defined constants for resource
 classes

ensures a typo won't compile
---
 crmd/remote_lrmd_ra.c         |  2 +-
 fencing/main.c                |  6 ++--
 include/crm/services.h        |  9 +++++
 lib/lrmd/lrmd_client.c        | 23 +++++++------
 lib/pengine/complex.c         |  2 +-
 lib/pengine/native.c          |  8 ++---
 lib/pengine/remote.c          |  3 +-
 lib/pengine/unpack.c          |  2 +-
 lib/services/services.c       | 79 ++++++++++++++++++++++++-------------------
 lib/services/services_linux.c |  9 +++--
 lrmd/lrmd.c                   | 44 +++++++++++++-----------
 pengine/native.c              |  2 +-
 tools/crm_resource_print.c    |  2 +-
 tools/crm_resource_runtime.c  |  2 +-
 tools/fake_transition.c       | 17 +++++-----
 15 files changed, 119 insertions(+), 91 deletions(-)

diff --git a/crmd/remote_lrmd_ra.c b/crmd/remote_lrmd_ra.c
index eb995ea..e68d784 100644
--- a/crmd/remote_lrmd_ra.c
+++ b/crmd/remote_lrmd_ra.c
@@ -841,7 +841,7 @@ remote_ra_get_rsc_info(lrm_state_t * lrm_state, const char *rsc_id)
 
         info->id = strdup(rsc_id);
         info->type = strdup(REMOTE_LRMD_RA);
-        info->class = strdup("ocf");
+        info->class = strdup(PCMK_RESOURCE_CLASS_OCF);
         info->provider = strdup("pacemaker");
     }
 
diff --git a/fencing/main.c b/fencing/main.c
index 95a1408..e6eb087 100644
--- a/fencing/main.c
+++ b/fencing/main.c
@@ -455,7 +455,7 @@ remove_cib_device(xmlXPathObjectPtr xpathObj)
             standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
         }
 
-        if (safe_str_neq(standard, "stonith")) {
+        if (safe_str_neq(standard, PCMK_RESOURCE_CLASS_STONITH)) {
             continue;
         }
 
@@ -630,7 +630,7 @@ static void cib_device_update(resource_t *rsc, pe_working_set_t *data_set)
 
     /* We only care about STONITH resources. */
     rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
-    if(safe_str_neq(rclass, "stonith")) {
+    if (safe_str_neq(rclass, PCMK_RESOURCE_CLASS_STONITH)) {
         return;
     }
 
@@ -848,7 +848,7 @@ update_cib_stonith_devices_v1(const char *event, xmlNode * msg)
             rsc_id = crm_element_value(match, XML_ATTR_ID);
             standard = crm_element_value(match, XML_AGENT_ATTR_CLASS);
 
-            if (safe_str_neq(standard, "stonith")) {
+            if (safe_str_neq(standard, PCMK_RESOURCE_CLASS_STONITH)) {
                 continue;
             }
 
diff --git a/include/crm/services.h b/include/crm/services.h
index 28bb76a..e1fae9b 100644
--- a/include/crm/services.h
+++ b/include/crm/services.h
@@ -52,6 +52,15 @@ extern "C" {
 #    define SERVICE_SCRIPT "/sbin/service"
 #  endif
 
+/* Known resource classes */
+#define PCMK_RESOURCE_CLASS_OCF     "ocf"
+#define PCMK_RESOURCE_CLASS_SERVICE "service"
+#define PCMK_RESOURCE_CLASS_LSB     "lsb"
+#define PCMK_RESOURCE_CLASS_SYSTEMD "systemd"
+#define PCMK_RESOURCE_CLASS_UPSTART "upstart"
+#define PCMK_RESOURCE_CLASS_HB      "heartbeat"
+#define PCMK_RESOURCE_CLASS_NAGIOS  "nagios"
+#define PCMK_RESOURCE_CLASS_STONITH "stonith"
 
 /* This is the string passed in the OCF_EXIT_REASON_PREFIX
  * environment variable. The stderr output that occurs
diff --git a/lib/lrmd/lrmd_client.c b/lib/lrmd/lrmd_client.c
index e009078..64b66b1 100644
--- a/lib/lrmd/lrmd_client.c
+++ b/lib/lrmd/lrmd_client.c
@@ -1438,7 +1438,7 @@ lrmd_api_register_rsc(lrmd_t * lrmd,
     if (!class || !type || !rsc_id) {
         return -EINVAL;
     }
-    if (safe_str_eq(class, "ocf") && !provider) {
+    if (safe_str_eq(class, PCMK_RESOURCE_CLASS_OCF) && !provider) {
         return -EINVAL;
     }
 
@@ -1525,7 +1525,7 @@ lrmd_api_get_rsc_info(lrmd_t * lrmd, const char *rsc_id, enum lrmd_call_options
     if (!class || !type) {
         free_xml(output);
         return NULL;
-    } else if (safe_str_eq(class, "ocf") && !provider) {
+    } else if (safe_str_eq(class, PCMK_RESOURCE_CLASS_OCF) && !provider) {
         free_xml(output);
         return NULL;
     }
@@ -1969,20 +1969,20 @@ lrmd_api_get_metadata(lrmd_t * lrmd,
         return -EINVAL;
     }
 
-    if (safe_str_eq(class, "service")) {
+    if (safe_str_eq(class, PCMK_RESOURCE_CLASS_SERVICE)) {
         class = resources_find_service_class(type);
     }
 
-    if (safe_str_eq(class, "stonith")) {
+    if (safe_str_eq(class, PCMK_RESOURCE_CLASS_STONITH)) {
         return stonith_get_metadata(provider, type, output);
-    } else if (safe_str_eq(class, "lsb")) {
+    } else if (safe_str_eq(class, PCMK_RESOURCE_CLASS_LSB)) {
         return lsb_get_metadata(type, output);
 #if SUPPORT_NAGIOS
-    } else if (safe_str_eq(class, "nagios")) {
+    } else if (safe_str_eq(class, PCMK_RESOURCE_CLASS_NAGIOS)) {
         return nagios_get_metadata(type, output);
 #endif
 #if SUPPORT_HEARTBEAT
-    } else if (safe_str_eq(class, "heartbeat")) {
+    } else if (safe_str_eq(class, PCMK_RESOURCE_CLASS_HB)) {
 	return heartbeat_get_metadata(type, output);
 #endif
     }
@@ -2073,7 +2073,7 @@ lrmd_api_list_agents(lrmd_t * lrmd, lrmd_list_t ** resources, const char *class,
 {
     int rc = 0;
 
-    if (safe_str_eq(class, "stonith")) {
+    if (safe_str_eq(class, PCMK_RESOURCE_CLASS_STONITH)) {
         rc += list_stonith_agents(resources);
 
     } else {
@@ -2124,11 +2124,12 @@ lrmd_api_list_ocf_providers(lrmd_t * lrmd, const char *agent, lrmd_list_t ** pro
     GList *ocf_providers = NULL;
     GListPtr gIter = NULL;
 
-    ocf_providers = resources_list_providers("ocf");
+    ocf_providers = resources_list_providers(PCMK_RESOURCE_CLASS_OCF);
 
     for (gIter = ocf_providers; gIter != NULL; gIter = gIter->next) {
         provider = gIter->data;
-        if (!agent || does_provider_have_agent(agent, provider, "ocf")) {
+        if (!agent || does_provider_have_agent(agent, provider,
+                                               PCMK_RESOURCE_CLASS_OCF)) {
             *providers = lrmd_list_add(*providers, (const char *)gIter->data);
             rc++;
         }
@@ -2153,7 +2154,7 @@ lrmd_api_list_standards(lrmd_t * lrmd, lrmd_list_t ** supported)
     }
 
     if (list_stonith_agents(NULL) > 0) {
-        *supported = lrmd_list_add(*supported, "stonith");
+        *supported = lrmd_list_add(*supported, PCMK_RESOURCE_CLASS_STONITH);
         rc++;
     }
 
diff --git a/lib/pengine/complex.c b/lib/pengine/complex.c
index 7fe622f..a83929d 100644
--- a/lib/pengine/complex.c
+++ b/lib/pengine/complex.c
@@ -651,7 +651,7 @@ common_unpack(xmlNode * xml_obj, resource_t ** rsc,
         }
     }
 
-    if (safe_str_eq(rclass, "stonith")) {
+    if (safe_str_eq(rclass, PCMK_RESOURCE_CLASS_STONITH)) {
         set_bit(data_set->flags, pe_flag_have_stonith_resource);
         set_bit((*rsc)->flags, pe_rsc_fence_device);
     }
diff --git a/lib/pengine/native.c b/lib/pengine/native.c
index 11febf4..5f3cfc3 100644
--- a/lib/pengine/native.c
+++ b/lib/pengine/native.c
@@ -136,14 +136,14 @@ native_unpack(resource_t * rsc, pe_working_set_t * data_set)
 
     if (is_set(rsc->flags, pe_rsc_unique) && rsc->parent) {
 
-        if (safe_str_eq(class, "lsb")) {
+        if (safe_str_eq(class, PCMK_RESOURCE_CLASS_LSB)) {
             resource_t *top = uber_parent(rsc);
 
             force_non_unique_clone(top, rsc->id, data_set);
         }
     }
 
-    if (safe_str_eq(class, "ocf") == FALSE) {
+    if (safe_str_eq(class, PCMK_RESOURCE_CLASS_OCF) == FALSE) {
         const char *stateful = g_hash_table_lookup(parent->meta, "stateful");
 
         if (safe_str_eq(stateful, XML_BOOLEAN_TRUE)) {
@@ -533,7 +533,7 @@ native_print(resource_t * rsc, const char *pre_text, long options, void *print_d
     }
     offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", rsc_printable_id(rsc));
     offset += snprintf(buffer + offset, LINE_MAX - offset, "\t(%s", class);
-    if (safe_str_eq(class, "ocf")) {
+    if (safe_str_eq(class, PCMK_RESOURCE_CLASS_OCF)) {
         const char *prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
         offset += snprintf(buffer + offset, LINE_MAX - offset, "::%s", prov);
     }
@@ -799,7 +799,7 @@ get_rscs_brief(GListPtr rsc_list, GHashTable * rsc_table, GHashTable * active_ta
         }
 
         offset += snprintf(buffer + offset, LINE_MAX - offset, "%s", class);
-        if (safe_str_eq(class, "ocf")) {
+        if (safe_str_eq(class, PCMK_RESOURCE_CLASS_OCF)) {
             const char *prov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
             offset += snprintf(buffer + offset, LINE_MAX - offset, "::%s", prov);
         }
diff --git a/lib/pengine/remote.c b/lib/pengine/remote.c
index e8d9bab..c5a3eec 100644
--- a/lib/pengine/remote.c
+++ b/lib/pengine/remote.c
@@ -94,7 +94,8 @@ xml_contains_remote_node(xmlNode *xml)
     const char *provider = crm_element_value(xml, XML_AGENT_ATTR_PROVIDER);
     const char *agent = crm_element_value(xml, XML_ATTR_TYPE);
 
-    if (safe_str_eq(agent, "remote") && safe_str_eq(provider, "pacemaker") && safe_str_eq(class, "ocf")) {
+    if (safe_str_eq(agent, "remote") && safe_str_eq(provider, "pacemaker")
+        && safe_str_eq(class, PCMK_RESOURCE_CLASS_OCF)) {
         return TRUE;
     }
     return FALSE;
diff --git a/lib/pengine/unpack.c b/lib/pengine/unpack.c
index e0a3452..e6a8f58 100644
--- a/lib/pengine/unpack.c
+++ b/lib/pengine/unpack.c
@@ -422,7 +422,7 @@ expand_remote_rsc_meta(xmlNode *xml_obj, xmlNode *parent, GHashTable **rsc_name_
     xml_rsc = create_xml_node(parent, XML_CIB_TAG_RESOURCE);
 
     crm_xml_add(xml_rsc, XML_ATTR_ID, remote_name);
-    crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, "ocf");
+    crm_xml_add(xml_rsc, XML_AGENT_ATTR_CLASS, PCMK_RESOURCE_CLASS_OCF);
     crm_xml_add(xml_rsc, XML_AGENT_ATTR_PROVIDER, "pacemaker");
     crm_xml_add(xml_rsc, XML_ATTR_TYPE, "remote");
 
diff --git a/lib/services/services.c b/lib/services/services.c
index 3ffaeb6..52d3b55 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -51,7 +51,8 @@ static void handle_blocked_ops(void);
 svc_action_t *
 services_action_create(const char *name, const char *action, int interval, int timeout)
 {
-    return resources_action_create(name, "lsb", NULL, name, action, interval, timeout, NULL, 0);
+    return resources_action_create(name, PCMK_RESOURCE_CLASS_LSB, NULL, name,
+                                   action, interval, timeout, NULL, 0);
 }
 
 const char *
@@ -70,20 +71,20 @@ resources_find_service_class(const char *agent)
     rc = asprintf(&path, "%s/%s", LSB_ROOT_DIR, agent);
     if (rc > 0 && stat(path, &st) == 0) {
         free(path);
-        return "lsb";
+        return PCMK_RESOURCE_CLASS_LSB;
     }
     free(path);
 #endif
 
 #if SUPPORT_SYSTEMD
     if (systemd_unit_exists(agent)) {
-        return "systemd";
+        return PCMK_RESOURCE_CLASS_SYSTEMD;
     }
 #endif
 
 #if SUPPORT_UPSTART
     if (upstart_job_exists(agent)) {
-        return "upstart";
+        return PCMK_RESOURCE_CLASS_UPSTART;
     }
 #endif
     return NULL;
@@ -100,8 +101,8 @@ resources_find_service_class(const char *agent)
 static inline gboolean
 inflight_systemd_or_upstart(svc_action_t *op)
 {
-    return (safe_str_eq(op->standard, "systemd")
-            || safe_str_eq(op->standard, "upstart"))
+    return (safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD)
+            || safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_UPSTART))
             && (g_list_find(inflight_ops, op) != NULL);
 }
 
@@ -122,7 +123,7 @@ expand_resource_class(const char *rsc, const char *standard, const char *agent)
 {
     char *expanded_class = NULL;
 
-    if (strcasecmp(standard, "service") == 0) {
+    if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0) {
         const char *found_class = resources_find_service_class(agent);
 
         if (found_class) {
@@ -131,7 +132,7 @@ expand_resource_class(const char *rsc, const char *standard, const char *agent)
         } else {
             crm_info("Assuming resource class lsb for agent %s for %s",
                      agent, rsc);
-            expanded_class = strdup("lsb");
+            expanded_class = strdup(PCMK_RESOURCE_CLASS_LSB);
         }
     } else {
         expanded_class = strdup(standard);
@@ -162,7 +163,8 @@ resources_action_create(const char *name, const char *standard, const char *prov
         goto return_error;
     }
 
-    if (!strcasecmp(standard, "ocf") && crm_strlen_zero(provider)) {
+    if (!strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF)
+        && crm_strlen_zero(provider)) {
         crm_err("Cannot create OCF operation for %s without provider", name);
         goto return_error;
     }
@@ -194,14 +196,14 @@ resources_action_create(const char *name, const char *standard, const char *prov
 
     if (safe_str_eq(action, "monitor") && (
 #if SUPPORT_HEARTBEAT
-        safe_str_eq(op->standard, "heartbeat") ||
+        safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_HB) ||
 #endif
-        safe_str_eq(op->standard, "lsb"))) {
+        safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_LSB))) {
         action = "status";
     }
     op->action = strdup(action);
 
-    if (strcasecmp(op->standard, "ocf") == 0) {
+    if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
         op->provider = strdup(provider);
         op->params = params;
         params = NULL;
@@ -213,7 +215,7 @@ resources_action_create(const char *name, const char *standard, const char *prov
         op->opaque->args[0] = strdup(op->opaque->exec);
         op->opaque->args[1] = strdup(action);
 
-    } else if (strcasecmp(op->standard, "lsb") == 0) {
+    } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
         if (op->agent[0] == '/') {
             /* if given an absolute path, use that instead
              * of tacking on the LSB_ROOT_DIR path to the front */
@@ -226,7 +228,7 @@ resources_action_create(const char *name, const char *standard, const char *prov
         op->opaque->args[1] = strdup(op->action);
         op->opaque->args[2] = NULL;
 #if SUPPORT_HEARTBEAT
-    } else if (strcasecmp(op->standard, "heartbeat") == 0) {
+    } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_HB) == 0) {
         int index;
         int param_num;
         char buf_tmp[20];
@@ -262,15 +264,15 @@ resources_action_create(const char *name, const char *standard, const char *prov
         op->opaque->args[param_num] = NULL;
 #endif
 #if SUPPORT_SYSTEMD
-    } else if (strcasecmp(op->standard, "systemd") == 0) {
+    } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
         op->opaque->exec = strdup("systemd-dbus");
 #endif
 #if SUPPORT_UPSTART
-    } else if (strcasecmp(op->standard, "upstart") == 0) {
+    } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
         op->opaque->exec = strdup("upstart-dbus");
 #endif
 #if SUPPORT_NAGIOS
-    } else if (strcasecmp(op->standard, "nagios") == 0) {
+    } else if (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
         int index = 0;
 
         if (op->agent[0] == '/') {
@@ -637,11 +639,13 @@ inline static gboolean
 action_exec_helper(svc_action_t * op)
 {
     /* Whether a/synchronous must be decided (op->synchronous) beforehand. */
-    if (op->standard && strcasecmp(op->standard, "upstart") == 0) {
+    if (op->standard
+        && (strcasecmp(op->standard, PCMK_RESOURCE_CLASS_UPSTART) == 0)) {
 #if SUPPORT_UPSTART
         return upstart_job_exec(op);
 #endif
-    } else if (op->standard && strcasecmp(op->standard, "systemd") == 0) {
+    } else if (op->standard && strcasecmp(op->standard,
+                                          PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
 #if SUPPORT_SYSTEMD
         return systemd_unit_exec(op);
 #endif
@@ -803,7 +807,7 @@ get_directory_list(const char *root, gboolean files, gboolean executable)
 GList *
 services_list(void)
 {
-    return resources_list_agents("lsb", NULL);
+    return resources_list_agents(PCMK_RESOURCE_CLASS_LSB, NULL);
 }
 
 #if SUPPORT_HEARTBEAT
@@ -820,14 +824,15 @@ resources_list_standards(void)
     GList *standards = NULL;
     GList *agents = NULL;
 
-    standards = g_list_append(standards, strdup("ocf"));
-    standards = g_list_append(standards, strdup("lsb"));
-    standards = g_list_append(standards, strdup("service"));
+    standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_OCF));
+    standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_LSB));
+    standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_SERVICE));
 
 #if SUPPORT_SYSTEMD
     agents = systemd_unit_listall();
     if (agents) {
-        standards = g_list_append(standards, strdup("systemd"));
+        standards = g_list_append(standards,
+                                  strdup(PCMK_RESOURCE_CLASS_SYSTEMD));
         g_list_free_full(agents, free);
     }
 #endif
@@ -835,7 +840,8 @@ resources_list_standards(void)
 #if SUPPORT_UPSTART
     agents = upstart_job_listall();
     if (agents) {
-        standards = g_list_append(standards, strdup("upstart"));
+        standards = g_list_append(standards,
+                                  strdup(PCMK_RESOURCE_CLASS_UPSTART));
         g_list_free_full(agents, free);
     }
 #endif
@@ -843,13 +849,14 @@ resources_list_standards(void)
 #if SUPPORT_NAGIOS
     agents = resources_os_list_nagios_agents();
     if (agents) {
-        standards = g_list_append(standards, strdup("nagios"));
+        standards = g_list_append(standards,
+                                  strdup(PCMK_RESOURCE_CLASS_NAGIOS));
         g_list_free_full(agents, free);
     }
 #endif
 
 #if SUPPORT_HEARTBEAT
-    standards = g_list_append(standards, strdup("heartbeat"));
+    standards = g_list_append(standards, strdup(PCMK_RESOURCE_CLASS_HB));
 #endif
 
     return standards;
@@ -858,7 +865,7 @@ resources_list_standards(void)
 GList *
 resources_list_providers(const char *standard)
 {
-    if (strcasecmp(standard, "ocf") == 0) {
+    if (strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
         return resources_os_list_ocf_providers();
     }
 
@@ -868,7 +875,9 @@ resources_list_providers(const char *standard)
 GList *
 resources_list_agents(const char *standard, const char *provider)
 {
-    if (standard == NULL || strcasecmp(standard, "service") == 0) {
+    if ((standard == NULL)
+        || (strcasecmp(standard, PCMK_RESOURCE_CLASS_SERVICE) == 0)) {
+
         GList *tmp1;
         GList *tmp2;
         GList *result = resources_os_list_lsb_agents();
@@ -898,24 +907,24 @@ resources_list_agents(const char *standard, const char *provider)
 
         return result;
 
-    } else if (strcasecmp(standard, "ocf") == 0) {
+    } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_OCF) == 0) {
         return resources_os_list_ocf_agents(provider);
-    } else if (strcasecmp(standard, "lsb") == 0) {
+    } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_LSB) == 0) {
         return resources_os_list_lsb_agents();
 #if SUPPORT_HEARTBEAT
-    } else if (strcasecmp(standard, "heartbeat") == 0) {
+    } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_HB) == 0) {
         return resources_os_list_hb_agents();
 #endif
 #if SUPPORT_SYSTEMD
-    } else if (strcasecmp(standard, "systemd") == 0) {
+    } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_SYSTEMD) == 0) {
         return systemd_unit_listall();
 #endif
 #if SUPPORT_UPSTART
-    } else if (strcasecmp(standard, "upstart") == 0) {
+    } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_UPSTART) == 0) {
         return upstart_job_listall();
 #endif
 #if SUPPORT_NAGIOS
-    } else if (strcasecmp(standard, "nagios") == 0) {
+    } else if (strcasecmp(standard, PCMK_RESOURCE_CLASS_NAGIOS) == 0) {
         return resources_os_list_nagios_agents();
 #endif
     }
diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c
index 1fe3ec1..c2ac43d 100644
--- a/lib/services/services_linux.c
+++ b/lib/services/services_linux.c
@@ -177,7 +177,8 @@ 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 || strcasecmp("ocf", op->standard) != 0) {
+    if ((op->standard == NULL)
+        || (strcasecmp(PCMK_RESOURCE_CLASS_OCF, op->standard) != 0)) {
         return;
     }
 
@@ -338,13 +339,15 @@ services_handle_exec_error(svc_action_t * op, int error)
     int rc_not_installed, rc_insufficient_priv, rc_exec_error;
 
     /* Mimic the return codes for each standard as that's what we'll convert back from in get_uniform_rc() */
-    if (safe_str_eq(op->standard, "lsb") && safe_str_eq(op->action, "status")) {
+    if (safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_LSB)
+        && safe_str_eq(op->action, "status")) {
+
         rc_not_installed = PCMK_LSB_STATUS_NOT_INSTALLED;
         rc_insufficient_priv = PCMK_LSB_STATUS_INSUFFICIENT_PRIV;
         rc_exec_error = PCMK_LSB_STATUS_UNKNOWN;
 
 #if SUPPORT_NAGIOS
-    } else if (safe_str_eq(op->standard, "nagios")) {
+    } else if (safe_str_eq(op->standard, PCMK_RESOURCE_CLASS_NAGIOS)) {
         rc_not_installed = NAGIOS_NOT_INSTALLED;
         rc_insufficient_priv = NAGIOS_INSUFFICIENT_PRIV;
         rc_exec_error = PCMK_OCF_EXEC_ERROR;
diff --git a/lrmd/lrmd.c b/lrmd/lrmd.c
index 724edb7..5669d34 100644
--- a/lrmd/lrmd.c
+++ b/lrmd/lrmd.c
@@ -139,8 +139,9 @@ 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"))) {
+        (safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_LSB) ||
+         safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_SERVICE)
+         || safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_SYSTEMD))) {
         return "status";
     }
     return action;
@@ -435,7 +436,7 @@ merge_dup:
     cmd->userdata_str = NULL;
     dup->call_id = cmd->call_id;
 
-    if (safe_str_eq(rsc->class, "stonith")) {
+    if (safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH)) {
         /* if we are waiting for the next interval, kick it off now */
         if (dup_pending == TRUE) {
             g_source_remove(cmd->stonith_recurring_id);
@@ -854,16 +855,16 @@ nagios2uniform_rc(const char *action, int rc)
 static int
 get_uniform_rc(const char *standard, const char *action, int rc)
 {
-    if (safe_str_eq(standard, "ocf")) {
+    if (safe_str_eq(standard, PCMK_RESOURCE_CLASS_OCF)) {
         return ocf2uniform_rc(rc);
-    } else if (safe_str_eq(standard, "stonith")) {
+    } else if (safe_str_eq(standard, PCMK_RESOURCE_CLASS_STONITH)) {
         return stonith2uniform_rc(action, rc);
-    } else if (safe_str_eq(standard, "systemd")) {
+    } else if (safe_str_eq(standard, PCMK_RESOURCE_CLASS_SYSTEMD)) {
         return rc;
-    } else if (safe_str_eq(standard, "upstart")) {
+    } else if (safe_str_eq(standard, PCMK_RESOURCE_CLASS_UPSTART)) {
         return rc;
 #if SUPPORT_NAGIOS
-    } else if (safe_str_eq(standard, "nagios")) {
+    } else if (safe_str_eq(standard, PCMK_RESOURCE_CLASS_NAGIOS)) {
         return nagios2uniform_rc(action, rc);
 #endif
     } else {
@@ -876,7 +877,7 @@ action_get_uniform_rc(svc_action_t * action)
 {
     lrmd_cmd_t *cmd = action->cb_data;
 #if SUPPORT_HEARTBEAT
-    if (safe_str_eq(action->standard, "heartbeat")) {
+    if (safe_str_eq(action->standard, PCMK_RESOURCE_CLASS_HB)) {
         return hb2uniform_rc(cmd->action, action->rc, action->stdout_data);
     }
 #endif
@@ -991,13 +992,13 @@ action_complete(svc_action_t * action)
     cmd->lrmd_op_status = action->status;
     rsc = cmd->rsc_id ? g_hash_table_lookup(rsc_list, cmd->rsc_id) : NULL;
 
-    if(rsc && safe_str_eq(rsc->class, "service")) {
+    if (rsc && safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_SERVICE)) {
         rclass = resources_find_service_class(rsc->class);
     } else if(rsc) {
         rclass = rsc->class;
     }
 
-    if (safe_str_eq(rclass, "systemd")) {
+    if (safe_str_eq(rclass, PCMK_RESOURCE_CLASS_SYSTEMD)) {
         if(cmd->exec_rc == PCMK_OCF_OK && safe_str_eq(cmd->action, "start")) {
             /* systemd I curse thee!
              *
@@ -1042,7 +1043,7 @@ action_complete(svc_action_t * action)
     }
 
 #if SUPPORT_NAGIOS
-    if (rsc && safe_str_eq(rsc->class, "nagios")) {
+    if (rsc && safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_NAGIOS)) {
         if (safe_str_eq(cmd->action, "monitor") &&
             cmd->interval == 0 && cmd->exec_rc == PCMK_OCF_OK) {
             /* Successfully executed --version for the nagios plugin */
@@ -1123,7 +1124,7 @@ stonith_action_complete(lrmd_cmd_t * cmd, int rc)
     int recurring = cmd->interval;
     lrmd_rsc_t *rsc = NULL;
 
-    cmd->exec_rc = get_uniform_rc("stonith", cmd->action, rc);
+    cmd->exec_rc = get_uniform_rc(PCMK_RESOURCE_CLASS_STONITH, cmd->action, rc);
 
     rsc = g_hash_table_lookup(rsc_list, cmd->rsc_id);
 
@@ -1184,7 +1185,7 @@ stonith_connection_failed(void)
 
     g_hash_table_iter_init(&iter, rsc_list);
     while (g_hash_table_iter_next(&iter, (gpointer *) & key, (gpointer *) & rsc)) {
-        if (safe_str_eq(rsc->class, "stonith")) {
+        if (safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH)) {
             if (rsc->active) {
                 cmd_list = g_list_append(cmd_list, rsc->active);
             }
@@ -1219,7 +1220,8 @@ lrmd_rsc_execute_stonith(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
     stonith_t *stonith_api = get_stonith_connection();
 
     if (!stonith_api) {
-        cmd->exec_rc = get_uniform_rc("stonith", cmd->action, -ENOTCONN);
+        cmd->exec_rc = get_uniform_rc(PCMK_RESOURCE_CLASS_STONITH, cmd->action,
+                                      -ENOTCONN);
         cmd->lrmd_op_status = PCMK_LRM_OP_ERROR;
         cmd_finalize(cmd, rsc);
         return -EUNATCH;
@@ -1302,7 +1304,9 @@ lrmd_rsc_execute_service_lib(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
 
 #if SUPPORT_NAGIOS
     /* Recurring operations are cancelled anyway for a stop operation */
-    if (safe_str_eq(rsc->class, "nagios") && safe_str_eq(cmd->action, "stop")) {
+    if (safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_NAGIOS)
+        && safe_str_eq(cmd->action, "stop")) {
+
         cmd->exec_rc = PCMK_OCF_OK;
         goto exec_done;
     }
@@ -1320,7 +1324,7 @@ lrmd_rsc_execute_service_lib(lrmd_rsc_t * rsc, lrmd_cmd_t * cmd)
     if (cmd->isolation_wrapper) {
         g_hash_table_remove(params_copy, "CRM_meta_isolation_wrapper");
         action = resources_action_create(rsc->rsc_id,
-                                         "ocf",
+                                         PCMK_RESOURCE_CLASS_OCF,
                                          LRMD_ISOLATION_PROVIDER,
                                          cmd->isolation_wrapper,
                                          cmd->action, /*action will be normalized in wrapper*/
@@ -1417,7 +1421,7 @@ lrmd_rsc_execute(lrmd_rsc_t * rsc)
 
     log_execute(cmd);
 
-    if (safe_str_eq(rsc->class, "stonith")) {
+    if (safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH)) {
         lrmd_rsc_execute_stonith(rsc, cmd);
     } else {
         lrmd_rsc_execute_service_lib(rsc, cmd);
@@ -1437,7 +1441,7 @@ free_rsc(gpointer data)
 {
     GListPtr gIter = NULL;
     lrmd_rsc_t *rsc = data;
-    int is_stonith = safe_str_eq(rsc->class, "stonith");
+    int is_stonith = safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH);
 
     gIter = rsc->pending_ops;
     while (gIter != NULL) {
@@ -1679,7 +1683,7 @@ cancel_op(const char *rsc_id, const char *action, int interval)
         }
     }
 
-    if (safe_str_eq(rsc->class, "stonith")) {
+    if (safe_str_eq(rsc->class, PCMK_RESOURCE_CLASS_STONITH)) {
         /* The service library does not handle stonith operations.
          * We have to handle recurring stonith operations ourselves. */
         for (gIter = rsc->recurring_ops; gIter != NULL; gIter = gIter->next) {
diff --git a/pengine/native.c b/pengine/native.c
index 0c972ea..52eba4f 100644
--- a/pengine/native.c
+++ b/pengine/native.c
@@ -2701,7 +2701,7 @@ native_create_probe(resource_t * rsc, node_t * node, action_t * complete,
     if (is_remote_node(node)) {
         const char *class = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
 
-        if (safe_str_eq(class, "stonith")) {
+        if (safe_str_eq(class, PCMK_RESOURCE_CLASS_STONITH)) {
             pe_rsc_trace(rsc, "Skipping probe for %s on node %s, remote-nodes do not run stonith agents.", rsc->id, node->details->id);
             return FALSE;
         } else if (rsc_contains_remote_node(data_set, rsc)) {
diff --git a/tools/crm_resource_print.c b/tools/crm_resource_print.c
index 5e84723..65841e5 100644
--- a/tools/crm_resource_print.c
+++ b/tools/crm_resource_print.c
@@ -69,7 +69,7 @@ cli_resource_print_cts(resource_t * rsc)
     const char *rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
     const char *rclass = crm_element_value(rsc->xml, XML_AGENT_ATTR_CLASS);
 
-    if (safe_str_eq(rclass, "stonith")) {
+    if (safe_str_eq(rclass, PCMK_RESOURCE_CLASS_STONITH)) {
         xmlNode *op = NULL;
 
         needs_quorum = FALSE;
diff --git a/tools/crm_resource_runtime.c b/tools/crm_resource_runtime.c
index 7a64bc9..61aabe9 100644
--- a/tools/crm_resource_runtime.c
+++ b/tools/crm_resource_runtime.c
@@ -1496,7 +1496,7 @@ cli_resource_execute(const char *rsc_id, const char *rsc_action, GHashTable *ove
     rprov = crm_element_value(rsc->xml, XML_AGENT_ATTR_PROVIDER);
     rtype = crm_element_value(rsc->xml, XML_ATTR_TYPE);
 
-    if(safe_str_eq(rclass, "stonith")){
+    if (safe_str_eq(rclass, PCMK_RESOURCE_CLASS_STONITH)) {
         CMD_ERR("Sorry, --%s doesn't support %s resources yet", rsc_action, rclass);
         crm_exit(EOPNOTSUPP);
     }
diff --git a/tools/fake_transition.c b/tools/fake_transition.c
index c45349f..6448c14 100644
--- a/tools/fake_transition.c
+++ b/tools/fake_transition.c
@@ -298,17 +298,18 @@ inject_resource(xmlNode * cib_node, const char *resource, const char *rclass, co
                 "  Please supply the class and type to continue\n", resource, ID(cib_node));
         return NULL;
 
-    } else if (safe_str_neq(rclass, "ocf")
-               && safe_str_neq(rclass, "stonith")
-               && safe_str_neq(rclass, "heartbeat")
-               && safe_str_neq(rclass, "service")
-               && safe_str_neq(rclass, "upstart")
-               && safe_str_neq(rclass, "systemd")
-               && safe_str_neq(rclass, "lsb")) {
+    } else if (safe_str_neq(rclass, PCMK_RESOURCE_CLASS_OCF)
+               && safe_str_neq(rclass, PCMK_RESOURCE_CLASS_STONITH)
+               && safe_str_neq(rclass, PCMK_RESOURCE_CLASS_HB)
+               && safe_str_neq(rclass, PCMK_RESOURCE_CLASS_SERVICE)
+               && safe_str_neq(rclass, PCMK_RESOURCE_CLASS_UPSTART)
+               && safe_str_neq(rclass, PCMK_RESOURCE_CLASS_SYSTEMD)
+               && safe_str_neq(rclass, PCMK_RESOURCE_CLASS_LSB)) {
         fprintf(stderr, "Invalid class for %s: %s\n", resource, rclass);
         return NULL;
 
-    } else if (safe_str_eq(rclass, "ocf") && rprovider == NULL) {
+    } else if (safe_str_eq(rclass, PCMK_RESOURCE_CLASS_OCF)
+               && rprovider == NULL) {
         fprintf(stderr, "Please specify the provider for resource %s\n", resource);
         return NULL;
     }
-- 
1.8.3.1


From 1c534b5a773df5b62aeb8a46842d1e2d4d2266ef Mon Sep 17 00:00:00 2001
From: Ken Gaillot <kgaillot@redhat.com>
Date: Wed, 1 Mar 2017 15:04:35 -0600
Subject: [PATCH 10/10] Fix: libservices: ensure recurring actions table is
 created before using

---
 lib/services/services.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/lib/services/services.c b/lib/services/services.c
index 52d3b55..679f8e7 100644
--- a/lib/services/services.c
+++ b/lib/services/services.c
@@ -90,6 +90,15 @@ resources_find_service_class(const char *agent)
     return NULL;
 }
 
+static inline void
+init_recurring_actions(void)
+{
+    if (recurring_actions == NULL) {
+        recurring_actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL,
+                                                  NULL);
+    }
+}
+
 /*!
  * \internal
  * \brief Check whether op is in-flight systemd or upstart op
@@ -513,9 +522,11 @@ services_action_cancel(const char *name, const char *action, int interval)
 {
     gboolean cancelled = FALSE;
     char *id = generate_op_key(name, action, interval);
-    svc_action_t *op = g_hash_table_lookup(recurring_actions, id);
+    svc_action_t *op = NULL;
 
     /* We can only cancel a recurring action */
+    init_recurring_actions();
+    op = g_hash_table_lookup(recurring_actions, id);
     if (op == NULL) {
         goto done;
     }
@@ -575,6 +586,7 @@ services_action_kick(const char *name, const char *action, int interval /* ms */
     svc_action_t * op = NULL;
     char *id = generate_op_key(name, action, interval);
 
+    init_recurring_actions();
     op = g_hash_table_lookup(recurring_actions, id);
     free(id);
 
@@ -604,11 +616,6 @@ handle_duplicate_recurring(svc_action_t * op, void (*action_callback) (svc_actio
 {
     svc_action_t * dup = NULL;
 
-    if (recurring_actions == NULL) {
-        recurring_actions = g_hash_table_new_full(g_str_hash, g_str_equal, NULL, NULL);
-        return FALSE;
-    }
-
     /* check for duplicates */
     dup = g_hash_table_lookup(recurring_actions, op->id);
 
@@ -700,6 +707,7 @@ services_action_async(svc_action_t * op, void (*action_callback) (svc_action_t *
     }
 
     if (op->interval > 0) {
+        init_recurring_actions();
         if (handle_duplicate_recurring(op, action_callback) == TRUE) {
             /* entry rescheduled, dup freed */
             /* exit early */
-- 
1.8.3.1