-
Notifications
You must be signed in to change notification settings - Fork 1
/
chainif.c
132 lines (119 loc) · 3.01 KB
/
chainif.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <spawn.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;
static char **
getblock(char **const args)
{
if (!*args)
return NULL;
if (!**args) {
*args = NULL;
return &args[1];
}
if (**args != ' ')
return NULL;
++*args;
return getblock(&args[1]);
}
static void
usage(void)
{
static char const message[] =
"Usage: chainif [-AEn] { condition... } { chain... } cmd...\n";
if (fputs(message, stderr) == EOF)
perror("fputs");
}
int
main(int const argc, char **const argv)
{
bool appendflag = false;
bool envflag = false;
bool negateflag = false;
for (int opt; (opt = getopt(argc, argv, "+AEn")) != -1;) {
switch (opt) {
case 'A':
appendflag = true;
break;
case 'E':
envflag = true;
break;
case 'n':
negateflag = true;
break;
default:
usage();
return 2;
}
}
char **const condition = &argv[optind];
char **const chain = getblock(condition);
if (!chain) {
usage();
return 2;
}
char **const cmd = getblock(chain);
if (!cmd) {
usage();
return 2;
}
bool dochain = *condition == NULL;
unsigned char exitstatus = 0;
if (!dochain) {
pid_t pid;
int const ret = posix_spawnp(&pid, *condition, NULL, NULL,
condition, environ);
if (ret) {
perror("posix_spawnp");
return 2;
}
int status;
for (;;) {
int const ret = waitpid(pid, &status, 0);
if (ret != -1)
break;
if (errno != EINTR) {
perror("waitpid");
return 2;
}
}
exitstatus = WIFEXITED(status)
? WEXITSTATUS(status)
: 128 + WTERMSIG(status);
dochain = WIFEXITED(status) && WEXITSTATUS(status) == 0;
}
char *const *const toexec = dochain != negateflag
? memmove(&chain[1], chain, (cmd - &chain[1]) * sizeof *chain)
: (appendflag ? chain : cmd);
if (!*toexec)
return 0;
if (envflag) {
char buf[3 + 1];
int const ret = snprintf(buf, sizeof buf, "%hhu", exitstatus);
if (ret < 0) {
perror("snprintf");
return 2;
}
if ((size_t)ret >= sizeof buf) {
static char const efmt[] =
"snprintf: the buffer is %zd byte%s too small.\n";
ptrdiff_t const diff = ret - sizeof buf;
if (fprintf(stderr, efmt, diff, &"s"[diff == 1]) == EOF)
perror("fprintf");
return 2;
}
if (setenv("?", buf, 1)) {
perror("setenv");
return 2;
}
}
(void)execvp(*toexec, toexec);
perror("execvp");
return 2;
}