fix and refactor child reaping logic in wordexp
[musl] / src / misc / wordexp.c
1 #include <wordexp.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <sys/wait.h>
9 #include <signal.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include "pthread_impl.h"
13
14 static void reap(pid_t pid)
15 {
16         int status;
17         for (;;) {
18                 if (waitpid(pid, &status, 0) < 0) {
19                         if (errno != EINTR) return;
20                 } else {
21                         if (WIFEXITED(status)) return;
22                 }
23         }
24 }
25
26 static char *getword(FILE *f)
27 {
28         char *s = 0;
29         return getdelim(&s, (size_t [1]){0}, 0, f) < 0 ? 0 : s;
30 }
31
32 static int do_wordexp(const char *s, wordexp_t *we, int flags)
33 {
34         size_t i, l;
35         int sq=0, dq=0;
36         size_t np=0;
37         char *w, **tmp;
38         char *redir = (flags & WRDE_SHOWERR) ? "" : "2>/dev/null";
39         int err = 0;
40         FILE *f;
41         size_t wc = 0;
42         char **wv = 0;
43         int p[2];
44         pid_t pid;
45         sigset_t set;
46
47         if (flags & WRDE_REUSE) wordfree(we);
48
49         if (flags & WRDE_NOCMD) for (i=0; s[i]; i++) switch (s[i]) {
50         case '\\':
51                 if (!sq) i++;
52                 break;
53         case '\'':
54                 if (!dq) sq^=1;
55                 break;
56         case '"':
57                 if (!sq) dq^=1;
58                 break;
59         case '(':
60                 if (np) {
61                         np++;
62                         break;
63                 }
64         case ')':
65                 if (np) {
66                         np--;
67                         break;
68                 }
69         case '\n':
70         case '|':
71         case '&':
72         case ';':
73         case '<':
74         case '>':
75         case '{':
76         case '}':
77                 if (!(sq|dq|np)) return WRDE_BADCHAR;
78                 break;
79         case '$':
80                 if (s[i+1]=='(' && s[i+2]=='(') {
81                         i += 2;
82                         np += 2;
83                         break;
84                 } else if (s[i+1] != '(') break;
85         case '`':
86                 if (sq) break;
87                 return WRDE_CMDSUB;
88         }
89
90         if (flags & WRDE_APPEND) {
91                 wc = we->we_wordc;
92                 wv = we->we_wordv;
93         }
94
95         i = wc;
96         if (flags & WRDE_DOOFFS) {
97                 if (we->we_offs > SIZE_MAX/sizeof(void *)/4)
98                         goto nospace;
99                 i += we->we_offs;
100         } else {
101                 we->we_offs = 0;
102         }
103
104         if (pipe2(p, O_CLOEXEC) < 0) goto nospace;
105         __block_all_sigs(&set);
106         pid = fork();
107         __restore_sigs(&set);
108         if (pid < 0) {
109                 close(p[0]);
110                 close(p[1]);
111                 goto nospace;
112         }
113         if (!pid) {
114                 if (p[1] == 1) fcntl(1, F_SETFD, 0);
115                 else dup2(p[1], 1);
116                 execl("/bin/sh", "sh", "-c",
117                         "eval \"printf %s\\\\\\\\0 x $1 $2\"",
118                         "sh", s, redir, (char *)0);
119                 _exit(1);
120         }
121         close(p[1]);
122         
123         f = fdopen(p[0], "r");
124         if (!f) {
125                 close(p[0]);
126                 kill(pid, SIGKILL);
127                 reap(pid);
128                 goto nospace;
129         }
130
131         l = wv ? i+1 : 0;
132
133         free(getword(f));
134         if (feof(f)) {
135                 fclose(f);
136                 reap(pid);
137                 return WRDE_SYNTAX;
138         }
139
140         while ((w = getword(f))) {
141                 if (i+1 >= l) {
142                         l += l/2+10;
143                         tmp = realloc(wv, l*sizeof(char *));
144                         if (!tmp) break;
145                         wv = tmp;
146                 }
147                 wv[i++] = w;
148                 wv[i] = 0;
149         }
150         if (!feof(f)) err = WRDE_NOSPACE;
151
152         fclose(f);
153         reap(pid);
154
155         if (!wv) wv = calloc(i+1, sizeof *wv);
156
157         we->we_wordv = wv;
158         we->we_wordc = i;
159
160         if (flags & WRDE_DOOFFS) {
161                 if (wv) for (i=we->we_offs; i; i--)
162                         we->we_wordv[i-1] = 0;
163                 we->we_wordc -= we->we_offs;
164         }
165         return err;
166
167 nospace:
168         if (!(flags & WRDE_APPEND)) {
169                 we->we_wordc = 0;
170                 we->we_wordv = 0;
171         }
172         return WRDE_NOSPACE;
173 }
174
175 int wordexp(const char *restrict s, wordexp_t *restrict we, int flags)
176 {
177         int r, cs;
178         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
179         r = do_wordexp(s, we, flags);
180         pthread_setcancelstate(cs, 0);
181         return r;
182 }
183
184 void wordfree(wordexp_t *we)
185 {
186         size_t i;
187         if (!we->we_wordv) return;
188         for (i=0; i<we->we_wordc; i++) free(we->we_wordv[we->we_offs+i]);
189         free(we->we_wordv);
190         we->we_wordv = 0;
191         we->we_wordc = 0;
192 }