Blob Blame History Raw
commit 385cb2b6a11d967145d8034fd5c05574a6594a4f
Author: Andrew Beekhof <andrew@beekhof.net>
Date:   Thu Aug 8 11:21:03 2013 +1000

    Feature: services: Detect missing agents and permission errors before forking
    
    Also set the operation status to distinguish between the agent being
    missing and something the agent needs being missing.
    
    (cherry picked from commit 20eee043d376ed03edf5e4bb4cc4639f8697e44a)

diff --git a/crmd/te_actions.c b/crmd/te_actions.c
index 63d15bb..4e3bc22 100644
--- a/crmd/te_actions.c
+++ b/crmd/te_actions.c
@@ -465,7 +465,7 @@ te_rsc_command(crm_graph_t * graph, crm_action_t * action)
         && safe_str_neq(task, CRMD_ACTION_DELETE)) {
         /* write a "pending" entry to the CIB, inhibit notification */
         crm_debug("Recording pending op %s in the CIB", task_uuid);
-        cib_action_update(action, PCMK_LRM_OP_PENDING, PCMK_OCF_STATUS_UNKNOWN);
+        cib_action_update(action, PCMK_LRM_OP_PENDING, PCMK_OCF_UNKNOWN);
     }
 
     return TRUE;
diff --git a/crmd/te_callbacks.c b/crmd/te_callbacks.c
index c0360b4..4b8aac5 100644
--- a/crmd/te_callbacks.c
+++ b/crmd/te_callbacks.c
@@ -548,7 +548,6 @@ action_timer_callback(gpointer data)
         }
 
         if (send_update) {
-            /* cib_action_update(timer->action, PCMK_LRM_OP_PENDING, PCMK_OCF_STATUS_UNKNOWN); */
             cib_action_update(timer->action, PCMK_LRM_OP_TIMEOUT, PCMK_OCF_UNKNOWN_ERROR);
         }
     }
