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