diff options
Diffstat (limited to '')
-rw-r--r-- | win32/process.c | 439 |
1 files changed, 439 insertions, 0 deletions
diff --git a/win32/process.c b/win32/process.c new file mode 100644 index 000000000..9a4b05597 --- /dev/null +++ b/win32/process.c | |||
@@ -0,0 +1,439 @@ | |||
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 | static intptr_t | ||
239 | mingw_spawn_applet(int mode, | ||
240 | const char *applet, | ||
241 | const char *const *argv, | ||
242 | const char *const *envp) | ||
243 | { | ||
244 | char **env = copy_environ(envp); | ||
245 | char path[MAX_PATH+20]; | ||
246 | intptr_t ret; | ||
247 | |||
248 | sprintf(path, "BUSYBOX_APPLET_NAME=%s", applet); | ||
249 | env = env_setenv(env, path); | ||
250 | ret = spawnveq(mode, get_busybox_exec_path(), argv, (const char *const *)env); | ||
251 | free_environ(env); | ||
252 | return ret; | ||
253 | } | ||
254 | |||
255 | static intptr_t | ||
256 | mingw_spawn_interpreter(int mode, const char *prog, const char *const *argv, const char *const *envp) | ||
257 | { | ||
258 | intptr_t ret; | ||
259 | char **opts; | ||
260 | int nopts; | ||
261 | const char *interpr = parse_interpreter(prog, &opts, &nopts); | ||
262 | const char **new_argv; | ||
263 | int argc = 0; | ||
264 | |||
265 | if (!interpr) | ||
266 | return spawnveq(mode, prog, argv, envp); | ||
267 | |||
268 | |||
269 | while (argv[argc]) | ||
270 | argc++; | ||
271 | new_argv = malloc(sizeof(*argv)*(argc+nopts+2)); | ||
272 | memcpy(new_argv+1, opts, sizeof(*opts)*nopts); | ||
273 | memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc); | ||
274 | new_argv[nopts+1] = prog; /* pass absolute path */ | ||
275 | |||
276 | if (ENABLE_FEATURE_PREFER_APPLETS && find_applet_by_name(interpr) >= 0) { | ||
277 | new_argv[0] = interpr; | ||
278 | ret = mingw_spawn_applet(mode, interpr, new_argv, envp); | ||
279 | } | ||
280 | else { | ||
281 | char *path = xstrdup(getenv("PATH")); | ||
282 | char *tmp = path; | ||
283 | char *iprog = find_executable(interpr, &tmp); | ||
284 | free(path); | ||
285 | if (!iprog) { | ||
286 | free(new_argv); | ||
287 | errno = ENOENT; | ||
288 | return -1; | ||
289 | } | ||
290 | new_argv[0] = iprog; | ||
291 | ret = spawnveq(mode, iprog, new_argv, envp); | ||
292 | free(iprog); | ||
293 | } | ||
294 | |||
295 | free(new_argv); | ||
296 | return ret; | ||
297 | } | ||
298 | |||
299 | static intptr_t | ||
300 | mingw_spawn_1(int mode, const char *cmd, const char *const *argv, const char *const *envp) | ||
301 | { | ||
302 | intptr_t ret; | ||
303 | |||
304 | if (ENABLE_FEATURE_PREFER_APPLETS && | ||
305 | find_applet_by_name(cmd) >= 0) | ||
306 | return mingw_spawn_applet(mode, cmd, argv, envp); | ||
307 | else if (is_absolute_path(cmd)) | ||
308 | return mingw_spawn_interpreter(mode, cmd, argv, envp); | ||
309 | else { | ||
310 | char *tmp, *path = getenv("PATH"); | ||
311 | char *prog; | ||
312 | |||
313 | if (!path) { | ||
314 | errno = ENOENT; | ||
315 | return -1; | ||
316 | } | ||
317 | |||
318 | /* executable_exists() does not return new file name */ | ||
319 | tmp = path = xstrdup(path); | ||
320 | prog = find_executable(cmd, &tmp); | ||
321 | free(path); | ||
322 | if (!prog) { | ||
323 | errno = ENOENT; | ||
324 | return -1; | ||
325 | } | ||
326 | ret = mingw_spawn_interpreter(mode, prog, argv, envp); | ||
327 | free(prog); | ||
328 | } | ||
329 | return ret; | ||
330 | } | ||
331 | |||
332 | pid_t FAST_FUNC | ||
333 | mingw_spawn(char **argv) | ||
334 | { | ||
335 | intptr_t ret; | ||
336 | |||
337 | ret = mingw_spawn_1(P_NOWAIT, argv[0], (const char *const *)argv, | ||
338 | (const char *const *)environ); | ||
339 | |||
340 | return ret == -1 ? -1 : GetProcessId((HANDLE)ret); | ||
341 | } | ||
342 | |||
343 | intptr_t FAST_FUNC | ||
344 | mingw_spawn_proc(char **argv) | ||
345 | { | ||
346 | return mingw_spawn_1(P_NOWAIT, argv[0], (const char *const *)argv, | ||
347 | (const char *const *)environ); | ||
348 | } | ||
349 | |||
350 | int | ||
351 | mingw_execvp(const char *cmd, const char *const *argv) | ||
352 | { | ||
353 | int ret = (int)mingw_spawn_1(P_WAIT, cmd, argv, (const char *const *)environ); | ||
354 | if (ret != -1) | ||
355 | exit(ret); | ||
356 | return ret; | ||
357 | } | ||
358 | |||
359 | int | ||
360 | mingw_execve(const char *cmd, const char *const *argv, const char *const *envp) | ||
361 | { | ||
362 | int ret; | ||
363 | int mode = P_WAIT; | ||
364 | |||
365 | if (ENABLE_FEATURE_PREFER_APPLETS && | ||
366 | find_applet_by_name(cmd) >= 0) | ||
367 | ret = mingw_spawn_applet(mode, cmd, argv, envp); | ||
368 | /* | ||
369 | * execve(bb_busybox_exec_path, argv, envp) won't work | ||
370 | * because argv[0] will be replaced to bb_busybox_exec_path | ||
371 | * by MSVC runtime | ||
372 | */ | ||
373 | else if (argv && cmd != argv[0] && cmd == bb_busybox_exec_path) | ||
374 | ret = mingw_spawn_applet(mode, argv[0], argv, envp); | ||
375 | else | ||
376 | ret = mingw_spawn_interpreter(mode, cmd, argv, envp); | ||
377 | if (ret != -1) | ||
378 | exit(ret); | ||
379 | return ret; | ||
380 | } | ||
381 | |||
382 | int | ||
383 | mingw_execv(const char *cmd, const char *const *argv) | ||
384 | { | ||
385 | return mingw_execve(cmd, argv, (const char *const *)environ); | ||
386 | } | ||
387 | |||
388 | /* POSIX version in libbb/procps.c */ | ||
389 | procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags UNUSED_PARAM) | ||
390 | { | ||
391 | PROCESSENTRY32 pe; | ||
392 | |||
393 | pe.dwSize = sizeof(pe); | ||
394 | if (!sp) { | ||
395 | sp = xzalloc(sizeof(struct procps_status_t)); | ||
396 | sp->snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | ||
397 | if (sp->snapshot == INVALID_HANDLE_VALUE) { | ||
398 | free(sp); | ||
399 | return NULL; | ||
400 | } | ||
401 | if (!Process32First(sp->snapshot, &pe)) { | ||
402 | CloseHandle(sp->snapshot); | ||
403 | free(sp); | ||
404 | return NULL; | ||
405 | } | ||
406 | } | ||
407 | else { | ||
408 | if (!Process32Next(sp->snapshot, &pe)) { | ||
409 | CloseHandle(sp->snapshot); | ||
410 | free(sp); | ||
411 | return NULL; | ||
412 | } | ||
413 | } | ||
414 | |||
415 | sp->pid = pe.th32ProcessID; | ||
416 | safe_strncpy(sp->comm, pe.szExeFile, COMM_LEN); | ||
417 | return sp; | ||
418 | } | ||
419 | |||
420 | int kill(pid_t pid, int sig) | ||
421 | { | ||
422 | HANDLE h; | ||
423 | |||
424 | if (pid > 0 && sig == SIGTERM) { | ||
425 | if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL && | ||
426 | TerminateProcess(h, 0)) { | ||
427 | CloseHandle(h); | ||
428 | return 0; | ||
429 | } | ||
430 | |||
431 | errno = err_win_to_posix(GetLastError()); | ||
432 | if (h != NULL) | ||
433 | CloseHandle(h); | ||
434 | return -1; | ||
435 | } | ||
436 | |||
437 | errno = EINVAL; | ||
438 | return -1; | ||
439 | } | ||