diff options
Diffstat (limited to 'win32/process.c')
-rw-r--r-- | win32/process.c | 425 |
1 files changed, 425 insertions, 0 deletions
diff --git a/win32/process.c b/win32/process.c new file mode 100644 index 000000000..968ea9afd --- /dev/null +++ b/win32/process.c | |||
@@ -0,0 +1,425 @@ | |||
1 | #include "libbb.h" | ||
2 | #include <tlhelp32.h> | ||
3 | |||
4 | int waitpid(pid_t pid, int *status, int options) | ||
5 | { | ||
6 | HANDLE proc; | ||
7 | intptr_t ret; | ||
8 | |||
9 | /* Windows does not understand parent-child */ | ||
10 | if (pid > 0 && options == 0) { | ||
11 | if ( (proc=OpenProcess(SYNCHRONIZE|PROCESS_QUERY_INFORMATION, | ||
12 | FALSE, pid)) != NULL ) { | ||
13 | ret = _cwait(status, (intptr_t)proc, 0); | ||
14 | CloseHandle(proc); | ||
15 | return ret == -1 ? -1 : pid; | ||
16 | } | ||
17 | } | ||
18 | errno = EINVAL; | ||
19 | return -1; | ||
20 | } | ||
21 | |||
22 | const char * | ||
23 | next_path_sep(const char *path) | ||
24 | { | ||
25 | static const char *from = NULL, *to; | ||
26 | static int has_semicolon; | ||
27 | int len = strlen(path); | ||
28 | |||
29 | if (!from || !(path >= from && path+len <= to)) { | ||
30 | from = path; | ||
31 | to = from+len; | ||
32 | has_semicolon = strchr(path, ';') != NULL; | ||
33 | } | ||
34 | |||
35 | /* Semicolons take precedence, it's Windows PATH */ | ||
36 | if (has_semicolon) | ||
37 | return strchr(path, ';'); | ||
38 | /* PATH=C:, not really a separator */ | ||
39 | return strchr(has_dos_drive_prefix(path) ? path+2 : path, ':'); | ||
40 | } | ||
41 | |||
42 | #define MAX_OPT 10 | ||
43 | |||
44 | static const char * | ||
45 | parse_interpreter(const char *cmd, char ***opts, int *nopts) | ||
46 | { | ||
47 | static char buf[100], *opt[MAX_OPT]; | ||
48 | char *p, *s, *t; | ||
49 | int n, fd; | ||
50 | |||
51 | *nopts = 0; | ||
52 | *opts = opt; | ||
53 | |||
54 | /* don't even try a .exe */ | ||
55 | n = strlen(cmd); | ||
56 | if (n >= 4 && | ||
57 | (!strcasecmp(cmd+n-4, ".exe") || | ||
58 | !strcasecmp(cmd+n-4, ".com"))) | ||
59 | return NULL; | ||
60 | |||
61 | fd = open(cmd, O_RDONLY); | ||
62 | if (fd < 0) | ||
63 | return NULL; | ||
64 | n = read(fd, buf, sizeof(buf)-1); | ||
65 | close(fd); | ||
66 | if (n < 4) /* at least '#!/x' and not error */ | ||
67 | return NULL; | ||
68 | |||
69 | /* | ||
70 | * See http://www.in-ulm.de/~mascheck/various/shebang/ for trivia | ||
71 | * relating to '#!'. | ||
72 | */ | ||
73 | if (buf[0] != '#' || buf[1] != '!') | ||
74 | return NULL; | ||
75 | buf[n] = '\0'; | ||
76 | p = strchr(buf, '\n'); | ||
77 | if (!p) | ||
78 | return NULL; | ||
79 | *p = '\0'; | ||
80 | |||
81 | /* remove trailing whitespace */ | ||
82 | while ( isspace(*--p) ) { | ||
83 | *p = '\0'; | ||
84 | } | ||
85 | |||
86 | /* skip whitespace after '#!' */ | ||
87 | for ( s=buf+2; *s && isspace(*s); ++s ) { | ||
88 | } | ||
89 | |||
90 | /* move to end of interpreter path (which may not contain spaces) */ | ||
91 | for ( ; *s && !isspace(*s); ++s ) { | ||
92 | } | ||
93 | |||
94 | n = 0; | ||
95 | if ( *s != '\0' ) { | ||
96 | /* there are options */ | ||
97 | *s++ = '\0'; | ||
98 | |||
99 | while ( (t=strtok(s, " \t")) && n < MAX_OPT ) { | ||
100 | s = NULL; | ||
101 | opt[n++] = t; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /* find interpreter name */ | ||
106 | if (!(p = strrchr(buf+2, '/'))) | ||
107 | return NULL; | ||
108 | |||
109 | *nopts = n; | ||
110 | *opts = opt; | ||
111 | |||
112 | return p+1; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx | ||
117 | * (Parsing C++ Command-Line Arguments) | ||
118 | */ | ||
119 | static char * | ||
120 | quote_arg(const char *arg) | ||
121 | { | ||
122 | int len = 0, n = 0; | ||
123 | int force_quotes = 0; | ||
124 | char *q, *d; | ||
125 | const char *p = arg; | ||
126 | |||
127 | /* empty arguments must be quoted */ | ||
128 | if (!*p) { | ||
129 | force_quotes = 1; | ||
130 | } | ||
131 | |||
132 | while (*p) { | ||
133 | if (isspace(*p)) { | ||
134 | /* arguments containing whitespace must be quoted */ | ||
135 | force_quotes = 1; | ||
136 | } | ||
137 | else if (*p == '"') { | ||
138 | /* double quotes in arguments need to be escaped */ | ||
139 | n++; | ||
140 | } | ||
141 | else if (*p == '\\') { | ||
142 | /* count contiguous backslashes */ | ||
143 | int count = 0; | ||
144 | while (*p == '\\') { | ||
145 | count++; | ||
146 | p++; | ||
147 | len++; | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Only escape backslashes before explicit double quotes or | ||
152 | * or where the backslashes are at the end of an argument | ||
153 | * that is scheduled to be quoted. | ||
154 | */ | ||
155 | if (*p == '"' || (force_quotes && *p == '\0')) { | ||
156 | n += count*2 + 1; | ||
157 | } | ||
158 | |||
159 | if (*p == '\0') { | ||
160 | break; | ||
161 | } | ||
162 | continue; | ||
163 | } | ||
164 | len++; | ||
165 | p++; | ||
166 | } | ||
167 | |||
168 | if (!force_quotes && n == 0) { | ||
169 | return (char*)arg; | ||
170 | } | ||
171 | |||
172 | /* insert double quotes and backslashes where necessary */ | ||
173 | d = q = xmalloc(len+n+3); | ||
174 | if (force_quotes) { | ||
175 | *d++ = '"'; | ||
176 | } | ||
177 | |||
178 | while (*arg) { | ||
179 | if (*arg == '"') { | ||
180 | *d++ = '\\'; | ||
181 | } | ||
182 | else if (*arg == '\\') { | ||
183 | int count = 0; | ||
184 | while (*arg == '\\') { | ||
185 | count++; | ||
186 | *d++ = *arg++; | ||
187 | } | ||
188 | |||
189 | if (*arg == '"' || (force_quotes && *arg == '\0')) { | ||
190 | while (count-- > 0) { | ||
191 | *d++ = '\\'; | ||
192 | } | ||
193 | if (*arg == '"') { | ||
194 | *d++ = '\\'; | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | if (*arg != '\0') { | ||
199 | *d++ = *arg++; | ||
200 | } | ||
201 | } | ||
202 | if (force_quotes) { | ||
203 | *d++ = '"'; | ||
204 | } | ||
205 | *d = '\0'; | ||
206 | |||
207 | return q; | ||
208 | } | ||
209 | |||
210 | static intptr_t | ||
211 | spawnveq(int mode, const char *path, const char *const *argv, const char *const *env) | ||
212 | { | ||
213 | char **new_argv; | ||
214 | int i, argc = 0; | ||
215 | intptr_t ret; | ||
216 | |||
217 | if (!argv) { | ||
218 | const char *empty_argv[] = { path, NULL }; | ||
219 | return spawnve(mode, path, empty_argv, env); | ||
220 | } | ||
221 | |||
222 | |||
223 | while (argv[argc]) | ||
224 | argc++; | ||
225 | |||
226 | new_argv = malloc(sizeof(*argv)*(argc+1)); | ||
227 | for (i = 0;i < argc;i++) | ||
228 | new_argv[i] = quote_arg(argv[i]); | ||
229 | new_argv[argc] = NULL; | ||
230 | ret = spawnve(mode, path, (const char *const *)new_argv, env); | ||
231 | for (i = 0;i < argc;i++) | ||
232 | if (new_argv[i] != argv[i]) | ||
233 | free(new_argv[i]); | ||
234 | free(new_argv); | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | #if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE | ||
239 | static intptr_t | ||
240 | mingw_spawn_applet(int mode, | ||
241 | const char *const *argv, | ||
242 | const char *const *envp) | ||
243 | { | ||
244 | return spawnveq(mode, bb_busybox_exec_path, argv, envp); | ||
245 | } | ||
246 | #endif | ||
247 | |||
248 | static intptr_t | ||
249 | mingw_spawn_interpreter(int mode, const char *prog, const char *const *argv, const char *const *envp) | ||
250 | { | ||
251 | intptr_t ret; | ||
252 | char **opts; | ||
253 | int nopts; | ||
254 | const char *interpr = parse_interpreter(prog, &opts, &nopts); | ||
255 | const char **new_argv; | ||
256 | int argc = 0; | ||
257 | |||
258 | if (!interpr) | ||
259 | return spawnveq(mode, prog, argv, envp); | ||
260 | |||
261 | |||
262 | while (argv[argc]) | ||
263 | argc++; | ||
264 | new_argv = malloc(sizeof(*argv)*(argc+nopts+2)); | ||
265 | memcpy(new_argv+1, opts, sizeof(*opts)*nopts); | ||
266 | memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc); | ||
267 | new_argv[nopts+1] = prog; /* pass absolute path */ | ||
268 | |||
269 | #if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE | ||
270 | if (find_applet_by_name(interpr) >= 0) { | ||
271 | new_argv[0] = interpr; | ||
272 | ret = mingw_spawn_applet(mode, new_argv, envp); | ||
273 | } else | ||
274 | #endif | ||
275 | { | ||
276 | char *path = xstrdup(getenv("PATH")); | ||
277 | char *tmp = path; | ||
278 | char *iprog = find_executable(interpr, &tmp); | ||
279 | free(path); | ||
280 | if (!iprog) { | ||
281 | free(new_argv); | ||
282 | errno = ENOENT; | ||
283 | return -1; | ||
284 | } | ||
285 | new_argv[0] = iprog; | ||
286 | ret = spawnveq(mode, iprog, new_argv, envp); | ||
287 | free(iprog); | ||
288 | } | ||
289 | |||
290 | free(new_argv); | ||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | static intptr_t | ||
295 | mingw_spawn_1(int mode, const char *cmd, const char *const *argv, const char *const *envp) | ||
296 | { | ||
297 | intptr_t ret; | ||
298 | |||
299 | #if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE | ||
300 | if (find_applet_by_name(cmd) >= 0) | ||
301 | return mingw_spawn_applet(mode, argv, envp); | ||
302 | else | ||
303 | #endif | ||
304 | if (strchr(cmd, '/') || strchr(cmd, '\\')) | ||
305 | return mingw_spawn_interpreter(mode, cmd, argv, envp); | ||
306 | else { | ||
307 | char *tmp, *path = getenv("PATH"); | ||
308 | char *prog; | ||
309 | |||
310 | if (!path) { | ||
311 | errno = ENOENT; | ||
312 | return -1; | ||
313 | } | ||
314 | |||
315 | /* executable_exists() does not return new file name */ | ||
316 | tmp = path = xstrdup(path); | ||
317 | prog = find_executable(cmd, &tmp); | ||
318 | free(path); | ||
319 | if (!prog) { | ||
320 | errno = ENOENT; | ||
321 | return -1; | ||
322 | } | ||
323 | ret = mingw_spawn_interpreter(mode, prog, argv, envp); | ||
324 | free(prog); | ||
325 | } | ||
326 | return ret; | ||
327 | } | ||
328 | |||
329 | pid_t FAST_FUNC | ||
330 | mingw_spawn(char **argv) | ||
331 | { | ||
332 | intptr_t ret; | ||
333 | |||
334 | ret = mingw_spawn_1(P_NOWAIT, argv[0], (const char *const *)argv, | ||
335 | (const char *const *)environ); | ||
336 | |||
337 | return ret == -1 ? -1 : GetProcessId((HANDLE)ret); | ||
338 | } | ||
339 | |||
340 | intptr_t FAST_FUNC | ||
341 | mingw_spawn_proc(char **argv) | ||
342 | { | ||
343 | return mingw_spawn_1(P_NOWAIT, argv[0], (const char *const *)argv, | ||
344 | (const char *const *)environ); | ||
345 | } | ||
346 | |||
347 | int | ||
348 | mingw_execvp(const char *cmd, const char *const *argv) | ||
349 | { | ||
350 | int ret = (int)mingw_spawn_1(P_WAIT, cmd, argv, (const char *const *)environ); | ||
351 | if (ret != -1) | ||
352 | exit(ret); | ||
353 | return ret; | ||
354 | } | ||
355 | |||
356 | int | ||
357 | mingw_execve(const char *cmd, const char *const *argv, const char *const *envp) | ||
358 | { | ||
359 | int ret; | ||
360 | int mode = P_WAIT; | ||
361 | |||
362 | ret = (int)mingw_spawn_interpreter(mode, cmd, argv, envp); | ||
363 | if (ret != -1) | ||
364 | exit(ret); | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | int | ||
369 | mingw_execv(const char *cmd, const char *const *argv) | ||
370 | { | ||
371 | return mingw_execve(cmd, argv, (const char *const *)environ); | ||
372 | } | ||
373 | |||
374 | /* POSIX version in libbb/procps.c */ | ||
375 | procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags UNUSED_PARAM) | ||
376 | { | ||
377 | PROCESSENTRY32 pe; | ||
378 | |||
379 | pe.dwSize = sizeof(pe); | ||
380 | if (!sp) { | ||
381 | sp = xzalloc(sizeof(struct procps_status_t)); | ||
382 | sp->snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | ||
383 | if (sp->snapshot == INVALID_HANDLE_VALUE) { | ||
384 | free(sp); | ||
385 | return NULL; | ||
386 | } | ||
387 | if (!Process32First(sp->snapshot, &pe)) { | ||
388 | CloseHandle(sp->snapshot); | ||
389 | free(sp); | ||
390 | return NULL; | ||
391 | } | ||
392 | } | ||
393 | else { | ||
394 | if (!Process32Next(sp->snapshot, &pe)) { | ||
395 | CloseHandle(sp->snapshot); | ||
396 | free(sp); | ||
397 | return NULL; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | sp->pid = pe.th32ProcessID; | ||
402 | safe_strncpy(sp->comm, pe.szExeFile, COMM_LEN); | ||
403 | return sp; | ||
404 | } | ||
405 | |||
406 | int kill(pid_t pid, int sig) | ||
407 | { | ||
408 | HANDLE h; | ||
409 | |||
410 | if (pid > 0 && sig == SIGTERM) { | ||
411 | if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL && | ||
412 | TerminateProcess(h, 0)) { | ||
413 | CloseHandle(h); | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | errno = err_win_to_posix(GetLastError()); | ||
418 | if (h != NULL) | ||
419 | CloseHandle(h); | ||
420 | return -1; | ||
421 | } | ||
422 | |||
423 | errno = EINVAL; | ||
424 | return -1; | ||
425 | } | ||