about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeah Neukirchen <leah@vuxu.org>2023-08-01 18:49:26 +0200
committerLeah Neukirchen <leah@vuxu.org>2023-08-02 13:50:38 +0200
commitf350b6f16c668cd49dc24be3b6dd44c059fe2e0c (patch)
treebeb62542f4a90d688cd77a1512e605da7925d7cb
parent8a2a4899769335bc14c62aef12cdf26f2f487f2a (diff)
downloadxe-f350b6f16c668cd49dc24be3b6dd44c059fe2e0c.tar.gz
xe-f350b6f16c668cd49dc24be3b6dd44c059fe2e0c.tar.xz
xe-f350b6f16c668cd49dc24be3b6dd44c059fe2e0c.zip
properly forward errors on exec, return 123 on any exit status 1-254
Use the CLOEXEC pipe trick to detect if exec happened; read the errno
from the pipe if it wasn't closed due to exec.
-rwxr-xr-xt/errors.t23
-rw-r--r--xe.14
-rw-r--r--xe.c35
3 files changed, 51 insertions, 11 deletions
diff --git a/t/errors.t b/t/errors.t
index c2ecd5e..eb05dc0 100755
--- a/t/errors.t
+++ b/t/errors.t
@@ -1,7 +1,7 @@
 #!/bin/sh
 export "PATH=.:$PATH"
 
-printf '1..13\n'
+printf '1..16\n'
 printf '# error handling\n'
 
 tap3 'exit code on success' <<'EOF'
@@ -21,6 +21,27 @@ a
 >>>= 123
 EOF
 
+tap3 'exit code on when command fails with 126' <<'EOF'
+xe -s 'exit 126'
+<<<
+a
+>>>= 123
+EOF
+
+tap3 'exit code on when command fails with 127' <<'EOF'
+xe -s 'exit 127'
+<<<
+a
+>>>= 123
+EOF
+
+tap3 'exit code on when command fails with 250' <<'EOF'
+xe -s 'exit 250'
+<<<
+a
+>>>= 123
+EOF
+
 tap3 'exit code on when command fails with 255' <<'EOF'
 xe -s 'exit 255'
 <<<
diff --git a/xe.1 b/xe.1
index b0c1a78..e37db66 100644
--- a/xe.1
+++ b/xe.1
@@ -1,4 +1,4 @@
-.Dd November 3, 2017
+.Dd August 1, 2023
 .Dt XE 1
 .Os
 .Sh NAME
@@ -247,7 +247,7 @@ follows the convention of GNU and OpenBSD xargs:
 .It 0
 on success
 .It 123
-if any invocation of the command exited with status 1 to 125.
+if any invocation of the command exited with status 1 to 254.
 .It 124
 if the command exited with status 255
 .It 125
diff --git a/xe.c b/xe.c
index f220a82..804833c 100644
--- a/xe.c
+++ b/xe.c
@@ -99,17 +99,15 @@ mywait()
 
 my_child:
 	if (WIFEXITED(status)) {
-		if (WEXITSTATUS(status) >= 1 && WEXITSTATUS(status) <= 125) {
+		if (WEXITSTATUS(status) == 255) {
+			fprintf(stderr, "xe: job %ld [pid %ld] exited with status 255\n", children[i].iter, (long)pid);
+			exit(124);
+		} else if (WEXITSTATUS(status) > 0) {
 			if (Fflag) {
 				fprintf(stderr, "xe: job %ld [pid %ld] exited with status %d\n", children[i].iter, (long)pid, WEXITSTATUS(status));
 				exit(123);
 			}
 			failed = 1;
-		} else if (WEXITSTATUS(status) == 255) {
-			fprintf(stderr, "xe: job %ld [pid %ld] exited with status 255\n", children[i].iter, (long)pid);
-			exit(124);
-		} else if (WEXITSTATUS(status) > 125) {
-			exit(WEXITSTATUS(status));
 		}
 	} else if (WIFSIGNALED(status)) {
 		fprintf(stderr, "xe: job %ld [pid %ld] terminated by signal %d\n",
@@ -232,8 +230,17 @@ run()
 			exit(126);
 	}
 
+	unsigned char status;
+	int alivepipefd[2];
+	if (pipe(alivepipefd) < 0)
+		exit(126);
+	fcntl(alivepipefd[0], F_SETFD, FD_CLOEXEC);
+	fcntl(alivepipefd[1], F_SETFD, FD_CLOEXEC);
+
 	pid = fork();
 	if (pid == 0) {  // in child
+		close(alivepipefd[0]);
+
 		char iter[32];
 		snprintf(iter, sizeof iter, "%ld", iterations);
 		setenv("ITER", iter, 1);
@@ -268,14 +275,26 @@ run()
 
 		execvp(args[0], args);
 
-		int status = (errno == ENOENT ? 127 : 126);
+		status = (errno == ENOENT ? 127 : 126);
+		if (write(alivepipefd[1], &status, 1) != 1) {
+			/* ignored */
+		}
 		fprintf(stderr, "xe: %s: %s\n", args[0], strerror(errno));
-		exit(status);
+		_exit(status);
 	} else if (pid < 0) {  // fork failed
 		fprintf(stderr, "xe: %s: %s\n", args[0], strerror(errno));
 		exit(126);
 	}
 
+	close(alivepipefd[1]);
+	if (read(alivepipefd[0], &status, 1) == 1) {
+		if (status == 126)
+			exit(126);
+		if (status == 127)
+			exit(127);
+	}
+	close(alivepipefd[0]);
+
 	if (Lflag) {
 		long iter = iterations;
 		lpid = fork();