void forktree(const char *cur) { cprintf("%04x: I am '%s'\n", getpid(), cur); forkchild(cur, '0'); forkchild(cur, '1'); }
void forktree(const char *cur) { cprintf("%04x: I am '%s'\n", sys_getenvid(), cur); forkchild(cur, '0'); /* sys_debug_va_mapping(0xeebfdf9c); */ /* asm volatile("xchg %%bx, %%bx" : :); */ forkchild(cur, '1'); }
void forktree(const char *cur) { cprintf("%04x: I am '%s'\n", sys_getenvid(), cur); forkchild(cur, '0'); // Yield to give the child a chance to run. // This will make child environment IDs slightly more predictable. sys_yield(); forkchild(cur, '1'); }
/* forkchild: fork a right neighbour to filter numbers */ void forkchild(int fds[]) { pid_t child; if ((child = fork()) < 0) { perror("fork"); exit(1); } else if (child == 0) { int filedes[2], p, i; close(fds[1]); if (pipe(filedes) < 0) { perror("pipe"); exit(1); } forkchild(filedes); close(filedes[0]); /* read a prime from left neighbour and print it */ read(fds[0], &p, sizeof p); printf("%d ", p); fflush(stdout); while (1) { /* read numbers from left neighbour */ read(fds[0], &i, sizeof i); /* if not divisable by prime 'p' write it to right * neighbour */ if (i % p) write(filedes[1], &i, sizeof i); } exit(0); } }
int main(void) { int fds[2], i; if (pipe(fds) < 0) { perror("pipe"); exit(1); } forkchild(fds); close(fds[0]); for (i = 2; ; i++) { write(fds[1], &i, sizeof i); } return 0; }
int forkshell(struct job *jp, union node *n, int mode) { int pid; TRACE(("forkshell(%%%d, %p, %d) called\n", jp - jobtab, n, mode)); switch ((pid = fork())) { case -1: TRACE(("Fork failed, errno=%d\n", errno)); INTON; error("Cannot fork"); break; case 0: forkchild(jp, n, mode, 0); return 0; default: return forkparent(jp, n, mode, pid); } }
STATIC void evalcommand(shinstance *psh, union node *cmd, int flags, struct backcmd *backcmd) { struct stackmark smark; union node *argp; struct arglist arglist; struct arglist varlist; char **argv; int argc; char **envp; int varflag; struct strlist *sp; int mode; int pip[2]; struct cmdentry cmdentry; struct job *jp; struct jmploc jmploc; struct jmploc *volatile savehandler; char *volatile savecmdname; volatile struct shparam saveparam; struct localvar *volatile savelocalvars; volatile int e; char *lastarg; const char *path = pathval(psh); volatile int temp_path; #if __GNUC__ /* Avoid longjmp clobbering */ (void) &argv; (void) &argc; (void) &lastarg; (void) &flags; #endif psh->vforked = 0; /* First expand the arguments. */ TRACE((psh, "evalcommand(0x%lx, %d) called\n", (long)cmd, flags)); setstackmark(psh, &smark); psh->back_exitstatus = 0; arglist.lastp = &arglist.list; varflag = 1; /* Expand arguments, ignoring the initial 'name=value' ones */ for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { char *p = argp->narg.text; if (varflag && is_name(*p)) { do { p++; } while (is_in_name(*p)); if (*p == '=') continue; } expandarg(psh, argp, &arglist, EXP_FULL | EXP_TILDE); varflag = 0; } *arglist.lastp = NULL; expredir(psh, cmd->ncmd.redirect); /* Now do the initial 'name=value' ones we skipped above */ varlist.lastp = &varlist.list; for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { char *p = argp->narg.text; if (!is_name(*p)) break; do p++; while (is_in_name(*p)); if (*p != '=') break; expandarg(psh, argp, &varlist, EXP_VARTILDE); } *varlist.lastp = NULL; argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; argv = stalloc(psh, sizeof (char *) * (argc + 1)); for (sp = arglist.list ; sp ; sp = sp->next) { TRACE((psh, "evalcommand arg: %s\n", sp->text)); *argv++ = sp->text; } *argv = NULL; lastarg = NULL; if (iflag(psh) && psh->funcnest == 0 && argc > 0) lastarg = argv[-1]; argv -= argc; /* Print the command if xflag is set. */ if (xflag(psh)) { char sep = 0; out2str(psh, ps4val(psh)); for (sp = varlist.list ; sp ; sp = sp->next) { if (sep != 0) outc(sep, &psh->errout); out2str(psh, sp->text); sep = ' '; } for (sp = arglist.list ; sp ; sp = sp->next) { if (sep != 0) outc(sep, &psh->errout); out2str(psh, sp->text); sep = ' '; } outc('\n', &psh->errout); flushout(&psh->errout); } /* Now locate the command. */ if (argc == 0) { cmdentry.cmdtype = CMDSPLBLTIN; cmdentry.u.bltin = bltincmd; } else { static const char PATH[] = "PATH="; int cmd_flags = DO_ERR; /* * Modify the command lookup path, if a PATH= assignment * is present */ for (sp = varlist.list; sp; sp = sp->next) if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) path = sp->text + sizeof(PATH) - 1; do { int argsused, use_syspath; find_command(psh, argv[0], &cmdentry, cmd_flags, path); if (cmdentry.cmdtype == CMDUNKNOWN) { psh->exitstatus = 127; flushout(&psh->errout); goto out; } /* implement the 'command' builtin here */ if (cmdentry.cmdtype != CMDBUILTIN || cmdentry.u.bltin != bltincmd) break; cmd_flags |= DO_NOFUNC; argsused = parse_command_args(psh, argc, argv, &use_syspath); if (argsused == 0) { /* use 'type' builting to display info */ cmdentry.u.bltin = typecmd; break; } argc -= argsused; argv += argsused; if (use_syspath) path = syspath(psh) + 5; } while (argc != 0); if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC) /* posix mandates that 'command <splbltin>' act as if <splbltin> was a normal builtin */ cmdentry.cmdtype = CMDBUILTIN; } /* Fork off a child process if necessary. */ if (cmd->ncmd.backgnd || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0) || ((flags & EV_BACKCMD) != 0 && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN) || cmdentry.u.bltin == dotcmd || cmdentry.u.bltin == evalcmd))) { INTOFF; jp = makejob(psh, cmd, 1); mode = cmd->ncmd.backgnd; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (sh_pipe(psh, pip) < 0) error(psh, "Pipe call failed"); } #ifdef DO_SHAREDVFORK /* It is essential that if DO_SHAREDVFORK is defined that the * child's address space is actually shared with the parent as * we rely on this. */ if (cmdentry.cmdtype == CMDNORMAL) { pid_t pid; savelocalvars = psh->localvars; psh->localvars = NULL; psh->vforked = 1; switch (pid = vfork()) { case -1: TRACE((psh, "Vfork failed, errno=%d\n", errno)); INTON; error(psh, "Cannot vfork"); break; case 0: /* Make sure that exceptions only unwind to * after the vfork(2) */ if (setjmp(jmploc.loc)) { if (psh->exception == EXSHELLPROC) { /* We can't progress with the vfork, * so, set vforked = 2 so the parent * knows, and _exit(); */ psh->vforked = 2; sh__exit(psh, 0); } else { sh__exit(psh, psh->exerrno); } } savehandler = psh->handler; psh->handler = &jmploc; listmklocal(psh, varlist.list, VEXPORT | VNOFUNC); forkchild(psh, jp, cmd, mode, psh->vforked); break; default: psh->handler = savehandler; /* restore from vfork(2) */ poplocalvars(psh); psh->localvars = savelocalvars; if (psh->vforked == 2) { psh->vforked = 0; (void)sh_waitpid(psh, pid, NULL, 0); /* We need to progress in a normal fork fashion */ goto normal_fork; } psh->vforked = 0; forkparent(psh, jp, cmd, mode, pid); goto parent; } } else { normal_fork: #endif if (forkshell(psh, jp, cmd, mode) != 0) goto parent; /* at end of routine */ FORCEINTON; #ifdef DO_SHAREDVFORK } #endif if (flags & EV_BACKCMD) { if (!psh->vforked) { FORCEINTON; } shfile_close(&psh->fdtab, pip[0]); if (pip[1] != 1) { movefd(psh, pip[1], 1); } } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ switch (cmdentry.cmdtype) { case CMDFUNCTION: #ifdef DEBUG trputs(psh, "Shell function: "); trargs(psh, argv); #endif redirect(psh, cmd->ncmd.redirect, REDIR_PUSH); saveparam = psh->shellparam; psh->shellparam.malloc = 0; psh->shellparam.reset = 1; psh->shellparam.nparam = argc - 1; psh->shellparam.p = argv + 1; psh->shellparam.optnext = NULL; INTOFF; savelocalvars = psh->localvars; psh->localvars = NULL; INTON; if (setjmp(jmploc.loc)) { if (psh->exception == EXSHELLPROC) { freeparam(psh, (volatile struct shparam *) &saveparam); } else { freeparam(psh, &psh->shellparam); psh->shellparam = saveparam; } poplocalvars(psh); psh->localvars = savelocalvars; psh->handler = savehandler; longjmp(psh->handler->loc, 1); } savehandler = psh->handler; psh->handler = &jmploc; listmklocal(psh, varlist.list, 0); /* stop shell blowing its stack */ if (++psh->funcnest > 1000) error(psh, "too many nested function calls"); evaltree(psh, cmdentry.u.func, flags & EV_TESTED); psh->funcnest--; INTOFF; poplocalvars(psh); psh->localvars = savelocalvars; freeparam(psh, &psh->shellparam); psh->shellparam = saveparam; psh->handler = savehandler; popredir(psh); INTON; if (psh->evalskip == SKIPFUNC) { psh->evalskip = 0; psh->skipcount = 0; } if (flags & EV_EXIT) exitshell(psh, psh->exitstatus); break; case CMDBUILTIN: case CMDSPLBLTIN: #ifdef DEBUG trputs(psh, "builtin command: "); trargs(psh, argv); #endif mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { psh->memout.nleft = 0; psh->memout.nextc = psh->memout.buf; psh->memout.bufsize = 64; mode |= REDIR_BACKQ; } e = -1; savehandler = psh->handler; savecmdname = psh->commandname; psh->handler = &jmploc; if (!setjmp(jmploc.loc)) { /* We need to ensure the command hash table isn't * corruped by temporary PATH assignments. * However we must ensure the 'local' command works! */ if (path != pathval(psh) && (cmdentry.u.bltin == hashcmd || cmdentry.u.bltin == typecmd)) { savelocalvars = psh->localvars; psh->localvars = 0; mklocal(psh, path - 5 /* PATH= */, 0); temp_path = 1; } else temp_path = 0; redirect(psh, cmd->ncmd.redirect, mode); /* exec is a special builtin, but needs this list... */ psh->cmdenviron = varlist.list; /* we must check 'readonly' flag for all builtins */ listsetvar(psh, varlist.list, cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET); psh->commandname = argv[0]; /* initialize nextopt */ psh->argptr = argv + 1; psh->optptr = NULL; /* and getopt */ #if 0 /** @todo fix getop usage! */ #if defined(__FreeBSD__) || defined(__EMX__) || defined(__APPLE__) optreset = 1; optind = 1; #else optind = 0; /* init */ #endif #endif psh->exitstatus = cmdentry.u.bltin(psh, argc, argv); } else { e = psh->exception; psh->exitstatus = e == EXINT ? SIGINT + 128 : e == EXEXEC ? psh->exerrno : 2; } psh->handler = savehandler; output_flushall(psh); psh->out1 = &psh->output; psh->out2 = &psh->errout; freestdout(psh); if (temp_path) { poplocalvars(psh); psh->localvars = savelocalvars; } psh->cmdenviron = NULL; if (e != EXSHELLPROC) { psh->commandname = savecmdname; if (flags & EV_EXIT) exitshell(psh, psh->exitstatus); } if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.cmdtype == CMDSPLBLTIN) exraise(psh, e); FORCEINTON; } if (cmdentry.u.bltin != execcmd) popredir(psh); if (flags == EV_BACKCMD) { backcmd->buf = psh->memout.buf; backcmd->nleft = (int)(psh->memout.nextc - psh->memout.buf); psh->memout.buf = NULL; } break; default: #ifdef DEBUG trputs(psh, "normal command: "); trargs(psh, argv); #endif clearredir(psh, psh->vforked); redirect(psh, cmd->ncmd.redirect, psh->vforked ? REDIR_VFORK : 0); if (!psh->vforked) for (sp = varlist.list ; sp ; sp = sp->next) setvareq(psh, sp->text, VEXPORT|VSTACK); envp = environment(psh); shellexec(psh, argv, envp, path, cmdentry.u.index, psh->vforked); break; } goto out; parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ psh->exitstatus = waitforjob(psh, jp); } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; shfile_close(&psh->fdtab, pip[1]); backcmd->jp = jp; } FORCEINTON; out: if (lastarg) /* dsl: I think this is intended to be used to support * '_' in 'vi' command mode during line editing... * However I implemented that within libedit itself. */ setvar(psh, "_", lastarg, 0); popstackmark(psh, &smark); if (eflag(psh) && psh->exitstatus && !(flags & EV_TESTED)) exitshell(psh, psh->exitstatus); }
int main(int argc, char **argv, char **envp) { int c; char *file, *sp; struct charvbuf prog; int inpath, addfile, cd; int infd, outfd; FILE *in, *out; char **headers; pid_t child; int estat; environ = envp; signal(SIGPIPE, SIG_IGN); bufinit(prog); inpath = 0; addfile = 1; cd = 0; while((c = getopt(argc, argv, "cp:P:")) >= 0) { switch(c) { case 'c': cd = 1; break; case 'p': bufadd(prog, optarg); inpath = 1; break; case 'P': prog.d = 0; bufadd(prog, optarg); while(1) { if(optind >= argc) { flog(LOG_ERR, "callcgi: unterminated argument list for -P"); exit(1); } if(!strcmp(argv[optind], ";")) { optind++; break; } bufadd(prog, argv[optind++]); } if(prog.d == 0) { flog(LOG_ERR, "callcgi: -P option needs at least a program name"); exit(1); } inpath = 1; addfile = 0; break; default: usage(); exit(1); } } if(argc - optind < 3) { usage(); exit(1); } if(((file = getenv("REQ_X_ASH_FILE")) == NULL) && (prog.d == 0)) { flog(LOG_ERR, "callcgi: needs to be called with the X-Ash-File header"); exit(1); } if(cd) { /* This behavior is encouraged by the CGI specification (RFC 3875, 7.2), * but not strictly required, and I get the feeling it might break some * relative paths here or there, so it's not the default for now. */ if((sp = strrchr(file, '/')) != NULL) { *sp = 0; if(chdir(file)) { *sp = '/'; } else { file = sp + 1; } } } if(prog.d == 0) bufadd(prog, file); if(addfile && (file != NULL)) bufadd(prog, file); bufadd(prog, NULL); child = forkchild(inpath, prog.b, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd); in = fdopen(infd, "w"); passdata(stdin, in); /* Ignore errors, perhaps? */ fclose(in); out = fdopen(outfd, "r"); if((headers = parsecgiheaders(out)) == NULL) { flog(LOG_WARNING, "CGI handler returned invalid headers"); exit(1); } sendstatus(headers, stdout); sendheaders(headers, stdout); printf("\n"); if(passdata(out, stdout)) kill(child, SIGINT); fclose(out); if(waitpid(child, &estat, 0) == child) { if(WCOREDUMP(estat)) flog(LOG_WARNING, "CGI handler `%s' dumped core", prog.b[0]); if(WIFEXITED(estat) && !WEXITSTATUS(estat)) return(0); else return(1); } flog(LOG_WARNING, "could not wait for CGI handler: %s", strerror(errno)); return(1); }
uintptr sysrfork(va_list list) { Proc *p; int n, i; Fgrp *ofg; Pgrp *opg; Rgrp *org; Egrp *oeg; ulong pid, flag; Mach *wm; flag = va_arg(list, ulong); /* Check flags before we commit */ if((flag & (RFFDG|RFCFDG)) == (RFFDG|RFCFDG)) error(Ebadarg); if((flag & (RFNAMEG|RFCNAMEG)) == (RFNAMEG|RFCNAMEG)) error(Ebadarg); if((flag & (RFENVG|RFCENVG)) == (RFENVG|RFCENVG)) error(Ebadarg); if((flag&RFPROC) == 0) { if(flag & (RFMEM|RFNOWAIT)) error(Ebadarg); if(flag & (RFFDG|RFCFDG)) { ofg = up->fgrp; if(flag & RFFDG) up->fgrp = dupfgrp(ofg); else up->fgrp = dupfgrp(nil); closefgrp(ofg); } if(flag & (RFNAMEG|RFCNAMEG)) { opg = up->pgrp; up->pgrp = newpgrp(); if(flag & RFNAMEG) pgrpcpy(up->pgrp, opg); /* inherit noattach */ up->pgrp->noattach = opg->noattach; closepgrp(opg); } if(flag & RFNOMNT) up->pgrp->noattach = 1; if(flag & RFREND) { org = up->rgrp; up->rgrp = newrgrp(); closergrp(org); } if(flag & (RFENVG|RFCENVG)) { oeg = up->egrp; up->egrp = smalloc(sizeof(Egrp)); up->egrp->ref = 1; if(flag & RFENVG) envcpy(up->egrp, oeg); closeegrp(oeg); } if(flag & RFNOTEG) up->noteid = pidalloc(0); return 0; } p = newproc(); p->scallnr = up->scallnr; p->s = up->s; p->nerrlab = 0; p->slash = up->slash; p->dot = up->dot; incref(p->dot); memmove(p->note, up->note, sizeof(p->note)); p->privatemem = up->privatemem; p->noswap = up->noswap; p->nnote = up->nnote; p->notified = 0; p->lastnote = up->lastnote; p->notify = up->notify; p->ureg = up->ureg; p->dbgreg = 0; /* Abort the child process on error */ if(waserror()){ p->kp = 1; kprocchild(p, abortion, 0); ready(p); nexterror(); } /* Make a new set of memory segments */ n = flag & RFMEM; qlock(&p->seglock); if(waserror()){ qunlock(&p->seglock); nexterror(); } for(i = 0; i < NSEG; i++) if(up->seg[i] != nil) p->seg[i] = dupseg(up->seg, i, n); qunlock(&p->seglock); poperror(); /* File descriptors */ if(flag & (RFFDG|RFCFDG)) { if(flag & RFFDG) p->fgrp = dupfgrp(up->fgrp); else p->fgrp = dupfgrp(nil); } else { p->fgrp = up->fgrp; incref(p->fgrp); } /* Process groups */ if(flag & (RFNAMEG|RFCNAMEG)) { p->pgrp = newpgrp(); if(flag & RFNAMEG) pgrpcpy(p->pgrp, up->pgrp); /* inherit noattach */ p->pgrp->noattach = up->pgrp->noattach; } else { p->pgrp = up->pgrp; incref(p->pgrp); } if(flag & RFNOMNT) p->pgrp->noattach = 1; if(flag & RFREND) p->rgrp = newrgrp(); else { incref(up->rgrp); p->rgrp = up->rgrp; } /* Environment group */ if(flag & (RFENVG|RFCENVG)) { p->egrp = smalloc(sizeof(Egrp)); p->egrp->ref = 1; if(flag & RFENVG) envcpy(p->egrp, up->egrp); } else { p->egrp = up->egrp; incref(p->egrp); } p->hang = up->hang; p->procmode = up->procmode; if(up->procctl == Proc_tracesyscall) p->procctl = Proc_tracesyscall; poperror(); /* abortion */ /* Craft a return frame which will cause the child to pop out of * the scheduler in user mode with the return register zero */ forkchild(p, up->dbgreg); p->parent = up; if((flag&RFNOWAIT) == 0){ p->parentpid = up->pid; lock(&up->exl); up->nchild++; unlock(&up->exl); } if((flag&RFNOTEG) == 0) p->noteid = up->noteid; pid = p->pid; memset(p->time, 0, sizeof(p->time)); p->time[TReal] = MACHP(0)->ticks; kstrdup(&p->text, up->text); kstrdup(&p->user, up->user); procfork(p); /* * since the bss/data segments are now shareable, * any mmu info about this process is now stale * (i.e. has bad properties) and has to be discarded. */ flushmmu(); p->basepri = up->basepri; p->priority = up->basepri; p->fixedpri = up->fixedpri; p->mp = up->mp; wm = up->wired; if(wm) procwired(p, wm->machno); ready(p); sched(); return pid; }
pid_t forkchild_no_wait(struct fzp **sin, struct fzp **sout, struct fzp **serr, const char *path, char * const argv[]) { return forkchild(sin, sout, serr, path, argv); }
/* TODO: make arg1..n an array */ int run_script(const char *script, struct strlist **userargs, int userargc, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *arg6, const char *arg7, const char *arg8, const char *arg9, const char *arg10, struct cntr *cntr, int do_wait, int logfunc) { int a=0; int l=0; pid_t p; FILE *serr=NULL; FILE *sout=NULL; char *cmd[64]={ NULL }; #ifndef HAVE_WIN32 int s=0; #endif if(!script) return 0; cmd[l++]=(char *)script; if(arg1) cmd[l++]=(char *)arg1; if(arg2) cmd[l++]=(char *)arg2; if(arg3) cmd[l++]=(char *)arg3; if(arg4) cmd[l++]=(char *)arg4; if(arg5) cmd[l++]=(char *)arg5; if(arg6) cmd[l++]=(char *)arg6; if(arg7) cmd[l++]=(char *)arg7; if(arg8) cmd[l++]=(char *)arg8; if(arg9) cmd[l++]=(char *)arg9; if(arg10) cmd[l++]=(char *)arg10; for(a=0; a<userargc && l<64-1; a++) cmd[l++]=userargs[a]->path; cmd[l++]=NULL; #ifndef HAVE_WIN32 setup_signal(SIGCHLD, run_script_sigchld_handler); #endif fflush(stdout); fflush(stderr); if(do_wait) { if((p=forkchild(NULL, &sout, &serr, cmd[0], cmd))==-1) return -1; } else { if((p=forkchild_no_wait(NULL, &sout, &serr, cmd[0], cmd))==-1) return -1; return 0; } #ifdef HAVE_WIN32 // My windows forkchild currently just executes, then returns. return 0; #else s=run_script_select(&sout, &serr, cntr, logfunc); // Set SIGCHLD back to default. setup_signal(SIGCHLD, SIG_DFL); if(s) return -1; if(WIFEXITED(run_script_status)) { int ret=WEXITSTATUS(run_script_status); logp("%s returned: %d\n", script, ret); if(cntr && ret) logw(cntr, "%s returned: %d\n", script, ret); return ret; } else if(WIFSIGNALED(run_script_status)) { logp("%s terminated on signal %d\n", script, WTERMSIG(run_script_status)); if(cntr) logw(cntr, "%s terminated on signal %d\n", script, WTERMSIG(run_script_status)); } else { logp("Strange return when trying to run %s\n", script); if(cntr) logw(cntr, "Strange return when trying to run %s\n", script); } return -1; #endif }