xref: /inferno-os/utils/rcsh/Nt.c (revision 6e425a9de8c003b5a733621a6b6730ec3cc902b8)
1 #include "rc.h"
2 #include <windows.h>
3 
4 enum {
5 	Nchild	= 100,
6 };
7 
8 typedef struct Child	Child;
9 
10 struct Child {
11 	int	pid;
12 	HANDLE	handle;
13 };
14 
15 static Child child[Nchild];
16 
17 static void
18 winerror(void)
19 {
20 	int e, r;
21 	char buf[100], *p, *q;
22 
23 	e = GetLastError();
24 
25 	r = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
26 		0, e, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
27 		buf, sizeof(buf), 0);
28 
29 	if(r == 0)
30 		snprint(buf, sizeof(buf), "windows error %d", e);
31 
32 	for(p=q=buf; *p; p++) {
33 		if(*p == '\r')
34 			continue;
35 		if(*p == '\n')
36 			*q++ = ' ';
37 		else
38 			*q++ = *p;
39 	}
40 	*q = 0;
41 	errstr(buf, sizeof buf);
42 }
43 
44 static int
45 badentry(char *filename)
46 {
47 	if(*filename == 0)
48 		return 1;
49 	if(filename[0] == '.'){
50 		if(filename[1] == 0)
51 			return 1;
52 		if(filename[1] == '.' && filename[2] == 0)
53 			return 1;
54 	}
55 	return 0;
56 }
57 
58 Direntry*
59 readdirect(char *path)
60 {
61 	long n;
62 	HANDLE h;
63 	Direntry *d;
64 	char fullpath[MAX_PATH];
65 	WIN32_FIND_DATA data;
66 
67 	snprint(fullpath, MAX_PATH, "%s\\*.*", path);
68 
69 	h = FindFirstFile(fullpath, &data);
70 	if(h == INVALID_HANDLE_VALUE)
71 		return 0;
72 
73 	n = 0;
74 	d = 0;
75 	for(;;){
76 		if(!badentry(data.cFileName)){
77 			d = realloc(d, (n+2)*sizeof(Direntry));
78 			if(d == 0){
79 				werrstr("memory allocation");
80 				return 0;
81 			}
82 			d[n].name = malloc(strlen(data.cFileName)+1);
83 			if(d[n].name == 0){
84 				werrstr("memory allocation");
85 				return 0;
86 			}
87 			strcpy(d[n].name, data.cFileName);
88 			if(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
89 				d[n].isdir = 1;
90 			else
91 				d[n].isdir = 0;
92 			n++;
93 		}
94 		if(FindNextFile(h, &data) == 0)
95 			break;
96 	}
97 	FindClose(h);
98 	if(d){
99 		d[n].name = 0;
100 		d[n].isdir = 0;
101 	}
102 	return d;
103 }
104 
105 void
106 fatal(char *fmt, ...)
107 {
108 	char buf[512];
109 	va_list arg;
110 
111 	va_start(arg, fmt);
112 	vseprint(buf, buf+sizeof(buf), fmt, arg);
113 	va_end(arg);
114 
115 	fprint(2, "rc: %s\n", buf);
116 	_exits(buf);
117 }
118 
119 static int
120 tas(int *p)
121 {
122 	int v;
123 
124 	_asm {
125 		mov	eax, p
126 		mov	ebx, 1
127 		xchg	ebx, [eax]
128 		mov	v, ebx
129 	}
130 
131 	return v;
132 }
133 
134 static void
135 lock(Lock *lk)
136 {
137 	int i;
138 
139 	/* easy case */
140 	if(!tas(&lk->val))
141 		return;
142 
143 	/* for muli processor machines */
144 	for(i=0; i<100; i++)
145 		if(!tas(&lk->val))
146 			return;
147 
148 	SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
149 	for(;;) {
150 		for(i=0; i<10000; i++) {
151 			Sleep(0);
152 			if(!tas(&lk->val)) {
153 				SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_NORMAL);
154 				return;
155 			}
156 		}
157 	}
158 }
159 
160 static void
161 unlock(Lock *lk)
162 {
163 	lk->val = 0;
164 }
165 
166 int
167 refinc(Ref *r)
168 {
169 	int i;
170 
171 	lock(&r->lk);
172 	i = r->ref;
173 	r->ref++;
174 	unlock(&r->lk);
175 	return i;
176 }
177 
178 int
179 refdec(Ref *r)
180 {
181 	int i;
182 
183 	lock(&r->lk);
184 	r->ref--;
185 	i = r->ref;
186 	unlock(&r->lk);
187 
188 	return i;
189 }
190 
191 /*
192  * windows quoting rules - I think
193  * Words are seperated by space or tab
194  * Words containing a space or tab can be quoted using "
195  * 2N backslashes + " ==> N backslashes and end quote
196  * 2N+1 backslashes + " ==> N backslashes + literal "
197  * N backslashes not followed by " ==> N backslashes
198  */
199 static char *
200 dblquote(char *cmd, char *s)
201 {
202 	int nb;
203 	char *p;
204 
205 	for(p=s; *p; p++)
206 		if(*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r' || *p == '"')
207 			break;
208 
209 	if(*p == 0){				/* easy case */
210 		strcpy(cmd, s);
211 		return cmd+(p-s);
212 	}
213 
214 	*cmd++ = '"';
215 	for(;;) {
216 		for(nb=0; *s=='\\'; nb++)
217 			*cmd++ = *s++;
218 
219 		if(*s == 0) {			/* trailing backslashes -> 2N */
220 			while(nb-- > 0)
221 				*cmd++ = '\\';
222 			break;
223 		}
224 
225 		if(*s == '"') {			/* literal quote -> 2N+1 backslashes */
226 			while(nb-- > 0)
227 				*cmd++ = '\\';
228 			*cmd++ = '\\';		/* escape the quote */
229 		}
230 		*cmd++ = *s++;
231 	}
232 
233 	*cmd++ = '"';
234 	*cmd = 0;
235 
236 	return cmd;
237 }
238 
239 static char *
240 proccmd(char **argv)
241 {
242 	int i, n;
243 	char *cmd, *p;
244 
245 		/* conservatively calculate length of command;
246 		 * backslash expansion can cause growth in dblquote().
247 		 */
248 	for(i=0,n=0; argv[i]; i++) {
249 		n += 2*strlen(argv[i]);
250 	}
251 	n++;
252 
253 	cmd = malloc(n);
254 	for(i=0,p=cmd; argv[i]; i++) {
255 		p = dblquote(p, argv[i]);
256 		*p++ = ' ';
257 	}
258 	if(p != cmd)
259 		p--;
260 	*p = 0;
261 
262 	return cmd;
263 }
264 
265 static char *
266 exportenv(char **e)
267 {
268 	int i, j, n;
269 	char *buf;
270 
271 	if(e == 0 || *e == 0)
272 		return 0;
273 
274 	buf = 0;
275 	n = 0;
276 	for(i = 0; *e; e++, i++) {
277 		j = strlen(*e)+1;
278 		buf = realloc(buf, n+j);
279 		strcpy(buf+n, *e);
280 		n += j;
281 	}
282 	/* final null */
283 	buf = realloc(buf, n+1);
284 	buf[n] = 0;
285 
286 	return buf;
287 }
288 
289 static int
290 setpath(char *path, char *file)
291 {
292 	char *p, *last, tmp[MAX_PATH+1];
293 	int n;
294 
295 	if(strlen(file) >= MAX_PATH){
296 		werrstr("file name too long");
297 		return -1;
298 	}
299 	strcpy(tmp, file);
300 
301 	for(p=tmp; *p; p++) {
302 		if(*p == '/')
303 			*p = '\\';
304 	}
305 
306 	if(tmp[0] != 0 && tmp[1] == ':') {
307 		if(tmp[2] == 0) {
308 			tmp[2] = '\\';
309 			tmp[3] = 0;
310 		} else if(tmp[2] != '\\') {
311 			/* don't allow c:foo - only c:\foo */
312 			werrstr("illegal file name");
313 			return -1;
314 		}
315 	}
316 
317 	path[0] = 0;
318 	n = GetFullPathName(tmp, MAX_PATH, path, &last);
319 	if(n >= MAX_PATH) {
320 		werrstr("file name too long");
321 		return -1;
322 	}
323 	if(n == 0 && tmp[0] == '\\' && tmp[1] == '\\' && tmp[2] != 0) {
324 		strcpy(path, tmp);
325 		return -1;
326 	}
327 
328 	if(n == 0) {
329 		werrstr("bad file name");
330 		return -1;
331 	}
332 
333 	for(p=path; *p; p++) {
334 		if(*p < 32 || *p == '*' || *p == '?') {
335 			werrstr("file not found");
336 			return -1;
337 		}
338 	}
339 
340 	/* get rid of trailling \ */
341 	if(path[n-1] == '\\') {
342 		if(n <= 2) {
343 			werrstr("illegal file name");
344 			return -1;
345 		}
346 		path[n-1] = 0;
347 		n--;
348 	}
349 
350 	if(path[1] == ':' && path[2] == 0) {
351 		path[2] = '\\';
352 		path[3] = '.';
353 		path[4] = 0;
354 		return -1;
355 	}
356 
357 	if(path[0] != '\\' || path[1] != '\\')
358 		return 0;
359 
360 	for(p=path+2,n=0; *p; p++)
361 		if(*p == '\\')
362 			n++;
363 	if(n == 0)
364 		return -1;
365 	if(n == 1)
366 		return -1;
367 	return 0;
368 }
369 
370 
371 static int
372 execpath(char *path, char *file)
373 {
374 	int n;
375 
376 	if(setpath(path, file) < 0)
377 		return 0;
378 
379 	n = strlen(path)-4;
380 	if(path[n] == '.') {
381 		if(GetFileAttributes(path) != -1)
382 			return 1;
383 	}
384 	strncat(path, ".exe", MAX_PATH);
385 	path[MAX_PATH-1] = 0;
386 	if(GetFileAttributes(path) != -1)
387 		return 1;
388 	return 0;
389 }
390 
391 static HANDLE
392 fdexport(int fd)
393 {
394 	HANDLE h, r;
395 
396 	if(fd < 0)
397 		return INVALID_HANDLE_VALUE;
398 
399 	h = (HANDLE)_get_osfhandle(fd);
400 	if(h < 0)
401 		return INVALID_HANDLE_VALUE;
402 
403 	if(!DuplicateHandle(GetCurrentProcess(), h,
404 				GetCurrentProcess(), &r, DUPLICATE_SAME_ACCESS,
405 				1, DUPLICATE_SAME_ACCESS))
406 		return INVALID_HANDLE_VALUE;
407 	return r;
408 }
409 
410 static int
411 addchild(int pid, HANDLE handle)
412 {
413 	int i;
414 
415 	for(i=0; i<Nchild; i++) {
416 		if(child[i].handle == 0) {
417 			child[i].handle = handle;
418 			child[i].pid = pid;
419 			return 1;
420 		}
421 	}
422 	werrstr("child table full");
423 	return 0;
424 }
425 
426 int
427 procwait(uint pid)
428 {
429 	HANDLE h;
430 	int i, exit;
431 
432 	if(pid == 0)
433 		return 0;
434 
435 	h = 0;
436 	for(i = 0; i < Nchild; i++){
437 		if(child[i].pid == pid){
438 			h = child[i].handle;
439 			child[i].pid = 0;
440 			child[i].handle = 0;
441 			break;
442 		}
443 	}
444 
445 	if(h == 0){	/* we don't know about this one - let the system try to find it */
446 		h = OpenProcess(PROCESS_ALL_ACCESS, 0, pid);
447 		if(h == 0)
448 			return 0;		/* can't find it */
449 	}
450 
451 	if(WaitForSingleObject(h, INFINITE) == WAIT_FAILED) {
452 		winerror();
453 		fatal("procwait: ");
454 	}
455 
456 	if(!GetExitCodeProcess(h, &exit)) {
457 		winerror();
458 		exit = 1;
459 	}
460 
461 	CloseHandle(h);
462 	return exit;
463 }
464 
465 uint
466 proc(char **argv, int stdin, int stdout, int stderr)
467 {
468 	char *p, *arg0, *q, buf[MAX_PATH], path[MAX_PATH], *cmd, *eb;
469 	STARTUPINFO si;
470 	PROCESS_INFORMATION pi;
471 	int r, found, full;
472 	extern char **_environ;
473 	Word *w;
474 
475 	arg0 = argv[0];
476 	if(arg0 == 0) {
477 		werrstr("null argv[0]");
478 		return 0;
479 	}
480 
481 	full = arg0[0] == '\\' || arg0[0] == '/' || arg0[0] == '.';
482 	found = execpath(path, arg0);
483 
484 	if(!found && !full) {
485 		w = vlook("path")->val;
486 		if(w)
487 			p = w->word;
488 		else
489 			p = getenv("path");
490 		for(; p && *p; p = q){
491 			q = strchr(p, ';');
492 			if(q)
493 				*q = 0;
494 			snprint(buf, sizeof(buf), "%s/%s", p, arg0);
495 			if(q)
496 				*q++ = ';';
497 			found = execpath(path, buf);
498 			if(found)
499 				break;
500 		}
501 	}
502 
503 	if(!found) {
504 		werrstr("file not found");
505 		return 0;
506 	}
507 
508 	memset(&si, 0, sizeof(si));
509 	si.cb = sizeof(si);
510 	si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
511 	si.wShowWindow = SW_SHOW;
512 	si.hStdInput = fdexport(stdin);
513 	si.hStdOutput = fdexport(stdout);
514 	si.hStdError = fdexport(stderr);
515 
516 	eb = exportenv(_environ);
517 
518 	cmd = proccmd(argv);
519 
520 	r = CreateProcess(path, cmd, 0, 0, 1, 0, eb, 0, &si, &pi);
521 
522 	/* allow child to run */
523 	Sleep(0);
524 
525 	free(cmd);
526 	free(eb);
527 
528 	CloseHandle(si.hStdInput);
529 	CloseHandle(si.hStdOutput);
530 	CloseHandle(si.hStdError);
531 
532 	if(!r) {
533 		winerror();
534 		return 0;
535 	}
536 
537 	CloseHandle(pi.hThread);
538 
539 	if(addchild(pi.dwProcessId, pi.hProcess) == 0)
540 		return 0;
541 
542 	return pi.dwProcessId;
543 }
544 
545 int
546 pipe(int *fd)
547 {
548 	return _pipe(fd, 0, _O_BINARY);
549 }
550