ac201b690420782d7e320c9060a875a2201a7ead — Drew DeVault 18 days ago 27145de
Implement 'wait' builtin
4 files changed, 112 insertions(+), 0 deletions(-)

M builtin/builtin.c
A builtin/wait.c
M include/builtin.h
M meson.build
M builtin/builtin.c => builtin/builtin.c +1 -0
@@ 38,6 38,7 @@ { "umask", builtin_umask, false },
  	{ "unalias", builtin_unalias, false },
  	{ "unset", builtin_unset, true },
+ 	{ "wait", builtin_wait, false },
  };
  
  // The following commands are explicitly unspecified by POSIX

A builtin/wait.c => builtin/wait.c +109 -0
@@ 0,0 1,109 @@
+ #define _POSIX_C_SOURCE 200112L
+ #include <errno.h>
+ #include <stdbool.h>
+ #include <stdlib.h>
+ #include <stdio.h>
+ #include <string.h>
+ #include <sys/wait.h>
+ #include "builtin.h"
+ #include "shell/process.h"
+ #include "shell/shell.h"
+ 
+ struct wait_handle {
+ 	pid_t pid;
+ 	int status;
+ };
+ 
+ int builtin_wait(struct mrsh_state *state, int argc, char *argv[]) {
+ 	int npids = argc - 1;
+ 	if (npids == 0) {
+ 		npids = state->processes.len;
+ 	}
+ 	struct wait_handle *pids = malloc(npids * sizeof(struct wait_handle));
+ 	if (pids == NULL) {
+ 		fprintf(stderr, "wait: unable to allocate pid list");
+ 		return EXIT_FAILURE;
+ 	}
+ 
+ 	if (argc == 1) {
+ 		/* All known processes */
+ 		int _npids = 0;
+ 		for (size_t j = 0; j < state->processes.len; ++j) {
+ 			struct process *process = state->processes.data[j];
+ 			if (process->terminated) {
+ 				continue;
+ 			}
+ 			pids[_npids].pid = process->pid;
+ 			pids[_npids].status = -1;
+ 			++_npids;
+ 		}
+ 		npids = _npids;
+ 	} else {
+ 		for (int i = 1; i < argc; ++i) {
+ 			if (argv[i][0] == '%') {
+ 				// TODO
+ 				fprintf(stderr, "wait: job control IDs are unimplemented\n");
+ 				goto failure;
+ 			} else {
+ 				char *endptr;
+ 				pid_t pid = (pid_t)strtol(argv[i], &endptr, 10);
+ 				if (*endptr != '\0' || argv[i][0] == '\0') {
+ 					fprintf(stderr, "wait: error parsing pid '%s'", argv[i]);
+ 					goto failure;
+ 				}
+ 				if (pid <= 0) {
+ 					fprintf(stderr, "wait: invalid process ID\n");
+ 					goto failure;
+ 				}
+ 				pids[i - 1].pid = pid;
+ 				pids[i - 1].status = -1;
+ 				/* Check if this pid is known */
+ 				bool found = false;
+ 				for (size_t j = 0; j < state->processes.len; ++j) {
+ 					struct process *process = state->processes.data[j];
+ 					if (process->pid == pid) {
+ 						if (process->terminated) {
+ 							pids[i - 1].status = process->stat;
+ 						}
+ 						found = true;
+ 						break;
+ 					}
+ 				}
+ 				if (!found) {
+ 					/* Unknown pids are assumed to have exited 127 */
+ 					pids[i - 1].status = 127;
+ 				}
+ 			}
+ 		}
+ 	}
+ 
+ 	for (int i = 0; i < npids; ++i) {
+ 		int stat;
+ 		pid_t waited = waitpid(pids[i].pid, &stat, 0);
+ 		// TODO: update jobs internal state?
+ 		update_process(state, waited, stat);
+ 		if (waited == -1) {
+ 			if (errno == ECHILD) {
+ 				continue;
+ 			}
+ 			fprintf(stderr, "wait: %s\n", strerror(errno));
+ 			goto failure;
+ 		}
+ 		if (WIFEXITED(stat)) {
+ 			pids[i].status = WEXITSTATUS(stat);
+ 		} else {
+ 			pids[i].status = 129;
+ 		}
+ 	}
+ 
+ 	free(pids);
+ 	if (argc == 1) {
+ 		return EXIT_SUCCESS;
+ 	} else {
+ 		return pids[npids - 1].status;
+ 	}
+ 
+ failure:
+ 	free(pids);
+ 	return EXIT_FAILURE;
+ }

M include/builtin.h => include/builtin.h +1 -0
@@ 31,6 31,7 @@ int builtin_umask(struct mrsh_state *state, int argc, char *argv[]);
  int builtin_unalias(struct mrsh_state *state, int argc, char *argv[]);
  int builtin_unset(struct mrsh_state *state, int argc, char *argv[]);
+ int builtin_wait(struct mrsh_state *state, int argc, char *argv[]);
  
  int builtin_unspecified(struct mrsh_state *state, int argc, char *argv[]);
  

M meson.build => meson.build +1 -0
@@ 97,6 97,7 @@ 'builtin/unalias.c',
  		'builtin/unset.c',
  		'builtin/unspecified.c',
+ 		'builtin/wait.c',
  		'getopt.c',
  		'hashtable.c',
  		'parser/arithm.c',