xref: /plan9/sys/src/cmd/rc/plan9.c (revision f9e1cf08d3be51592e03e639fc848a68dc31a55e)
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 %s %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 int
307 ForkExecute(char *file, char **argv, int sin, int sout, int serr)
308 {
309 	int pid;
310 
311 	if(access(file, 1) != 0)
312 		return -1;
313 	switch(pid = fork()){
314 	case -1:
315 		return -1;
316 	case 0:
317 		if(sin >= 0)
318 			dup(sin, 0);
319 		else
320 			close(0);
321 		if(sout >= 0)
322 			dup(sout, 1);
323 		else
324 			close(1);
325 		if(serr >= 0)
326 			dup(serr, 2);
327 		else
328 			close(2);
329 		exec(file, argv);
330 		exits(file);
331 	}
332 	return pid;
333 }
334 
335 void
336 Execute(word *args, word *path)
337 {
338 	char **argv = mkargv(args);
339 	char file[1024];
340 	int nc;
341 	Updenv();
342 	for(;path;path = path->next){
343 		nc = strlen(path->word);
344 		if(nc < sizeof file - 1){	/* 1 for / */
345 			strcpy(file, path->word);
346 			if(file[0]){
347 				strcat(file, "/");
348 				nc++;
349 			}
350 			if(nc + strlen(argv[1]) < sizeof file){
351 				strcat(file, argv[1]);
352 				exec(file, argv+1);
353 			}
354 			else werrstr("command name too long");
355 		}
356 	}
357 	rerrstr(file, sizeof file);
358 	pfmt(err, "%s: %s\n", argv[1], file);
359 	efree((char *)argv);
360 }
361 #define	NDIR	256		/* shoud be a better way */
362 
363 int
364 Globsize(char *p)
365 {
366 	int isglob = 0, globlen = NDIR+1;
367 	for(;*p;p++){
368 		if(*p==GLOB){
369 			p++;
370 			if(*p!=GLOB)
371 				isglob++;
372 			globlen+=*p=='*'?NDIR:1;
373 		}
374 		else
375 			globlen++;
376 	}
377 	return isglob?globlen:0;
378 }
379 #define	NFD	50
380 
381 struct{
382 	Dir	*dbuf;
383 	int	i;
384 	int	n;
385 }dir[NFD];
386 
387 int
388 Opendir(char *name)
389 {
390 	Dir *db;
391 	int f;
392 	f = open(name, 0);
393 	if(f==-1)
394 		return f;
395 	db = dirfstat(f);
396 	if(db!=nil && (db->mode&DMDIR)){
397 		if(f<NFD){
398 			dir[f].i = 0;
399 			dir[f].n = 0;
400 		}
401 		free(db);
402 		return f;
403 	}
404 	free(db);
405 	close(f);
406 	return -1;
407 }
408 
409 static int
410 trimdirs(Dir *d, int nd)
411 {
412 	int r, w;
413 
414 	for(r=w=0; r<nd; r++)
415 		if(d[r].mode&DMDIR)
416 			d[w++] = d[r];
417 	return w;
418 }
419 
420 /*
421  * onlydirs is advisory -- it means you only
422  * need to return the directories.  it's okay to
423  * return files too (e.g., on unix where you can't
424  * tell during the readdir), but that just makes
425  * the globber work harder.
426  */
427 int
428 Readdir(int f, void *p, int onlydirs)
429 {
430 	int n;
431 
432 	if(f<0 || f>=NFD)
433 		return 0;
434 Again:
435 	if(dir[f].i==dir[f].n){	/* read */
436 		free(dir[f].dbuf);
437 		dir[f].dbuf = 0;
438 		n = dirread(f, &dir[f].dbuf);
439 		if(n>0){
440 			if(onlydirs){
441 				n = trimdirs(dir[f].dbuf, n);
442 				if(n == 0)
443 					goto Again;
444 			}
445 			dir[f].n = n;
446 		}else
447 			dir[f].n = 0;
448 		dir[f].i = 0;
449 	}
450 	if(dir[f].i == dir[f].n)
451 		return 0;
452 	strcpy(p, dir[f].dbuf[dir[f].i].name);
453 	dir[f].i++;
454 	return 1;
455 }
456 
457 void
458 Closedir(int f)
459 {
460 	if(f>=0 && f<NFD){
461 		free(dir[f].dbuf);
462 		dir[f].i = 0;
463 		dir[f].n = 0;
464 		dir[f].dbuf = 0;
465 	}
466 	close(f);
467 }
468 int interrupted = 0;
469 void
470 notifyf(void*, char *s)
471 {
472 	int i;
473 	for(i = 0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
474 		if(strncmp(s, "sys: ", 5)!=0) interrupted = 1;
475 		goto Out;
476 	}
477 	pfmt(err, "rc: note: %s\n", s);
478 	noted(NDFLT);
479 	return;
480 Out:
481 	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
482 		trap[i]++;
483 		ntrap++;
484 	}
485 	if(ntrap>=32){	/* rc is probably in a trap loop */
486 		pfmt(err, "rc: Too many traps (trap %s), aborting\n", s);
487 		abort();
488 	}
489 	noted(NCONT);
490 }
491 
492 void
493 Trapinit(void)
494 {
495 	notify(notifyf);
496 }
497 
498 void
499 Unlink(char *name)
500 {
501 	remove(name);
502 }
503 
504 long
505 Write(int fd, void *buf, long cnt)
506 {
507 	return write(fd, buf, cnt);
508 }
509 
510 long
511 Read(int fd, void *buf, long cnt)
512 {
513 	return read(fd, buf, cnt);
514 }
515 
516 long
517 Seek(int fd, long cnt, long whence)
518 {
519 	return seek(fd, cnt, whence);
520 }
521 
522 int
523 Executable(char *file)
524 {
525 	Dir *statbuf;
526 	int ret;
527 
528 	statbuf = dirstat(file);
529 	if(statbuf == nil)
530 		return 0;
531 	ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0);
532 	free(statbuf);
533 	return ret;
534 }
535 
536 int
537 Creat(char *file)
538 {
539 	return create(file, 1, 0666L);
540 }
541 
542 int
543 Dup(int a, int b)
544 {
545 	return dup(a, b);
546 }
547 
548 int
549 Dup1(int)
550 {
551 	return -1;
552 }
553 
554 void
555 Exit(char *stat)
556 {
557 	Updenv();
558 	setstatus(stat);
559 	exits(truestatus()?"":getstatus());
560 }
561 
562 int
563 Eintr(void)
564 {
565 	return interrupted;
566 }
567 
568 void
569 Noerror(void)
570 {
571 	interrupted = 0;
572 }
573 
574 int
575 Isatty(int fd)
576 {
577 	char buf[64];
578 
579 	if(fd2path(fd, buf, sizeof buf) != 0)
580 		return 0;
581 
582 	/* might be #c/cons during boot - fixed 22 april 2005, remove this later */
583 	if(strcmp(buf, "#c/cons") == 0)
584 		return 1;
585 
586 	/* might be /mnt/term/dev/cons */
587 	return strlen(buf) >= 9 && strcmp(buf+strlen(buf)-9, "/dev/cons") == 0;
588 }
589 
590 void
591 Abort(void)
592 {
593 	pfmt(err, "aborting\n");
594 	flush(err);
595 	Exit("aborting");
596 }
597 
598 void
599 Memcpy(void *a, void *b, long n)
600 {
601 	memmove(a, b, n);
602 }
603 
604 void*
605 Malloc(ulong n)
606 {
607 	return malloc(n);
608 }
609 
610 int *waitpids;
611 int nwaitpids;
612 
613 void
614 addwaitpid(int pid)
615 {
616 	waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]);
617 	if(waitpids == 0)
618 		panic("Can't realloc %d waitpids", nwaitpids+1);
619 	waitpids[nwaitpids++] = pid;
620 }
621 
622 void
623 delwaitpid(int pid)
624 {
625 	int r, w;
626 
627 	for(r=w=0; r<nwaitpids; r++)
628 		if(waitpids[r] != pid)
629 			waitpids[w++] = waitpids[r];
630 	nwaitpids = w;
631 }
632 
633 void
634 clearwaitpids(void)
635 {
636 	nwaitpids = 0;
637 }
638 
639 int
640 havewaitpid(int pid)
641 {
642 	int i;
643 
644 	for(i=0; i<nwaitpids; i++)
645 		if(waitpids[i] == pid)
646 			return 1;
647 	return 0;
648 }
649