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