xref: /plan9/sys/src/cmd/rc/plan9.c (revision ff8c3af2f44d95267f67219afa20ba82ff6cf7e4)
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 	while((w = wait()) != nil){
211 		if(w->pid==pid){
212 			setstatus(w->msg);
213 			free(w);
214 			return 0;
215 		}
216 		for(p = runq->ret;p;p = p->ret)
217 			if(p->pid==w->pid){
218 				p->pid=-1;
219 				strcpy(p->status, w->msg);
220 			}
221 		free(w);
222 	}
223 
224 	errstr(errbuf, sizeof errbuf);
225 	if(strcmp(errbuf, "interrupted")==0) return -1;
226 	return 0;
227 }
228 
229 char*
230 *mkargv(word *a)
231 {
232 	char **argv = (char **)emalloc((count(a)+2)*sizeof(char *));
233 	char **argp = argv+1;	/* leave one at front for runcoms */
234 	for(;a;a = a->next) *argp++=a->word;
235 	*argp = 0;
236 	return argv;
237 }
238 
239 void
240 addenv(var *v)
241 {
242 	char envname[256];
243 	word *w;
244 	int f;
245 	io *fd;
246 	if(v->changed){
247 		v->changed = 0;
248 		snprint(envname, sizeof envname, "/env/%s", v->name);
249 		if((f = Creat(envname))<0)
250 			pfmt(err, "rc: can't open %s: %r\n", envname);
251 		else{
252 			for(w = v->val;w;w = w->next)
253 				write(f, w->word, strlen(w->word)+1L);
254 			close(f);
255 		}
256 	}
257 	if(v->fnchanged){
258 		v->fnchanged = 0;
259 		snprint(envname, sizeof envname, "/env/fn#%s", v->name);
260 		if((f = Creat(envname))<0)
261 			pfmt(err, "rc: can't open %s: %r\n", envname);
262 		else{
263 			if(v->fn){
264 				fd = openfd(f);
265 				pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
266 				closeio(fd);
267 			}
268 			close(f);
269 		}
270 	}
271 }
272 
273 void
274 updenvlocal(var *v)
275 {
276 	if(v){
277 		updenvlocal(v->next);
278 		addenv(v);
279 	}
280 }
281 
282 void
283 Updenv(void)
284 {
285 	var *v, **h;
286 	for(h = gvar;h!=&gvar[NVAR];h++)
287 		for(v=*h;v;v = v->next)
288 			addenv(v);
289 	if(runq)
290 		updenvlocal(runq->local);
291 }
292 
293 int
294 ForkExecute(char *file, char **argv, int sin, int sout, int serr)
295 {
296 	int pid;
297 
298 	if(access(file, 1) != 0)
299 		return -1;
300 	switch(pid = fork()){
301 	case -1:
302 		return -1;
303 	case 0:
304 		if(sin >= 0)
305 			dup(sin, 0);
306 		else
307 			close(0);
308 		if(sout >= 0)
309 			dup(sout, 1);
310 		else
311 			close(1);
312 		if(serr >= 0)
313 			dup(serr, 2);
314 		else
315 			close(2);
316 		exec(file, argv);
317 		exits(file);
318 	}
319 	return pid;
320 }
321 
322 void
323 Execute(word *args, word *path)
324 {
325 	char **argv = mkargv(args);
326 	char file[1024];
327 	int nc;
328 	Updenv();
329 	for(;path;path = path->next){
330 		nc = strlen(path->word);
331 		if(nc<1024){
332 			strcpy(file, path->word);
333 			if(file[0]){
334 				strcat(file, "/");
335 				nc++;
336 			}
337 			if(nc+strlen(argv[1])<1024){
338 				strcat(file, argv[1]);
339 				exec(file, argv+1);
340 			}
341 			else werrstr("command name too long");
342 		}
343 	}
344 	rerrstr(file, sizeof file);
345 	pfmt(err, "%s: %s\n", argv[1], file);
346 	efree((char *)argv);
347 }
348 #define	NDIR	256		/* shoud be a better way */
349 
350 int
351 Globsize(char *p)
352 {
353 	ulong isglob = 0, globlen = NDIR+1;
354 	for(;*p;p++){
355 		if(*p==GLOB){
356 			p++;
357 			if(*p!=GLOB)
358 				isglob++;
359 			globlen+=*p=='*'?NDIR:1;
360 		}
361 		else
362 			globlen++;
363 	}
364 	return isglob?globlen:0;
365 }
366 #define	NFD	50
367 #define	NDBUF	32
368 struct{
369 	Dir	*dbuf;
370 	int	i;
371 	int	n;
372 }dir[NFD];
373 
374 int
375 Opendir(char *name)
376 {
377 	Dir *db;
378 	int f;
379 	f = open(name, 0);
380 	if(f==-1)
381 		return f;
382 	db = dirfstat(f);
383 	if(db!=nil && (db->mode&DMDIR)){
384 		if(f<NFD){
385 			dir[f].i = 0;
386 			dir[f].n = 0;
387 		}
388 		free(db);
389 		return f;
390 	}
391 	free(db);
392 	close(f);
393 	return -1;
394 }
395 
396 static int
397 trimdirs(Dir *d, int nd)
398 {
399 	int r, w;
400 
401 	for(r=w=0; r<nd; r++)
402 		if(d[r].mode&DMDIR)
403 			d[w++] = d[r];
404 	return w;
405 }
406 
407 /*
408  * onlydirs is advisory -- it means you only
409  * need to return the directories.  it's okay to
410  * return files too (e.g., on unix where you can't
411  * tell during the readdir), but that just makes
412  * the globber work harder.
413  */
414 int
415 Readdir(int f, char *p, int onlydirs)
416 {
417 	int n;
418 
419 	if(f<0 || f>=NFD)
420 		return 0;
421 Again:
422 	if(dir[f].i==dir[f].n){	/* read */
423 		free(dir[f].dbuf);
424 		dir[f].dbuf = 0;
425 		n = dirread(f, &dir[f].dbuf);
426 		if(n>0){
427 			if(onlydirs){
428 				n = trimdirs(dir[f].dbuf, n);
429 				if(n == 0)
430 					goto Again;
431 			}
432 			dir[f].n = n;
433 		}else
434 			dir[f].n = 0;
435 		dir[f].i = 0;
436 	}
437 	if(dir[f].i == dir[f].n)
438 		return 0;
439 	strcpy(p, dir[f].dbuf[dir[f].i].name);
440 	dir[f].i++;
441 	return 1;
442 }
443 
444 void
445 Closedir(int f)
446 {
447 	if(f>=0 && f<NFD){
448 		free(dir[f].dbuf);
449 		dir[f].i = 0;
450 		dir[f].n = 0;
451 		dir[f].dbuf = 0;
452 	}
453 	close(f);
454 }
455 int interrupted = 0;
456 void
457 notifyf(void*, char *s)
458 {
459 	int i;
460 	for(i = 0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
461 		if(strncmp(s, "sys: ", 5)!=0) interrupted = 1;
462 		goto Out;
463 	}
464 	pfmt(err, "rc: note: %s\n", s);
465 	noted(NDFLT);
466 	return;
467 Out:
468 	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
469 		trap[i]++;
470 		ntrap++;
471 	}
472 	if(ntrap>=32){	/* rc is probably in a trap loop */
473 		pfmt(err, "rc: Too many traps (trap %s), aborting\n", s);
474 		abort();
475 	}
476 	noted(NCONT);
477 }
478 
479 void
480 Trapinit(void)
481 {
482 	notify(notifyf);
483 }
484 
485 void
486 Unlink(char *name)
487 {
488 	remove(name);
489 }
490 
491 long
492 Write(int fd, char *buf, long cnt)
493 {
494 	return write(fd, buf, (long)cnt);
495 }
496 
497 long
498 Read(int fd, char *buf, long cnt)
499 {
500 	return read(fd, buf, cnt);
501 }
502 
503 long
504 Seek(int fd, long cnt, long whence)
505 {
506 	return seek(fd, cnt, whence);
507 }
508 
509 int
510 Executable(char *file)
511 {
512 	Dir *statbuf;
513 	int ret;
514 
515 	statbuf = dirstat(file);
516 	if(statbuf == nil)
517 		return 0;
518 	ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0);
519 	free(statbuf);
520 	return ret;
521 }
522 
523 int
524 Creat(char *file)
525 {
526 	return create(file, 1, 0666L);
527 }
528 
529 int
530 Dup(int a, int b)
531 {
532 	return dup(a, b);
533 }
534 
535 int
536 Dup1(int)
537 {
538 	return -1;
539 }
540 
541 void
542 Exit(char *stat)
543 {
544 	Updenv();
545 	setstatus(stat);
546 	exits(truestatus()?"":getstatus());
547 }
548 
549 int
550 Eintr(void)
551 {
552 	return interrupted;
553 }
554 
555 void
556 Noerror(void)
557 {
558 	interrupted = 0;
559 }
560 
561 int
562 Isatty(int fd)
563 {
564 	Dir *d1, *d2;
565 	int ret;
566 
567 	d1 = dirfstat(fd);
568 	if(d1 == nil)
569 		return 0;
570 	if(strncmp(d1->name, "ptty", 4)==0){	/* fwd complaints to philw */
571 		free(d1);
572 		return 1;
573 	}
574 	d2 = dirstat("/dev/cons");
575 	if(d2 == nil){
576 		free(d1);
577 		return 0;
578 	}
579 	ret = (d1->type==d2->type&&d1->dev==d2->dev&&d1->qid.path==d2->qid.path);
580 	free(d1);
581 	free(d2);
582 	return ret;
583 }
584 
585 void
586 Abort(void)
587 {
588 	pfmt(err, "aborting\n");
589 	flush(err);
590 	Exit("aborting");
591 }
592 
593 void
594 Memcpy(char *a, char *b, long n)
595 {
596 	memmove(a, b, (long)n);
597 }
598 
599 void*
600 Malloc(ulong n)
601 {
602 	return malloc(n);
603 }
604