| /* |
| * 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; |
| } |