diff options
Diffstat (limited to 'Src/exec.c')
-rw-r--r-- | Src/exec.c | 136 |
1 files changed, 103 insertions, 33 deletions
diff --git a/Src/exec.c b/Src/exec.c index 50027654a..27d49e005 100644 --- a/Src/exec.c +++ b/Src/exec.c @@ -84,7 +84,7 @@ int nohistsave; /* error flag: bits from enum errflag_bits */ /**/ -mod_export int errflag; +mod_export volatile int errflag; /* * State of trap return value. Value is from enum trap_state. @@ -122,7 +122,7 @@ int subsh; /* != 0 if we have a return pending */ /**/ -mod_export int retflag; +mod_export volatile int retflag; /**/ long lastval2; @@ -547,10 +547,29 @@ zexecve(char *pth, char **argv, char **newenvp) } } } else if (eno == ENOEXEC) { - for (t0 = 0; t0 != ct; t0++) - if (!execvebuf[t0]) - break; - if (t0 == ct) { + /* Perform binary safety check on classic shell * + * scripts (shebang wasn't introduced until UNIX * + * Seventh Edition). POSIX says we shall allow * + * execution of scripts with concatenated binary * + * and suggests checking a line exists before the * + * first NUL character with a lowercase letter or * + * expansion. This is consistent with FreeBSD sh. */ + int isbinary, hasletter; + if (!(ptr2 = memchr(execvebuf, '\0', ct))) { + isbinary = 0; + } else { + isbinary = 1; + hasletter = 0; + for (ptr = execvebuf; ptr < ptr2; ptr++) { + if (islower(*ptr) || *ptr == '$' || *ptr == '`') + hasletter = 1; + if (hasletter && *ptr == '\n') { + isbinary = 0; + break; + } + } + } + if (!isbinary) { argv[-1] = "sh"; winch_unblock(); execve("/bin/sh", argv - 1, newenvp); @@ -665,8 +684,10 @@ execute(LinkList args, int flags, int defpath) /* If the parameter STTY is set in the command's environment, * * we first run the stty command with the value of this * - * parameter as it arguments. */ - if ((s = STTYval) && isatty(0) && (GETPGRP() == getpid())) { + * parameter as it arguments. If the parameter is empty, we * + * do nothing, but this causes the terminal settings to be * + * restored later which can be useful. */ + if ((s = STTYval) && *s && isatty(0) && (GETPGRP() == getpid())) { char *t = tricat("stty", " ", s); STTYval = 0; /* this prevents infinite recursion */ @@ -1014,7 +1035,8 @@ entersubsh(int flags, struct entersubsh_ret *retp) if (!(flags & ESUB_KEEPTRAP)) for (sig = 0; sig < SIGCOUNT; sig++) - if (!(sigtrapped[sig] & ZSIG_FUNC)) + if (!(sigtrapped[sig] & ZSIG_FUNC) && + !(isset(POSIXTRAPS) && (sigtrapped[sig] & ZSIG_IGNORED))) unsettrap(sig); monitor = isset(MONITOR); job_control_ok = monitor && (flags & ESUB_JOB_CONTROL) && isset(POSIXJOBS); @@ -1036,7 +1058,8 @@ entersubsh(int flags, struct entersubsh_ret *retp) } else if (thisjob != -1 && (flags & ESUB_PGRP)) { if (jobtab[list_pipe_job].gleader && (list_pipe || list_pipe_child)) { if (setpgrp(0L, jobtab[list_pipe_job].gleader) == -1 || - killpg(jobtab[list_pipe_job].gleader, 0) == -1) { + (killpg(jobtab[list_pipe_job].gleader, 0) == -1 && + errno == ESRCH)) { jobtab[list_pipe_job].gleader = jobtab[thisjob].gleader = (list_pipe_child ? mypgrp : getpid()); setpgrp(0L, jobtab[list_pipe_job].gleader); @@ -1248,7 +1271,9 @@ execsimple(Estate state) } else { int q = queue_signal_level(); dont_queue_signals(); - if (code == WC_FUNCDEF) + if (errflag) + lv = errflag; + else if (code == WC_FUNCDEF) lv = execfuncdef(state, NULL); else lv = (execfuncs[code - WC_CURSH])(state, 0); @@ -1664,6 +1689,7 @@ execpline(Estate state, wordcode slcode, int how, int last1) execpline2(state, code, how, opipe[0], ipipe[1], last1); pline_level--; if (how & Z_ASYNC) { + clearoldjobtab(); lastwj = newjob; if (thisjob == list_pipe_job) @@ -2142,14 +2168,15 @@ clobber_open(struct redir *f) { struct stat buf; int fd, oerrno; + char *ufname = unmeta(f->name); /* If clobbering, just open. */ if (isset(CLOBBER) || IS_CLOBBER_REDIR(f->type)) - return open(unmeta(f->name), + return open(ufname, O_WRONLY | O_CREAT | O_TRUNC | O_NOCTTY, 0666); /* If not clobbering, attempt to create file exclusively. */ - if ((fd = open(unmeta(f->name), + if ((fd = open(ufname, O_WRONLY | O_CREAT | O_EXCL | O_NOCTTY, 0666)) >= 0) return fd; @@ -2157,11 +2184,27 @@ clobber_open(struct redir *f) * Try opening, and if it's a regular file then close it again * * because we weren't supposed to open it. */ oerrno = errno; - if ((fd = open(unmeta(f->name), O_WRONLY | O_NOCTTY)) != -1) { - if(!fstat(fd, &buf) && !S_ISREG(buf.st_mode)) - return fd; + if ((fd = open(ufname, O_WRONLY | O_NOCTTY)) != -1) { + if(!fstat(fd, &buf)) { + if (!S_ISREG(buf.st_mode)) + return fd; + /* + * If CLOBBER_EMPTY is in effect and the file is empty, + * we are allowed to re-use it. + * + * Note: there is an intrinsic race here because another + * process can write to this file at any time. The only fix + * would be file locking, which we wish to avoid in basic + * file operations at this level. This would not be + * fixed. just additionally complicated, by re-opening the + * file and truncating. + */ + if (isset(CLOBBEREMPTY) && buf.st_size == 0) + return fd; + } close(fd); } + errno = oerrno; return -1; } @@ -2771,8 +2814,7 @@ execcmd_fork(Estate state, int how, int type, Wordcode varspc, /* Check if we should run background jobs at a lower priority. */ if ((how & Z_ASYNC) && isset(BGNICE)) { errno = 0; - nice(5); - if (errno) + if (nice(5) == -1 && errno) zwarn("nice(5) failed: %e", errno); } #endif /* HAVE_NICE */ @@ -2865,11 +2907,13 @@ execcmd_exec(Estate state, Execcmd_params eparams, pushnode(args, dupstring("fg")); } - if ((how & Z_ASYNC) || output) { + if ((how & Z_ASYNC) || output || + (last1 == 2 && input && EMULATION(EMULATE_SH))) { /* - * If running in the background, or not the last command in a - * pipeline, we don't need any of the rest of this function to - * affect the state in the main shell, so fork immediately. + * If running in the background, not the last command in a + * pipeline, or the last command in a multi-stage pipeline + * in sh mode, we don't need any of the rest of this function + * to affect the state in the main shell, so fork immediately. * * In other cases we may need to process the command line * a bit further before we make the decision. @@ -3383,7 +3427,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, int rmall; s[l - 2] = 0; - rmall = checkrmall(s); + rmall = checkrmall(l == 2 ? "/" : s); s[l - 2] = t; if (!rmall) { @@ -3913,7 +3957,7 @@ execcmd_exec(Estate state, Execcmd_params eparams, if (type == WC_AUTOFN) { /* * We pre-loaded this to get any redirs. - * So we execuate a simplified function here. + * So we execute a simplified function here. */ lastval = execautofn_basic(state, do_exec); } else @@ -3968,8 +4012,8 @@ execcmd_exec(Estate state, Execcmd_params eparams, if (is_shfunc) { /* It's a shell function */ - pipecleanfilelist(filelist, 0); execshfunc((Shfunc) hn, args); + pipecleanfilelist(filelist, 0); } else { /* It's a builtin */ LinkList assigns = (LinkList)0; @@ -4581,7 +4625,7 @@ getoutput(char *cmd, int qt) char *s; int onc = nocomments; - nocomments = (interact && unset(INTERACTIVECOMMENTS)); + nocomments = (interact && !sourcelevel && unset(INTERACTIVECOMMENTS)); prog = parse_string(cmd, 0); nocomments = onc; @@ -4793,8 +4837,10 @@ getoutputfile(char *cmd, char **eptr) singsub(&s); if (errflag) s = NULL; - else + else { untokenize(s); + s = dyncat(s, "\n"); + } } if (!s) /* Unclear why we need to do this before open() */ @@ -5146,33 +5192,51 @@ exectime(Estate state, UNUSED(int do_exec)) return lastval; } -/* Define a shell function */ - +/* The string displayed in lieu of the name of an anonymous function (in PS4, + * zprof output, etc) + */ static const char *const ANONYMOUS_FUNCTION_NAME = "(anon)"; +/* + * Take a function name argument and return true iff it is equal to the string + * used for the names of anonymous functions, "(anon)". + * + * Note that it's possible to define a named function literally called "(anon)" + * (though I doubt anyone would ever do that). + */ +/**/ +int is_anonymous_function_name(const char *name) +{ + return !strcmp(name, ANONYMOUS_FUNCTION_NAME); +} + +/* Define a shell function */ + /**/ static int execfuncdef(Estate state, Eprog redir_prog) { Shfunc shf; char *s = NULL; - int signum, nprg, sbeg, nstrs, npats, len, plen, i, htok = 0, ret = 0; + int signum, nprg, sbeg, nstrs, npats, do_tracing, len, plen, i, htok = 0, ret = 0; int anon_func = 0; Wordcode beg = state->pc, end; Eprog prog; Patprog *pp; LinkList names; + int tracing_flags; end = beg + WC_FUNCDEF_SKIP(state->pc[-1]); names = ecgetlist(state, *state->pc++, EC_DUPTOK, &htok); - nprg = end - beg; sbeg = *state->pc++; nstrs = *state->pc++; npats = *state->pc++; + do_tracing = *state->pc++; nprg = (end - state->pc); plen = nprg * sizeof(wordcode); len = plen + (npats * sizeof(Patprog)) + nstrs; + tracing_flags = do_tracing ? PM_TAGGED_LOCAL : 0; if (htok && names) { execsubst(names); @@ -5222,7 +5286,7 @@ execfuncdef(Estate state, Eprog redir_prog) shf = (Shfunc) zalloc(sizeof(*shf)); shf->funcdef = prog; - shf->node.flags = 0; + shf->node.flags = tracing_flags; /* No dircache here, not a directory */ shf->filename = ztrdup(scriptfilename); shf->lineno = @@ -5317,6 +5381,12 @@ execfuncdef(Estate state, Eprog redir_prog) */ removetrapnode(signum); } + /* Is this function traced and redefining itself? */ + if (funcstack && funcstack->tp == FS_FUNC && + !strcmp(s, funcstack->name)) { + Shfunc old = ((Shfunc)shfunctab->getnode(shfunctab, s)); + shf->node.flags |= old->node.flags & (PM_TAGGED|PM_TAGGED_LOCAL); + } shfunctab->addnode(shfunctab, ztrdup(s), shf); } } @@ -6132,7 +6202,7 @@ stripkshdef(Eprog prog, char *name) int sbeg = pc[2], nstrs = pc[3], nprg, npats = pc[4], plen, len, i; Patprog *pp; - pc += 5; + pc += 6; nprg = end - pc; plen = nprg * sizeof(wordcode); |