static int bin_clone(char *nam, char **args, UNUSED(Options ops), UNUSED(int func)) { int ttyfd, pid, cttyfd; unmetafy(*args, NULL); ttyfd = open(*args, O_RDWR|O_NOCTTY); if (ttyfd < 0) { zwarnnam(nam, "%s: %e", *args, errno); return 1; } pid = fork(); if (!pid) { clearjobtab(0); ppid = getppid(); mypid = getpid(); #ifdef HAVE_SETSID if (setsid() != mypid) zwarnnam(nam, "failed to create new session: %e", NULL, errno); #elif defined(TIOCNOTTY) if (ioctl(SHTTY, TIOCNOTTY, 0)) zwarnnam(*args, "%e", NULL, errno); setpgrp(0L, mypid); #endif dup2(ttyfd,0); dup2(ttyfd,1); dup2(ttyfd,2); if (ttyfd > 2) close(ttyfd); closem(0); close(coprocin); close(coprocout); /* Acquire a controlling terminal */ cttyfd = open(*args, O_RDWR); if (cttyfd == -1) zwarnnam(nam, "%e", NULL, errno); else { #ifdef TIOCSCTTY ioctl(cttyfd, TIOCSCTTY, 0); #endif close(cttyfd); } /* check if we acquired the tty successfully */ cttyfd = open("/dev/tty", O_RDWR); if (cttyfd == -1) zwarnnam(nam, "could not make %s my controlling tty, job control " "disabled", *args, 0); else close(cttyfd); /* Clear mygrp so that acquire_pgrp() gets the new process group. * (acquire_pgrp() is called from init_io()) */ mypgrp = 0; init_io(); setsparam("TTY", ztrdup(ttystrname)); } close(ttyfd); if (pid < 0) { zerrnam(nam, "fork failed: %e", NULL, errno); return 1; } lastpid = pid; return 0; }
static void backeval(struct blk_buf *bb, struct Strbuf *word, Char *cp, int literal) { ssize_t icnt; Char c, *ip; struct command faket; int hadnl; int pvec[2], quoted; Char *fakecom[2], ibuf[BUFSIZE]; char tibuf[BUFSIZE]; hadnl = 0; icnt = 0; quoted = (literal || (cp[0] & QUOTE)) ? QUOTE : 0; faket.t_dtyp = NODE_COMMAND; faket.t_dflg = F_BACKQ; faket.t_dlef = 0; faket.t_drit = 0; faket.t_dspr = 0; faket.t_dcom = fakecom; fakecom[0] = STRfakecom1; fakecom[1] = 0; /* * We do the psave job to temporarily change the current job so that the * following fork is considered a separate job. This is so that when * backquotes are used in a builtin function that calls glob the "current * job" is not corrupted. We only need one level of pushed jobs as long as * we are sure to fork here. */ psavejob(); cleanup_push(&faket, psavejob_cleanup); /* faket is only a marker */ /* * It would be nicer if we could integrate this redirection more with the * routines in sh.sem.c by doing a fake execute on a builtin function that * was piped out. */ mypipe(pvec); cleanup_push(&pvec[0], open_cleanup); cleanup_push(&pvec[1], open_cleanup); if (pfork(&faket, -1) == 0) { jmp_buf_t osetexit; struct command *t; size_t omark; xclose(pvec[0]); (void) dmove(pvec[1], 1); (void) dmove(SHDIAG, 2); initdesc(); closem(); arginp = cp; for (arginp = cp; *cp; cp++) { *cp &= TRIM; if (is_set(STRcsubstnonl) && (*cp == '\n' || *cp == '\r')) *cp = ' '; } /* * In the child ``forget'' everything about current aliases or * eval vectors. */ alvec = NULL; evalvec = NULL; alvecp = NULL; evalp = NULL; omark = cleanup_push_mark(); getexit(osetexit); for (;;) { (void) setexit(); justpr = 0; if (haderr) { /* unwind */ doneinp = 0; cleanup_pop_mark(omark); resexit(osetexit); reset(); } if (seterr) { xfree(seterr); seterr = NULL; } (void) lex(¶ml); cleanup_push(¶ml, lex_cleanup); if (seterr) stderror(ERR_OLD); alias(¶ml); t = syntax(paraml.next, ¶ml, 0); if (t == NULL) return; cleanup_push(t, syntax_cleanup); /* The F_BACKQ flag must set so the job output is correct if * printexitvalue is set. If it's not set, the job output * will have "Exit N" appended where N is the exit status. */ t->t_dflg = F_BACKQ|F_NOFORK; if (seterr) stderror(ERR_OLD); #ifdef SIGTSTP signal(SIGTSTP, SIG_IGN); #endif #ifdef SIGTTIN signal(SIGTTIN, SIG_IGN); #endif #ifdef SIGTTOU signal(SIGTTOU, SIG_IGN); #endif execute(t, -1, NULL, NULL, TRUE); cleanup_until(¶ml); } } cleanup_until(&pvec[1]); c = 0; ip = NULL; do { ssize_t cnt = 0; char *tmp; tmp = tibuf; for (;;) { while (icnt == 0) { int i, eof; ip = ibuf; icnt = xread(pvec[0], tmp, tibuf + BUFSIZE - tmp); eof = 0; if (icnt <= 0) { if (tmp == tibuf) goto eof; icnt = 0; eof = 1; } icnt += tmp - tibuf; i = 0; tmp = tibuf; while (tmp < tibuf + icnt) { int len; len = normal_mbtowc(&ip[i], tmp, tibuf + icnt - tmp); if (len == -1) { reset_mbtowc(); if (!eof && (size_t)(tibuf + icnt - tmp) < MB_CUR_MAX) { break; /* Maybe a partial character */ } ip[i] = (unsigned char) *tmp | INVALID_BYTE; /* Error */ } if (len <= 0) len = 1; i++; tmp += len; } if (tmp != tibuf) memmove (tibuf, tmp, tibuf + icnt - tmp); tmp = tibuf + (tibuf + icnt - tmp); icnt = i; } if (hadnl) break; --icnt; c = (*ip++ & TRIM); if (c == 0) break; #if defined(WINNT_NATIVE) || defined(__CYGWIN__) if (c == '\r') c = ' '; #endif /* WINNT_NATIVE || __CYGWIN__ */ if (c == '\n') { /* * Continue around the loop one more time, so that we can eat * the last newline without terminating this word. */ hadnl = 1; continue; } if (!quoted && (c == ' ' || c == '\t')) break; cnt++; Strbuf_append1(word, c | quoted); } /* * Unless at end-of-file, we will form a new word here if there were * characters in the word, or in any case when we take text literally. * If we didn't make empty words here when literal was set then we * would lose blank lines. */ if (c != 0 && (cnt || literal)) pword(bb, word); hadnl = 0; } while (c > 0); eof: cleanup_until(&pvec[0]); pwait(); cleanup_until(&faket); /* psavejob_cleanup(); */ }
static int newptycmd(char *nam, char *pname, char **args, int echo, int nblock) { Ptycmd p; int master, slave, pid, oineval = ineval, ret; char *oscriptname = scriptname, syncch; Eprog prog; /* code borrowed from bin_eval() */ ineval = !isset(EVALLINENO); if (!ineval) scriptname = "(zpty)"; prog = parse_string(zjoin(args, ' ', 1), 0); if (!prog) { errflag &= ~ERRFLAG_ERROR; scriptname = oscriptname; ineval = oineval; return 1; } if (get_pty(1, &master)) { zwarnnam(nam, "can't open pseudo terminal: %e", errno); scriptname = oscriptname; ineval = oineval; return 1; } if ((pid = fork()) == -1) { zwarnnam(nam, "can't create pty command %s: %e", pname, errno); close(master); scriptname = oscriptname; ineval = oineval; return 1; } else if (!pid) { /* This code copied from the clone module, except for getting * * the descriptor from get_pty() and duplicating it to 0/1/2. */ deletehookfunc("exit", ptyhook); clearjobtab(0); ppid = getppid(); mypid = getpid(); #ifdef HAVE_SETSID if (setsid() != mypid) { zwarnnam(nam, "failed to create new session: %e", errno); #endif #ifdef TIOCNOTTY if (ioctl(SHTTY, TIOCNOTTY, 0)) zwarnnam(nam, "%e", errno); setpgrp(0L, mypid); #endif #ifdef HAVE_SETSID } #endif if (get_pty(0, &slave)) exit(1); SHTTY = slave; attachtty(mypid); #ifdef TIOCGWINSZ /* Set the window size before associating with the terminal * * so that we don't get hit with a SIGWINCH. I'm paranoid. */ if (interact) { struct ttyinfo info; if (ioctl(slave, TIOCGWINSZ, (char *) &info.winsize) == 0) { info.winsize.ws_row = zterm_lines; info.winsize.ws_col = zterm_columns; ioctl(slave, TIOCSWINSZ, (char *) &info.winsize); } } #endif /* TIOCGWINSZ */ if (!echo) { struct ttyinfo info; if (!ptygettyinfo(slave, &info)) { #ifdef HAVE_TERMIOS_H info.tio.c_lflag &= ~ECHO; #else #ifdef HAVE_TERMIO_H info.tio.c_lflag &= ~ECHO; #else info.tio.lmodes &= ~ECHO; /**** dunno if this is right */ #endif #endif ptysettyinfo(slave, &info); } } #ifdef TIOCSCTTY ioctl(slave, TIOCSCTTY, 0); #endif close(0); close(1); close(2); dup2(slave, 0); dup2(slave, 1); dup2(slave, 2); closem(FDT_UNUSED, 0); close(slave); close(master); close(coprocin); close(coprocout); init_io(NULL); setsparam("TTY", ztrdup(ttystrname)); opts[INTERACTIVE] = 0; syncch = 0; do { ret = write(1, &syncch, 1); } while (ret != 1 && ( #ifdef EWOULDBLOCK errno == EWOULDBLOCK || #else #ifdef EAGAIN errno == EAGAIN || #endif #endif errno == EINTR)); execode(prog, 1, 0, "zpty"); stopmsg = 2; mypid = 0; /* trick to ensure we _exit() */ zexit(lastval, 0); } master = movefd(master); if (master == -1) { zerrnam(nam, "cannot duplicate fd %d: %e", master, errno); scriptname = oscriptname; ineval = oineval; return 1; } p = (Ptycmd) zalloc(sizeof(*p)); p->name = ztrdup(pname); p->args = zarrdup(args); p->fd = master; p->pid = pid; p->echo = echo; p->nblock = nblock; p->fin = 0; p->read = -1; p->old = NULL; p->olen = 0; p->next = ptycmds; ptycmds = p; if (nblock) ptynonblock(master); scriptname = oscriptname; ineval = oineval; do { ret = read(master, &syncch, 1); } while (ret != 1 && ( #ifdef EWOULDBLOCK errno == EWOULDBLOCK || #else #ifdef EAGAIN errno == EAGAIN || #endif #endif errno == EINTR)); setiparam_no_convert("REPLY", (zlong)master); return 0; }