xref: /plan9-contrib/sys/src/cmd/rc/plan9.c (revision 3468a4915d661daa200976acc4f80f51aae144b2)
1 /*
2  * Plan 9 versions of system-specific functions
3  *	By convention, exported routines herein have names beginning with an
4  *	upper case letter.
5  */
6 #include "rc.h"
7 #include "exec.h"
8 #include "io.h"
9 #include "fns.h"
10 #include "getflags.h"
11 
12 enum {
13 	Maxenvname = 256,	/* undocumented limit */
14 };
15 
16 char *Signame[] = {
17 	"sigexit",	"sighup",	"sigint",	"sigquit",
18 	"sigalrm",	"sigkill",	"sigfpe",	"sigterm",
19 	0
20 };
21 char *syssigname[] = {
22 	"exit",		/* can't happen */
23 	"hangup",
24 	"interrupt",
25 	"quit",		/* can't happen */
26 	"alarm",
27 	"kill",
28 	"sys: fp: ",
29 	"term",
30 	0
31 };
32 char Rcmain[]="/rc/lib/rcmain";
33 char Fdprefix[]="/fd/";
34 void execfinit(void);
35 void execbind(void);
36 void execmount(void);
37 void execnewpgrp(void);
38 builtin Builtin[] = {
39 	"cd",		execcd,
40 	"whatis",	execwhatis,
41 	"eval",		execeval,
42 	"exec",		execexec,	/* but with popword first */
43 	"exit",		execexit,
44 	"shift",	execshift,
45 	"wait",		execwait,
46 	".",		execdot,
47 	"finit",	execfinit,
48 	"flag",		execflag,
49 	"rfork",	execnewpgrp,
50 	0
51 };
52 
53 void
54 execnewpgrp(void)
55 {
56 	int arg;
57 	char *s;
58 	switch(count(runq->argv->words)){
59 	case 1:
60 		arg = RFENVG|RFNAMEG|RFNOTEG;
61 		break;
62 	case 2:
63 		arg = 0;
64 		for(s = runq->argv->words->next->word;*s;s++) switch(*s){
65 		default:
66 			goto Usage;
67 		case 'n':
68 			arg|=RFNAMEG;  break;
69 		case 'N':
70 			arg|=RFCNAMEG;
71 			break;
72 		case 'm':
73 			arg|=RFNOMNT;  break;
74 		case 'e':
75 			arg|=RFENVG;   break;
76 		case 'E':
77 			arg|=RFCENVG;  break;
78 		case 's':
79 			arg|=RFNOTEG;  break;
80 		case 'f':
81 			arg|=RFFDG;    break;
82 		case 'F':
83 			arg|=RFCFDG;   break;
84 		}
85 		break;
86 	default:
87 	Usage:
88 		pfmt(err, "Usage: %s [fnesFNEm]\n", runq->argv->words->word);
89 		setstatus("rfork usage");
90 		poplist();
91 		return;
92 	}
93 	if(rfork(arg)==-1){
94 		pfmt(err, "rc: %s failed\n", runq->argv->words->word);
95 		setstatus("rfork failed");
96 	}
97 	else
98 		setstatus("");
99 	poplist();
100 }
101 
102 void
103 Vinit(void)
104 {
105 	int dir, f, len, i, n, nent;
106 	char *buf, *s;
107 	char envname[Maxenvname];
108 	word *val;
109 	Dir *ent;
110 
111 	dir = open("/env", OREAD);
112 	if(dir<0){
113 		pfmt(err, "rc: can't open /env: %r\n");
114 		return;
115 	}
116 	ent = nil;
117 	for(;;){
118 		nent = dirread(dir, &ent);
119 		if(nent <= 0)
120 			break;
121 		for(i = 0; i<nent; i++){
122 			len = ent[i].length;
123 			if(len && strncmp(ent[i].name, "fn#", 3)!=0){
124 				snprint(envname, sizeof envname, "/env/%s", ent[i].name);
125 				if((f = open(envname, 0))>=0){
126 					buf = emalloc(len+1);
127 					n = readn(f, buf, len);
128 					if (n <= 0)
129 						buf[0] = '\0';
130 					else
131 						buf[n] = '\0';
132 					val = 0;
133 					/* Charitably add a 0 at the end if need be */
134 					if(buf[len-1])
135 						buf[len++]='\0';
136 					s = buf+len-1;
137 					for(;;){
138 						while(s!=buf && s[-1]!='\0') --s;
139 						val = newword(s, val);
140 						if(s==buf)
141 							break;
142 						--s;
143 					}
144 					setvar(ent[i].name, val);
145 					vlook(ent[i].name)->changed = 0;
146 					close(f);
147 					efree(buf);
148 				}
149 			}
150 		}
151 		free(ent);
152 	}
153 	close(dir);
154 }
155 int envdir;
156 
157 void
158 Xrdfn(void)
159 {
160 	int f, len;
161 	Dir *e;
162 	char envname[Maxenvname];
163 	static Dir *ent, *allocent;
164 	static int nent;
165 
166 	for(;;){
167 		if(nent == 0){
168 			free(allocent);
169 			nent = dirread(envdir, &allocent);
170 			ent = allocent;
171 		}
172 		if(nent <= 0)
173 			break;
174 		while(nent){
175 			e = ent++;
176 			nent--;
177 			len = e->length;
178 			if(len && strncmp(e->name, "fn#", 3)==0){
179 				snprint(envname, sizeof envname, "/env/%s", e->name);
180 				if((f = open(envname, 0))>=0){
181 					execcmds(openfd(f));
182 					return;
183 				}
184 			}
185 		}
186 	}
187 	close(envdir);
188 	Xreturn();
189 }
190 union code rdfns[4];
191 
192 void
193 execfinit(void)
194 {
195 	static int first = 1;
196 	if(first){
197 		rdfns[0].i = 1;
198 		rdfns[1].f = Xrdfn;
199 		rdfns[2].f = Xjump;
200 		rdfns[3].i = 1;
201 		first = 0;
202 	}
203 	Xpopm();
204 	envdir = open("/env", 0);
205 	if(envdir<0){
206 		pfmt(err, "rc: can't open /env: %r\n");
207 		return;
208 	}
209 	start(rdfns, 1, runq->local);
210 }
211 
212 int
213 Waitfor(int pid, int)
214 {
215 	thread *p;
216 	Waitmsg *w;
217 	char errbuf[ERRMAX];
218 
219 	if(pid >= 0 && !havewaitpid(pid))
220 		return 0;
221 
222 	while((w = wait()) != nil){
223 		delwaitpid(w->pid);
224 		if(w->pid==pid){
225 			setstatus(w->msg);
226 			free(w);
227 			return 0;
228 		}
229 		for(p = runq->ret;p;p = p->ret)
230 			if(p->pid==w->pid){
231 				p->pid=-1;
232 				strcpy(p->status, w->msg);
233 			}
234 		free(w);
235 	}
236 
237 	errstr(errbuf, sizeof errbuf);
238 	if(strcmp(errbuf, "interrupted")==0) return -1;
239 	return 0;
240 }
241 
242 char **
243 mkargv(word *a)
244 {
245 	char **argv = (char **)emalloc((count(a)+2)*sizeof(char *));
246 	char **argp = argv+1;	/* leave one at front for runcoms */
247 	for(;a;a = a->next) *argp++=a->word;
248 	*argp = 0;
249 	return argv;
250 }
251 
252 void
253 addenv(var *v)
254 {
255 	char envname[Maxenvname];
256 	word *w;
257 	int f;
258 	io *fd;
259 	if(v->changed){
260 		v->changed = 0;
261 		snprint(envname, sizeof envname, "/env/%s", v->name);
262 		if((f = Creat(envname))<0)
263 			pfmt(err, "rc: can't open %s: %r\n", envname);
264 		else{
265 			for(w = v->val;w;w = w->next)
266 				write(f, w->word, strlen(w->word)+1L);
267 			close(f);
268 		}
269 	}
270 	if(v->fnchanged){
271 		v->fnchanged = 0;
272 		snprint(envname, sizeof envname, "/env/fn#%s", v->name);
273 		if((f = Creat(envname))<0)
274 			pfmt(err, "rc: can't open %s: %r\n", envname);
275 		else{
276 			if(v->fn){
277 				fd = openfd(f);
278 				pfmt(fd, "fn %q %s\n", v->name, v->fn[v->pc-1].s);
279 				closeio(fd);
280 			}
281 			close(f);
282 		}
283 	}
284 }
285 
286 void
287 updenvlocal(var *v)
288 {
289 	if(v){
290 		updenvlocal(v->next);
291 		addenv(v);
292 	}
293 }
294 
295 void
296 Updenv(void)
297 {
298 	var *v, **h;
299 	for(h = gvar;h!=&gvar[NVAR];h++)
300 		for(v=*h;v;v = v->next)
301 			addenv(v);
302 	if(runq)
303 		updenvlocal(runq->local);
304 }
305 
306 /* not used on plan 9 */
307 int
308 ForkExecute(char *file, char **argv, int sin, int sout, int serr)
309 {
310 	int pid;
311 
312 	if(access(file, 1) != 0)
313 		return -1;
314 	switch(pid = fork()){
315 	case -1:
316 		return -1;
317 	case 0:
318 		if(sin >= 0)
319 			dup(sin, 0);
320 		else
321 			close(0);
322 		if(sout >= 0)
323 			dup(sout, 1);
324 		else
325 			close(1);
326 		if(serr >= 0)
327 			dup(serr, 2);
328 		else
329 			close(2);
330 		exec(file, argv);
331 		exits(file);
332 	}
333 	return pid;
334 }
335 
336 void
337 Execute(word *args, word *path)
338 {
339 	char **argv = mkargv(args);
340 	char file[1024], errstr[1024];
341 	int nc;
342 
343 	Updenv();
344 	errstr[0] = '\0';
345 	for(;path;path = path->next){
346 		nc = strlen(path->word);
347 		if(nc < sizeof file - 1){	/* 1 for / */
348 			strcpy(file, path->word);
349 			if(file[0]){
350 				strcat(file, "/");
351 				nc++;
352 			}
353 			if(nc + strlen(argv[1]) < sizeof file){
354 				strcat(file, argv[1]);
355 				exec(file, argv+1);
356 				rerrstr(errstr, sizeof errstr);
357 				/*
358 				 * if file exists and is executable, exec should
359 				 * have worked, unless it's a directory or an
360 				 * executable for another architecture.  in
361 				 * particular, if it failed due to lack of
362 				 * swap/vm (e.g., arg. list too long) or other
363 				 * allocation failure, stop searching and print
364 				 * the reason for failure.
365 				 */
366 				if (strstr(errstr, " allocat") != nil ||
367 				    strstr(errstr, " full") != nil)
368 					break;
369 			}
370 			else werrstr("command name too long");
371 		}
372 	}
373 	pfmt(err, "%s: %s\n", argv[1], errstr);
374 	efree((char *)argv);
375 }
376 #define	NDIR	256		/* shoud be a better way */
377 
378 int
379 Globsize(char *p)
380 {
381 	int isglob = 0, globlen = NDIR+1;
382 	for(;*p;p++){
383 		if(*p==GLOB){
384 			p++;
385 			if(*p!=GLOB)
386 				isglob++;
387 			globlen+=*p=='*'?NDIR:1;
388 		}
389 		else
390 			globlen++;
391 	}
392 	return isglob?globlen:0;
393 }
394 #define	NFD	50
395 
396 struct{
397 	Dir	*dbuf;
398 	int	i;
399 	int	n;
400 }dir[NFD];
401 
402 int
403 Opendir(char *name)
404 {
405 	Dir *db;
406 	int f;
407 	f = open(name, 0);
408 	if(f==-1)
409 		return f;
410 	db = dirfstat(f);
411 	if(db!=nil && (db->mode&DMDIR)){
412 		if(f<NFD){
413 			dir[f].i = 0;
414 			dir[f].n = 0;
415 		}
416 		free(db);
417 		return f;
418 	}
419 	free(db);
420 	close(f);
421 	return -1;
422 }
423 
424 static int
425 trimdirs(Dir *d, int nd)
426 {
427 	int r, w;
428 
429 	for(r=w=0; r<nd; r++)
430 		if(d[r].mode&DMDIR)
431 			d[w++] = d[r];
432 	return w;
433 }
434 
435 /*
436  * onlydirs is advisory -- it means you only
437  * need to return the directories.  it's okay to
438  * return files too (e.g., on unix where you can't
439  * tell during the readdir), but that just makes
440  * the globber work harder.
441  */
442 int
443 Readdir(int f, void *p, int onlydirs)
444 {
445 	int n;
446 
447 	if(f<0 || f>=NFD)
448 		return 0;
449 Again:
450 	if(dir[f].i==dir[f].n){	/* read */
451 		free(dir[f].dbuf);
452 		dir[f].dbuf = 0;
453 		n = dirread(f, &dir[f].dbuf);
454 		if(n>0){
455 			if(onlydirs){
456 				n = trimdirs(dir[f].dbuf, n);
457 				if(n == 0)
458 					goto Again;
459 			}
460 			dir[f].n = n;
461 		}else
462 			dir[f].n = 0;
463 		dir[f].i = 0;
464 	}
465 	if(dir[f].i == dir[f].n)
466 		return 0;
467 	strcpy(p, dir[f].dbuf[dir[f].i].name);
468 	dir[f].i++;
469 	return 1;
470 }
471 
472 void
473 Closedir(int f)
474 {
475 	if(f>=0 && f<NFD){
476 		free(dir[f].dbuf);
477 		dir[f].i = 0;
478 		dir[f].n = 0;
479 		dir[f].dbuf = 0;
480 	}
481 	close(f);
482 }
483 int interrupted = 0;
484 void
485 notifyf(void*, char *s)
486 {
487 	int i;
488 	for(i = 0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
489 		if(strncmp(s, "sys: ", 5)!=0) interrupted = 1;
490 		goto Out;
491 	}
492 	pfmt(err, "rc: note: %s\n", s);
493 	noted(NDFLT);
494 	return;
495 Out:
496 	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
497 		trap[i]++;
498 		ntrap++;
499 	}
500 	if(ntrap>=32){	/* rc is probably in a trap loop */
501 		pfmt(err, "rc: Too many traps (trap %s), aborting\n", s);
502 		abort();
503 	}
504 	noted(NCONT);
505 }
506 
507 void
508 Trapinit(void)
509 {
510 	notify(notifyf);
511 }
512 
513 void
514 Unlink(char *name)
515 {
516 	remove(name);
517 }
518 
519 long
520 Write(int fd, void *buf, long cnt)
521 {
522 	return write(fd, buf, cnt);
523 }
524 
525 long
526 Read(int fd, void *buf, long cnt)
527 {
528 	return read(fd, buf, cnt);
529 }
530 
531 long
532 Seek(int fd, long cnt, long whence)
533 {
534 	return seek(fd, cnt, whence);
535 }
536 
537 int
538 Executable(char *file)
539 {
540 	Dir *statbuf;
541 	int ret;
542 
543 	statbuf = dirstat(file);
544 	if(statbuf == nil)
545 		return 0;
546 	ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0);
547 	free(statbuf);
548 	return ret;
549 }
550 
551 int
552 Creat(char *file)
553 {
554 	return create(file, 1, 0666L);
555 }
556 
557 int
558 Dup(int a, int b)
559 {
560 	return dup(a, b);
561 }
562 
563 int
564 Dup1(int)
565 {
566 	return -1;
567 }
568 
569 void
570 Exit(char *stat)
571 {
572 	Updenv();
573 	setstatus(stat);
574 	exits(truestatus()?"":getstatus());
575 }
576 
577 int
578 Eintr(void)
579 {
580 	return interrupted;
581 }
582 
583 void
584 Noerror(void)
585 {
586 	interrupted = 0;
587 }
588 
589 int
590 Isatty(int fd)
591 {
592 	char buf[64];
593 
594 	if(fd2path(fd, buf, sizeof buf) != 0)
595 		return 0;
596 
597 	/* might be #c/cons during boot - fixed 22 april 2005, remove this later */
598 	if(strcmp(buf, "#c/cons") == 0)
599 		return 1;
600 
601 	/* might be /mnt/term/dev/cons */
602 	return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
603 }
604 
605 void
606 Abort(void)
607 {
608 	pfmt(err, "aborting\n");
609 	flush(err);
610 	Exit("aborting");
611 }
612 
613 void
614 Memcpy(void *a, void *b, long n)
615 {
616 	memmove(a, b, n);
617 }
618 
619 void*
620 Malloc(ulong n)
621 {
622 	return malloc(n);
623 }
624 
625 int *waitpids;
626 int nwaitpids;
627 
628 void
629 addwaitpid(int pid)
630 {
631 	waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]);
632 	if(waitpids == 0)
633 		panic("Can't realloc %d waitpids", nwaitpids+1);
634 	waitpids[nwaitpids++] = pid;
635 }
636 
637 void
638 delwaitpid(int pid)
639 {
640 	int r, w;
641 
642 	for(r=w=0; r<nwaitpids; r++)
643 		if(waitpids[r] != pid)
644 			waitpids[w++] = waitpids[r];
645 	nwaitpids = w;
646 }
647 
648 void
649 clearwaitpids(void)
650 {
651 	nwaitpids = 0;
652 }
653 
654 int
655 havewaitpid(int pid)
656 {
657 	int i;
658 
659 	for(i=0; i<nwaitpids; i++)
660 		if(waitpids[i] == pid)
661 			return 1;
662 	return 0;
663 }
664 
665 /* avoid loading any floating-point library code */
666 int
667 _efgfmt(Fmt *)
668 {
669 	return -1;
670 }
671