xref: /plan9/sys/src/cmd/rc/plan9.c (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
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 int Readdir(int f, char *p)
322 {
323 	int n;
324 	if(f<0 || f>=NFD)
325 		return 0;
326 	if(dir[f].i==dir[f].n){	/* read */
327 		free(dir[f].dbuf);
328 		dir[f].dbuf=0;
329 		n=dirread(f, &dir[f].dbuf);
330 		if(n>=0)
331 			dir[f].n=n;
332 		else
333 			dir[f].n=0;
334 		dir[f].i=0;
335 	}
336 	if(dir[f].i==dir[f].n)
337 		return 0;
338 	strcpy(p, dir[f].dbuf[dir[f].i].name);
339 	dir[f].i++;
340 	return 1;
341 }
342 void Closedir(int f){
343 	if(f>=0 && f<NFD){
344 		free(dir[f].dbuf);
345 		dir[f].i=0;
346 		dir[f].n=0;
347 		dir[f].dbuf=0;
348 	}
349 	close(f);
350 }
351 int interrupted = 0;
352 void
353 notifyf(void*, char *s)
354 {
355 	int i;
356 	for(i=0;syssigname[i];i++) if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){
357 		if(strncmp(s, "sys: ", 5)!=0) interrupted=1;
358 		goto Out;
359 	}
360 	pfmt(err, "rc: note: %s\n", s);
361 	noted(NDFLT);
362 	return;
363 Out:
364 	if(strcmp(s, "interrupt")!=0 || trap[i]==0){
365 		trap[i]++;
366 		ntrap++;
367 	}
368 	if(ntrap>=32){	/* rc is probably in a trap loop */
369 		pfmt(err, "rc: Too many traps (trap %s), aborting\n", s);
370 		abort();
371 	}
372 	noted(NCONT);
373 }
374 void Trapinit(void){
375 	notify(notifyf);
376 }
377 void Unlink(char *name)
378 {
379 	remove(name);
380 }
381 long Write(int fd, char *buf, long cnt)
382 {
383 	return write(fd, buf, (long)cnt);
384 }
385 long Read(int fd, char *buf, long cnt)
386 {
387 	return read(fd, buf, cnt);
388 }
389 long Seek(int fd, long cnt, long whence)
390 {
391 	return seek(fd, cnt, whence);
392 }
393 int Executable(char *file)
394 {
395 	Dir *statbuf;
396 	int ret;
397 
398 	statbuf = dirstat(file);
399 	if(statbuf == nil) return 0;
400 	ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0);
401 	free(statbuf);
402 	return ret;
403 }
404 int Creat(char *file)
405 {
406 	return create(file, 1, 0666L);
407 }
408 int Dup(int a, int b){
409 	return dup(a, b);
410 }
411 int Dup1(int){
412 	return -1;
413 }
414 void Exit(char *stat)
415 {
416 	Updenv();
417 	setstatus(stat);
418 	exits(truestatus()?"":getstatus());
419 }
420 int Eintr(void){
421 	return interrupted;
422 }
423 void Noerror(void){
424 	interrupted=0;
425 }
426 int Isatty(int fd){
427 	Dir *d1, *d2;
428 	int ret;
429 
430 	d1 = dirfstat(fd);
431 	if(d1 == nil) return 0;
432 	if(strncmp(d1->name, "ptty", 4)==0){	/* fwd complaints to philw */
433 		free(d1);
434 		return 1;
435 	}
436 	d2 = dirstat("/dev/cons");
437 	if(d2 == nil){
438 		free(d1);
439 		return 0;
440 	}
441 	ret = (d1->type==d2->type&&d1->dev==d2->dev&&d1->qid.path==d2->qid.path);
442 	free(d1);
443 	free(d2);
444 	return ret;
445 }
446 void Abort(void){
447 	pfmt(err, "aborting\n");
448 	flush(err);
449 	Exit("aborting");
450 }
451 void Memcpy(char *a, char *b, long n)
452 {
453 	memmove(a, b, (long)n);
454 }
455 void *Malloc(ulong n){
456 	return malloc(n);
457 }
458