507244cb6a98882a34b12191d722cdefa701f12a — Simon Ser 16 days ago 2ddc157
Print background job status notifications

Closes: https://github.com/emersion/mrsh/issues/108
4 files changed, 90 insertions(+), 36 deletions(-)

M builtin/jobs.c
M include/shell/job.h
M shell/job.c
M shell/task/task.c
M builtin/jobs.c => builtin/jobs.c +1 -35
@@ 12,45 12,11 @@
 
 static const char jobs_usage[] = "usage: jobs [-l|-p] [job_id...]\n";
 
-static char *job_state_str(struct mrsh_job *job, bool r) {
-	int status = job_poll(job);
-	switch (status) {
-	case TASK_STATUS_WAIT:
-		return "Running";
-	case TASK_STATUS_ERROR:
-		return "Error";
-	case TASK_STATUS_STOPPED:
-		if (job->processes.len > 0) {
-			struct mrsh_process *proc = job->processes.data[0];
-			switch (proc->signal) {
-			case SIGSTOP:
-				return r ? "Stopped (SIGSTOP)" : "Suspended (SIGSTOP)";
-			case SIGTTIN:
-				return r ? "Stopped (SIGTTIN)" : "Suspended (SIGTTIN)";
-			case SIGTTOU:
-				return r ? "Stopped (SIGTTOU)" : "Suspended (SIGTTOU)";
-			}
-		}
-		return r ? "Stopped" : "Suspended";
-	default:
-		if (job->processes.len > 0) {
-			struct mrsh_process *proc = job->processes.data[0];
-			if (proc->stat != 0) {
-				static char stat[128];
-				snprintf(stat, sizeof(stat), "Done(%d)", proc->stat);
-				return stat;
-			}
-		}
-		assert(status >= 0);
-		return "Done";
-	}
-}
-
 struct jobs_context {
 	struct mrsh_job *current, *previous;
 	bool pids;
 	bool pgids;
-	int r;
+	bool r;
 };
 
 static void show_job(struct mrsh_job *job, const struct jobs_context *ctx) {

M include/shell/job.h => include/shell/job.h +7 -0
@@ 28,6 28,9 @@ struct mrsh_job {
 	struct termios term_modes; // only valid if stopped
 	struct mrsh_state *state;
 	struct mrsh_array processes; // struct mrsh_process *
+
+	bool pending_notification; // need to print a job status notification
+	int last_status;
 };
 
 /**


@@ 88,5 91,9 @@ void update_job(struct mrsh_state *state, pid_t pid, int stat);
  */
 struct mrsh_job *job_by_id(struct mrsh_state *state,
 		const char *id, bool interactive);
+/**
+ * Return a string describing the process' state. `r` is a random boolean.
+ */
+const char *job_state_str(struct mrsh_job *job, bool r);
 
 #endif

M shell/job.c => shell/job.c +53 -0
@@ 104,6 104,7 @@ struct mrsh_job *job_create(struct mrsh_state *state,
 	job->node = mrsh_node_copy(node);
 	job->pgid = -1;
 	job->job_id = id;
+	job->last_status = TASK_STATUS_WAIT;
 	mrsh_array_add(&priv->jobs, job);
 	return job;
 }


@@ 146,6 147,20 @@ void job_add_process(struct mrsh_job *job, struct mrsh_process *proc) {
 	mrsh_array_add(&job->processes, proc);
 }
 
+static void queue_job_notifications(struct mrsh_state *state) {
+	struct mrsh_state_priv *priv = state_get_priv(state);
+
+	for (size_t i = 0; i < priv->jobs.len; ++i) {
+		struct mrsh_job *job = priv->jobs.data[i];
+		int status = job_poll(job);
+		if (status != job->last_status && job->pgid > 0 &&
+				priv->foreground_job != job) {
+			job->pending_notification = true;
+		}
+		job->last_status = status;
+	}
+}
+
 bool job_set_foreground(struct mrsh_job *job, bool foreground, bool cont) {
 	struct mrsh_state *state = job->state;
 	struct mrsh_state_priv *priv = state_get_priv(state);


@@ 191,6 206,8 @@ bool job_set_foreground(struct mrsh_job *job, bool foreground, bool cont) {
 		}
 	}
 
+	queue_job_notifications(state);
+
 	return true;
 }
 


@@ 313,6 330,8 @@ void update_job(struct mrsh_state *state, pid_t pid, int stat) {
 			}
 		}
 	}
+
+	queue_job_notifications(state);
 }
 
 // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_204


@@ 420,3 439,37 @@ struct mrsh_job *job_by_id(struct mrsh_state *state,
 	}
 	return NULL;
 }
+
+const char *job_state_str(struct mrsh_job *job, bool r) {
+	int status = job_poll(job);
+	switch (status) {
+	case TASK_STATUS_WAIT:
+		return "Running";
+	case TASK_STATUS_ERROR:
+		return "Error";
+	case TASK_STATUS_STOPPED:
+		if (job->processes.len > 0) {
+			struct mrsh_process *proc = job->processes.data[0];
+			switch (proc->signal) {
+			case SIGSTOP:
+				return r ? "Stopped (SIGSTOP)" : "Suspended (SIGSTOP)";
+			case SIGTTIN:
+				return r ? "Stopped (SIGTTIN)" : "Suspended (SIGTTIN)";
+			case SIGTTOU:
+				return r ? "Stopped (SIGTTOU)" : "Suspended (SIGTTOU)";
+			}
+		}
+		return r ? "Stopped" : "Suspended";
+	default:
+		if (job->processes.len > 0) {
+			struct mrsh_process *proc = job->processes.data[0];
+			if (proc->stat != 0) {
+				static char stat[128];
+				snprintf(stat, sizeof(stat), "Done(%d)", proc->stat);
+				return stat;
+			}
+		}
+		assert(status >= 0);
+		return "Done";
+	}
+}

M shell/task/task.c => shell/task/task.c +29 -1
@@ 382,16 382,44 @@ int run_command_list_array(struct mrsh_context *ctx, struct mrsh_array *array) {
 	return ret;
 }
 
+static void show_job(struct mrsh_job *job, struct mrsh_job *current,
+		struct mrsh_job *previous, bool r) {
+	char curprev = ' ';
+	if (job == current) {
+		curprev = '+';
+	} else if (job == previous) {
+		curprev = '-';
+	}
+	char *cmd = mrsh_node_format(job->node);
+	fprintf(stderr, "[%d] %c %s %s\n", job->job_id, curprev,
+		job_state_str(job, r), cmd);
+	free(cmd);
+}
+
 static void destroy_terminated_jobs(struct mrsh_state *state) {
 	struct mrsh_state_priv *priv = state_get_priv(state);
 
+	struct mrsh_job *current = job_by_id(state, "%+", false),
+		*previous = job_by_id(state, "%-", false);
+	bool r = rand() % 2 == 0;
+
 	for (size_t i = 0; i < priv->jobs.len; ++i) {
 		struct mrsh_job *job = priv->jobs.data[i];
-		if (job_poll(job) >= 0) {
+
+		int status = job_poll(job);
+
+		if (state->options & MRSH_OPT_NOTIFY && job->pending_notification) {
+			show_job(job, current, previous, r);
+			job->pending_notification = false;
+		}
+
+		if (status >= 0) {
 			job_destroy(job);
 			--i;
 		}
 	}
+
+	fflush(stderr);
 }
 
 int mrsh_run_program(struct mrsh_state *state, struct mrsh_program *prog) {