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