| /* | 
 |  * Plan 9 versions of system-specific functions | 
 |  *	By convention, exported routines herein have names beginning with an | 
 |  *	upper case letter. | 
 |  */ | 
 | #include "rc.h" | 
 | #include "exec.h" | 
 | #include "io.h" | 
 | #include "fns.h" | 
 | #include "getflags.h" | 
 | char *Signame[]={ | 
 | 	"sigexit",	"sighup",	"sigint",	"sigquit", | 
 | 	"sigalrm",	"sigkill",	"sigfpe",	"sigterm", | 
 | 	0 | 
 | }; | 
 | char *syssigname[]={ | 
 | 	"exit",		/* can't happen */ | 
 | 	"hangup", | 
 | 	"interrupt", | 
 | 	"quit",		/* can't happen */ | 
 | 	"alarm", | 
 | 	"kill", | 
 | 	"sys: fp: ", | 
 | 	"term", | 
 | 	0 | 
 | }; | 
 | char* | 
 | Rcmain(void) | 
 | { | 
 | 	return unsharp("#9/rcmain"); | 
 | } | 
 |  | 
 | char Fdprefix[]="/dev/fd/"; | 
 | long readnb(int, char *, long); | 
 | void execfinit(void); | 
 | void execbind(void); | 
 | void execmount(void); | 
 | void execulimit(void); | 
 | void execumask(void); | 
 | void execrfork(void); | 
 | builtin Builtin[]={ | 
 | 	"cd",		execcd, | 
 | 	"whatis",	execwhatis, | 
 | 	"eval",		execeval, | 
 | 	"exec",		execexec,	/* but with popword first */ | 
 | 	"exit",		execexit, | 
 | 	"shift",	execshift, | 
 | 	"wait",		execwait, | 
 | 	".",		execdot, | 
 | 	"finit",	execfinit, | 
 | 	"flag",		execflag, | 
 | 	"ulimit",	execulimit, | 
 | 	"umask",	execumask, | 
 | 	"rfork",	execrfork, | 
 | 	0 | 
 | }; | 
 |  | 
 | void | 
 | execrfork(void) | 
 | { | 
 | 	int arg; | 
 | 	char *s; | 
 |  | 
 | 	switch(count(runq->argv->words)){ | 
 | 	case 1: | 
 | 		arg = RFENVG|RFNOTEG|RFNAMEG; | 
 | 		break; | 
 | 	case 2: | 
 | 		arg = 0; | 
 | 		for(s = runq->argv->words->next->word;*s;s++) switch(*s){ | 
 | 		default: | 
 | 			goto Usage; | 
 | 		case 'n': | 
 | 			arg|=RFNAMEG;  break; | 
 | 		case 'N': | 
 | 			arg|=RFCNAMEG; | 
 | 			break; | 
 | 		case 'e': | 
 | 			/* arg|=RFENVG; */  break; | 
 | 		case 'E': | 
 | 			arg|=RFCENVG;  break; | 
 | 		case 's': | 
 | 			arg|=RFNOTEG;  break; | 
 | 		case 'f': | 
 | 			arg|=RFFDG;    break; | 
 | 		case 'F': | 
 | 			arg|=RFCFDG;   break; | 
 | 		} | 
 | 		break; | 
 | 	default: | 
 | 	Usage: | 
 | 		pfmt(err, "Usage: %s [nNeEsfF]\n", runq->argv->words->word); | 
 | 		setstatus("rfork usage"); | 
 | 		poplist(); | 
 | 		return; | 
 | 	} | 
 | 	if(rfork(arg)==-1){ | 
 | 		pfmt(err, "rc: %s failed\n", runq->argv->words->word); | 
 | 		setstatus("rfork failed"); | 
 | 	} | 
 | 	else | 
 | 		setstatus(""); | 
 | 	poplist(); | 
 | } | 
 |  | 
 |  | 
 |  | 
 | #define	SEP	'\1' | 
 | char **environp; | 
 | struct word *enval(s) | 
 | register char *s; | 
 | { | 
 | 	register char *t, c; | 
 | 	register struct word *v; | 
 | 	for(t=s;*t && *t!=SEP;t++); | 
 | 	c=*t; | 
 | 	*t='\0'; | 
 | 	v=newword(s, c=='\0'?(struct word *)0:enval(t+1)); | 
 | 	*t=c; | 
 | 	return v; | 
 | } | 
 | void Vinit(void){ | 
 | 	extern char **environ; | 
 | 	register char *s; | 
 | 	register char **env=environ; | 
 | 	environp=env; | 
 | 	for(;*env;env++){ | 
 | 		for(s=*env;*s && *s!='(' && *s!='=';s++); | 
 | 		switch(*s){ | 
 | 		case '\0': | 
 | 		/*	pfmt(err, "rc: odd environment %q?\n", *env); */ | 
 | 			break; | 
 | 		case '=': | 
 | 			*s='\0'; | 
 | 			setvar(*env, enval(s+1)); | 
 | 			*s='='; | 
 | 			break; | 
 | 		case '(':	/* ignore functions for now */ | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | } | 
 | char **envp; | 
 | void Xrdfn(void){ | 
 | 	char *p; | 
 | 	register char *s; | 
 | 	register int len; | 
 | 	for(;*envp;envp++){ | 
 | 		s = *envp; | 
 | 		if(strncmp(s, "fn#", 3) == 0){ | 
 | 			p = strchr(s, '='); | 
 | 			if(p == nil) | 
 | 				continue; | 
 | 			*p = ' '; | 
 | 			s[2] = ' '; | 
 | 			len = strlen(s); | 
 | 			execcmds(opencore(s, len)); | 
 | 			s[len] = '\0'; | 
 | 			return; | 
 | 		} | 
 | #if 0 | 
 | 		for(s=*envp;*s && *s!='(' && *s!='=';s++); | 
 | 		switch(*s){ | 
 | 		case '\0': | 
 | 			pfmt(err, "environment %q?\n", *envp); | 
 | 			break; | 
 | 		case '=':	/* ignore variables */ | 
 | 			break; | 
 | 		case '(':		/* Bourne again */ | 
 | 			s=*envp+3; | 
 | 			envp++; | 
 | 			len=strlen(s); | 
 | 			s[len]='\n'; | 
 | 			execcmds(opencore(s, len+1)); | 
 | 			s[len]='\0'; | 
 | 			return; | 
 | 		} | 
 | #endif | 
 | 	} | 
 | 	Xreturn(); | 
 | } | 
 | union code rdfns[4]; | 
 | void execfinit(void){ | 
 | 	static int first=1; | 
 | 	if(first){ | 
 | 		rdfns[0].i=1; | 
 | 		rdfns[1].f=Xrdfn; | 
 | 		rdfns[2].f=Xjump; | 
 | 		rdfns[3].i=1; | 
 | 		first=0; | 
 | 	} | 
 | 	Xpopm(); | 
 | 	envp=environp; | 
 | 	start(rdfns, 1, runq->local); | 
 | } | 
 | extern int mapfd(int); | 
 | int Waitfor(int pid, int unused0){ | 
 | 	thread *p; | 
 | 	Waitmsg *w; | 
 | 	char errbuf[ERRMAX]; | 
 |  | 
 | 	if(pid >= 0 && !havewaitpid(pid)) | 
 | 		return 0; | 
 | 	while((w = wait()) != nil){ | 
 | 		delwaitpid(w->pid); | 
 | 		if(w->pid==pid){ | 
 | 			if(strncmp(w->msg, "signal: ", 8) == 0) | 
 | 				fprint(mapfd(2), "%d: %s\n", w->pid, w->msg); | 
 | 			setstatus(w->msg); | 
 | 			free(w); | 
 | 			return 0; | 
 | 		} | 
 | 		if(runq->iflag && strncmp(w->msg, "signal: ", 8) == 0) | 
 | 			fprint(2, "%d: %s\n", w->pid, w->msg); | 
 | 		for(p=runq->ret;p;p=p->ret) | 
 | 			if(p->pid==w->pid){ | 
 | 				p->pid=-1; | 
 | 				strcpy(p->status, w->msg); | 
 | 			} | 
 | 		free(w); | 
 | 	} | 
 |  | 
 | 	rerrstr(errbuf, sizeof errbuf); | 
 | 	if(strcmp(errbuf, "interrupted")==0) return -1; | 
 | 	return 0; | 
 | } | 
 | char **mkargv(word *a) | 
 | { | 
 | 	char **argv=(char **)emalloc((count(a)+2)*sizeof(char *)); | 
 | 	char **argp=argv+1;	/* leave one at front for runcoms */ | 
 | 	for(;a;a=a->next) *argp++=a->word; | 
 | 	*argp=0; | 
 | 	return argv; | 
 | } | 
 | /* | 
 | void addenv(var *v) | 
 | { | 
 | 	char envname[256]; | 
 | 	word *w; | 
 | 	int f; | 
 | 	io *fd; | 
 | 	if(v->changed){ | 
 | 		v->changed=0; | 
 | 		snprint(envname, sizeof envname, "/env/%s", v->name); | 
 | 		if((f=Creat(envname))<0) | 
 | 			pfmt(err, "rc: can't open %s: %r\n", envname); | 
 | 		else{ | 
 | 			for(w=v->val;w;w=w->next) | 
 | 				write(f, w->word, strlen(w->word)+1L); | 
 | 			close(f); | 
 | 		} | 
 | 	} | 
 | 	if(v->fnchanged){ | 
 | 		v->fnchanged=0; | 
 | 		snprint(envname, sizeof envname, "/env/fn#%s", v->name); | 
 | 		if((f=Creat(envname))<0) | 
 | 			pfmt(err, "rc: can't open %s: %r\n", envname); | 
 | 		else{ | 
 | 			if(v->fn){ | 
 | 				fd=openfd(f); | 
 | 				pfmt(fd, "fn %s %s\n", v->name, v->fn[v->pc-1].s); | 
 | 				closeio(fd); | 
 | 			} | 
 | 			close(f); | 
 | 		} | 
 | 	} | 
 | } | 
 | void updenvlocal(var *v) | 
 | { | 
 | 	if(v){ | 
 | 		updenvlocal(v->next); | 
 | 		addenv(v); | 
 | 	} | 
 | } | 
 | void Updenv(void){ | 
 | 	var *v, **h; | 
 | 	for(h=gvar;h!=&gvar[NVAR];h++) | 
 | 		for(v=*h;v;v=v->next) | 
 | 			addenv(v); | 
 | 	if(runq) updenvlocal(runq->local); | 
 | } | 
 | */ | 
 | int | 
 | cmpenv(const void *a, const void *b) | 
 | { | 
 | 	return strcmp(*(char**)a, *(char**)b); | 
 | } | 
 | char **mkenv(){ | 
 | 	register char **env, **ep, *p, *q; | 
 | 	register struct var **h, *v; | 
 | 	register struct word *a; | 
 | 	register int nvar=0, nchr=0, sep; | 
 | 	/* | 
 | 	 * Slightly kludgy loops look at locals then globals | 
 | 	 */ | 
 | 	for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){ | 
 | 		if((v==vlook(v->name)) && v->val){ | 
 | 			nvar++; | 
 | 			nchr+=strlen(v->name)+1; | 
 | 			for(a=v->val;a;a=a->next) | 
 | 				nchr+=strlen(a->word)+1; | 
 | 		} | 
 | 		if(v->fn){ | 
 | 			nvar++; | 
 | 			nchr+=strlen(v->name)+strlen(v->fn[v->pc-1].s)+8; | 
 | 		} | 
 | 	} | 
 | 	env=(char **)emalloc((nvar+1)*sizeof(char *)+nchr); | 
 | 	ep=env; | 
 | 	p=(char *)&env[nvar+1]; | 
 | 	for(h=gvar-1;h!=&gvar[NVAR];h++) for(v=h>=gvar?*h:runq->local;v;v=v->next){ | 
 | 		if((v==vlook(v->name)) && v->val){ | 
 | 			*ep++=p; | 
 | 			q=v->name; | 
 | 			while(*q) *p++=*q++; | 
 | 			sep='='; | 
 | 			for(a=v->val;a;a=a->next){ | 
 | 				*p++=sep; | 
 | 				sep=SEP; | 
 | 				q=a->word; | 
 | 				while(*q) *p++=*q++; | 
 | 			} | 
 | 			*p++='\0'; | 
 | 		} | 
 | 		if(v->fn){ | 
 | 			*ep++=p; | 
 | #if 0 | 
 | 			*p++='#'; *p++='('; *p++=')';	/* to fool Bourne */ | 
 | 			*p++='f'; *p++='n'; *p++=' '; | 
 | 			q=v->name; | 
 | 			while(*q) *p++=*q++; | 
 | 			*p++=' '; | 
 | #endif | 
 | 			*p++='f'; *p++='n'; *p++='#'; | 
 | 			q=v->name; | 
 | 			while(*q) *p++=*q++; | 
 | 			*p++='='; | 
 | 			q=v->fn[v->pc-1].s; | 
 | 			while(*q) *p++=*q++; | 
 | 			*p++='\n'; | 
 | 			*p++='\0'; | 
 | 		} | 
 | 	} | 
 | 	*ep=0; | 
 | 	qsort((char *)env, nvar, sizeof ep[0], cmpenv); | 
 | 	return env;	 | 
 | } | 
 | void Updenv(void){} | 
 | void Execute(word *args, word *path) | 
 | { | 
 | 	char **argv=mkargv(args); | 
 | 	char **env=mkenv(); | 
 | 	char file[1024]; | 
 | 	int nc; | 
 | 	Updenv(); | 
 | 	for(;path;path=path->next){ | 
 | 		nc=strlen(path->word); | 
 | 		if(nc<1024){ | 
 | 			strcpy(file, path->word); | 
 | 			if(file[0]){ | 
 | 				strcat(file, "/"); | 
 | 				nc++; | 
 | 			} | 
 | 			if(nc+strlen(argv[1])<1024){ | 
 | 				strcat(file, argv[1]); | 
 | 				execve(file, argv+1, env); | 
 | 			} | 
 | 			else werrstr("command name too long"); | 
 | 		} | 
 | 	} | 
 | 	rerrstr(file, sizeof file); | 
 | 	pfmt(err, "%s: %s\n", argv[1], file); | 
 | 	efree((char *)argv); | 
 | } | 
 | #define	NDIR	256		/* shoud be a better way */ | 
 | int Globsize(char *p) | 
 | { | 
 | 	ulong isglob=0, globlen=NDIR+1; | 
 | 	for(;*p;p++){ | 
 | 		if(*p==GLOB){ | 
 | 			p++; | 
 | 			if(*p!=GLOB) isglob++; | 
 | 			globlen+=*p=='*'?NDIR:1; | 
 | 		} | 
 | 		else | 
 | 			globlen++; | 
 | 	} | 
 | 	return isglob?globlen:0; | 
 | } | 
 | #define	NFD	50 | 
 | #define	NDBUF	32 | 
 | struct{ | 
 | 	Dir	*dbuf; | 
 | 	int	i; | 
 | 	int	n; | 
 | }dir[NFD]; | 
 | int Opendir(char *name) | 
 | { | 
 | 	Dir *db; | 
 | 	int f; | 
 | 	f=open(name, 0); | 
 | 	if(f==-1) | 
 | 		return f; | 
 | 	db = dirfstat(f); | 
 | 	if(db!=nil && (db->mode&DMDIR)){ | 
 | 		if(f<NFD){ | 
 | 			dir[f].i=0; | 
 | 			dir[f].n=0; | 
 | 		} | 
 | 		free(db); | 
 | 		return f; | 
 | 	} | 
 | 	free(db); | 
 | 	close(f); | 
 | 	return -1; | 
 | } | 
 | int Readdir(int f, char *p, int onlydirs) | 
 | { | 
 | 	int n; | 
 | 	USED(onlydirs);	/* only advisory */ | 
 |  | 
 | 	if(f<0 || f>=NFD) | 
 | 		return 0; | 
 | 	if(dir[f].i==dir[f].n){	/* read */ | 
 | 		free(dir[f].dbuf); | 
 | 		dir[f].dbuf=0; | 
 | 		n=dirread(f, &dir[f].dbuf); | 
 | 		if(n>=0) | 
 | 			dir[f].n=n; | 
 | 		else | 
 | 			dir[f].n=0; | 
 | 		dir[f].i=0; | 
 | 	} | 
 | 	if(dir[f].i==dir[f].n) | 
 | 		return 0; | 
 | 	strcpy(p, dir[f].dbuf[dir[f].i].name); | 
 | 	dir[f].i++; | 
 | 	return 1; | 
 | } | 
 | void Closedir(int f){ | 
 | 	if(f>=0 && f<NFD){ | 
 | 		free(dir[f].dbuf); | 
 | 		dir[f].i=0; | 
 | 		dir[f].n=0; | 
 | 		dir[f].dbuf=0; | 
 | 	} | 
 | 	close(f); | 
 | } | 
 | int interrupted = 0; | 
 | void | 
 | notifyf(void *unused0, char *s) | 
 | { | 
 | 	int i; | 
 | 	for(i=0;syssigname[i];i++) | 
 | 		if(strncmp(s, syssigname[i], strlen(syssigname[i]))==0){ | 
 | 			if(strncmp(s, "sys: ", 5)!=0){ | 
 | 				if(kidpid && !interrupted){ | 
 | 					interrupted=1; | 
 | 					postnote(PNGROUP, kidpid, s); | 
 | 				} | 
 | 				interrupted = 1; | 
 | 			} | 
 | 			goto Out; | 
 | 		} | 
 | 	if(strcmp(s, "sys: window size change") != 0) | 
 | 	if(strcmp(s, "sys: write on closed pipe") != 0) | 
 | 	if(strcmp(s, "sys: child") != 0) | 
 | 		pfmt(err, "rc: note: %s\n", s); | 
 | 	noted(NDFLT); | 
 | 	return; | 
 | Out: | 
 | 	if(strcmp(s, "interrupt")!=0 || trap[i]==0){ | 
 | 		trap[i]++; | 
 | 		ntrap++; | 
 | 	} | 
 | 	if(ntrap>=32){	/* rc is probably in a trap loop */ | 
 | 		pfmt(err, "rc: Too many traps (trap %s), aborting\n", s); | 
 | 		abort(); | 
 | 	} | 
 | 	noted(NCONT); | 
 | } | 
 | void Trapinit(void){ | 
 | 	notify(notifyf); | 
 | } | 
 | void Unlink(char *name) | 
 | { | 
 | 	remove(name); | 
 | } | 
 | long Write(int fd, char *buf, long cnt) | 
 | { | 
 | 	return write(fd, buf, (long)cnt); | 
 | } | 
 | long Read(int fd, char *buf, long cnt) | 
 | { | 
 | 	int i; | 
 |  | 
 | 	i = readnb(fd, buf, cnt); | 
 | 	if(ntrap) dotrap(); | 
 | 	return i; | 
 | } | 
 | long Seek(int fd, long cnt, long whence) | 
 | { | 
 | 	return seek(fd, cnt, whence); | 
 | } | 
 | int Executable(char *file) | 
 | { | 
 | 	Dir *statbuf; | 
 | 	int ret; | 
 |  | 
 | 	statbuf = dirstat(file); | 
 | 	if(statbuf == nil) return 0; | 
 | 	ret = ((statbuf->mode&0111)!=0 && (statbuf->mode&DMDIR)==0); | 
 | 	free(statbuf); | 
 | 	return ret; | 
 | } | 
 | int Creat(char *file) | 
 | { | 
 | 	return create(file, 1, 0666L); | 
 | } | 
 | int Dup(int a, int b){ | 
 | 	return dup(a, b); | 
 | } | 
 | int Dup1(int a){ | 
 | 	return dup(a, -1); | 
 | } | 
 | void Exit(char *stat) | 
 | { | 
 | 	Updenv(); | 
 | 	setstatus(stat); | 
 | 	exits(truestatus()?"":getstatus()); | 
 | } | 
 | int Eintr(void){ | 
 | 	return interrupted; | 
 | } | 
 | void Noerror(void){ | 
 | 	interrupted=0; | 
 | } | 
 | int | 
 | Isatty(int fd){ | 
 | 	return isatty(fd); | 
 | } | 
 | void Abort(void){ | 
 | 	pfmt(err, "aborting\n"); | 
 | 	flush(err); | 
 | 	Exit("aborting"); | 
 | } | 
 | void Memcpy(char *a, char *b, long n) | 
 | { | 
 | 	memmove(a, b, (long)n); | 
 | } | 
 | void *Malloc(ulong n){ | 
 | 	return malloc(n); | 
 | } | 
 |  | 
 | int | 
 | exitcode(char *msg) | 
 | { | 
 | 	int n; | 
 | 	 | 
 | 	n = atoi(msg); | 
 | 	if(n == 0) | 
 | 		n = 1; | 
 | 	return n; | 
 | } | 
 |  | 
 | int *waitpids; | 
 | int nwaitpids; | 
 |  | 
 | void | 
 | addwaitpid(int pid) | 
 | { | 
 | 	waitpids = realloc(waitpids, (nwaitpids+1)*sizeof waitpids[0]); | 
 | 	if(waitpids == 0) | 
 | 		panic("Can't realloc %d waitpids", nwaitpids+1); | 
 | 	waitpids[nwaitpids++] = pid; | 
 | } | 
 |  | 
 | void | 
 | delwaitpid(int pid) | 
 | { | 
 | 	int r, w; | 
 | 	 | 
 | 	for(r=w=0; r<nwaitpids; r++) | 
 | 		if(waitpids[r] != pid) | 
 | 			waitpids[w++] = waitpids[r]; | 
 | 	nwaitpids = w; | 
 | } | 
 |  | 
 | void | 
 | clearwaitpids(void) | 
 | { | 
 | 	nwaitpids = 0; | 
 | } | 
 |  | 
 | int | 
 | havewaitpid(int pid) | 
 | { | 
 | 	int i; | 
 | 	 | 
 | 	for(i=0; i<nwaitpids; i++) | 
 | 		if(waitpids[i] == pid) | 
 | 			return 1; | 
 | 	return 0; | 
 | } |