diff --git a/include/crm/services.h b/include/crm/services.h
index 0c0cca9..9fe9d37 100644
--- a/include/crm/services.h
+++ b/include/crm/services.h
@@ -88,9 +88,10 @@ enum ocf_exitcode {
     PCMK_OCF_RUNNING_MASTER       = 8,
     PCMK_OCF_FAILED_MASTER        = 9,
 
-    PCMK_OCF_STATUS_UNKNOWN       = 14, /* Already in use */
 
     /* 150-199	reserved for application use */
+    PCMK_OCF_EXEC_ERROR    = 192, /* Generic problem invoking the agent */
+    PCMK_OCF_UNKNOWN       = 193, /* State of the service is unknown - used for recording in-flight operations */
     PCMK_OCF_SIGNAL        = 194,
     PCMK_OCF_NOT_SUPPORTED = 195,
     PCMK_OCF_PENDING       = 196,
@@ -106,6 +107,7 @@ enum op_status {
     PCMK_LRM_OP_TIMEOUT,
     PCMK_LRM_OP_NOTSUPPORTED,
     PCMK_LRM_OP_ERROR,
+    PCMK_LRM_OP_NOT_INSTALLED,
 };
 
 enum nagios_exitcode {
@@ -263,6 +265,7 @@ enum nagios_exitcode {
                 case PCMK_LRM_OP_TIMEOUT:return "Timed Out";
                 case PCMK_LRM_OP_NOTSUPPORTED:return "NOT SUPPORTED";
                 case PCMK_LRM_OP_ERROR:return "Error";
+                case PCMK_LRM_OP_NOT_INSTALLED:return "Not installed";
                 default:return "UNKNOWN!";
     }} static inline const char *services_ocf_exitcode_str(enum ocf_exitcode code) {
         switch (code) {
diff --git a/lib/services/services_linux.c b/lib/services/services_linux.c
index 868dc5b..ac0138f 100644
--- a/lib/services/services_linux.c
+++ b/lib/services/services_linux.c
@@ -307,14 +307,62 @@ operation_finished(mainloop_child_t * p, pid_t pid, int core, int signo, int exi
     operation_finalize(op);
 }
 
+static void
+services_handle_exec_error(svc_action_t * op, int error)
+{
+    op->stdout_data = NULL;
+    op->stderr_data = NULL;
+
+    /* Need to mimic the return codes for each standard as thats what we'll convert back from in get_uniform_rc() */
+    if (safe_str_eq(op->standard, "lsb") && safe_str_eq(op->action, "status")) {
+        switch (errno) {    /* see execve(2) */
+            case ENOENT:   /* No such file or directory */
+            case EISDIR:   /* Is a directory */
+                op->rc = PCMK_LSB_STATUS_NOT_INSTALLED;
+                op->status = PCMK_LRM_OP_NOT_INSTALLED;
+                break;
+            case EACCES:   /* permission denied (various errors) */
+                /* LSB status ops don't support 'not installed' */
+                break;
+        }
+
+#if SUPPORT_NAGIOS
+    } else if (safe_str_eq(op->standard, "nagios")) {
+        switch (errno) {
+            case ENOENT:   /* No such file or directory */
+            case EISDIR:   /* Is a directory */
+                op->rc = NAGIOS_NOT_INSTALLED;
+                op->status = PCMK_LRM_OP_NOT_INSTALLED;
+                break;
+            case EACCES:   /* permission denied (various errors) */
+                op->rc = NAGIOS_INSUFFICIENT_PRIV;
+                break;
+        }
+#endif
+
+    } else {
+        switch (errno) {
+            case ENOENT:   /* No such file or directory */
+            case EISDIR:   /* Is a directory */
+                op->rc = PCMK_OCF_NOT_INSTALLED; /* Valid for LSB */
+                op->status = PCMK_LRM_OP_NOT_INSTALLED;
+                break;
+            case EACCES:   /* permission denied (various errors) */
+                op->rc = PCMK_OCF_INSUFFICIENT_PRIV; /* Valid for LSB */
+                break;
+        }
+    }
+}
+
 gboolean
 services_os_action_execute(svc_action_t * op, gboolean synchronous)
 {
-    int rc, lpc;
+    int lpc;
     int stdout_fd[2];
     int stderr_fd[2];
     sigset_t mask;
     sigset_t old_mask;
+    struct stat st;
 
     if (pipe(stdout_fd) < 0) {
         crm_err("pipe() failed");
@@ -324,6 +372,13 @@ services_os_action_execute(svc_action_t * op, gboolean synchronous)
         crm_err("pipe() failed");
     }
 
+    /* Fail fast */
+    if(stat(op->opaque->exec, &st) != 0) {
+        crm_warn("Cannot execute '%s': %s (%d)", op->opaque->exec, pcmk_strerror(errno), errno);
+        services_handle_exec_error(op, errno);
+        return FALSE;
+    }
+
     if (synchronous) {
         sigemptyset(&mask);
         sigaddset(&mask, SIGCHLD);
@@ -337,11 +392,14 @@ services_os_action_execute(svc_action_t * op, gboolean synchronous)
     op->pid = fork();
     switch (op->pid) {
         case -1:
-            crm_err("fork() failed");
+            crm_err("Could not execute '%s': %s (%d)", op->opaque->exec, pcmk_strerror(errno), errno);
+
             close(stdout_fd[0]);
             close(stdout_fd[1]);
             close(stderr_fd[0]);
             close(stderr_fd[1]);
+
+            services_handle_exec_error(op, errno);
             return FALSE;
 
         case 0:                /* Child */
@@ -390,29 +448,9 @@ services_os_action_execute(svc_action_t * op, gboolean synchronous)
             /* execute the RA */
             execvp(op->opaque->exec, op->opaque->args);
 
-            switch (errno) {    /* see execve(2) */
-                case ENOENT:   /* No such file or directory */
-                case EISDIR:   /* Is a directory */
-                    rc = PCMK_OCF_NOT_INSTALLED;
-#if SUPPORT_NAGIOS
-                    if (safe_str_eq(op->standard, "nagios")) {
-                        rc = NAGIOS_NOT_INSTALLED;
-                    }
-#endif
-                    break;
-                case EACCES:   /* permission denied (various errors) */
-                    rc = PCMK_OCF_INSUFFICIENT_PRIV;
-#if SUPPORT_NAGIOS
-                    if (safe_str_eq(op->standard, "nagios")) {
-                        rc = NAGIOS_INSUFFICIENT_PRIV;
-                    }
-#endif
-                    break;
-                default:
-                    rc = PCMK_OCF_UNKNOWN_ERROR;
-                    break;
-            }
-            _exit(rc);
+            /* Most cases should have been already handled by stat() */
+            services_handle_exec_error(op, errno);
+            _exit(op->rc);
     }
 
     /* Only the parent reaches here */
diff --git a/lib/services/systemd.c b/lib/services/systemd.c
index 783caca..ea804c8 100644
--- a/lib/services/systemd.c
+++ b/lib/services/systemd.c
@@ -385,6 +385,7 @@ systemd_unit_exec_done(GObject * source_object, GAsyncResult * res, gpointer use
 
             } else {
                 op->rc = PCMK_OCF_NOT_INSTALLED;
+                op->status = PCMK_LRM_OP_NOT_INSTALLED;
             }
 
         } else {
@@ -441,6 +442,7 @@ systemd_unit_exec(svc_action_t * op, gboolean synchronous)
                   error ? error->message : "unknown");
         if (error && strstr(error->message, "systemd1.NoSuchUnit")) {
             op->rc = PCMK_OCF_NOT_INSTALLED;
+            op->status = PCMK_LRM_OP_NOT_INSTALLED;
         }
         if(error) {
             g_error_free(error);
diff --git a/lib/services/upstart.c b/lib/services/upstart.c
index daeb398..831e7cf 100644
--- a/lib/services/upstart.c
+++ b/lib/services/upstart.c
@@ -446,6 +446,7 @@ upstart_job_exec(svc_action_t * op, gboolean synchronous)
             op->rc = PCMK_OCF_OK;
         } else {
             op->rc = PCMK_OCF_NOT_INSTALLED;
+            op->status = PCMK_LRM_OP_NOT_INSTALLED;
         }
         goto cleanup;
     }
diff --git a/lrmd/regression.py.in b/lrmd/regression.py.in
index 8d5fea4..9009e1c 100755
--- a/lrmd/regression.py.in
+++ b/lrmd/regression.py.in
@@ -629,7 +629,7 @@ if __name__ == "__main__":
 			test.add_cmd("-c register_rsc -r \"test_rsc\" -C systemd -T this_is_fake1234 -t 3000 "
 				     "-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 3000 "
-				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:complete\" ")
+				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:Not installed\" ")
 			test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 3000 "
 				     "-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
@@ -638,7 +638,7 @@ if __name__ == "__main__":
 			test.add_cmd("-c register_rsc -r \"test_rsc\" -C upstart -T this_is_fake1234 -t 3000 "
 				     "-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 			test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 3000 "
-				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:complete\" ")
+				     "-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:Not installed\" ")
 			test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 3000 "
 				     "-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
@@ -647,7 +647,7 @@ if __name__ == "__main__":
 		test.add_cmd("-c register_rsc -r \"test_rsc\" -C ocf -P pacemaker -T this_is_fake1234 -t 3000 "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 3000 "
-			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:complete\" ")
+			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:Not installed\" ")
 		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 3000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 
@@ -656,7 +656,7 @@ if __name__ == "__main__":
 		test.add_cmd("-c register_rsc -r \"test_rsc\" -C ocf -P pancakes -T Dummy -t 3000 "
 			"-l \"NEW_EVENT event_type:register rsc_id:test_rsc action:none rc:ok op_status:complete\" ")
 		test.add_cmd("-c exec -r \"test_rsc\" -a \"start\" -t 3000 "
-			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:complete\" ")
+			"-l \"NEW_EVENT event_type:exec_complete rsc_id:test_rsc action:start rc:not installed op_status:Not installed\" ")
 		test.add_cmd("-c unregister_rsc -r \"test_rsc\" -t 3000 "
 			"-l \"NEW_EVENT event_type:unregister rsc_id:test_rsc action:none rc:ok op_status:complete\" ")