xref: /plan9-contrib/sys/src/cmd/rc/plan9.c (revision a6a9e07217f318acf170f99684a55fba5200524f)
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 void execnewpgrp(void){
48 	int arg;
49 	char *s;
50 	switch(count(runq->argv->words)){
51 	case 1: arg=RFENVG|RFNAMEG|RFNOTEG; break;
52 	case 2:
53 		arg=0;
54 		for(s=runq->argv->words->next->word;*s;s++) switch(*s){
55 		default:
56 			goto Usage;
57 		case 'n': arg|=RFNAMEG;  break;
58 		case 'N': arg|=RFCNAMEG; break;
59 		case 'm': arg|=RFNOMNT;  break;
60 		case 'e': arg|=RFENVG;   break;
61 		case 'E': arg|=RFCENVG;  break;
62 		case 's': arg|=RFNOTEG;  break;
63 		case 'f': arg|=RFFDG;    break;
64 		case 'F': arg|=RFCFDG;   break;
65 		}
66 		break;
67 	default:
68 	Usage:
69 		pfmt(err, "Usage: %s [fnesFNEm]\n", runq->argv->words->word);
70 		setstatus("rfork usage");
71 		poplist();
72 		return;
73 	}
74 	if(rfork(arg)==-1){
75 		pfmt(err, "rc: %s failed\n", runq->argv->words->word);
76 		setstatus("rfork failed");
77 	}
78 	else
79 		setstatus("");
80 	poplist();
81 }
82 void Vinit(void){
83 	int dir, f, len;
84 	word *val;
85 	char *buf, *s;
86 	Dir *ent;
87 	int i, nent;
88 	char envname[256];
89 	dir=open("/env", OREAD);
90 	if(dir<0){
91 		pfmt(err, "rc: can't open /env: %r\n");
92 		return;
93 	}
94 	ent = nil;
95 	for(;;){
96 		nent = dirread(dir, &ent);
97 		if(nent <= 0)
98 			break;
99 		for(i=0; i<nent; i++){
100 			len=ent[i].length;
101 			if(len && strncmp(ent[i].name, "fn#", 3)!=0){
102 				snprint(envname, sizeof envname, "/env/%s", ent[i].name);
103 				if((f=open(envname, 0))>=0){
104 					buf=emalloc((int)len+1);
105 					read(f, buf, (long)len);
106 					val=0;
107 					/* Charitably add a 0 at the end if need be */
108 					if(buf[len-1]) buf[len++]='\0';
109 					s=buf+len-1;
110 					for(;;){
111 						while(s!=buf && s[-1]!='\0') --s;
112 						val=newword(s, val);
113 						if(s==buf) break;
114 						--s;
115 					}
116 					setvar(ent[i].name, val);
117 					vlook(ent[i].name)->changed=0;
118 					close(f);
119 					efree(buf);
120 				}
121 			}
122 		}
123 		free(ent);
124 	}
125 	close(dir);
126 }
127 int envdir;
128 void Xrdfn(void){
129 	int f, len;
130 	static Dir *ent, *allocent;
131 	static int nent;
132 	Dir *e;
133 	char envname[256];
134 
135 	for(;;){
136 		if(nent == 0){
137 			free(allocent);
138 			nent = dirread(envdir, &allocent);
139 			ent = allocent;
140 		}
141 		if(nent <= 0)
142 			break;
143 		while(nent){
144 			e = ent++;
145 			nent--;
146 			len=e->length;
147 			if(len && strncmp(e->name, "fn#", 3)==0){
148 				snprint(envname, sizeof envname, "/env/%s", e->name);
149 				if((f=open(envname, 0))>=0){
150 					execcmds(openfd(f));
151 					return;
152 				}
153 			}
154 		}
155 	}
156 	close(envdir);
157 	Xreturn();
158 }
159 union code rdfns[4];
160 void execfinit(void){
161 	static int first=1;
162 	if(first){
163 		rdfns[0].i=1;
164 		rdfns[1].f=Xrdfn;
165 		rdfns[2].f=Xjump;
166 		rdfns[3].i=1;
167 		first=0;
168 	}
169 	Xpopm();
170 	envdir=open("/env", 0);
171 	if(envdir<0){
172 		pfmt(err, "rc: can't open /env: %r\n");
173 		return;
174 	}
175 	start(rdfns, 1, runq->local);
176 }
177 int Waitfor(int pid, int){
178 	thread *p;
179 	Waitmsg *w;
180 	char errbuf[ERRMAX];
181 
182 	while((w = wait()) != nil){
183 		if(w->pid==pid){
184 			setstatus(w->msg);
185 			free(w);
186 			return 0;
187 		}
188 		for(p=runq->ret;p;p=p->ret)
189 			if(p->pid==w->pid){
190 				p->pid=-1;
191 				strcpy(p->status, w->msg);
192 			}
193 		free(w);
194 	}
195 
196 	errstr(errbuf, sizeof errbuf);
197 	if(strcmp(errbuf, "interrupted")==0) return -1;
198 	return 0;
199 }
200 char **mkargv(word *a)
201 {
202 	char **argv=(char **)emalloc((count(a)+2)*sizeof(char *));
203 	char **argp=argv+1;	/* leave one at front for runcoms */
204 	for(;a;a=a->next) *argp++=a->word;
205 	*argp=0;
206 	return argv;
207 }
208 void addenv(var *v)
209 {
210 	char envname[256];
211 	word *w;
212 	int f;
213 	io *fd;
214 	if(v->changed){
215 		v->changed=0;
216 		snprint(envname, sizeof envname, "/env/%s", v->name);
217 		if((f=Creat(envname))<0)
218 			pfmt(err, "rc: can't open %s: %r\n", envname);
219 		else{
220 			for(w=v->val;w;w=w->next)
221 				write(f, w->word, strlen(w->word)+1L);
222 			close(f);
223 		}
224 	}
225 	if(v->fnchanged){
226 		v->fnchanged=0;
227 		snprint(envname, sizeof envname, "/env/fn#%s", v->name);
228 		if((f=Creat(envname))<0)
229 			pfmt(err, "rc: can't open %s: %r\n", envname);
230 		else{
231 			if(v->fn){
232 				fd=openfd(f);
233 				pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s);
234 				closeio(fd);
235 			}
236 			close(f);
237 		}
238 	}
239 }
240 void updenvlocal(var *v)
241 {
242 	if(v){
243 		updenvlocal(v->next);
244 		addenv(v);
245 	}
246 }
247 void Updenv(void){
248 	var *v, **h;
249 	for(h=gvar;h!=&gvar[NVAR];h++)
250 		for(v=*h;v;v=v->next)
251 			addenv(v);
252 	if(runq) updenvlocal(runq->local);
253 }
254 void Execute(word *args, word *path)
255 {
256 	char **argv=mkargv(args);
257 	char file[1024];
258 	int nc;
259 	Updenv();
260 	for(;path;path=path->next){
261 		nc=strlen(path->word);
262 		if(nc<1024){
263 			strcpy(file, path->word);
264 			if(file[0]){
265 				strcat(file, "/");
266 				nc++;
267 			}
268 			if(nc+strlen(argv[1])<1024){
269 				strcat(file, argv[1]);
270 				exec(file, argv+1);
271 			}
272 			else werrstr("command name too long");
273 		}
274 	}
275 	rerrstr(file, sizeof file);
276 	pfmt(err, "%s: %s\n", argv[1], file);
277 	efree((char *)argv);
278 }
279 #define	NDIR	256		/* shoud be a better way */
280 int Globsize(char *p)
281 {
282 	ulong isglob=0, globlen=NDIR+1;
283 	for(;*p;p++){
284 		if(*p==GLOB){
285 			p++;
286 			if(*p!=GLOB) isglob++;
287 			globlen+=*p=='*'?NDIR:1;
288 		}
289 		else
290 			globlen++;
291 	}
292 	return isglob?globlen:0;
293 }
294 #define	NFD	50
295 #define	NDBUF	32
296 struct{
297 	Dir	*dbuf;
298 	int	i;
299 	int	n;
300 }dir[NFD];
301 int Opendir(char *name)
302 {
303 	Dir *db;
304 	int f;
305 	f=open(name, 0);
306 	if(f==-1)
307 		return f;
308 	db = dirfstat(f);
309 	if(db!=nil && (db->mode&DMDIR)){
310 		if(f<NFD){
311 			dir[f].i=0;
312 			dir[f].n=0;
313 		}
314 		free(db);
315 		return f;
316 	}
317 	free(db);
318 	close(f);
319 	return -1;
320 }
321 
322 static int
323 trimdirs(Dir *d, int nd)
324 {
325 	int r, w;
326 
327 	for(r=w=0; r<nd; r++)
328 		if(d[r].mode&DMDIR)
329 			d[w++] = d[r];
330 	return w;
331 }
332 
333 /*
334  * onlydirs is advisory -- it means you only
335  * need to return the directories.  it's okay to
336  * return files too (e.g., on unix where you can't
337  * tell during the readdir), but that just makes
338  * the globber work harder.
339  */
340 int Readdir(int f, char *p, int onlydirs)
341 {
342 	int n;
343 	if(f<0 || f>=NFD)
344 		return 0;
345 Again:
346 	if(dir[f].i==dir[f].n){	/* read */
347 		free(dir[f].dbuf);
348 		dir[f].dbuf=0;
349 		n=dirread(f, &dir[f].dbuf);
350 		if(n>0){
351 			if(onlydirs){
352 				n=trimdirs(dir[f].dbuf, n);
353 				if(n==0)
354 					goto Again;
355 			}
356 			dir[f].n=n;
357 		}else
358 			dir[f].n=0;
359 		dir[f].i=0;
360 	}
361 	if(dir[f].i==dir[f].n)
362 		return 0;
363 	strcpy(p, dir[f].dbuf[dir[f].i].name);
364 	dir[f].i++;
365 	return 1;
366 }
367 void Closedir(int f){
368 	if(f>=0 && f<NFD){
369 		free(dir[f].dbuf);
370 		dir[f].i=0;
371 		dir[f].n=0;
372 		dir[f].dbuf=0;
373 	}
374 	close(f);
375 }
376 int interrupted = 0;
377 void
378 notifyf(void*, char *s)
379 {
380 	int i;
381 	for(i=0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
382 		if(strncmp(s, "sys: ", 5)!=0) interrupted=1;
383 		goto Out;
384 	}
385 	pfmt(err, "rc: note: %s\n", s);
386 	noted(NDFLT);
387 	return;
388 Out:
389 	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
390 		trap[i]++;
391 		ntrap++;
392 	}
393 	if(ntrap>=32){	/* rc is probably in a trap loop */
394 		pfmt(err, "rc: Too many traps (trap %s), aborting\n", s);
395 		abort();
396 	}
397 	noted(NCONT);
398 }
399 void Trapinit(void){
400 	notify(notifyf);
401 }
402 void Unlink(char *name)
403 {
404 	remove(name);
405 }
406 long Write(int fd, char *buf, long cnt)
407 {
408 	return write(fd, buf, (long)cnt);
409 }
410 long Read(int fd, char *buf, long cnt)
411 {
412 	return read(fd, buf, cnt);
413 }
414 long Seek(int fd, long cnt, long whence)
415 {
416 	return seek(fd, cnt, whence);
417 }
418 int Executable(char *file)
419 {
420 	Dir *statbuf;
421 	int ret;
422 
423 	statbuf = dirstat(file);
424 	if(statbuf == nil) return 0;
425 	ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0);
426 	free(statbuf);
427 	return ret;
428 }
429 int Creat(char *file)
430 {
431 	return create(file, 1, 0666L);
432 }
433 int Dup(int a, int b){
434 	return dup(a, b);
435 }
436 int Dup1(int){
437 	return -1;
438 }
439 void Exit(char *stat)
440 {
441 	Updenv();
442 	setstatus(stat);
443 	exits(truestatus()?"":getstatus());
444 }
445 int Eintr(void){
446 	return interrupted;
447 }
448 void Noerror(void){
449 	interrupted=0;
450 }
451 int Isatty(int fd){
452 	Dir *d1, *d2;
453 	int ret;
454 
455 	d1 = dirfstat(fd);
456 	if(d1 == nil) return 0;
457 	if(strncmp(d1->name, "ptty", 4)==0){	/* fwd complaints to philw */
458 		free(d1);
459 		return 1;
460 	}
461 	d2 = dirstat("/dev/cons");
462 	if(d2 == nil){
463 		free(d1);
464 		return 0;
465 	}
466 	ret = (d1->type==d2->type&&d1->dev==d2->dev&&d1->qid.path==d2->qid.path);
467 	free(d1);
468 	free(d2);
469 	return ret;
470 }
471 void Abort(void){
472 	pfmt(err, "aborting\n");
473 	flush(err);
474 	Exit("aborting");
475 }
476 void Memcpy(char *a, char *b, long n)
477 {
478 	memmove(a, b, (long)n);
479 }
480 void *Malloc(ulong n){
481 	return malloc(n);
482 }
483