Browse Source

kexec: First version of exec_action_sequence

Serj Kalichev 2 years ago
parent
commit
a00e980fc0
7 changed files with 101 additions and 30 deletions
  1. 2 2
      bin/klishd/klishd.c
  2. 5 0
      klish/ischeme/iaction.c
  3. 3 1
      klish/kaction.h
  4. 3 0
      klish/kexec.h
  5. 28 0
      klish/kscheme/kaction.c
  6. 1 1
      klish/ksession/kcontext.c
  7. 59 26
      klish/ksession/kexec.c

+ 2 - 2
bin/klishd/klishd.c

@@ -406,9 +406,9 @@ static kscheme_t *load_all_dbs(const char *dbs,
 		return NULL;
 	}
 
-/*
+
 	// Debug
-	{
+/*	{
 		kdb_t *deploy_db = NULL;
 
 		// Deploy (for testing purposes)

+ 5 - 0
klish/ischeme/iaction.c

@@ -68,6 +68,8 @@ bool_t iaction_parse(const iaction_t *info, kaction_t *action, faux_error_t *err
 			c = KACTION_COND_SUCCESS;
 		else if (!faux_str_casecmp(info->exec_on, "always"))
 			c = KACTION_COND_ALWAYS;
+		else if (!faux_str_casecmp(info->exec_on, "never"))
+			c = KACTION_COND_NEVER;
 		if ((KACTION_COND_NONE == c) || !kaction_set_exec_on(action, c)) {
 			faux_error_add(error, TAG": Illegal 'exec_on' attribute");
 			retcode = BOOL_FALSE;
@@ -162,6 +164,9 @@ char *iaction_deploy(const kaction_t *kaction, int level)
 	case KACTION_COND_ALWAYS:
 		exec_on = "always";
 		break;
+	case KACTION_COND_NEVER:
+		exec_on = "never";
+		break;
 	default:
 		exec_on = NULL;
 	}

+ 3 - 1
klish/kaction.h

@@ -16,7 +16,8 @@ typedef enum {
 	KACTION_COND_NONE,
 	KACTION_COND_FAIL,
 	KACTION_COND_SUCCESS,
-	KACTION_COND_ALWAYS
+	KACTION_COND_ALWAYS,
+	KACTION_COND_NEVER,
 } kaction_cond_e;
 
 
@@ -39,6 +40,7 @@ bool_t kaction_set_interactive(kaction_t *action, bool_t interactive);
 
 kaction_cond_e kaction_exec_on(const kaction_t *action);
 bool_t kaction_set_exec_on(kaction_t *action, kaction_cond_e exec_on);
+bool_t kaction_meet_exec_conditions(const kaction_t *action, int current_retcode);
 
 bool_t kaction_update_retcode(const kaction_t *action);
 bool_t kaction_set_update_retcode(kaction_t *action, bool_t update_retcode);

+ 3 - 0
klish/kexec.h

@@ -19,6 +19,9 @@ C_DECL_BEGIN
 kexec_t *kexec_new(void);
 void kexec_free(kexec_t *exec);
 
+// Dry-run
+bool_t kexec_dry_run(const kexec_t *exec);
+bool_t kexec_set_dry_run(kexec_t *exec, bool_t dry_run);
 // STDIN
 int kexec_stdin(const kexec_t *exec);
 bool_t kexec_set_stdin(kexec_t *exec, int stdin);

+ 28 - 0
klish/kscheme/kaction.c

@@ -106,6 +106,34 @@ void kaction_free(kaction_t *action)
 }
 
 
+bool_t kaction_meet_exec_conditions(const kaction_t *action, int current_retcode)
+{
+	bool_t r = BOOL_FALSE; // Default is pessimistic
+
+	assert(action);
+	if (!action)
+		return BOOL_FALSE;
+
+	switch (kaction_exec_on(action)) {
+	case KACTION_COND_ALWAYS:
+		r = BOOL_TRUE;
+		break;
+	case KACTION_COND_SUCCESS:
+		if (0 == current_retcode)
+			r = BOOL_TRUE;
+		break;
+	case KACTION_COND_FAIL:
+		if (current_retcode != 0)
+			r = BOOL_TRUE;
+		break;
+	default:
+		r = BOOL_FALSE; // NEVER or NONE
+	}
+
+	return r;
+}
+
+
 bool_t kaction_is_permanent(const kaction_t *action)
 {
 	ksym_t *sym = NULL;

+ 1 - 1
klish/ksession/kcontext.c

@@ -95,7 +95,7 @@ kcontext_t *kcontext_new(kcontext_type_e type)
 	context->stdin = -1;
 	context->stdout = -1;
 	context->stderr = -1;
-	context->pid = 0; // PID of currently executed ACTION
+	context->pid = -1; // PID of currently executed ACTION
 	context->done = BOOL_FALSE;
 
 	return context;

+ 59 - 26
klish/ksession/kexec.c

@@ -13,11 +13,15 @@
 
 struct kexec_s {
 	faux_list_t *contexts;
+	bool_t dry_run;
 	int stdin;
 	int stdout;
 	int stderr;
 };
 
+// Dry-run
+KGET_BOOL(exec, dry_run);
+KSET_BOOL(exec, dry_run);
 
 // STDIN
 KGET(exec, int, stdin);
@@ -48,6 +52,8 @@ kexec_t *kexec_new()
 	if (!exec)
 		return NULL;
 
+	exec->dry_run = BOOL_FALSE;
+
 	// List of execute contexts
 	exec->contexts = faux_list_new(FAUX_LIST_UNSORTED, FAUX_LIST_NONUNIQUE,
 		NULL, NULL, (void (*)(void *))kcontext_free);
@@ -171,19 +177,25 @@ static bool_t kexec_prepare(kexec_t *exec)
 }
 
 
-static bool_t exec_action(kcontext_t *context, const kaction_t *action)
+static int exec_action(kcontext_t *context, const kaction_t *action, pid_t *pid)
 {
 	context = context;
 	action = action;
+	*pid = -1;
 
-	return BOOL_TRUE;
+	printf("DDD: exec_action\n");
+
+	return 0;
 }
 
 
-static bool_t exec_action_sequence(kcontext_t *context, pid_t pid, int wstatus)
+static bool_t exec_action_sequence(const kexec_t *exec, kcontext_t *context,
+	pid_t pid, int wstatus)
 {
 	faux_list_node_t *iter = NULL;
 	int exitstatus = WEXITSTATUS(wstatus);
+	int *pexitstatus = &exitstatus;
+	pid_t new_pid = -1; // PID of newly forked ACTION process
 
 	assert(context);
 	if (!context)
@@ -196,44 +208,65 @@ static bool_t exec_action_sequence(kcontext_t *context, pid_t pid, int wstatus)
 	if (kcontext_done(context) || (kcontext_pid(context) != pid))
 		return BOOL_TRUE;
 
+	// Here we know that given PID is our PID
 	iter = kcontext_action_iter(context); // Get saved current ACTION
 	do {
 		faux_list_t *actions = NULL;
 		const kaction_t *action = NULL;
-		// Here we know that given PID is our PID and some ACTIONs are
-		// left to be executed.
 
-		// If some process returns then compute current retcode.
-		if (iter) {
+		// Compute new value for retcode.
+		// Here iter is a pointer to previous action but not new.
+		// If iter == NULL then it will be a first ACTION from the sequence.
+		if (iter && pexitstatus) {
 			const kaction_t *terminated_action = NULL;
 			terminated_action = faux_list_data(iter);
 			assert(terminated_action);
 			if (kaction_update_retcode(terminated_action))
-				kcontext_set_retcode(context, exitstatus);
+				kcontext_set_retcode(context, *pexitstatus);
 		}
 
-	if (!iter) { // Is it the first ACTION within list
-		actions = kentry_actions(kpargv_command(kcontext_pargv(context)));
-		assert(actions);
-		iter = faux_list_head(actions);
-	} else {
-		iter = faux_list_next_node(iter);
-	}
-	kcontext_set_action_iter(context, iter);
+		// Get next ACTION from sequence
+		if (!iter) { // Is it the first ACTION within list
+			actions = kentry_actions(kpargv_command(kcontext_pargv(context)));
+			assert(actions);
+			iter = faux_list_head(actions);
+		} else {
+			iter = faux_list_next_node(iter);
+		}
+		kcontext_set_action_iter(context, iter);
 
-	if (!iter) { // It was last ACTION
-		kcontext_set_done(context, BOOL_TRUE);
-	}
+		// Was it end of ACTION sequence?
+		if (!iter) {
+			kcontext_set_done(context, BOOL_TRUE);
+			break;
+		}
+
+		// Not all ACTIONs has an exit status. Some can have condition to
+		// skip real execution. So they has no exit status.
+		pexitstatus = NULL;
+
+		// Get new ACTION to execute
+		action = (const kaction_t *)faux_list_data(iter);
+		assert(action);
 
-	action = (const kaction_t *)faux_list_data(iter);
-	assert(action);
-	exec_action(context, action);
+		// Check for previous retcode to find out if next command must
+		// be executed.
+		if (!kaction_meet_exec_conditions(action, kcontext_retcode(context)))
+			continue; // Skip execution
 
-printf("CONTEXT\n");
+		// Here we know that process will be executed. Dry-run mode is a
+		// pseudo-execution too i.e. ACTION has exit status.
+		pexitstatus = &exitstatus;
+
+		// Check for dry-run flag and 'permanent' feature of ACTION.
+		if (kexec_dry_run(exec) && !kaction_permanent(action)) {
+			exitstatus = 0; // Exit status while dry-run is always 0
+			continue;
+		}
 
-	} while (iter);
+		exitstatus = exec_action(context, action, &new_pid);
 
-	wstatus = wstatus;
+	} while (-1 == new_pid); // PID is not -1 when new process was forked
 
 	return BOOL_TRUE;
 }
@@ -250,7 +283,7 @@ static bool_t continue_command_execution(kexec_t *exec, pid_t pid, int wstatus)
 
 	iter = kexec_contexts_iter(exec);
 	while ((context = kexec_contexts_each(&iter))) {
-		exec_action_sequence(context, pid, wstatus);
+		exec_action_sequence(exec, context, pid, wstatus);
 	}
 
 	return BOOL_TRUE;