1*d1c86d36Skre /* $NetBSD: jobs.c,v 1.123 2024/10/09 13:43:32 kre Exp $ */ 249f0ad86Scgd 361f28255Scgd /*- 437ed7877Sjtc * Copyright (c) 1991, 1993 537ed7877Sjtc * The Regents of the University of California. All rights reserved. 661f28255Scgd * 761f28255Scgd * This code is derived from software contributed to Berkeley by 861f28255Scgd * Kenneth Almquist. 961f28255Scgd * 1061f28255Scgd * Redistribution and use in source and binary forms, with or without 1161f28255Scgd * modification, are permitted provided that the following conditions 1261f28255Scgd * are met: 1361f28255Scgd * 1. Redistributions of source code must retain the above copyright 1461f28255Scgd * notice, this list of conditions and the following disclaimer. 1561f28255Scgd * 2. Redistributions in binary form must reproduce the above copyright 1661f28255Scgd * notice, this list of conditions and the following disclaimer in the 1761f28255Scgd * documentation and/or other materials provided with the distribution. 18b5b29542Sagc * 3. Neither the name of the University nor the names of its contributors 1961f28255Scgd * may be used to endorse or promote products derived from this software 2061f28255Scgd * without specific prior written permission. 2161f28255Scgd * 2261f28255Scgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2361f28255Scgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2461f28255Scgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2561f28255Scgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2661f28255Scgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2761f28255Scgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2861f28255Scgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2961f28255Scgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3061f28255Scgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3161f28255Scgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3261f28255Scgd * SUCH DAMAGE. 3361f28255Scgd */ 3461f28255Scgd 35cd799663Schristos #include <sys/cdefs.h> 3661f28255Scgd #ifndef lint 3749f0ad86Scgd #if 0 3807bae7edSchristos static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; 3949f0ad86Scgd #else 40*d1c86d36Skre __RCSID("$NetBSD: jobs.c,v 1.123 2024/10/09 13:43:32 kre Exp $"); 4149f0ad86Scgd #endif 4261f28255Scgd #endif /* not lint */ 4361f28255Scgd 444e9fc30dSkre #include <stdio.h> 4507bae7edSchristos #include <fcntl.h> 4607bae7edSchristos #include <signal.h> 4707bae7edSchristos #include <errno.h> 4807bae7edSchristos #include <unistd.h> 4907bae7edSchristos #include <stdlib.h> 50618cca24Sfair #include <paths.h> 5107bae7edSchristos #include <sys/types.h> 5207bae7edSchristos #include <sys/param.h> 5307bae7edSchristos #ifdef BSD 5407bae7edSchristos #include <sys/wait.h> 5507bae7edSchristos #include <sys/time.h> 5607bae7edSchristos #include <sys/resource.h> 5707bae7edSchristos #endif 58a20d7942Schristos #include <sys/ioctl.h> 5907bae7edSchristos 6061f28255Scgd #include "shell.h" 6161f28255Scgd #if JOBS 62a20d7942Schristos #if OLD_TTY_DRIVER 6361f28255Scgd #include "sgtty.h" 64a20d7942Schristos #else 65a20d7942Schristos #include <termios.h> 66a20d7942Schristos #endif 6761f28255Scgd #undef CEOF /* syntax.h redefines this */ 6861f28255Scgd #endif 6907bae7edSchristos #include "redir.h" 7007bae7edSchristos #include "show.h" 7161f28255Scgd #include "main.h" 7261f28255Scgd #include "parser.h" 7361f28255Scgd #include "nodes.h" 7461f28255Scgd #include "jobs.h" 754e9fc30dSkre #include "var.h" 7661f28255Scgd #include "options.h" 774fc4fe2eSchristos #include "builtins.h" 7861f28255Scgd #include "trap.h" 7961f28255Scgd #include "syntax.h" 8061f28255Scgd #include "input.h" 8161f28255Scgd #include "output.h" 8261f28255Scgd #include "memalloc.h" 8361f28255Scgd #include "error.h" 8461f28255Scgd #include "mystring.h" 8561f28255Scgd 8661f28255Scgd 879572a5c2Skre #ifndef WCONTINUED 889572a5c2Skre #define WCONTINUED 0 /* So we can compile on old systems */ 899572a5c2Skre #endif 909572a5c2Skre #ifndef WIFCONTINUED 919572a5c2Skre #define WIFCONTINUED(x) (0) /* ditto */ 929572a5c2Skre #endif 939572a5c2Skre 949572a5c2Skre 95c02b3bbdSchristos static struct job *jobtab; /* array of jobs */ 96c02b3bbdSchristos static int njobs; /* size of array */ 97c02b3bbdSchristos static int jobs_invalid; /* set in child */ 98c02b3bbdSchristos MKINIT pid_t backgndpid = -1; /* pid of last background process */ 9961f28255Scgd #if JOBS 10061f28255Scgd int initialpgrp; /* pgrp of shell on invocation */ 101c02b3bbdSchristos static int curjob = -1; /* current job */ 10261f28255Scgd #endif 103e6ab6d48Schristos static int ttyfd = -1; 10461f28255Scgd 105c02b3bbdSchristos STATIC void restartjob(struct job *); 106c02b3bbdSchristos STATIC void freejob(struct job *); 107c02b3bbdSchristos STATIC struct job *getjob(const char *, int); 1084e9fc30dSkre STATIC int dowait(int, struct job *, struct job **); 10997f12feaSchristos #define WBLOCK 1 11097f12feaSchristos #define WNOFREE 2 1119572a5c2Skre #define WSILENT 4 112bffe5190Skre STATIC int jobstatus(const struct job *, int); 113c02b3bbdSchristos STATIC int waitproc(int, struct job *, int *); 114601249a2Skre STATIC int cmdtxt(union node *, int); 115c02b3bbdSchristos STATIC void cmdlist(union node *, int); 116c02b3bbdSchristos STATIC void cmdputs(const char *); 1170fe4e128Skre inline static void cmdputi(int); 11861f28255Scgd 11921f00868Skre #define JNUM(j) ((int)((j) != NULL ? ((j) - jobtab) + 1 : 0)) 12021f00868Skre 12172c2627fSdholland #ifdef SYSV 12272c2627fSdholland STATIC int onsigchild(void); 12372c2627fSdholland #endif 12472c2627fSdholland 125e6ab6d48Schristos #ifdef OLD_TTY_DRIVER 126c02b3bbdSchristos static pid_t tcgetpgrp(int fd); 127c02b3bbdSchristos static int tcsetpgrp(int fd, pid_t pgrp); 128e6ab6d48Schristos 129e6ab6d48Schristos static pid_t 130c02b3bbdSchristos tcgetpgrp(int fd) 131e6ab6d48Schristos { 132e6ab6d48Schristos pid_t pgrp; 133e6ab6d48Schristos if (ioctl(fd, TIOCGPGRP, (char *)&pgrp) == -1) 134e6ab6d48Schristos return -1; 135e6ab6d48Schristos else 136e6ab6d48Schristos return pgrp; 137e6ab6d48Schristos } 138e6ab6d48Schristos 139e6ab6d48Schristos static int 140c02b3bbdSchristos tcsetpgrp(int fd, pid_tpgrp) 141e6ab6d48Schristos { 142e6ab6d48Schristos return ioctl(fd, TIOCSPGRP, (char *)&pgrp); 143e6ab6d48Schristos } 144e6ab6d48Schristos #endif 14561f28255Scgd 14651c4dfe4Skre static void 14751c4dfe4Skre ttyfd_change(int from, int to) 14851c4dfe4Skre { 14951c4dfe4Skre if (ttyfd == from) 15051c4dfe4Skre ttyfd = to; 15151c4dfe4Skre } 15251c4dfe4Skre 15361f28255Scgd /* 15461f28255Scgd * Turn job control on and off. 15561f28255Scgd * 15661f28255Scgd * Note: This code assumes that the third arg to ioctl is a character 15761f28255Scgd * pointer, which is true on Berkeley systems but not System V. Since 15861f28255Scgd * System V doesn't have job control yet, this isn't a problem now. 15961f28255Scgd */ 16061f28255Scgd 16161f28255Scgd MKINIT int jobctl; 16261f28255Scgd 16361f28255Scgd void 164c02b3bbdSchristos setjobctl(int on) 1655dad1439Scgd { 16637ed7877Sjtc #ifdef OLD_TTY_DRIVER 16761f28255Scgd int ldisc; 16837ed7877Sjtc #endif 16961f28255Scgd 17061f28255Scgd if (on == jobctl || rootshell == 0) 17161f28255Scgd return; 17261f28255Scgd if (on) { 173e6ab6d48Schristos #if defined(FIOCLEX) || defined(FD_CLOEXEC) 17466b91257Schristos int i; 17551c4dfe4Skre 176e6ab6d48Schristos if (ttyfd != -1) 17751c4dfe4Skre sh_close(ttyfd); 178de525ac6Schristos if ((ttyfd = open("/dev/tty", O_RDWR)) == -1) { 179de525ac6Schristos for (i = 0; i < 3; i++) { 180de525ac6Schristos if (isatty(i) && (ttyfd = dup(i)) != -1) 181de525ac6Schristos break; 182de525ac6Schristos } 183de525ac6Schristos if (i == 3) 184e6ab6d48Schristos goto out; 185de525ac6Schristos } 1861fad4bb6Schristos ttyfd = to_upper_fd(ttyfd); /* Move to a high fd */ 18751c4dfe4Skre register_sh_fd(ttyfd, ttyfd_change); 188e6ab6d48Schristos #else 189e6ab6d48Schristos out2str("sh: Need FIOCLEX or FD_CLOEXEC to support job control"); 190e6ab6d48Schristos goto out; 191e6ab6d48Schristos #endif 192e6ab6d48Schristos if ((initialpgrp = tcgetpgrp(ttyfd)) < 0) { 193e6ab6d48Schristos out: 19437ed7877Sjtc out2str("sh: can't access tty; job control turned off\n"); 19537ed7877Sjtc mflag = 0; 19661f28255Scgd return; 19761f28255Scgd } 19861f28255Scgd if (initialpgrp == -1) 199df81ab82Sjtc initialpgrp = getpgrp(); 200074335e1Srillig else if (initialpgrp != getpgrp()) 201e6ab6d48Schristos killpg(0, SIGTTIN); 202e6ab6d48Schristos 20337ed7877Sjtc #ifdef OLD_TTY_DRIVER 204e6ab6d48Schristos if (ioctl(ttyfd, TIOCGETD, (char *)&ldisc) < 0 205e6ab6d48Schristos || ldisc != NTTYDISC) { 20637ed7877Sjtc out2str("sh: need new tty driver to run job control; job control turned off\n"); 20737ed7877Sjtc mflag = 0; 20861f28255Scgd return; 20961f28255Scgd } 21037ed7877Sjtc #endif 211edcb4544Schristos setsignal(SIGTSTP, 0); 212edcb4544Schristos setsignal(SIGTTOU, 0); 213edcb4544Schristos setsignal(SIGTTIN, 0); 214964f98e7Stv if (getpgrp() != rootpid && setpgid(0, rootpid) == -1) 215edcb4544Schristos error("Cannot set process group (%s) at %d", 216edcb4544Schristos strerror(errno), __LINE__); 217edcb4544Schristos if (tcsetpgrp(ttyfd, rootpid) == -1) 218edcb4544Schristos error("Cannot set tty process group (%s) at %d", 219edcb4544Schristos strerror(errno), __LINE__); 22061f28255Scgd } else { /* turning job control off */ 221964f98e7Stv if (getpgrp() != initialpgrp && setpgid(0, initialpgrp) == -1) 222edcb4544Schristos error("Cannot set process group (%s) at %d", 223edcb4544Schristos strerror(errno), __LINE__); 224edcb4544Schristos if (tcsetpgrp(ttyfd, initialpgrp) == -1) 225edcb4544Schristos error("Cannot set tty process group (%s) at %d", 226edcb4544Schristos strerror(errno), __LINE__); 22751c4dfe4Skre sh_close(ttyfd); 228e6ab6d48Schristos ttyfd = -1; 229edcb4544Schristos setsignal(SIGTSTP, 0); 230edcb4544Schristos setsignal(SIGTTOU, 0); 231edcb4544Schristos setsignal(SIGTTIN, 0); 23261f28255Scgd } 23361f28255Scgd jobctl = on; 23461f28255Scgd } 23561f28255Scgd 23661f28255Scgd 23761f28255Scgd #ifdef mkinit 23807bae7edSchristos INCLUDE <stdlib.h> 23961f28255Scgd 24061f28255Scgd SHELLPROC { 24161f28255Scgd backgndpid = -1; 24261f28255Scgd #if JOBS 24361f28255Scgd jobctl = 0; 24461f28255Scgd #endif 24561f28255Scgd } 24661f28255Scgd 24761f28255Scgd #endif 24861f28255Scgd 24961f28255Scgd 25061f28255Scgd 25161f28255Scgd #if JOBS 252658a58d0Sdsl static int 253658a58d0Sdsl do_fgcmd(const char *arg_ptr) 2545dad1439Scgd { 25561f28255Scgd struct job *jp; 256edcb4544Schristos int i; 25761f28255Scgd int status; 25861f28255Scgd 2599572a5c2Skre if (jobs_invalid) 2609572a5c2Skre error("No current jobs"); 261658a58d0Sdsl jp = getjob(arg_ptr, 0); 26261f28255Scgd if (jp->jobctl == 0) 26361f28255Scgd error("job not created under job control"); 264c02b3bbdSchristos out1fmt("%s", jp->ps[0].cmd); 265c02b3bbdSchristos for (i = 1; i < jp->nprocs; i++) 266c02b3bbdSchristos out1fmt(" | %s", jp->ps[i].cmd ); 267c02b3bbdSchristos out1c('\n'); 268c02b3bbdSchristos flushall(); 269edcb4544Schristos 2701c627bdfSkre if (tcsetpgrp(ttyfd, jp->pgrp) == -1) { 271edcb4544Schristos error("Cannot set tty process group (%s) at %d", 272edcb4544Schristos strerror(errno), __LINE__); 273edcb4544Schristos } 27461f28255Scgd INTOFF; 275e8999de4Skre restartjob(jp); 27661f28255Scgd status = waitforjob(jp); 27761f28255Scgd INTON; 27861f28255Scgd return status; 27961f28255Scgd } 28061f28255Scgd 281658a58d0Sdsl int 282658a58d0Sdsl fgcmd(int argc, char **argv) 283658a58d0Sdsl { 284658a58d0Sdsl nextopt(""); 285658a58d0Sdsl return do_fgcmd(*argptr); 286658a58d0Sdsl } 287658a58d0Sdsl 288658a58d0Sdsl int 289658a58d0Sdsl fgcmd_percent(int argc, char **argv) 290658a58d0Sdsl { 291658a58d0Sdsl nextopt(""); 292658a58d0Sdsl return do_fgcmd(*argv); 293658a58d0Sdsl } 294658a58d0Sdsl 295c02b3bbdSchristos static void 296c02b3bbdSchristos set_curjob(struct job *jp, int mode) 297c02b3bbdSchristos { 298c02b3bbdSchristos struct job *jp1, *jp2; 299c02b3bbdSchristos int i, ji; 300c02b3bbdSchristos 301c02b3bbdSchristos ji = jp - jobtab; 302c02b3bbdSchristos 303c02b3bbdSchristos /* first remove from list */ 304c02b3bbdSchristos if (ji == curjob) 305c02b3bbdSchristos curjob = jp->prev_job; 306c02b3bbdSchristos else { 307c02b3bbdSchristos for (i = 0; i < njobs; i++) { 308c02b3bbdSchristos if (jobtab[i].prev_job != ji) 309c02b3bbdSchristos continue; 310c02b3bbdSchristos jobtab[i].prev_job = jp->prev_job; 311c02b3bbdSchristos break; 312c02b3bbdSchristos } 313c02b3bbdSchristos } 314c02b3bbdSchristos 315c02b3bbdSchristos /* Then re-insert in correct position */ 316c02b3bbdSchristos switch (mode) { 317c02b3bbdSchristos case 0: /* job being deleted */ 318c02b3bbdSchristos jp->prev_job = -1; 319c02b3bbdSchristos break; 320c02b3bbdSchristos case 1: /* newly created job or backgrounded job, 321c02b3bbdSchristos put after all stopped jobs. */ 322c02b3bbdSchristos if (curjob != -1 && jobtab[curjob].state == JOBSTOPPED) { 323c02b3bbdSchristos for (jp1 = jobtab + curjob; ; jp1 = jp2) { 324c02b3bbdSchristos if (jp1->prev_job == -1) 325c02b3bbdSchristos break; 326c02b3bbdSchristos jp2 = jobtab + jp1->prev_job; 327c02b3bbdSchristos if (jp2->state != JOBSTOPPED) 328c02b3bbdSchristos break; 329c02b3bbdSchristos } 330c02b3bbdSchristos jp->prev_job = jp1->prev_job; 331c02b3bbdSchristos jp1->prev_job = ji; 332c02b3bbdSchristos break; 333c02b3bbdSchristos } 334c02b3bbdSchristos /* FALLTHROUGH */ 335c02b3bbdSchristos case 2: /* newly stopped job - becomes curjob */ 336c02b3bbdSchristos jp->prev_job = curjob; 337c02b3bbdSchristos curjob = ji; 338c02b3bbdSchristos break; 339c02b3bbdSchristos } 340c02b3bbdSchristos } 34161f28255Scgd 3425dad1439Scgd int 343c02b3bbdSchristos bgcmd(int argc, char **argv) 3445dad1439Scgd { 34561f28255Scgd struct job *jp; 346c02b3bbdSchristos int i; 34761f28255Scgd 348c02b3bbdSchristos nextopt(""); 3499572a5c2Skre if (jobs_invalid) 3509572a5c2Skre error("No current jobs"); 35161f28255Scgd do { 352c02b3bbdSchristos jp = getjob(*argptr, 0); 35361f28255Scgd if (jp->jobctl == 0) 35461f28255Scgd error("job not created under job control"); 355c02b3bbdSchristos set_curjob(jp, 1); 35621f00868Skre out1fmt("[%d] %s", JNUM(jp), jp->ps[0].cmd); 357c02b3bbdSchristos for (i = 1; i < jp->nprocs; i++) 358c02b3bbdSchristos out1fmt(" | %s", jp->ps[i].cmd ); 359c02b3bbdSchristos out1c('\n'); 360c02b3bbdSchristos flushall(); 36161f28255Scgd restartjob(jp); 362c02b3bbdSchristos } while (*argptr && *++argptr); 36361f28255Scgd return 0; 36461f28255Scgd } 36561f28255Scgd 36661f28255Scgd 36761f28255Scgd STATIC void 368c02b3bbdSchristos restartjob(struct job *jp) 36961f28255Scgd { 37061f28255Scgd struct procstat *ps; 371ccf5ffdbSkre int i, e; 37261f28255Scgd 37361f28255Scgd if (jp->state == JOBDONE) 37461f28255Scgd return; 3751c627bdfSkre if (jp->pgrp == 0) 3761c627bdfSkre error("Job [%d] does not have a process group", JNUM(jp)); 3771c627bdfSkre 37861f28255Scgd INTOFF; 379ccf5ffdbSkre for (e = i = 0; i < jp->nprocs; i++) { 3807a2f8a05Skre /* 3817a2f8a05Skre * Don't touch a process we already waited for and collected 3827a2f8a05Skre * exit status, that pid may have been reused for something 3837a2f8a05Skre * else - even another of our jobs 3847a2f8a05Skre */ 3857a2f8a05Skre if (jp->ps[i].status != -1 && !WIFSTOPPED(jp->ps[i].status)) 3867a2f8a05Skre continue; 3877a2f8a05Skre 3887a2f8a05Skre /* 3897a2f8a05Skre * Otherwise tell it to continue, if it worked, we're done 3907a2f8a05Skre * (we signal the whole process group) 3917a2f8a05Skre */ 3921c627bdfSkre if (killpg(jp->pgrp, SIGCONT) != -1) 393edcb4544Schristos break; 394ccf5ffdbSkre e = errno; 3951c627bdfSkre break; /* no point trying again */ 396ccf5ffdbSkre } 3971c627bdfSkre 3981c627bdfSkre if (e != 0) 3991c627bdfSkre error("Cannot continue job (%s)", strerror(e)); 4001c627bdfSkre else if (i >= jp->nprocs) 4011c627bdfSkre error("Job [%d] has no stopped processes", JNUM(jp)); 4027a2f8a05Skre 4037a2f8a05Skre /* 4047a2f8a05Skre * Now change state of all stopped processes in the job to running 4057a2f8a05Skre * If there were any, the job is now running as well. 4067a2f8a05Skre */ 40761f28255Scgd for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { 4087f2a2717Schristos if (WIFSTOPPED(ps->status)) { 409f697d47eSkre VTRACE(DBG_JOBS, ( 41021f00868Skre "restartjob: [%d] pid %d status change" 411f697d47eSkre " from %#x (stopped) to -1 (running)\n", 41221f00868Skre JNUM(jp), ps->pid, ps->status)); 41361f28255Scgd ps->status = -1; 414c02b3bbdSchristos jp->state = JOBRUNNING; 41561f28255Scgd } 41661f28255Scgd } 41761f28255Scgd INTON; 41861f28255Scgd } 41961f28255Scgd #endif 42061f28255Scgd 4210fe4e128Skre inline static void 4223c6d76cdSkre cmdputi(int n) 4233c6d76cdSkre { 4243c6d76cdSkre char str[20]; 4253c6d76cdSkre 4263c6d76cdSkre fmtstr(str, sizeof str, "%d", n); 4273c6d76cdSkre cmdputs(str); 4283c6d76cdSkre } 4293c6d76cdSkre 430c02b3bbdSchristos static void 431c02b3bbdSchristos showjob(struct output *out, struct job *jp, int mode) 432c02b3bbdSchristos { 433c02b3bbdSchristos int procno; 434c02b3bbdSchristos int st; 435c02b3bbdSchristos struct procstat *ps; 436c02b3bbdSchristos int col; 437c02b3bbdSchristos char s[64]; 438c02b3bbdSchristos 439c02b3bbdSchristos #if JOBS 440c02b3bbdSchristos if (mode & SHOW_PGID) { 4411c627bdfSkre /* output only the process group ID (lead process ID) */ 442f55c8670Skre outfmt(out, "%ld\n", 443f55c8670Skre jp->pgrp != 0 ? (long)jp->pgrp : (long)jp->ps->pid); 444c02b3bbdSchristos return; 445c02b3bbdSchristos } 446c02b3bbdSchristos #endif 447c02b3bbdSchristos 448c02b3bbdSchristos procno = jp->nprocs; 449c02b3bbdSchristos if (!procno) 450c02b3bbdSchristos return; 451c02b3bbdSchristos 452c02b3bbdSchristos if (mode & SHOW_PID) 453c02b3bbdSchristos mode |= SHOW_MULTILINE; 454c02b3bbdSchristos 455c02b3bbdSchristos if ((procno > 1 && !(mode & SHOW_MULTILINE)) 456c02b3bbdSchristos || (mode & SHOW_SIGNALLED)) { 457c02b3bbdSchristos /* See if we have more than one status to report */ 458c02b3bbdSchristos ps = jp->ps; 459c02b3bbdSchristos st = ps->status; 460c02b3bbdSchristos do { 461c02b3bbdSchristos int st1 = ps->status; 462c02b3bbdSchristos if (st1 != st) 463c02b3bbdSchristos /* yes - need multi-line output */ 464c02b3bbdSchristos mode |= SHOW_MULTILINE; 465c02b3bbdSchristos if (st1 == -1 || !(mode & SHOW_SIGNALLED) || WIFEXITED(st1)) 466c02b3bbdSchristos continue; 467c02b3bbdSchristos if (WIFSTOPPED(st1) || ((st1 = WTERMSIG(st1) & 0x7f) 468c02b3bbdSchristos && st1 != SIGINT && st1 != SIGPIPE)) 469c02b3bbdSchristos mode |= SHOW_ISSIG; 470c02b3bbdSchristos 471c02b3bbdSchristos } while (ps++, --procno); 472c02b3bbdSchristos procno = jp->nprocs; 473c02b3bbdSchristos } 474c02b3bbdSchristos 475c02b3bbdSchristos if (mode & SHOW_SIGNALLED && !(mode & SHOW_ISSIG)) { 476c02b3bbdSchristos if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) { 47716bbc7c6Skre VTRACE(DBG_JOBS, ("showjob: freeing job %d\n", 47821f00868Skre JNUM(jp))); 479c02b3bbdSchristos freejob(jp); 480c02b3bbdSchristos } 481c02b3bbdSchristos return; 482c02b3bbdSchristos } 483c02b3bbdSchristos 484c02b3bbdSchristos for (ps = jp->ps; --procno >= 0; ps++) { /* for each process */ 485c02b3bbdSchristos if (ps == jp->ps) 48621f00868Skre fmtstr(s, 16, "[%d] %c ", 48721f00868Skre JNUM(jp), 488c02b3bbdSchristos #if JOBS 48913689a6cSkre jp - jobtab == curjob ? 49013689a6cSkre '+' : 49113689a6cSkre curjob != -1 && 49213689a6cSkre jp - jobtab == jobtab[curjob].prev_job ? 49313689a6cSkre '-' : 494c02b3bbdSchristos #endif 495c02b3bbdSchristos ' '); 496c02b3bbdSchristos else 497c02b3bbdSchristos fmtstr(s, 16, " " ); 498c02b3bbdSchristos col = strlen(s); 499c02b3bbdSchristos if (mode & SHOW_PID) { 500c02b3bbdSchristos fmtstr(s + col, 16, "%ld ", (long)ps->pid); 501c02b3bbdSchristos col += strlen(s + col); 502c02b3bbdSchristos } 503c02b3bbdSchristos if (ps->status == -1) { 504c02b3bbdSchristos scopy("Running", s + col); 505c02b3bbdSchristos } else if (WIFEXITED(ps->status)) { 506c02b3bbdSchristos st = WEXITSTATUS(ps->status); 507c02b3bbdSchristos if (st) 508c02b3bbdSchristos fmtstr(s + col, 16, "Done(%d)", st); 509c02b3bbdSchristos else 510c02b3bbdSchristos fmtstr(s + col, 16, "Done"); 511c02b3bbdSchristos } else { 512c02b3bbdSchristos #if JOBS 513c02b3bbdSchristos if (WIFSTOPPED(ps->status)) 514c02b3bbdSchristos st = WSTOPSIG(ps->status); 515c02b3bbdSchristos else /* WIFSIGNALED(ps->status) */ 516c02b3bbdSchristos #endif 517c02b3bbdSchristos st = WTERMSIG(ps->status); 518185226c2Skre scopyn(strsignal(st), s + col, 32); 519c02b3bbdSchristos if (WCOREDUMP(ps->status)) { 520c02b3bbdSchristos col += strlen(s + col); 521c02b3bbdSchristos scopyn(" (core dumped)", s + col, 64 - col); 522c02b3bbdSchristos } 523c02b3bbdSchristos } 524c02b3bbdSchristos col += strlen(s + col); 525c02b3bbdSchristos outstr(s, out); 526c02b3bbdSchristos do { 527c02b3bbdSchristos outc(' ', out); 528c02b3bbdSchristos col++; 529c02b3bbdSchristos } while (col < 30); 530c02b3bbdSchristos outstr(ps->cmd, out); 531c02b3bbdSchristos if (mode & SHOW_MULTILINE) { 532c02b3bbdSchristos if (procno > 0) { 533c02b3bbdSchristos outc(' ', out); 534c02b3bbdSchristos outc('|', out); 535c02b3bbdSchristos } 536c02b3bbdSchristos } else { 537c02b3bbdSchristos while (--procno >= 0) 538c02b3bbdSchristos outfmt(out, " | %s", (++ps)->cmd ); 539c02b3bbdSchristos } 540c02b3bbdSchristos outc('\n', out); 541c02b3bbdSchristos } 542c02b3bbdSchristos flushout(out); 5434e9fc30dSkre jp->flags &= ~JOBCHANGED; 544c02b3bbdSchristos if (jp->state == JOBDONE && !(mode & SHOW_NO_FREE)) 545c02b3bbdSchristos freejob(jp); 546c02b3bbdSchristos } 547c02b3bbdSchristos 54861f28255Scgd int 549c02b3bbdSchristos jobscmd(int argc, char **argv) 5505dad1439Scgd { 551c02b3bbdSchristos int mode, m; 552c02b3bbdSchristos 553c02b3bbdSchristos mode = 0; 5544e2a4778Schristos while ((m = nextopt("lpZ"))) 5554e2a4778Schristos switch (m) { 5564e2a4778Schristos case 'l': 557c02b3bbdSchristos mode = SHOW_PID; 5584e2a4778Schristos break; 5594e2a4778Schristos case 'p': 560c02b3bbdSchristos mode = SHOW_PGID; 5614e2a4778Schristos break; 5624e2a4778Schristos case 'Z': 5634e2a4778Schristos mode = SHOW_PROCTITLE; 5644e2a4778Schristos break; 5654e2a4778Schristos } 5664e2a4778Schristos 5674e2a4778Schristos if (mode == SHOW_PROCTITLE) { 568777df0a3Skre if (*argptr && **argptr) 5694e2a4778Schristos setproctitle("%s", *argptr); 570777df0a3Skre else 571777df0a3Skre setproctitle(NULL); 5724e2a4778Schristos return 0; 5734e2a4778Schristos } 574ccf5ffdbSkre 575ccf5ffdbSkre if (!iflag && !posix) 5769572a5c2Skre mode |= SHOW_NO_FREE; 577ccf5ffdbSkre 578ccf5ffdbSkre if (*argptr) { 579c02b3bbdSchristos do 580c02b3bbdSchristos showjob(out1, getjob(*argptr,0), mode); 581c02b3bbdSchristos while (*++argptr); 582ccf5ffdbSkre } else 583c02b3bbdSchristos showjobs(out1, mode); 58461f28255Scgd return 0; 58561f28255Scgd } 58661f28255Scgd 58761f28255Scgd 58861f28255Scgd /* 58961f28255Scgd * Print a list of jobs. If "change" is nonzero, only print jobs whose 59061f28255Scgd * statuses have changed since the last call to showjobs. 59161f28255Scgd * 59261f28255Scgd * If the shell is interrupted in the process of creating a job, the 59361f28255Scgd * result may be a job structure containing zero processes. Such structures 59461f28255Scgd * will be freed here. 59561f28255Scgd */ 59661f28255Scgd 59761f28255Scgd void 598c02b3bbdSchristos showjobs(struct output *out, int mode) 5995dad1439Scgd { 60061f28255Scgd int jobno; 60161f28255Scgd struct job *jp; 602edcb4544Schristos int silent = 0, gotpid; 60361f28255Scgd 60416bbc7c6Skre CTRACE(DBG_JOBS, ("showjobs(%x) called\n", mode)); 605edcb4544Schristos 6067a2f8a05Skre /* Collect everything pending in the kernel */ 6077a2f8a05Skre if ((gotpid = dowait(WSILENT, NULL, NULL)) > 0) 6084e9fc30dSkre while (dowait(WSILENT, NULL, NULL) > 0) 609edcb4544Schristos continue; 610edcb4544Schristos #ifdef JOBS 611edcb4544Schristos /* 612edcb4544Schristos * Check if we are not in our foreground group, and if not 613edcb4544Schristos * put us in it. 614edcb4544Schristos */ 615f3dfd6e6Schristos if (mflag && gotpid != -1 && tcgetpgrp(ttyfd) != getpid()) { 616edcb4544Schristos if (tcsetpgrp(ttyfd, getpid()) == -1) 617edcb4544Schristos error("Cannot set tty process group (%s) at %d", 618edcb4544Schristos strerror(errno), __LINE__); 61916bbc7c6Skre VTRACE(DBG_JOBS|DBG_INPUT, ("repaired tty process group\n")); 620edcb4544Schristos silent = 1; 621edcb4544Schristos } 622edcb4544Schristos #endif 623c02b3bbdSchristos 62461f28255Scgd for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { 62561f28255Scgd if (!jp->used) 62661f28255Scgd continue; 62761f28255Scgd if (jp->nprocs == 0) { 628feb6abd7Skre if (!jobs_invalid) 62961f28255Scgd freejob(jp); 63061f28255Scgd continue; 63161f28255Scgd } 6324e9fc30dSkre if ((mode & SHOW_CHANGED) && !(jp->flags & JOBCHANGED)) 63361f28255Scgd continue; 6344e9fc30dSkre if (silent && (jp->flags & JOBCHANGED)) { 6354e9fc30dSkre jp->flags &= ~JOBCHANGED; 636edcb4544Schristos continue; 637edcb4544Schristos } 638c02b3bbdSchristos showjob(out, jp, mode); 63961f28255Scgd } 64061f28255Scgd } 64161f28255Scgd 64261f28255Scgd /* 64361f28255Scgd * Mark a job structure as unused. 64461f28255Scgd */ 64561f28255Scgd 64661f28255Scgd STATIC void 647c02b3bbdSchristos freejob(struct job *jp) 64861f28255Scgd { 64961f28255Scgd INTOFF; 650bac9369aSchristos if (jp->ps != &jp->ps0) { 65161f28255Scgd ckfree(jp->ps); 652bac9369aSchristos jp->ps = &jp->ps0; 653bac9369aSchristos } 654bac9369aSchristos jp->nprocs = 0; 65561f28255Scgd jp->used = 0; 65661f28255Scgd #if JOBS 657c02b3bbdSchristos set_curjob(jp, 0); 65861f28255Scgd #endif 65961f28255Scgd INTON; 66061f28255Scgd } 66161f28255Scgd 662bffe5190Skre /* 663bffe5190Skre * Extract the status of a completed job (for $?) 664bffe5190Skre */ 665bffe5190Skre STATIC int 666bffe5190Skre jobstatus(const struct job *jp, int raw) 667bffe5190Skre { 668bffe5190Skre int status = 0; 669bffe5190Skre int retval; 670bffe5190Skre 671f53fd6e9Skre if ((jp->flags & JPIPEFAIL) && jp->nprocs) { 672bffe5190Skre int i; 673bffe5190Skre 674bffe5190Skre for (i = 0; i < jp->nprocs; i++) 675bffe5190Skre if (jp->ps[i].status != 0) 676bffe5190Skre status = jp->ps[i].status; 677bffe5190Skre } else 678bffe5190Skre status = jp->ps[jp->nprocs ? jp->nprocs - 1 : 0].status; 679bffe5190Skre 680bffe5190Skre if (raw) 681bffe5190Skre return status; 682bffe5190Skre 683bffe5190Skre if (WIFEXITED(status)) 684bffe5190Skre retval = WEXITSTATUS(status); 685bffe5190Skre #if JOBS 686bffe5190Skre else if (WIFSTOPPED(status)) 687bffe5190Skre retval = WSTOPSIG(status) + 128; 688bffe5190Skre #endif 689bffe5190Skre else { 690bffe5190Skre /* XXX: limits number of signals */ 691bffe5190Skre retval = WTERMSIG(status) + 128; 692bffe5190Skre } 693bffe5190Skre 694bffe5190Skre return retval; 695bffe5190Skre } 696bffe5190Skre 69761f28255Scgd 69861f28255Scgd 69961f28255Scgd int 700c02b3bbdSchristos waitcmd(int argc, char **argv) 7015dad1439Scgd { 7024e9fc30dSkre struct job *job, *last; 703bffe5190Skre int retval; 70461f28255Scgd struct job *jp; 7054e9fc30dSkre int i; 7064e9fc30dSkre int any = 0; 7074e9fc30dSkre int found; 70845f1e871Skre int oldwait = 0; 7094e9fc30dSkre char *pid = NULL, *fpid; 7104e9fc30dSkre char **arg; 7114e9fc30dSkre char idstring[20]; 71261f28255Scgd 7134e9fc30dSkre while ((i = nextopt("np:")) != '\0') { 7144e9fc30dSkre switch (i) { 7154e9fc30dSkre case 'n': 7164e9fc30dSkre any = 1; 7174e9fc30dSkre break; 7184e9fc30dSkre case 'p': 7194e9fc30dSkre if (pid) 7204e9fc30dSkre error("more than one -p unsupported"); 7214e9fc30dSkre pid = optionarg; 7224e9fc30dSkre break; 7234e9fc30dSkre } 7244e9fc30dSkre } 7254e9fc30dSkre 72645f1e871Skre if (!any && *argptr == 0) 72745f1e871Skre oldwait = 1; 72845f1e871Skre 7294e9fc30dSkre if (pid != NULL) { 7304e9fc30dSkre if (!validname(pid, '\0', NULL)) 7314e9fc30dSkre error("invalid name: -p '%s'", pid); 7324e9fc30dSkre if (unsetvar(pid, 0)) 7334e9fc30dSkre error("%s readonly", pid); 7344e9fc30dSkre } 735c02b3bbdSchristos 7369572a5c2Skre /* 7379572a5c2Skre * If we have forked, and not yet created any new jobs, then 7389572a5c2Skre * we have no children, whatever jobtab claims, 7399572a5c2Skre * so simply return in that case. 7409572a5c2Skre * 7419572a5c2Skre * The return code is 127 if we had any pid args (none are found) 7424e9fc30dSkre * or if we had -n (nothing exited), but 0 for plain old "wait". 7439572a5c2Skre */ 7444e9fc30dSkre if (jobs_invalid) { 7454e9fc30dSkre CTRACE(DBG_WAIT, ("builtin wait%s%s in child, invalid jobtab\n", 7464e9fc30dSkre any ? " -n" : "", *argptr ? " pid..." : "")); 74745f1e871Skre return oldwait ? 0 : 127; 748c02b3bbdSchristos } 749c02b3bbdSchristos 750da460725Skre /* 751da460725Skre * clear stray flags left from previous waitcmd 752da460725Skre * or set them instead if anything will do ("wait -n") 753da460725Skre */ 7544e9fc30dSkre for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { 755da460725Skre if (any && *argptr == NULL) 756da460725Skre jp->flags |= JOBWANTED; 757da460725Skre else 7584e9fc30dSkre jp->flags &= ~JOBWANTED; 7594e9fc30dSkre jp->ref = NULL; 7604e9fc30dSkre } 7614e9fc30dSkre 7624e9fc30dSkre CTRACE(DBG_WAIT, 7634e9fc30dSkre ("builtin wait%s%s\n", any ? " -n" : "", *argptr ? " pid..." : "")); 7644e9fc30dSkre 7654e9fc30dSkre /* 7664e9fc30dSkre * First, validate the jobnum args, count how many refer to 7674e9fc30dSkre * (different) running jobs, and if we had -n, and found that one has 7684e9fc30dSkre * already finished, we return that one. Otherwise remember 7694e9fc30dSkre * which ones we are looking for (JOBWANTED). 7704e9fc30dSkre */ 7714e9fc30dSkre found = 0; 7724e9fc30dSkre last = NULL; 7734e9fc30dSkre for (arg = argptr; *arg; arg++) { 7744e9fc30dSkre last = jp = getjob(*arg, 1); 7754e9fc30dSkre if (!jp) 776c02b3bbdSchristos continue; 7774e9fc30dSkre if (jp->ref == NULL) 7784e9fc30dSkre jp->ref = *arg; 7794e9fc30dSkre if (any && jp->state == JOBDONE) { 7804e9fc30dSkre /* 7814e9fc30dSkre * We just want any of them, and this one is 7824e9fc30dSkre * ready for consumption, bon apetit ... 7834e9fc30dSkre */ 7844e9fc30dSkre retval = jobstatus(jp, 0); 7854e9fc30dSkre if (pid) 7864e9fc30dSkre setvar(pid, *arg, 0); 78761f28255Scgd if (!iflag) 7884e9fc30dSkre freejob(jp); 7894e9fc30dSkre CTRACE(DBG_WAIT, ("wait -n found %s already done: %d\n", *arg, retval)); 7907f2a2717Schristos return retval; 79161f28255Scgd } 7924e9fc30dSkre if (!(jp->flags & JOBWANTED)) { 7934e9fc30dSkre /* 7944e9fc30dSkre * It is possible to list the same job several 7954e9fc30dSkre * times - the obvious "wait 1 1 1" or 7964e9fc30dSkre * "wait %% %2 102" where job 2 is current and pid 102 7974e9fc30dSkre * However many times it is requested, it is found once. 7984e9fc30dSkre */ 7994e9fc30dSkre found++; 8004e9fc30dSkre jp->flags |= JOBWANTED; 8014e9fc30dSkre } 8024e9fc30dSkre job = jp; 8034e9fc30dSkre } 80461f28255Scgd 8054e9fc30dSkre VTRACE(DBG_WAIT, ("wait %s%s%sfound %d candidates (last %s)\n", 8064e9fc30dSkre any ? "-n " : "", *argptr ? *argptr : "", 8074e9fc30dSkre argptr[0] && argptr[1] ? "... " : " ", found, 8088821fc2cSkre job && job->used ? (job->ref ? job->ref : "<no-arg>") : "none")); 8094e9fc30dSkre 8104e9fc30dSkre /* 8114e9fc30dSkre * If we were given a list of jobnums: 8124e9fc30dSkre * and none of those exist, then we're done. 8134e9fc30dSkre */ 8144e9fc30dSkre if (*argptr && found == 0) 8154e9fc30dSkre return 127; 8164e9fc30dSkre 8174e9fc30dSkre /* 8184e9fc30dSkre * Otherwise we need to wait for something to complete 8194e9fc30dSkre * When it does, we check and see if it is one of the 8204e9fc30dSkre * jobs we're waiting on, and if so, we clean it up. 8214e9fc30dSkre * If we had -n, then we're done, otherwise we do it all again 8224e9fc30dSkre * until all we had listed are done, of if there were no 8234e9fc30dSkre * jobnum args, all are done. 8244e9fc30dSkre */ 8254e9fc30dSkre 8264e9fc30dSkre retval = any || *argptr ? 127 : 0; 8274e9fc30dSkre fpid = NULL; 8284e9fc30dSkre for (;;) { 8294e9fc30dSkre VTRACE(DBG_WAIT, ("wait waiting (%d remain): ", found)); 830da460725Skre job = NULL; 8314e9fc30dSkre for (jp = jobtab, i = njobs; --i >= 0; jp++) { 8324e9fc30dSkre if (jp->used && jp->flags & JOBWANTED && 833da460725Skre jp->state == JOBDONE) { 834da460725Skre job = jp; 8354e9fc30dSkre break; 8364e9fc30dSkre } 837da460725Skre if (jp->used && jp->state == JOBRUNNING) 838da460725Skre job = jp; 839da460725Skre } 840da460725Skre if (i < 0 && job == NULL) { 8414e9fc30dSkre CTRACE(DBG_WAIT, ("nothing running (ret: %d) fpid %s\n", 8424e9fc30dSkre retval, fpid ? fpid : "unset")); 8434e9fc30dSkre if (pid && fpid) 8444e9fc30dSkre setvar(pid, fpid, 0); 8454e9fc30dSkre return retval; 8464e9fc30dSkre } 847da460725Skre jp = job; 8484e9fc30dSkre VTRACE(DBG_WAIT, ("found @%d/%d state: %d\n", njobs-i, njobs, 8494e9fc30dSkre jp->state)); 8504e9fc30dSkre 8514e9fc30dSkre /* 8524e9fc30dSkre * There is at least 1 job running, so we can 8537a2f8a05Skre * safely wait() (blocking) for something to exit. 8544e9fc30dSkre */ 8554e9fc30dSkre if (jp->state == JOBRUNNING) { 8564e9fc30dSkre job = NULL; 8574e9fc30dSkre if ((i = dowait(WBLOCK|WNOFREE, NULL, &job)) == -1) 8584e9fc30dSkre return 128 + lastsig(); 8594e9fc30dSkre 8607a2f8a05Skre /* 8617a2f8a05Skre * This happens if an interloper has died 8627a2f8a05Skre * (eg: a child of the executable that exec'd us) 8637a2f8a05Skre * Simply go back and start all over again 8647a2f8a05Skre * (this is rare). 8657a2f8a05Skre */ 8667a2f8a05Skre if (job == NULL) 8677aa4a7e2Skre continue; 8687aa4a7e2Skre 8694e9fc30dSkre /* 8707a2f8a05Skre * one of the reported job's processes exited, 8717a2f8a05Skre * but there are more still running, back for more 8724e9fc30dSkre */ 8734e9fc30dSkre if (job->state == JOBRUNNING) 8744e9fc30dSkre continue; 8754e9fc30dSkre } else 8764e9fc30dSkre job = jp; /* we want this, and it is done */ 8774e9fc30dSkre 878da460725Skre if (job->flags & JOBWANTED) { 8794e9fc30dSkre int rv; 8804e9fc30dSkre 8814e9fc30dSkre job->flags &= ~JOBWANTED; /* got it */ 8824e9fc30dSkre rv = jobstatus(job, 0); 8834e9fc30dSkre VTRACE(DBG_WAIT, ( 8844e9fc30dSkre "wanted %d (%s) done: st=%d", i, 8854e9fc30dSkre job->ref ? job->ref : "", rv)); 8864e9fc30dSkre if (any || job == last) { 8874e9fc30dSkre retval = rv; 8884e9fc30dSkre fpid = job->ref; 8894e9fc30dSkre 8904e9fc30dSkre VTRACE(DBG_WAIT, (" save")); 8914e9fc30dSkre if (pid) { 8924e9fc30dSkre /* 8934e9fc30dSkre * don't need fpid unless we are going 8944e9fc30dSkre * to return it. 8954e9fc30dSkre */ 8964e9fc30dSkre if (fpid == NULL) { 8974e9fc30dSkre /* 8984e9fc30dSkre * this only happens with "wait -n" 8994e9fc30dSkre * (that is, no pid args) 9004e9fc30dSkre */ 9014e9fc30dSkre snprintf(idstring, sizeof idstring, 9024e9fc30dSkre "%d", job->ps[ job->nprocs ? 9030827e1f9Skre job->nprocs-1 : 0 ].pid); 9044e9fc30dSkre fpid = idstring; 9054e9fc30dSkre } 9064e9fc30dSkre VTRACE(DBG_WAIT, (" (for %s)", fpid)); 9074e9fc30dSkre } 9084e9fc30dSkre } 9094e9fc30dSkre 9104e9fc30dSkre if (job->state == JOBDONE) { 9114e9fc30dSkre VTRACE(DBG_WAIT, (" free")); 9124e9fc30dSkre freejob(job); 9134e9fc30dSkre } 9144e9fc30dSkre 9154e9fc30dSkre if (any || (found > 0 && --found == 0)) { 9164e9fc30dSkre if (pid && fpid) 9174e9fc30dSkre setvar(pid, fpid, 0); 9184e9fc30dSkre VTRACE(DBG_WAIT, (" return %d\n", retval)); 9194e9fc30dSkre return retval; 9204e9fc30dSkre } 9214e9fc30dSkre VTRACE(DBG_WAIT, ("\n")); 9224e9fc30dSkre continue; 9234e9fc30dSkre } 9244e9fc30dSkre 9254e9fc30dSkre /* this is to handle "wait" (no args) */ 92645f1e871Skre if (oldwait && job->state == JOBDONE) { 9274e9fc30dSkre VTRACE(DBG_JOBS|DBG_WAIT, ("Cleanup: %d\n", i)); 9284e9fc30dSkre freejob(job); 9294e9fc30dSkre } 9304e9fc30dSkre } 9314e9fc30dSkre } 93261f28255Scgd 93361f28255Scgd 9345dad1439Scgd int 935c02b3bbdSchristos jobidcmd(int argc, char **argv) 9365dad1439Scgd { 93761f28255Scgd struct job *jp; 93861f28255Scgd int i; 939f697d47eSkre int pg = 0, onep = 0, job = 0; 94061f28255Scgd 941f697d47eSkre while ((i = nextopt("gjp"))) { 942f697d47eSkre switch (i) { 943f697d47eSkre case 'g': pg = 1; break; 944f697d47eSkre case 'j': job = 1; break; 945f697d47eSkre case 'p': onep = 1; break; 946f697d47eSkre } 947f697d47eSkre } 948f697d47eSkre CTRACE(DBG_JOBS, ("jobidcmd%s%s%s%s %s\n", pg ? " -g" : "", 949f697d47eSkre onep ? " -p" : "", job ? " -j" : "", jobs_invalid ? " [inv]" : "", 950f697d47eSkre *argptr ? *argptr : "<implicit %%>")); 951f697d47eSkre if (pg + onep + job > 1) 952f697d47eSkre error("-g -j and -p options cannot be combined"); 953f697d47eSkre 954f697d47eSkre if (argptr[0] && argptr[1]) 955f697d47eSkre error("usage: jobid [-g|-p|-r] jobid"); 956f697d47eSkre 957c02b3bbdSchristos jp = getjob(*argptr, 0); 958f697d47eSkre if (job) { 95921f00868Skre out1fmt("%%%d\n", JNUM(jp)); 960f697d47eSkre return 0; 961f697d47eSkre } 962f697d47eSkre if (pg) { 963f697d47eSkre if (jp->pgrp != 0) { 964f697d47eSkre out1fmt("%ld\n", (long)jp->pgrp); 965f697d47eSkre return 0; 966f697d47eSkre } 967f697d47eSkre return 1; 968f697d47eSkre } 969f697d47eSkre if (onep) { 970f697d47eSkre i = jp->nprocs - 1; 971f697d47eSkre if (i < 0) 972f697d47eSkre return 1; 973f697d47eSkre out1fmt("%ld\n", (long)jp->ps[i].pid); 974f697d47eSkre return 0; 975f697d47eSkre } 97661f28255Scgd for (i = 0 ; i < jp->nprocs ; ) { 97796133865Schristos out1fmt("%ld", (long)jp->ps[i].pid); 97861f28255Scgd out1c(++i < jp->nprocs ? ' ' : '\n'); 97961f28255Scgd } 98061f28255Scgd return 0; 98161f28255Scgd } 98261f28255Scgd 983*d1c86d36Skre #if JOBS 984*d1c86d36Skre #ifndef SMALL 985*d1c86d36Skre 986*d1c86d36Skre static int 987*d1c86d36Skre stop_me(int sig, int force, int pgrp, pid_t pid) 988*d1c86d36Skre { 989*d1c86d36Skre if (force || (!loginsh && mflag && rootshell)) { 990*d1c86d36Skre struct sigaction sig_dfl, sig_was; 991*d1c86d36Skre 992*d1c86d36Skre sig_dfl.sa_handler = SIG_DFL; 993*d1c86d36Skre sig_dfl.sa_flags = 0; 994*d1c86d36Skre sigemptyset(&sig_dfl.sa_mask); 995*d1c86d36Skre 996*d1c86d36Skre (void)sigaction(sig, &sig_dfl, &sig_was); 997*d1c86d36Skre 998*d1c86d36Skre if (kill(pgrp ? 0 : pid, sig) == -1) { 999*d1c86d36Skre sh_warn("suspend myself"); 1000*d1c86d36Skre (void)sigaction(sig, &sig_was, NULL); 1001*d1c86d36Skre error(NULL); 1002*d1c86d36Skre } 1003*d1c86d36Skre 1004*d1c86d36Skre (void)sigaction(sig, &sig_was, NULL); 1005*d1c86d36Skre 1006*d1c86d36Skre return 0; 1007*d1c86d36Skre } 1008*d1c86d36Skre 1009*d1c86d36Skre if (!rootshell) 1010*d1c86d36Skre sh_warnx("subshell environment"); 1011*d1c86d36Skre else if (!mflag) 1012*d1c86d36Skre sh_warnx("job control disabled"); 1013*d1c86d36Skre else if (loginsh) 1014*d1c86d36Skre sh_warnx("login shell"); 1015*d1c86d36Skre else 1016*d1c86d36Skre sh_warnx("not possible??"); 1017*d1c86d36Skre 1018*d1c86d36Skre return 1; 1019*d1c86d36Skre } 1020*d1c86d36Skre 1021*d1c86d36Skre int 1022*d1c86d36Skre suspendcmd(int argc, char **argv) 1023*d1c86d36Skre { 1024*d1c86d36Skre int sig = SIGTSTP; 1025*d1c86d36Skre int force = 0; 1026*d1c86d36Skre int pgrp = 0; 1027*d1c86d36Skre int status = 0; 1028*d1c86d36Skre char *target; 1029*d1c86d36Skre int c; 1030*d1c86d36Skre 1031*d1c86d36Skre while ((c = nextopt("fgs:")) != 0) { 1032*d1c86d36Skre switch (c) { 1033*d1c86d36Skre case 'f': 1034*d1c86d36Skre force = 1; 1035*d1c86d36Skre break; 1036*d1c86d36Skre case 'g': 1037*d1c86d36Skre pgrp = 1; 1038*d1c86d36Skre break; 1039*d1c86d36Skre case 's': 1040*d1c86d36Skre sig = signame_to_signum(optionarg); 1041*d1c86d36Skre 1042*d1c86d36Skre if (sig != SIGSTOP && sig != SIGTSTP && 1043*d1c86d36Skre sig != SIGTTIN && sig != SIGTTOU) 1044*d1c86d36Skre error("bad signal '%s'", optionarg); 1045*d1c86d36Skre break; 1046*d1c86d36Skre } 1047*d1c86d36Skre } 1048*d1c86d36Skre 1049*d1c86d36Skre if (!*argptr) /* suspend myself */ 1050*d1c86d36Skre return stop_me(sig, force, pgrp, getpid()); 1051*d1c86d36Skre 1052*d1c86d36Skre while ((target = *argptr++) != NULL) 1053*d1c86d36Skre { 1054*d1c86d36Skre int pid; 1055*d1c86d36Skre 1056*d1c86d36Skre if (is_number(target)) { 1057*d1c86d36Skre if ((pid = number(target)) == 0) { 1058*d1c86d36Skre sh_warnx("Cannot (yet) suspend kernel (%s)", 1059*d1c86d36Skre target); 1060*d1c86d36Skre status = 1; 1061*d1c86d36Skre continue; 1062*d1c86d36Skre } 1063*d1c86d36Skre } else if ((pid = getjobpgrp(target)) == 0) { 1064*d1c86d36Skre sh_warnx("Unknown job: %s", target); 1065*d1c86d36Skre status = 1; 1066*d1c86d36Skre continue; 1067*d1c86d36Skre } 1068*d1c86d36Skre 1069*d1c86d36Skre if (pid == rootpid || pid == getpid()) { 1070*d1c86d36Skre status |= stop_me(sig, force, pgrp, pid); 1071*d1c86d36Skre continue; 1072*d1c86d36Skre } 1073*d1c86d36Skre 1074*d1c86d36Skre if (pid == 1 || pid == -1) { 1075*d1c86d36Skre sh_warnx("Don't be funny"); 1076*d1c86d36Skre status = 1; 1077*d1c86d36Skre continue; 1078*d1c86d36Skre } 1079*d1c86d36Skre 1080*d1c86d36Skre if (pid > 0 && pgrp) 1081*d1c86d36Skre pid = -pid; 1082*d1c86d36Skre 1083*d1c86d36Skre if (kill(pid, sig) == -1) { 1084*d1c86d36Skre sh_warn("failed to suspend %s", target); 1085*d1c86d36Skre status = 1; 1086*d1c86d36Skre } 1087*d1c86d36Skre } 1088*d1c86d36Skre 1089*d1c86d36Skre return status; 1090*d1c86d36Skre } 1091*d1c86d36Skre #endif /* SMALL */ 1092*d1c86d36Skre #endif /* JOBS */ 1093*d1c86d36Skre 1094c02b3bbdSchristos int 1095c02b3bbdSchristos getjobpgrp(const char *name) 1096c02b3bbdSchristos { 1097c02b3bbdSchristos struct job *jp; 109861f28255Scgd 10999572a5c2Skre if (jobs_invalid) 1100*d1c86d36Skre return 0; 1101c02b3bbdSchristos jp = getjob(name, 1); 1102c02b3bbdSchristos if (jp == 0) 1103c02b3bbdSchristos return 0; 11048ad9ebd9Skre return -jp->pgrp; 1105c02b3bbdSchristos } 110661f28255Scgd 110761f28255Scgd /* 110861f28255Scgd * Convert a job name to a job structure. 110961f28255Scgd */ 111061f28255Scgd 111161f28255Scgd STATIC struct job * 1112c02b3bbdSchristos getjob(const char *name, int noerror) 111361f28255Scgd { 1114c02b3bbdSchristos int jobno = -1; 111548250187Stls struct job *jp; 111661f28255Scgd int pid; 111761f28255Scgd int i; 1118c02b3bbdSchristos const char *err_msg = "No such job: %s"; 111961f28255Scgd 112061f28255Scgd if (name == NULL) { 112161f28255Scgd #if JOBS 1122c02b3bbdSchristos jobno = curjob; 112361f28255Scgd #endif 1124c02b3bbdSchristos err_msg = "No current job"; 112561f28255Scgd } else if (name[0] == '%') { 1126c02b3bbdSchristos if (is_number(name + 1)) { 1127c02b3bbdSchristos jobno = number(name + 1) - 1; 11284e9fc30dSkre } else if (!name[1] || !name[2]) { 1129c02b3bbdSchristos switch (name[1]) { 113061f28255Scgd #if JOBS 1131c02b3bbdSchristos case 0: 1132c02b3bbdSchristos case '+': 1133c02b3bbdSchristos case '%': 1134c02b3bbdSchristos jobno = curjob; 1135c02b3bbdSchristos err_msg = "No current job"; 1136c02b3bbdSchristos break; 1137c02b3bbdSchristos case '-': 1138c02b3bbdSchristos jobno = curjob; 1139c02b3bbdSchristos if (jobno != -1) 1140c02b3bbdSchristos jobno = jobtab[jobno].prev_job; 1141c02b3bbdSchristos err_msg = "No previous job"; 1142c02b3bbdSchristos break; 114361f28255Scgd #endif 1144c02b3bbdSchristos default: 1145c02b3bbdSchristos goto check_pattern; 1146c02b3bbdSchristos } 114761f28255Scgd } else { 1148c02b3bbdSchristos struct job *found; 1149c02b3bbdSchristos check_pattern: 1150c02b3bbdSchristos found = NULL; 115161f28255Scgd for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { 1152c02b3bbdSchristos if (!jp->used || jp->nprocs <= 0) 1153c02b3bbdSchristos continue; 1154c02b3bbdSchristos if ((name[1] == '?' 1155c02b3bbdSchristos && strstr(jp->ps[0].cmd, name + 2)) 1156c02b3bbdSchristos || prefix(name + 1, jp->ps[0].cmd)) { 1157c02b3bbdSchristos if (found) { 1158c02b3bbdSchristos err_msg = "%s: ambiguous"; 1159c02b3bbdSchristos found = 0; 1160c02b3bbdSchristos break; 1161c02b3bbdSchristos } 116261f28255Scgd found = jp; 116361f28255Scgd } 116461f28255Scgd } 116561f28255Scgd if (found) 116661f28255Scgd return found; 116761f28255Scgd } 1168c02b3bbdSchristos 116961f28255Scgd } else if (is_number(name)) { 117061f28255Scgd pid = number(name); 117161f28255Scgd for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { 117261f28255Scgd if (jp->used && jp->nprocs > 0 117361f28255Scgd && jp->ps[jp->nprocs - 1].pid == pid) 117461f28255Scgd return jp; 117561f28255Scgd } 117661f28255Scgd } 1177c02b3bbdSchristos 11789572a5c2Skre if (jobno >= 0 && jobno < njobs) { 1179c02b3bbdSchristos jp = jobtab + jobno; 1180c02b3bbdSchristos if (jp->used) 1181c02b3bbdSchristos return jp; 1182c02b3bbdSchristos } 1183c02b3bbdSchristos if (!noerror) 1184c02b3bbdSchristos error(err_msg, name); 1185c02b3bbdSchristos return 0; 118661f28255Scgd } 118761f28255Scgd 118861f28255Scgd 11898821fc2cSkre /* 11908821fc2cSkre * Find out if there are any running (that is, unwaited upon) 11918821fc2cSkre * background children of the current shell. 11928821fc2cSkre * 11938821fc2cSkre * Return 1/0 (yes, no). 11948821fc2cSkre * 11958821fc2cSkre * Needed as we cannot optimise away sub-shell creation if 11968821fc2cSkre * we have such a child, or a "wait" in that sub-shell would 11978821fc2cSkre * observe the already existing job. 11988821fc2cSkre */ 11998821fc2cSkre int 12008821fc2cSkre anyjobs(void) 12018821fc2cSkre { 12028821fc2cSkre struct job *jp; 12038821fc2cSkre int i; 12048821fc2cSkre 12058821fc2cSkre if (jobs_invalid) 12068821fc2cSkre return 0; 12078821fc2cSkre 12088821fc2cSkre for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) { 12098821fc2cSkre if (jp->used) 12108821fc2cSkre return 1; 12118821fc2cSkre } 12128821fc2cSkre 12138821fc2cSkre return 0; 12148821fc2cSkre } 121561f28255Scgd 121661f28255Scgd /* 1217aec4b6e2Skre * Output the (new) POSIX required "[%d] %d" string whenever an 1218aec4b6e2Skre * async (ie: background) job is started in an interactive shell. 1219aec4b6e2Skre * Note that a subshell environment is not regarded as interactive. 1220aec4b6e2Skre */ 1221aec4b6e2Skre void 1222aec4b6e2Skre jobstarted(struct job *jp) 1223aec4b6e2Skre { 1224aec4b6e2Skre if (!iflag || !rootshell) 1225aec4b6e2Skre return; 1226aec4b6e2Skre 1227aec4b6e2Skre outfmt(out2, "[%d] %ld\n", JNUM(jp), 1228aec4b6e2Skre jp->pgrp != 0 ? (long)jp->pgrp : (long)jp->ps->pid); 1229aec4b6e2Skre } 1230aec4b6e2Skre 1231aec4b6e2Skre /* 123261f28255Scgd * Return a new job structure, 123361f28255Scgd */ 123461f28255Scgd 123561f28255Scgd struct job * 1236c02b3bbdSchristos makejob(union node *node, int nprocs) 123761f28255Scgd { 123861f28255Scgd int i; 123961f28255Scgd struct job *jp; 124061f28255Scgd 1241c02b3bbdSchristos if (jobs_invalid) { 12428821fc2cSkre VTRACE(DBG_JOBS, ("makejob(%p, %d) clearing jobtab (%d)\n", 12438821fc2cSkre (void *)node, nprocs, njobs)); 1244c02b3bbdSchristos for (i = njobs, jp = jobtab ; --i >= 0 ; jp++) { 1245c02b3bbdSchristos if (jp->used) 1246c02b3bbdSchristos freejob(jp); 1247c02b3bbdSchristos } 1248c02b3bbdSchristos jobs_invalid = 0; 1249c02b3bbdSchristos } 1250c02b3bbdSchristos 125161f28255Scgd for (i = njobs, jp = jobtab ; ; jp++) { 125261f28255Scgd if (--i < 0) { 125361f28255Scgd INTOFF; 125461f28255Scgd if (njobs == 0) { 125561f28255Scgd jobtab = ckmalloc(4 * sizeof jobtab[0]); 125661f28255Scgd } else { 125761f28255Scgd jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); 125806f53b68Smycroft memcpy(jp, jobtab, njobs * sizeof jp[0]); 1259edae5eadSpk /* Relocate `ps' pointers */ 1260edae5eadSpk for (i = 0; i < njobs; i++) 1261edae5eadSpk if (jp[i].ps == &jobtab[i].ps0) 1262edae5eadSpk jp[i].ps = &jp[i].ps0; 126361f28255Scgd ckfree(jobtab); 126461f28255Scgd jobtab = jp; 126561f28255Scgd } 126661f28255Scgd jp = jobtab + njobs; 1267d27db487Schristos for (i = 4 ; --i >= 0 ; njobs++) { 1268d27db487Schristos jobtab[njobs].used = 0; 1269d27db487Schristos jobtab[njobs].prev_job = -1; 1270d27db487Schristos } 127161f28255Scgd INTON; 127261f28255Scgd break; 127361f28255Scgd } 127461f28255Scgd if (jp->used == 0) 127561f28255Scgd break; 127661f28255Scgd } 127761f28255Scgd INTOFF; 1278c02b3bbdSchristos jp->state = JOBRUNNING; 127961f28255Scgd jp->used = 1; 1280f53fd6e9Skre jp->flags = pipefail ? JPIPEFAIL : 0; 128161f28255Scgd jp->nprocs = 0; 1282f697d47eSkre jp->pgrp = 0; 128361f28255Scgd #if JOBS 128461f28255Scgd jp->jobctl = jobctl; 1285c02b3bbdSchristos set_curjob(jp, 1); 128661f28255Scgd #endif 128761f28255Scgd if (nprocs > 1) { 128861f28255Scgd jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); 128961f28255Scgd } else { 129061f28255Scgd jp->ps = &jp->ps0; 129161f28255Scgd } 129261f28255Scgd INTON; 1293f53fd6e9Skre VTRACE(DBG_JOBS, ("makejob(%p, %d)%s returns %%%d\n", (void *)node, 129421f00868Skre nprocs, (jp->flags & JPIPEFAIL) ? " PF" : "", JNUM(jp))); 129561f28255Scgd return jp; 129661f28255Scgd } 129761f28255Scgd 129861f28255Scgd 129961f28255Scgd /* 1300c0b18597Slukem * Fork off a subshell. If we are doing job control, give the subshell its 130161f28255Scgd * own process group. Jp is a job structure that the job is to be added to. 130261f28255Scgd * N is the command that will be evaluated by the child. Both jp and n may 130361f28255Scgd * be NULL. The mode parameter can be one of the following: 130461f28255Scgd * FORK_FG - Fork off a foreground process. 130561f28255Scgd * FORK_BG - Fork off a background process. 130661f28255Scgd * FORK_NOJOB - Like FORK_FG, but don't give the process its own 130761f28255Scgd * process group even if job control is on. 130861f28255Scgd * 130961f28255Scgd * When job control is turned off, background processes have their standard 131061f28255Scgd * input redirected to /dev/null (except for the second and later processes 131161f28255Scgd * in a pipeline). 131261f28255Scgd */ 131361f28255Scgd 131461f28255Scgd int 1315c02b3bbdSchristos forkshell(struct job *jp, union node *n, int mode) 131661f28255Scgd { 13171468e9a3Schristos pid_t pid; 13181468e9a3Schristos int serrno; 131961f28255Scgd 132016bbc7c6Skre CTRACE(DBG_JOBS, ("forkshell(%%%d, %p, %d) called\n", 132121f00868Skre JNUM(jp), n, mode)); 132216bbc7c6Skre 1323edcb4544Schristos switch ((pid = fork())) { 1324edcb4544Schristos case -1: 13251468e9a3Schristos serrno = errno; 132616bbc7c6Skre VTRACE(DBG_JOBS, ("Fork failed, errno=%d\n", serrno)); 13271468e9a3Schristos error("Cannot fork (%s)", strerror(serrno)); 1328edcb4544Schristos break; 1329edcb4544Schristos case 0: 1330352391ffSkre SHELL_FORKED(); 13314783843fSchristos forkchild(jp, n, mode, 0); 1332edcb4544Schristos return 0; 1333edcb4544Schristos default: 13344783843fSchristos return forkparent(jp, n, mode, pid); 1335e6bccfe4Selric } 1336edcb4544Schristos } 1337e6bccfe4Selric 1338edcb4544Schristos int 1339c02b3bbdSchristos forkparent(struct job *jp, union node *n, int mode, pid_t pid) 1340edcb4544Schristos { 13411c627bdfSkre int pgrp = 0; 1342edcb4544Schristos 13436092f7d5Smycroft if (rootshell && mode != FORK_NOJOB && mflag) { 13441c627bdfSkre /* 13451c627bdfSkre * The process group ID must always be that of the 13461c627bdfSkre * first process created for the job. If this proc 13471c627bdfSkre * is the first, that's us, otherwise the pgrp has 13481c627bdfSkre * already been determined. 13491c627bdfSkre */ 13506092f7d5Smycroft if (jp == NULL || jp->nprocs == 0) 13516092f7d5Smycroft pgrp = pid; 13526092f7d5Smycroft else 13531c627bdfSkre pgrp = jp->pgrp; 13546092f7d5Smycroft /* This can fail because we are doing it in the child also */ 13556092f7d5Smycroft (void)setpgid(pid, pgrp); 13566092f7d5Smycroft } 135761f28255Scgd if (mode == FORK_BG) 135861f28255Scgd backgndpid = pid; /* set $! */ 135961f28255Scgd if (jp) { 136061f28255Scgd struct procstat *ps = &jp->ps[jp->nprocs++]; 136161f28255Scgd ps->pid = pid; 136261f28255Scgd ps->status = -1; 1363c02b3bbdSchristos ps->cmd[0] = 0; 13641c627bdfSkre jp->pgrp = pgrp; /* 0 if !mflag */ 1365c02b3bbdSchristos if (/* iflag && rootshell && */ n) 1366c02b3bbdSchristos commandtext(ps, n); 136761f28255Scgd } 1368f07d3f9bSkre CTRACE(DBG_JOBS, ("In parent shell: child = %d (mode %d)\n",pid,mode)); 136961f28255Scgd return pid; 137061f28255Scgd } 137161f28255Scgd 1372edcb4544Schristos void 1373c02b3bbdSchristos forkchild(struct job *jp, union node *n, int mode, int vforked) 1374edcb4544Schristos { 1375edcb4544Schristos int wasroot; 1376edcb4544Schristos int pgrp; 1377edcb4544Schristos const char *devnull = _PATH_DEVNULL; 1378edcb4544Schristos const char *nullerr = "Can't open %s"; 1379edcb4544Schristos 13804783843fSchristos wasroot = rootshell; 13819572a5c2Skre CTRACE(DBG_JOBS, ("Child shell %d %sforked from %d (mode %d)\n", 13829572a5c2Skre getpid(), vforked?"v":"", getppid(), mode)); 13837f63ac72Skre 13847f63ac72Skre if (!vforked) { 13854783843fSchristos rootshell = 0; 13867f63ac72Skre handler = &main_handler; 13877f63ac72Skre } 1388c02b3bbdSchristos 1389edcb4544Schristos closescript(vforked); 1390edcb4544Schristos clear_traps(vforked); 1391edcb4544Schristos #if JOBS 1392edcb4544Schristos if (!vforked) 1393edcb4544Schristos jobctl = 0; /* do job control only in root shell */ 1394edcb4544Schristos if (wasroot && mode != FORK_NOJOB && mflag) { 1395edcb4544Schristos if (jp == NULL || jp->nprocs == 0) 1396edcb4544Schristos pgrp = getpid(); 1397edcb4544Schristos else 1398edcb4544Schristos pgrp = jp->ps[0].pid; 13996092f7d5Smycroft /* This can fail because we are doing it in the parent also */ 14006092f7d5Smycroft (void)setpgid(0, pgrp); 1401edcb4544Schristos if (mode == FORK_FG) { 1402edcb4544Schristos if (tcsetpgrp(ttyfd, pgrp) == -1) 1403edcb4544Schristos error("Cannot set tty process group (%s) at %d", 1404edcb4544Schristos strerror(errno), __LINE__); 1405edcb4544Schristos } 1406edcb4544Schristos setsignal(SIGTSTP, vforked); 1407edcb4544Schristos setsignal(SIGTTOU, vforked); 1408edcb4544Schristos } else if (mode == FORK_BG) { 1409edcb4544Schristos ignoresig(SIGINT, vforked); 1410edcb4544Schristos ignoresig(SIGQUIT, vforked); 1411edcb4544Schristos if ((jp == NULL || jp->nprocs == 0) && 1412edcb4544Schristos ! fd0_redirected_p ()) { 1413edcb4544Schristos close(0); 1414edcb4544Schristos if (open(devnull, O_RDONLY) != 0) 1415edcb4544Schristos error(nullerr, devnull); 1416edcb4544Schristos } 1417edcb4544Schristos } 1418edcb4544Schristos #else 1419edcb4544Schristos if (mode == FORK_BG) { 1420edcb4544Schristos ignoresig(SIGINT, vforked); 1421edcb4544Schristos ignoresig(SIGQUIT, vforked); 1422edcb4544Schristos if ((jp == NULL || jp->nprocs == 0) && 1423edcb4544Schristos ! fd0_redirected_p ()) { 1424edcb4544Schristos close(0); 1425edcb4544Schristos if (open(devnull, O_RDONLY) != 0) 1426edcb4544Schristos error(nullerr, devnull); 1427edcb4544Schristos } 1428edcb4544Schristos } 1429edcb4544Schristos #endif 1430edcb4544Schristos if (wasroot && iflag) { 1431edcb4544Schristos setsignal(SIGINT, vforked); 1432edcb4544Schristos setsignal(SIGQUIT, vforked); 1433edcb4544Schristos setsignal(SIGTERM, vforked); 1434edcb4544Schristos } 1435c02b3bbdSchristos 1436c02b3bbdSchristos if (!vforked) 1437c02b3bbdSchristos jobs_invalid = 1; 1438edcb4544Schristos } 1439edcb4544Schristos 144061f28255Scgd /* 144161f28255Scgd * Wait for job to finish. 144261f28255Scgd * 144361f28255Scgd * Under job control we have the problem that while a child process is 144461f28255Scgd * running interrupts generated by the user are sent to the child but not 144561f28255Scgd * to the shell. This means that an infinite loop started by an inter- 144661f28255Scgd * active user may be hard to kill. With job control turned off, an 144761f28255Scgd * interactive user may place an interactive program inside a loop. If 144861f28255Scgd * the interactive program catches interrupts, the user doesn't want 144961f28255Scgd * these interrupts to also abort the loop. The approach we take here 145061f28255Scgd * is to have the shell ignore interrupt signals while waiting for a 145192e3dd64Sandvar * foreground process to terminate, and then send itself an interrupt 145261f28255Scgd * signal if the child process was terminated by an interrupt signal. 145361f28255Scgd * Unfortunately, some programs want to do a bit of cleanup and then 145461f28255Scgd * exit on interrupt; unless these processes terminate themselves by 145561f28255Scgd * sending a signal to themselves (instead of calling exit) they will 145661f28255Scgd * confuse this approach. 145761f28255Scgd */ 145861f28255Scgd 145961f28255Scgd int 1460c02b3bbdSchristos waitforjob(struct job *jp) 146161f28255Scgd { 146261f28255Scgd #if JOBS 1463df81ab82Sjtc int mypgrp = getpgrp(); 146461f28255Scgd #endif 146561f28255Scgd int status; 146661f28255Scgd int st; 146761f28255Scgd 146861f28255Scgd INTOFF; 146921f00868Skre VTRACE(DBG_JOBS, ("waitforjob(%%%d) called\n", JNUM(jp))); 1470c02b3bbdSchristos while (jp->state == JOBRUNNING) { 14714e9fc30dSkre dowait(WBLOCK, jp, NULL); 147261f28255Scgd } 147361f28255Scgd #if JOBS 147461f28255Scgd if (jp->jobctl) { 1475edcb4544Schristos if (tcsetpgrp(ttyfd, mypgrp) == -1) 1476edcb4544Schristos error("Cannot set tty process group (%s) at %d", 1477edcb4544Schristos strerror(errno), __LINE__); 147861f28255Scgd } 1479c02b3bbdSchristos if (jp->state == JOBSTOPPED && curjob != jp - jobtab) 1480c02b3bbdSchristos set_curjob(jp, 2); 148161f28255Scgd #endif 1482bffe5190Skre status = jobstatus(jp, 1); 1483bffe5190Skre 148461f28255Scgd /* convert to 8 bits */ 14857f2a2717Schristos if (WIFEXITED(status)) 14867f2a2717Schristos st = WEXITSTATUS(status); 148761f28255Scgd #if JOBS 14887f2a2717Schristos else if (WIFSTOPPED(status)) 14897f2a2717Schristos st = WSTOPSIG(status) + 128; 149061f28255Scgd #endif 149161f28255Scgd else 14927f2a2717Schristos st = WTERMSIG(status) + 128; 149316bbc7c6Skre 149416bbc7c6Skre VTRACE(DBG_JOBS, ("waitforjob: job %d, nproc %d, status %d, st %x\n", 149521f00868Skre JNUM(jp), jp->nprocs, status, st)); 14962806df57Smycroft #if JOBS 14972806df57Smycroft if (jp->jobctl) { 1498c06643b8Smycroft /* 14992806df57Smycroft * This is truly gross. 15002806df57Smycroft * If we're doing job control, then we did a TIOCSPGRP which 15012806df57Smycroft * caused us (the shell) to no longer be in the controlling 15022806df57Smycroft * session -- so we wouldn't have seen any ^C/SIGINT. So, we 15032806df57Smycroft * intuit from the subprocess exit status whether a SIGINT 1504456dff6cSwiz * occurred, and if so interrupt ourselves. Yuck. - mycroft 1505c06643b8Smycroft */ 15067f2a2717Schristos if (WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) 1507c06643b8Smycroft raise(SIGINT); 15082806df57Smycroft } 1509c06643b8Smycroft #endif 15102806df57Smycroft if (! JOBS || jp->state == JOBDONE) 15112806df57Smycroft freejob(jp); 151261f28255Scgd INTON; 151361f28255Scgd return st; 151461f28255Scgd } 151561f28255Scgd 151661f28255Scgd 151761f28255Scgd 151861f28255Scgd /* 15197a2f8a05Skre * Wait for a process (any process) to terminate. 15207a2f8a05Skre * 15217a2f8a05Skre * If "job" is given (not NULL), then its jobcontrol status (and mflag) 15227a2f8a05Skre * are used to determine if we wait for stopping/continuing processes or 15237a2f8a05Skre * only terminating ones, and the decision whether to report to stdout 15247a2f8a05Skre * or not varies depending what happened, and whether the affected job 15257a2f8a05Skre * is the one that was requested or not. 15267a2f8a05Skre * 15277a2f8a05Skre * If "changed" is not NULL, then the job which changed because a 15287a2f8a05Skre * process terminated/stopped will be reported by setting *changed, 15297a2f8a05Skre * if there is any such job, otherwise we set *changed = NULL. 153061f28255Scgd */ 153161f28255Scgd 153261f28255Scgd STATIC int 15334e9fc30dSkre dowait(int flags, struct job *job, struct job **changed) 153461f28255Scgd { 153561f28255Scgd int pid; 153661f28255Scgd int status; 153761f28255Scgd struct procstat *sp; 153861f28255Scgd struct job *jp; 153961f28255Scgd struct job *thisjob; 154061f28255Scgd int done; 154161f28255Scgd int stopped; 15427a2f8a05Skre int err; 154361f28255Scgd 15447a2f8a05Skre VTRACE(DBG_JOBS|DBG_PROCS, ("dowait(%x) called for job %d%s\n", 154521f00868Skre flags, JNUM(job), changed ? " [report change]" : "")); 15464e9fc30dSkre 15474e9fc30dSkre if (changed != NULL) 15484e9fc30dSkre *changed = NULL; 15494e9fc30dSkre 15507a2f8a05Skre /* 15517a2f8a05Skre * First deal with the kernel, collect info on any (one) of our 15527a2f8a05Skre * children that has changed state since we last asked. 15537a2f8a05Skre * (loop if we're interrupted by a signal that we aren't processing) 15547a2f8a05Skre */ 155561f28255Scgd do { 15567a2f8a05Skre err = 0; 155797f12feaSchristos pid = waitproc(flags & WBLOCK, job, &status); 15587a2f8a05Skre if (pid == -1) 15597a2f8a05Skre err = errno; 15607a2f8a05Skre VTRACE(DBG_JOBS|DBG_PROCS, 15617a2f8a05Skre ("wait returns pid %d (e:%d), status %#x (ps=%d)\n", 15627a2f8a05Skre pid, err, status, pendingsigs)); 15637a2f8a05Skre } while (pid == -1 && err == EINTR && pendingsigs == 0); 15647a2f8a05Skre 15657a2f8a05Skre /* 15667a2f8a05Skre * if nothing exited/stopped/..., we have nothing else to do 15677a2f8a05Skre */ 156861f28255Scgd if (pid <= 0) 156961f28255Scgd return pid; 15707a2f8a05Skre 15717a2f8a05Skre /* 15727a2f8a05Skre * Otherwise, try to find the process, somewhere in our job table 15737a2f8a05Skre */ 157461f28255Scgd INTOFF; 157561f28255Scgd thisjob = NULL; 157661f28255Scgd for (jp = jobtab ; jp < jobtab + njobs ; jp++) { 157761f28255Scgd if (jp->used) { 15787a2f8a05Skre /* 15797a2f8a05Skre * For each job that is in use (this is one) 15807a2f8a05Skre */ 15817a2f8a05Skre done = 1; /* assume it is finished */ 15827a2f8a05Skre stopped = 1; /* and has stopped */ 15837a2f8a05Skre 15847a2f8a05Skre /* 15857a2f8a05Skre * Now scan all our child processes of the job 15867a2f8a05Skre */ 158761f28255Scgd for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { 158861f28255Scgd if (sp->pid == -1) 158961f28255Scgd continue; 15907a2f8a05Skre /* 15917a2f8a05Skre * If the process that changed is the one 15927a2f8a05Skre * we're looking at, and it was previously 15937a2f8a05Skre * running (-1) or was stopped (anything else 15947a2f8a05Skre * and it must have already finished earlier, 15957a2f8a05Skre * so cannot be the process that just changed) 15967a2f8a05Skre * then we update its status 15977a2f8a05Skre */ 15989572a5c2Skre if (sp->pid == pid && 15999572a5c2Skre (sp->status==-1 || WIFSTOPPED(sp->status))) { 160016bbc7c6Skre VTRACE(DBG_JOBS | DBG_PROCS, 160121f00868Skre ("Job %d: changing status of proc %d from %#x to ", 160221f00868Skre JNUM(jp), pid, sp->status)); 16037a2f8a05Skre 16047a2f8a05Skre /* 16057a2f8a05Skre * If the process continued, 16067a2f8a05Skre * then update its status to running 16077a2f8a05Skre * and mark the job running as well. 16087a2f8a05Skre * 16097a2f8a05Skre * If it was anything but running 16107a2f8a05Skre * before, flag it as a change for 16117a2f8a05Skre * reporting purposes later 16127a2f8a05Skre */ 16139572a5c2Skre if (WIFCONTINUED(status)) { 16149572a5c2Skre if (sp->status != -1) 16154e9fc30dSkre jp->flags |= JOBCHANGED; 16169572a5c2Skre sp->status = -1; 161721f00868Skre jp->state = JOBRUNNING; 161821f00868Skre VTRACE(DBG_JOBS|DBG_PROCS, 161921f00868Skre ("running\n")); 16207a2f8a05Skre } else { 16217a2f8a05Skre /* otherwise update status */ 162261f28255Scgd sp->status = status; 162321f00868Skre VTRACE(DBG_JOBS|DBG_PROCS, 162421f00868Skre ("%#x\n", status)); 16257a2f8a05Skre } 16267a2f8a05Skre 16277a2f8a05Skre /* 16287a2f8a05Skre * We now know the affected job 16297a2f8a05Skre */ 163061f28255Scgd thisjob = jp; 16314e9fc30dSkre if (changed != NULL) 16324e9fc30dSkre *changed = jp; 163361f28255Scgd } 16347a2f8a05Skre /* 16357a2f8a05Skre * After any update that might have just 16367a2f8a05Skre * happened, if this process is running, 16377a2f8a05Skre * the job is not stopped, or if the process 16387a2f8a05Skre * simply stopped (not terminated) then the 16397a2f8a05Skre * job is certainly not completed (done). 16407a2f8a05Skre */ 164161f28255Scgd if (sp->status == -1) 164261f28255Scgd stopped = 0; 16437f2a2717Schristos else if (WIFSTOPPED(sp->status)) 164461f28255Scgd done = 0; 164561f28255Scgd } 16467a2f8a05Skre 16477a2f8a05Skre /* 16487a2f8a05Skre * Once we have examined all processes for the 16497a2f8a05Skre * job, if we still show it as stopped, then... 16507a2f8a05Skre */ 165161f28255Scgd if (stopped) { /* stopped or done */ 16527a2f8a05Skre /* 16537a2f8a05Skre * it might be stopped, or finished, decide: 16547a2f8a05Skre */ 165561f28255Scgd int state = done ? JOBDONE : JOBSTOPPED; 16569572a5c2Skre 16577a2f8a05Skre /* 16587a2f8a05Skre * If that wasn't the same as it was before 16597a2f8a05Skre * then update its state, and if it just 16607a2f8a05Skre * completed, make it be the current job (%%) 16617a2f8a05Skre */ 166261f28255Scgd if (jp->state != state) { 166316bbc7c6Skre VTRACE(DBG_JOBS, 166416bbc7c6Skre ("Job %d: changing state from %d to %d\n", 166521f00868Skre JNUM(jp), jp->state, state)); 166661f28255Scgd jp->state = state; 166761f28255Scgd #if JOBS 1668c02b3bbdSchristos if (done) 1669c02b3bbdSchristos set_curjob(jp, 0); 167061f28255Scgd #endif 167161f28255Scgd } 167261f28255Scgd } 167361f28255Scgd } 167461f28255Scgd } 16757f2a2717Schristos 16767a2f8a05Skre /* 16777a2f8a05Skre * Now we have scanned all jobs. If we found the job that 16787a2f8a05Skre * the process that changed state belonged to (we occasionally 16797a2f8a05Skre * fork processes without associating them with a job, when one 16807a2f8a05Skre * of those finishes, we simply ignore it, the zombie has been 16817a2f8a05Skre * cleaned up, which is all that matters) then we need to 16827a2f8a05Skre * determine if we should say something about it to stdout 16837a2f8a05Skre */ 16847a2f8a05Skre 16854e9fc30dSkre if (thisjob && 16864e9fc30dSkre (thisjob->state != JOBRUNNING || thisjob->flags & JOBCHANGED)) { 1687c02b3bbdSchristos int mode = 0; 16889572a5c2Skre 1689c02b3bbdSchristos if (!rootshell || !iflag) 1690c02b3bbdSchristos mode = SHOW_SIGNALLED; 169197f12feaSchristos if ((job == thisjob && (flags & WNOFREE) == 0) || 16929572a5c2Skre job != thisjob) 1693c02b3bbdSchristos mode = SHOW_SIGNALLED | SHOW_NO_FREE; 16949572a5c2Skre if (mode && (flags & WSILENT) == 0) 1695c02b3bbdSchristos showjob(out2, thisjob, mode); 1696c02b3bbdSchristos else { 169716bbc7c6Skre VTRACE(DBG_JOBS, 16987a2f8a05Skre ("Not printing status for %p [%d], " 16997a2f8a05Skre "mode=%#x rootshell=%d, job=%p [%d]\n", 170021f00868Skre thisjob, JNUM(thisjob), mode, rootshell, 170121f00868Skre job, JNUM(job))); 17024e9fc30dSkre thisjob->flags |= JOBCHANGED; 170361f28255Scgd } 1704c02b3bbdSchristos } 1705c02b3bbdSchristos 1706edcb4544Schristos INTON; 17077a2f8a05Skre /* 17087a2f8a05Skre * Finally tell our caller that something happened (in general all 17097a2f8a05Skre * anyone tests for is <= 0 (or >0) so the actual pid value here 171021f00868Skre * doesn't matter much, but we know pid is >0 so we may as well 17117a2f8a05Skre * give back something meaningful 17127a2f8a05Skre */ 171361f28255Scgd return pid; 171461f28255Scgd } 171561f28255Scgd 171661f28255Scgd 171761f28255Scgd 171861f28255Scgd /* 171961f28255Scgd * Do a wait system call. If job control is compiled in, we accept 172061f28255Scgd * stopped processes. If block is zero, we return a value of zero 172161f28255Scgd * rather than blocking. 172261f28255Scgd * 172361f28255Scgd * System V doesn't have a non-blocking wait system call. It does 1724a640fe8cSsnj * have a SIGCLD signal that is sent to a process when one of its 172561f28255Scgd * children dies. The obvious way to use SIGCLD would be to install 172661f28255Scgd * a handler for SIGCLD which simply bumped a counter when a SIGCLD 172761f28255Scgd * was received, and have waitproc bump another counter when it got 172861f28255Scgd * the status of a process. Waitproc would then know that a wait 172961f28255Scgd * system call would not block if the two counters were different. 173061f28255Scgd * This approach doesn't work because if a process has children that 173161f28255Scgd * have not been waited for, System V will send it a SIGCLD when it 173261f28255Scgd * installs a signal handler for SIGCLD. What this means is that when 173361f28255Scgd * a child exits, the shell will be sent SIGCLD signals continuously 173461f28255Scgd * until is runs out of stack space, unless it does a wait call before 173561f28255Scgd * restoring the signal handler. The code below takes advantage of 173661f28255Scgd * this (mis)feature by installing a signal handler for SIGCLD and 173761f28255Scgd * then checking to see whether it was called. If there are any 173861f28255Scgd * children to be waited for, it will be. 173961f28255Scgd * 174061f28255Scgd * If neither SYSV nor BSD is defined, we don't implement nonblocking 174161f28255Scgd * waits at all. In this case, the user will not be informed when 174221f00868Skre * a background process ends until the next time she runs a real program 174361f28255Scgd * (as opposed to running a builtin command or just typing return), 174461f28255Scgd * and the jobs command may give out of date information. 174561f28255Scgd */ 174661f28255Scgd 174761f28255Scgd #ifdef SYSV 174861f28255Scgd STATIC int gotsigchild; 174961f28255Scgd 175061f28255Scgd STATIC int onsigchild() { 175161f28255Scgd gotsigchild = 1; 175261f28255Scgd } 175361f28255Scgd #endif 175461f28255Scgd 175561f28255Scgd 175661f28255Scgd STATIC int 1757c02b3bbdSchristos waitproc(int block, struct job *jp, int *status) 175861f28255Scgd { 175961f28255Scgd #ifdef BSD 1760a7560f06Schristos int flags = 0; 176161f28255Scgd 176261f28255Scgd #if JOBS 17639572a5c2Skre if (mflag || (jp != NULL && jp->jobctl)) 17649572a5c2Skre flags |= WUNTRACED | WCONTINUED; 176561f28255Scgd #endif 176661f28255Scgd if (block == 0) 176761f28255Scgd flags |= WNOHANG; 17689572a5c2Skre VTRACE(DBG_WAIT, ("waitproc: doing waitpid(flags=%#x)\n", flags)); 1769964f98e7Stv return waitpid(-1, status, flags); 177061f28255Scgd #else 177161f28255Scgd #ifdef SYSV 177261f28255Scgd int (*save)(); 177361f28255Scgd 177461f28255Scgd if (block == 0) { 177561f28255Scgd gotsigchild = 0; 177661f28255Scgd save = signal(SIGCLD, onsigchild); 177761f28255Scgd signal(SIGCLD, save); 177861f28255Scgd if (gotsigchild == 0) 177961f28255Scgd return 0; 178061f28255Scgd } 178161f28255Scgd return wait(status); 178261f28255Scgd #else 178361f28255Scgd if (block == 0) 178461f28255Scgd return 0; 178561f28255Scgd return wait(status); 178661f28255Scgd #endif 178761f28255Scgd #endif 178861f28255Scgd } 178961f28255Scgd 179037ed7877Sjtc /* 179137ed7877Sjtc * return 1 if there are stopped jobs, otherwise 0 179237ed7877Sjtc */ 179337ed7877Sjtc int job_warning = 0; 179437ed7877Sjtc int 1795c02b3bbdSchristos stoppedjobs(void) 179637ed7877Sjtc { 179748250187Stls int jobno; 179848250187Stls struct job *jp; 179961f28255Scgd 1800c02b3bbdSchristos if (job_warning || jobs_invalid) 180137ed7877Sjtc return (0); 180237ed7877Sjtc for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { 180337ed7877Sjtc if (jp->used == 0) 180437ed7877Sjtc continue; 180537ed7877Sjtc if (jp->state == JOBSTOPPED) { 180637ed7877Sjtc out2str("You have stopped jobs.\n"); 180737ed7877Sjtc job_warning = 2; 180837ed7877Sjtc return (1); 180937ed7877Sjtc } 181037ed7877Sjtc } 181137ed7877Sjtc 181237ed7877Sjtc return (0); 181337ed7877Sjtc } 181461f28255Scgd 181561f28255Scgd /* 181661f28255Scgd * Return a string identifying a command (to be printed by the 1817c02b3bbdSchristos * jobs command). 181861f28255Scgd */ 181961f28255Scgd 182061f28255Scgd STATIC char *cmdnextc; 182161f28255Scgd STATIC int cmdnleft; 182261f28255Scgd 1823c02b3bbdSchristos void 1824c02b3bbdSchristos commandtext(struct procstat *ps, union node *n) 182561f28255Scgd { 1826c02b3bbdSchristos int len; 182761f28255Scgd 1828c02b3bbdSchristos cmdnextc = ps->cmd; 1829ee301070Skre if (iflag || mflag || sizeof(ps->cmd) <= 60) 1830c02b3bbdSchristos len = sizeof(ps->cmd); 1831ee301070Skre else if (sizeof ps->cmd <= 400) 1832ee301070Skre len = 50; 1833ee301070Skre else if (sizeof ps->cmd <= 800) 1834ee301070Skre len = 80; 1835c02b3bbdSchristos else 1836c02b3bbdSchristos len = sizeof(ps->cmd) / 10; 1837c02b3bbdSchristos cmdnleft = len; 1838601249a2Skre (void)cmdtxt(n, 1); 1839c02b3bbdSchristos if (cmdnleft <= 0) { 1840c02b3bbdSchristos char *p = ps->cmd + len - 4; 1841c02b3bbdSchristos p[0] = '.'; 1842c02b3bbdSchristos p[1] = '.'; 1843c02b3bbdSchristos p[2] = '.'; 1844c02b3bbdSchristos p[3] = 0; 1845c02b3bbdSchristos } else 184661f28255Scgd *cmdnextc = '\0'; 184716bbc7c6Skre 184816bbc7c6Skre VTRACE(DBG_JOBS, 1849ee301070Skre ("commandtext: ps->cmd %p, end %p, left %d\n\t\"%s\"\n", 1850c02b3bbdSchristos ps->cmd, cmdnextc, cmdnleft, ps->cmd)); 185161f28255Scgd } 185261f28255Scgd 185361f28255Scgd 1854601249a2Skre /* 1855601249a2Skre * Generate a string describing tree node n & its descendants (recursive calls) 1856601249a2Skre * 1857601249a2Skre * Return true (non-zero) if the output is complete (ends with an operator) 1858601249a2Skre * so no ';' need be added before the following command. Return false (zero) 1859601249a2Skre * if a ';' is needed to terminate the output if it is followed by something 1860601249a2Skre * which is not an operator. 1861601249a2Skre */ 1862601249a2Skre STATIC int 1863601249a2Skre cmdtxt(union node *n, int top) 186461f28255Scgd { 186561f28255Scgd union node *np; 186661f28255Scgd struct nodelist *lp; 18673d424690Schristos const char *p; 186861f28255Scgd int i; 186961f28255Scgd 1870c02b3bbdSchristos if (n == NULL || cmdnleft <= 0) 1871601249a2Skre return 1; 187261f28255Scgd switch (n->type) { 187361f28255Scgd case NSEMI: 1874601249a2Skre if (!cmdtxt(n->nbinary.ch1, 0)) 187561f28255Scgd cmdputs(";"); 1876601249a2Skre cmdputs(" "); 1877601249a2Skre return cmdtxt(n->nbinary.ch2, 0); 187861f28255Scgd case NAND: 1879601249a2Skre (void)cmdtxt(n->nbinary.ch1, 0); 188061f28255Scgd cmdputs(" && "); 1881601249a2Skre return cmdtxt(n->nbinary.ch2, 0); 188261f28255Scgd case NOR: 1883601249a2Skre (void) cmdtxt(n->nbinary.ch1, 0); 188461f28255Scgd cmdputs(" || "); 1885601249a2Skre return cmdtxt(n->nbinary.ch2, 0); 1886aa563ca4Skre case NDNOT: 1887aa563ca4Skre cmdputs("! "); 1888aa563ca4Skre /* FALLTHROUGH */ 18898eee1dabSkre case NNOT: 18908eee1dabSkre cmdputs("! "); 1891601249a2Skre return cmdtxt(n->nnot.com, 0); 18928eee1dabSkre break; 189361f28255Scgd case NPIPE: 189461f28255Scgd for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 1895601249a2Skre (void) cmdtxt(lp->n, 0); 189661f28255Scgd if (lp->next) 189761f28255Scgd cmdputs(" | "); 189861f28255Scgd } 1899601249a2Skre if (!top && n->npipe.backgnd) { 190009ecfab9Skre cmdputs(" &"); 1901601249a2Skre return 1; 1902601249a2Skre } 1903601249a2Skre return 0; 190461f28255Scgd case NSUBSHELL: 190561f28255Scgd cmdputs("("); 1906601249a2Skre (void) cmdtxt(n->nredir.n, 0); 190761f28255Scgd cmdputs(")"); 1908601249a2Skre return 0; 190961f28255Scgd case NREDIR: 191061f28255Scgd case NBACKGND: 1911601249a2Skre return cmdtxt(n->nredir.n, top); 191261f28255Scgd case NIF: 191361f28255Scgd cmdputs("if "); 1914601249a2Skre if (!cmdtxt(n->nif.test, 0)) 1915601249a2Skre cmdputs(";"); 1916601249a2Skre cmdputs(" then "); 1917601249a2Skre i = cmdtxt(n->nif.ifpart, 0); 1918c02b3bbdSchristos if (n->nif.elsepart) { 1919601249a2Skre if (i == 0) 1920601249a2Skre cmdputs(";"); 1921601249a2Skre cmdputs(" else "); 1922601249a2Skre i = cmdtxt(n->nif.elsepart, 0); 1923c02b3bbdSchristos } 1924601249a2Skre if (i == 0) 1925601249a2Skre cmdputs(";"); 1926601249a2Skre cmdputs(" fi"); 1927601249a2Skre return 0; 192861f28255Scgd case NWHILE: 192961f28255Scgd cmdputs("while "); 193061f28255Scgd goto until; 193161f28255Scgd case NUNTIL: 193261f28255Scgd cmdputs("until "); 193361f28255Scgd until: 1934601249a2Skre if (!cmdtxt(n->nbinary.ch1, 0)) 1935601249a2Skre cmdputs(";"); 1936601249a2Skre cmdputs(" do "); 1937601249a2Skre if (!cmdtxt(n->nbinary.ch2, 0)) 1938601249a2Skre cmdputs(";"); 1939601249a2Skre cmdputs(" done"); 1940601249a2Skre return 0; 194161f28255Scgd case NFOR: 194261f28255Scgd cmdputs("for "); 194361f28255Scgd cmdputs(n->nfor.var); 1944c02b3bbdSchristos cmdputs(" in "); 1945c02b3bbdSchristos cmdlist(n->nfor.args, 1); 1946c02b3bbdSchristos cmdputs("; do "); 1947601249a2Skre if (!cmdtxt(n->nfor.body, 0)) 1948601249a2Skre cmdputs(";"); 1949601249a2Skre cmdputs(" done"); 1950601249a2Skre return 0; 195161f28255Scgd case NCASE: 195261f28255Scgd cmdputs("case "); 195361f28255Scgd cmdputs(n->ncase.expr->narg.text); 1954c02b3bbdSchristos cmdputs(" in "); 1955c02b3bbdSchristos for (np = n->ncase.cases; np; np = np->nclist.next) { 1956601249a2Skre (void) cmdtxt(np->nclist.pattern, 0); 1957c02b3bbdSchristos cmdputs(") "); 1958601249a2Skre (void) cmdtxt(np->nclist.body, 0); 19597d41ae4eSkre switch (n->type) { /* switch (not if) for later */ 19607d41ae4eSkre case NCLISTCONT: 19617d41ae4eSkre cmdputs(" ;& "); 19627d41ae4eSkre break; 19637d41ae4eSkre default: 1964c02b3bbdSchristos cmdputs(" ;; "); 19657d41ae4eSkre break; 19667d41ae4eSkre } 1967c02b3bbdSchristos } 1968c02b3bbdSchristos cmdputs("esac"); 1969601249a2Skre return 0; 197061f28255Scgd case NDEFUN: 197161f28255Scgd cmdputs(n->narg.text); 1972c02b3bbdSchristos cmdputs("() { ... }"); 1973601249a2Skre return 0; 197461f28255Scgd case NCMD: 1975c02b3bbdSchristos cmdlist(n->ncmd.args, 1); 1976c02b3bbdSchristos cmdlist(n->ncmd.redirect, 0); 1977601249a2Skre if (!top && n->ncmd.backgnd) { 197809ecfab9Skre cmdputs(" &"); 1979601249a2Skre return 1; 1980601249a2Skre } 1981601249a2Skre return 0; 198261f28255Scgd case NARG: 198361f28255Scgd cmdputs(n->narg.text); 1984601249a2Skre return 0; 198561f28255Scgd case NTO: 198661f28255Scgd p = ">"; i = 1; goto redir; 1987f629aa28Schristos case NCLOBBER: 1988f629aa28Schristos p = ">|"; i = 1; goto redir; 198961f28255Scgd case NAPPEND: 199061f28255Scgd p = ">>"; i = 1; goto redir; 199161f28255Scgd case NTOFD: 199261f28255Scgd p = ">&"; i = 1; goto redir; 199361f28255Scgd case NFROM: 199461f28255Scgd p = "<"; i = 0; goto redir; 199561f28255Scgd case NFROMFD: 199661f28255Scgd p = "<&"; i = 0; goto redir; 19976e50d7a8Schristos case NFROMTO: 19986e50d7a8Schristos p = "<>"; i = 0; goto redir; 199961f28255Scgd redir: 20003c6d76cdSkre if (n->nfile.fd != i) 20013c6d76cdSkre cmdputi(n->nfile.fd); 200261f28255Scgd cmdputs(p); 200361f28255Scgd if (n->type == NTOFD || n->type == NFROMFD) { 20043c6d76cdSkre if (n->ndup.dupfd < 0) 20053c6d76cdSkre cmdputs("-"); 20063c6d76cdSkre else 20073c6d76cdSkre cmdputi(n->ndup.dupfd); 200861f28255Scgd } else { 2009601249a2Skre (void) cmdtxt(n->nfile.fname, 0); 201061f28255Scgd } 2011601249a2Skre return 0; 201261f28255Scgd case NHERE: 201361f28255Scgd case NXHERE: 201461f28255Scgd cmdputs("<<..."); 2015601249a2Skre return 0; 201661f28255Scgd default: 201761f28255Scgd cmdputs("???"); 2018601249a2Skre return 0; 201961f28255Scgd } 2020601249a2Skre return 0; 202161f28255Scgd } 202261f28255Scgd 2023c02b3bbdSchristos STATIC void 2024c02b3bbdSchristos cmdlist(union node *np, int sep) 2025c02b3bbdSchristos { 2026c02b3bbdSchristos for (; np; np = np->narg.next) { 2027c02b3bbdSchristos if (!sep) 2028c02b3bbdSchristos cmdputs(" "); 2029601249a2Skre (void) cmdtxt(np, 0); 2030c02b3bbdSchristos if (sep && np->narg.next) 2031c02b3bbdSchristos cmdputs(" "); 2032c02b3bbdSchristos } 2033c02b3bbdSchristos } 203461f28255Scgd 203561f28255Scgd 203661f28255Scgd STATIC void 2037c02b3bbdSchristos cmdputs(const char *s) 203861f28255Scgd { 2039c02b3bbdSchristos const char *p, *str = 0; 2040c02b3bbdSchristos char c, cc[2] = " "; 2041c02b3bbdSchristos char *nextc; 2042c02b3bbdSchristos int nleft; 204361f28255Scgd int subtype = 0; 2044c02b3bbdSchristos int quoted = 0; 2045c02b3bbdSchristos static char vstype[16][4] = { "", "}", "-", "+", "?", "=", 2046d7c8afdfSkre "#", "##", "%", "%%", "}" }; 204761f28255Scgd 204861f28255Scgd p = s; 2049c02b3bbdSchristos nextc = cmdnextc; 2050c02b3bbdSchristos nleft = cmdnleft; 2051c02b3bbdSchristos while (nleft > 0 && (c = *p++) != 0) { 2052c02b3bbdSchristos switch (c) { 2053727a69dcSkre case CTLNONL: 2054727a69dcSkre c = '\0'; 2055727a69dcSkre break; 2056c02b3bbdSchristos case CTLESC: 2057c02b3bbdSchristos c = *p++; 2058c02b3bbdSchristos break; 2059c02b3bbdSchristos case CTLVAR: 206061f28255Scgd subtype = *p++; 2061727a69dcSkre if (subtype & VSLINENO) { /* undo LINENO hack */ 2062c02b3bbdSchristos if ((subtype & VSTYPE) == VSLENGTH) 2063727a69dcSkre str = "${#LINENO"; /*}*/ 2064d7c8afdfSkre else 2065727a69dcSkre str = "${LINENO"; /*}*/ 2066d7c8afdfSkre while (is_digit(*p)) 2067d7c8afdfSkre p++; 2068d7c8afdfSkre } else if ((subtype & VSTYPE) == VSLENGTH) 2069727a69dcSkre str = "${#"; /*}*/ 207061f28255Scgd else 2071727a69dcSkre str = "${"; /*}*/ 2072c02b3bbdSchristos if (!(subtype & VSQUOTE) != !(quoted & 1)) { 2073c02b3bbdSchristos quoted ^= 1; 2074c02b3bbdSchristos c = '"'; 2075d7c8afdfSkre } else { 2076c02b3bbdSchristos c = *str++; 2077d7c8afdfSkre } 2078c02b3bbdSchristos break; 2079727a69dcSkre case CTLENDVAR: /*{*/ 2080c02b3bbdSchristos c = '}'; 2081d7c8afdfSkre if (quoted & 1) 2082d7c8afdfSkre str = "\""; 2083c02b3bbdSchristos quoted >>= 1; 2084c02b3bbdSchristos subtype = 0; 2085c02b3bbdSchristos break; 2086c02b3bbdSchristos case CTLBACKQ: 2087c02b3bbdSchristos c = '$'; 2088c02b3bbdSchristos str = "(...)"; 2089c02b3bbdSchristos break; 2090c02b3bbdSchristos case CTLBACKQ+CTLQUOTE: 2091c02b3bbdSchristos c = '"'; 2092c02b3bbdSchristos str = "$(...)\""; 2093c02b3bbdSchristos break; 2094c02b3bbdSchristos case CTLARI: 2095c02b3bbdSchristos c = '$'; 2096727a69dcSkre if (*p == ' ') 2097727a69dcSkre p++; 2098727a69dcSkre str = "(("; /*))*/ 2099c02b3bbdSchristos break; 2100727a69dcSkre case CTLENDARI: /*((*/ 2101c02b3bbdSchristos c = ')'; 2102c02b3bbdSchristos str = ")"; 2103c02b3bbdSchristos break; 2104c02b3bbdSchristos case CTLQUOTEMARK: 2105c02b3bbdSchristos quoted ^= 1; 2106c02b3bbdSchristos c = '"'; 2107c02b3bbdSchristos break; 2108d7c8afdfSkre case CTLQUOTEEND: 2109d7c8afdfSkre quoted >>= 1; 2110d7c8afdfSkre c = '"'; 2111d7c8afdfSkre break; 2112c02b3bbdSchristos case '=': 2113c02b3bbdSchristos if (subtype == 0) 2114c02b3bbdSchristos break; 2115c02b3bbdSchristos str = vstype[subtype & VSTYPE]; 2116c02b3bbdSchristos if (subtype & VSNUL) 2117c02b3bbdSchristos c = ':'; 2118c02b3bbdSchristos else 2119727a69dcSkre c = *str++; /*{*/ 2120c02b3bbdSchristos if (c != '}') 2121c02b3bbdSchristos quoted <<= 1; 2122d7c8afdfSkre else if (*p == CTLENDVAR) 2123d7c8afdfSkre c = *str++; 2124d7c8afdfSkre subtype = 0; 2125c02b3bbdSchristos break; 2126c02b3bbdSchristos case '\'': 2127c02b3bbdSchristos case '\\': 2128c02b3bbdSchristos case '"': 2129c02b3bbdSchristos case '$': 2130c02b3bbdSchristos /* These can only happen inside quotes */ 2131c02b3bbdSchristos cc[0] = c; 2132c02b3bbdSchristos str = cc; 2133c02b3bbdSchristos c = '\\'; 2134c02b3bbdSchristos break; 2135c02b3bbdSchristos default: 213661f28255Scgd break; 213761f28255Scgd } 2138d7c8afdfSkre if (c != '\0') do { /* c == 0 implies nothing in str */ 2139c02b3bbdSchristos *nextc++ = c; 2140c02b3bbdSchristos } while (--nleft > 0 && str && (c = *str++)); 2141c02b3bbdSchristos str = 0; 214261f28255Scgd } 2143c02b3bbdSchristos if ((quoted & 1) && nleft) { 2144c02b3bbdSchristos *nextc++ = '"'; 2145c02b3bbdSchristos nleft--; 2146c02b3bbdSchristos } 2147c02b3bbdSchristos cmdnleft = nleft; 2148c02b3bbdSchristos cmdnextc = nextc; 214961f28255Scgd } 2150