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