1 /*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * Kenneth Almquist. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #ifndef lint 38 static char sccsid[] = "@(#)jobs.c 5.4 (Berkeley) 7/15/92"; 39 #endif /* not lint */ 40 41 #include "shell.h" 42 #if JOBS 43 #include "sgtty.h" 44 #undef CEOF /* syntax.h redefines this */ 45 #endif 46 #include "main.h" 47 #include "parser.h" 48 #include "nodes.h" 49 #include "jobs.h" 50 #include "options.h" 51 #include "trap.h" 52 #include "signames.h" 53 #include "syntax.h" 54 #include "input.h" 55 #include "output.h" 56 #include "memalloc.h" 57 #include "error.h" 58 #include "mystring.h" 59 #include <fcntl.h> 60 #include <signal.h> 61 #include <errno.h> 62 #ifdef BSD 63 #include <sys/types.h> 64 #include <sys/wait.h> 65 #include <sys/time.h> 66 #include <sys/resource.h> 67 #endif 68 69 70 71 struct job *jobtab; /* array of jobs */ 72 int njobs; /* size of array */ 73 MKINIT short backgndpid = -1; /* pid of last background process */ 74 #if JOBS 75 int initialpgrp; /* pgrp of shell on invocation */ 76 short curjob; /* current job */ 77 #endif 78 79 #ifdef __STDC__ 80 STATIC void restartjob(struct job *); 81 STATIC struct job *getjob(char *); 82 STATIC void freejob(struct job *); 83 STATIC int procrunning(int); 84 STATIC int dowait(int, struct job *); 85 STATIC int waitproc(int, int *); 86 #else 87 STATIC void restartjob(); 88 STATIC struct job *getjob(); 89 STATIC void freejob(); 90 STATIC int procrunning(); 91 STATIC int dowait(); 92 STATIC int waitproc(); 93 #endif 94 95 96 97 /* 98 * Turn job control on and off. 99 * 100 * Note: This code assumes that the third arg to ioctl is a character 101 * pointer, which is true on Berkeley systems but not System V. Since 102 * System V doesn't have job control yet, this isn't a problem now. 103 */ 104 105 MKINIT int jobctl; 106 107 void 108 setjobctl(on) { 109 int ldisc; 110 111 if (on == jobctl || rootshell == 0) 112 return; 113 if (on) { 114 do { /* while we are in the background */ 115 if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { 116 out2str("sh: can't access tty; job control turned off\n"); 117 mflag = 0; 118 return; 119 } 120 if (initialpgrp == -1) 121 initialpgrp = getpgrp(0); 122 else if (initialpgrp != getpgrp(0)) { 123 killpg(initialpgrp, SIGTTIN); 124 continue; 125 } 126 } while (0); 127 #ifdef OLD_TTY_DRIVER 128 if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { 129 out2str("sh: need new tty driver to run job control; job control turned off\n"); 130 mflag = 0; 131 return; 132 } 133 #endif 134 setsignal(SIGTSTP); 135 setsignal(SIGTTOU); 136 setsignal(SIGTTIN); 137 setpgrp(0, rootpid); 138 ioctl(2, TIOCSPGRP, (char *)&rootpid); 139 } else { /* turning job control off */ 140 setpgrp(0, initialpgrp); 141 ioctl(2, TIOCSPGRP, (char *)&initialpgrp); 142 setsignal(SIGTSTP); 143 setsignal(SIGTTOU); 144 setsignal(SIGTTIN); 145 } 146 jobctl = on; 147 } 148 149 150 #ifdef mkinit 151 152 SHELLPROC { 153 backgndpid = -1; 154 #if JOBS 155 jobctl = 0; 156 #endif 157 } 158 159 #endif 160 161 162 163 #if JOBS 164 fgcmd(argc, argv) char **argv; { 165 struct job *jp; 166 int pgrp; 167 int status; 168 169 jp = getjob(argv[1]); 170 if (jp->jobctl == 0) 171 error("job not created under job control"); 172 pgrp = jp->ps[0].pid; 173 ioctl(2, TIOCSPGRP, (char *)&pgrp); 174 restartjob(jp); 175 INTOFF; 176 status = waitforjob(jp); 177 INTON; 178 return status; 179 } 180 181 182 bgcmd(argc, argv) char **argv; { 183 struct job *jp; 184 185 do { 186 jp = getjob(*++argv); 187 if (jp->jobctl == 0) 188 error("job not created under job control"); 189 restartjob(jp); 190 } while (--argc > 1); 191 return 0; 192 } 193 194 195 STATIC void 196 restartjob(jp) 197 struct job *jp; 198 { 199 struct procstat *ps; 200 int i; 201 202 if (jp->state == JOBDONE) 203 return; 204 INTOFF; 205 killpg(jp->ps[0].pid, SIGCONT); 206 for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { 207 if ((ps->status & 0377) == 0177) { 208 ps->status = -1; 209 jp->state = 0; 210 } 211 } 212 INTON; 213 } 214 #endif 215 216 217 int 218 jobscmd(argc, argv) char **argv; { 219 showjobs(0); 220 return 0; 221 } 222 223 224 /* 225 * Print a list of jobs. If "change" is nonzero, only print jobs whose 226 * statuses have changed since the last call to showjobs. 227 * 228 * If the shell is interrupted in the process of creating a job, the 229 * result may be a job structure containing zero processes. Such structures 230 * will be freed here. 231 */ 232 233 void 234 showjobs(change) { 235 int jobno; 236 int procno; 237 int i; 238 struct job *jp; 239 struct procstat *ps; 240 int col; 241 char s[64]; 242 243 TRACE(("showjobs(%d) called\n", change)); 244 while (dowait(0, (struct job *)NULL) > 0); 245 for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { 246 if (! jp->used) 247 continue; 248 if (jp->nprocs == 0) { 249 freejob(jp); 250 continue; 251 } 252 if (change && ! jp->changed) 253 continue; 254 procno = jp->nprocs; 255 for (ps = jp->ps ; ; ps++) { /* for each process */ 256 if (ps == jp->ps) 257 fmtstr(s, 64, "[%d] %d ", jobno, ps->pid); 258 else 259 fmtstr(s, 64, " %d ", ps->pid); 260 out1str(s); 261 col = strlen(s); 262 s[0] = '\0'; 263 if (ps->status == -1) { 264 /* don't print anything */ 265 } else if ((ps->status & 0xFF) == 0) { 266 fmtstr(s, 64, "Exit %d", ps->status >> 8); 267 } else { 268 i = ps->status; 269 #if JOBS 270 if ((i & 0xFF) == 0177) 271 i >>= 8; 272 #endif 273 if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F]) 274 scopy(sigmesg[i & 0x7F], s); 275 else 276 fmtstr(s, 64, "Signal %d", i & 0x7F); 277 if (i & 0x80) 278 strcat(s, " (core dumped)"); 279 } 280 out1str(s); 281 col += strlen(s); 282 do { 283 out1c(' '); 284 col++; 285 } while (col < 30); 286 out1str(ps->cmd); 287 out1c('\n'); 288 if (--procno <= 0) 289 break; 290 } 291 jp->changed = 0; 292 if (jp->state == JOBDONE) { 293 freejob(jp); 294 } 295 } 296 } 297 298 299 /* 300 * Mark a job structure as unused. 301 */ 302 303 STATIC void 304 freejob(jp) 305 struct job *jp; 306 { 307 struct procstat *ps; 308 int i; 309 310 INTOFF; 311 for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { 312 if (ps->cmd != nullstr) 313 ckfree(ps->cmd); 314 } 315 if (jp->ps != &jp->ps0) 316 ckfree(jp->ps); 317 jp->used = 0; 318 #if JOBS 319 if (curjob == jp - jobtab + 1) 320 curjob = 0; 321 #endif 322 INTON; 323 } 324 325 326 327 int 328 waitcmd(argc, argv) char **argv; { 329 struct job *job; 330 int status; 331 struct job *jp; 332 333 if (argc > 1) { 334 job = getjob(argv[1]); 335 } else { 336 job = NULL; 337 } 338 for (;;) { /* loop until process terminated or stopped */ 339 if (job != NULL) { 340 if (job->state) { 341 status = job->ps[job->nprocs - 1].status; 342 if ((status & 0xFF) == 0) 343 status = status >> 8 & 0xFF; 344 #if JOBS 345 else if ((status & 0xFF) == 0177) 346 status = (status >> 8 & 0x7F) + 128; 347 #endif 348 else 349 status = (status & 0x7F) + 128; 350 if (! iflag) 351 freejob(job); 352 return status; 353 } 354 } else { 355 for (jp = jobtab ; ; jp++) { 356 if (jp >= jobtab + njobs) { /* no running procs */ 357 return 0; 358 } 359 if (jp->used && jp->state == 0) 360 break; 361 } 362 } 363 dowait(1, (struct job *)NULL); 364 } 365 } 366 367 368 369 jobidcmd(argc, argv) char **argv; { 370 struct job *jp; 371 int i; 372 373 jp = getjob(argv[1]); 374 for (i = 0 ; i < jp->nprocs ; ) { 375 out1fmt("%d", jp->ps[i].pid); 376 out1c(++i < jp->nprocs? ' ' : '\n'); 377 } 378 return 0; 379 } 380 381 382 383 /* 384 * Convert a job name to a job structure. 385 */ 386 387 STATIC struct job * 388 getjob(name) 389 char *name; 390 { 391 int jobno; 392 register struct job *jp; 393 int pid; 394 int i; 395 396 if (name == NULL) { 397 #if JOBS 398 currentjob: 399 if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0) 400 error("No current job"); 401 return &jobtab[jobno - 1]; 402 #else 403 error("No current job"); 404 #endif 405 } else if (name[0] == '%') { 406 if (is_digit(name[1])) { 407 jobno = number(name + 1); 408 if (jobno > 0 && jobno <= njobs 409 && jobtab[jobno - 1].used != 0) 410 return &jobtab[jobno - 1]; 411 #if JOBS 412 } else if (name[1] == '%' && name[2] == '\0') { 413 goto currentjob; 414 #endif 415 } else { 416 register struct job *found = NULL; 417 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { 418 if (jp->used && jp->nprocs > 0 419 && prefix(name + 1, jp->ps[0].cmd)) { 420 if (found) 421 error("%s: ambiguous", name); 422 found = jp; 423 } 424 } 425 if (found) 426 return found; 427 } 428 } else if (is_number(name)) { 429 pid = number(name); 430 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { 431 if (jp->used && jp->nprocs > 0 432 && jp->ps[jp->nprocs - 1].pid == pid) 433 return jp; 434 } 435 } 436 error("No such job: %s", name); 437 } 438 439 440 441 /* 442 * Return a new job structure, 443 */ 444 445 struct job * 446 makejob(node, nprocs) 447 union node *node; 448 { 449 int i; 450 struct job *jp; 451 452 for (i = njobs, jp = jobtab ; ; jp++) { 453 if (--i < 0) { 454 INTOFF; 455 if (njobs == 0) { 456 jobtab = ckmalloc(4 * sizeof jobtab[0]); 457 } else { 458 jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); 459 bcopy(jobtab, jp, njobs * sizeof jp[0]); 460 ckfree(jobtab); 461 jobtab = jp; 462 } 463 jp = jobtab + njobs; 464 for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0); 465 INTON; 466 break; 467 } 468 if (jp->used == 0) 469 break; 470 } 471 INTOFF; 472 jp->state = 0; 473 jp->used = 1; 474 jp->changed = 0; 475 jp->nprocs = 0; 476 #if JOBS 477 jp->jobctl = jobctl; 478 #endif 479 if (nprocs > 1) { 480 jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); 481 } else { 482 jp->ps = &jp->ps0; 483 } 484 INTON; 485 TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1)); 486 return jp; 487 } 488 489 490 /* 491 * Fork of a subshell. If we are doing job control, give the subshell its 492 * own process group. Jp is a job structure that the job is to be added to. 493 * N is the command that will be evaluated by the child. Both jp and n may 494 * be NULL. The mode parameter can be one of the following: 495 * FORK_FG - Fork off a foreground process. 496 * FORK_BG - Fork off a background process. 497 * FORK_NOJOB - Like FORK_FG, but don't give the process its own 498 * process group even if job control is on. 499 * 500 * When job control is turned off, background processes have their standard 501 * input redirected to /dev/null (except for the second and later processes 502 * in a pipeline). 503 */ 504 505 int 506 forkshell(jp, n, mode) 507 union node *n; 508 struct job *jp; 509 { 510 int pid; 511 int pgrp; 512 513 TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode)); 514 INTOFF; 515 pid = fork(); 516 if (pid == -1) { 517 TRACE(("Fork failed, errno=%d\n", errno)); 518 INTON; 519 error("Cannot fork"); 520 } 521 if (pid == 0) { 522 struct job *p; 523 int wasroot; 524 int i; 525 526 TRACE(("Child shell %d\n", getpid())); 527 wasroot = rootshell; 528 rootshell = 0; 529 for (i = njobs, p = jobtab ; --i >= 0 ; p++) 530 if (p->used) 531 freejob(p); 532 closescript(); 533 INTON; 534 clear_traps(); 535 #if JOBS 536 jobctl = 0; /* do job control only in root shell */ 537 if (wasroot && mode != FORK_NOJOB && mflag) { 538 if (jp == NULL || jp->nprocs == 0) 539 pgrp = getpid(); 540 else 541 pgrp = jp->ps[0].pid; 542 setpgrp(0, pgrp); 543 if (mode == FORK_FG) { 544 /*** this causes superfluous TIOCSPGRPS ***/ 545 if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) 546 error("TIOCSPGRP failed, errno=%d\n", errno); 547 } 548 setsignal(SIGTSTP); 549 setsignal(SIGTTOU); 550 } else if (mode == FORK_BG) { 551 ignoresig(SIGINT); 552 ignoresig(SIGQUIT); 553 if (jp == NULL || jp->nprocs == 0) { 554 close(0); 555 if (open("/dev/null", O_RDONLY) != 0) 556 error("Can't open /dev/null"); 557 } 558 } 559 #else 560 if (mode == FORK_BG) { 561 ignoresig(SIGINT); 562 ignoresig(SIGQUIT); 563 if (jp == NULL || jp->nprocs == 0) { 564 close(0); 565 if (open("/dev/null", O_RDONLY) != 0) 566 error("Can't open /dev/null"); 567 } 568 } 569 #endif 570 if (wasroot && iflag) { 571 setsignal(SIGINT); 572 setsignal(SIGQUIT); 573 setsignal(SIGTERM); 574 } 575 return pid; 576 } 577 if (rootshell && mode != FORK_NOJOB && mflag) { 578 if (jp == NULL || jp->nprocs == 0) 579 pgrp = pid; 580 else 581 pgrp = jp->ps[0].pid; 582 setpgrp(pid, pgrp); 583 } 584 if (mode == FORK_BG) 585 backgndpid = pid; /* set $! */ 586 if (jp) { 587 struct procstat *ps = &jp->ps[jp->nprocs++]; 588 ps->pid = pid; 589 ps->status = -1; 590 ps->cmd = nullstr; 591 if (iflag && rootshell && n) 592 ps->cmd = commandtext(n); 593 } 594 INTON; 595 TRACE(("In parent shell: child = %d\n", pid)); 596 return pid; 597 } 598 599 600 601 /* 602 * Wait for job to finish. 603 * 604 * Under job control we have the problem that while a child process is 605 * running interrupts generated by the user are sent to the child but not 606 * to the shell. This means that an infinite loop started by an inter- 607 * active user may be hard to kill. With job control turned off, an 608 * interactive user may place an interactive program inside a loop. If 609 * the interactive program catches interrupts, the user doesn't want 610 * these interrupts to also abort the loop. The approach we take here 611 * is to have the shell ignore interrupt signals while waiting for a 612 * forground process to terminate, and then send itself an interrupt 613 * signal if the child process was terminated by an interrupt signal. 614 * Unfortunately, some programs want to do a bit of cleanup and then 615 * exit on interrupt; unless these processes terminate themselves by 616 * sending a signal to themselves (instead of calling exit) they will 617 * confuse this approach. 618 */ 619 620 int 621 waitforjob(jp) 622 register struct job *jp; 623 { 624 #if JOBS 625 int mypgrp = getpgrp(0); 626 #endif 627 int status; 628 int st; 629 630 INTOFF; 631 TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); 632 while (jp->state == 0) { 633 dowait(1, jp); 634 } 635 #if JOBS 636 if (jp->jobctl) { 637 if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) 638 error("TIOCSPGRP failed, errno=%d\n", errno); 639 } 640 if (jp->state == JOBSTOPPED) 641 curjob = jp - jobtab + 1; 642 #endif 643 status = jp->ps[jp->nprocs - 1].status; 644 /* convert to 8 bits */ 645 if ((status & 0xFF) == 0) 646 st = status >> 8 & 0xFF; 647 #if JOBS 648 else if ((status & 0xFF) == 0177) 649 st = (status >> 8 & 0x7F) + 128; 650 #endif 651 else 652 st = (status & 0x7F) + 128; 653 if (! JOBS || jp->state == JOBDONE) 654 freejob(jp); 655 CLEAR_PENDING_INT; 656 if ((status & 0x7F) == SIGINT) 657 kill(getpid(), SIGINT); 658 INTON; 659 return st; 660 } 661 662 663 664 /* 665 * Wait for a process to terminate. 666 */ 667 668 STATIC int 669 dowait(block, job) 670 struct job *job; 671 { 672 int pid; 673 int status; 674 struct procstat *sp; 675 struct job *jp; 676 struct job *thisjob; 677 int done; 678 int stopped; 679 int core; 680 681 TRACE(("dowait(%d) called\n", block)); 682 do { 683 pid = waitproc(block, &status); 684 TRACE(("wait returns %d, status=%d\n", pid, status)); 685 } while (pid == -1 && errno == EINTR); 686 if (pid <= 0) 687 return pid; 688 INTOFF; 689 thisjob = NULL; 690 for (jp = jobtab ; jp < jobtab + njobs ; jp++) { 691 if (jp->used) { 692 done = 1; 693 stopped = 1; 694 for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { 695 if (sp->pid == -1) 696 continue; 697 if (sp->pid == pid) { 698 TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status)); 699 sp->status = status; 700 thisjob = jp; 701 } 702 if (sp->status == -1) 703 stopped = 0; 704 else if ((sp->status & 0377) == 0177) 705 done = 0; 706 } 707 if (stopped) { /* stopped or done */ 708 int state = done? JOBDONE : JOBSTOPPED; 709 if (jp->state != state) { 710 TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); 711 jp->state = state; 712 #if JOBS 713 if (done && curjob == jp - jobtab + 1) 714 curjob = 0; /* no current job */ 715 #endif 716 } 717 } 718 } 719 } 720 INTON; 721 if (! rootshell || ! iflag || (job && thisjob == job)) { 722 #if JOBS 723 if ((status & 0xFF) == 0177) 724 status >>= 8; 725 #endif 726 core = status & 0x80; 727 status &= 0x7F; 728 if (status != 0 && status != SIGINT && status != SIGPIPE) { 729 if (thisjob != job) 730 outfmt(out2, "%d: ", pid); 731 #if JOBS 732 if (status == SIGTSTP && rootshell && iflag) 733 outfmt(out2, "%%%d ", job - jobtab + 1); 734 #endif 735 if (status <= MAXSIG && sigmesg[status]) 736 out2str(sigmesg[status]); 737 else 738 outfmt(out2, "Signal %d", status); 739 if (core) 740 out2str(" - core dumped"); 741 out2c('\n'); 742 flushout(&errout); 743 } else { 744 TRACE(("Not printing status: status=%d\n", status)); 745 } 746 } else { 747 TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); 748 if (thisjob) 749 thisjob->changed = 1; 750 } 751 return pid; 752 } 753 754 755 756 /* 757 * Do a wait system call. If job control is compiled in, we accept 758 * stopped processes. If block is zero, we return a value of zero 759 * rather than blocking. 760 * 761 * System V doesn't have a non-blocking wait system call. It does 762 * have a SIGCLD signal that is sent to a process when one of it's 763 * children dies. The obvious way to use SIGCLD would be to install 764 * a handler for SIGCLD which simply bumped a counter when a SIGCLD 765 * was received, and have waitproc bump another counter when it got 766 * the status of a process. Waitproc would then know that a wait 767 * system call would not block if the two counters were different. 768 * This approach doesn't work because if a process has children that 769 * have not been waited for, System V will send it a SIGCLD when it 770 * installs a signal handler for SIGCLD. What this means is that when 771 * a child exits, the shell will be sent SIGCLD signals continuously 772 * until is runs out of stack space, unless it does a wait call before 773 * restoring the signal handler. The code below takes advantage of 774 * this (mis)feature by installing a signal handler for SIGCLD and 775 * then checking to see whether it was called. If there are any 776 * children to be waited for, it will be. 777 * 778 * If neither SYSV nor BSD is defined, we don't implement nonblocking 779 * waits at all. In this case, the user will not be informed when 780 * a background process until the next time she runs a real program 781 * (as opposed to running a builtin command or just typing return), 782 * and the jobs command may give out of date information. 783 */ 784 785 #ifdef SYSV 786 STATIC int gotsigchild; 787 788 STATIC int onsigchild() { 789 gotsigchild = 1; 790 } 791 #endif 792 793 794 STATIC int 795 waitproc(block, status) 796 int *status; 797 { 798 #ifdef BSD 799 int flags; 800 801 #if JOBS 802 flags = WUNTRACED; 803 #else 804 flags = 0; 805 #endif 806 if (block == 0) 807 flags |= WNOHANG; 808 return wait3(status, flags, (struct rusage *)NULL); 809 #else 810 #ifdef SYSV 811 int (*save)(); 812 813 if (block == 0) { 814 gotsigchild = 0; 815 save = signal(SIGCLD, onsigchild); 816 signal(SIGCLD, save); 817 if (gotsigchild == 0) 818 return 0; 819 } 820 return wait(status); 821 #else 822 if (block == 0) 823 return 0; 824 return wait(status); 825 #endif 826 #endif 827 } 828 829 /* 830 * return 1 if there are stopped jobs, otherwise 0 831 */ 832 int job_warning = 0; 833 int 834 stoppedjobs() 835 { 836 int i; 837 838 if (job_warning) 839 return (0); 840 for (i = 0; i <= njobs; i++) { 841 if (jobtab[i].used == 0) 842 continue; 843 if (jobtab[i].state == JOBSTOPPED) { 844 out2str("You have stopped jobs.\n"); 845 job_warning = 2; 846 return (1); 847 } 848 } 849 850 return (0); 851 } 852 853 /* 854 * Return a string identifying a command (to be printed by the 855 * jobs command. 856 */ 857 858 STATIC char *cmdnextc; 859 STATIC int cmdnleft; 860 STATIC void cmdtxt(), cmdputs(); 861 #define MAXCMDTEXT 200 862 863 char * 864 commandtext(n) 865 union node *n; 866 { 867 char *name; 868 869 cmdnextc = name = ckmalloc(MAXCMDTEXT); 870 cmdnleft = MAXCMDTEXT - 4; 871 cmdtxt(n); 872 *cmdnextc = '\0'; 873 return name; 874 } 875 876 877 STATIC void 878 cmdtxt(n) 879 union node *n; 880 { 881 union node *np; 882 struct nodelist *lp; 883 char *p; 884 int i; 885 char s[2]; 886 887 if (n == NULL) 888 return; 889 switch (n->type) { 890 case NSEMI: 891 cmdtxt(n->nbinary.ch1); 892 cmdputs("; "); 893 cmdtxt(n->nbinary.ch2); 894 break; 895 case NAND: 896 cmdtxt(n->nbinary.ch1); 897 cmdputs(" && "); 898 cmdtxt(n->nbinary.ch2); 899 break; 900 case NOR: 901 cmdtxt(n->nbinary.ch1); 902 cmdputs(" || "); 903 cmdtxt(n->nbinary.ch2); 904 break; 905 case NPIPE: 906 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 907 cmdtxt(lp->n); 908 if (lp->next) 909 cmdputs(" | "); 910 } 911 break; 912 case NSUBSHELL: 913 cmdputs("("); 914 cmdtxt(n->nredir.n); 915 cmdputs(")"); 916 break; 917 case NREDIR: 918 case NBACKGND: 919 cmdtxt(n->nredir.n); 920 break; 921 case NIF: 922 cmdputs("if "); 923 cmdtxt(n->nif.test); 924 cmdputs("; then "); 925 cmdtxt(n->nif.ifpart); 926 cmdputs("..."); 927 break; 928 case NWHILE: 929 cmdputs("while "); 930 goto until; 931 case NUNTIL: 932 cmdputs("until "); 933 until: 934 cmdtxt(n->nbinary.ch1); 935 cmdputs("; do "); 936 cmdtxt(n->nbinary.ch2); 937 cmdputs("; done"); 938 break; 939 case NFOR: 940 cmdputs("for "); 941 cmdputs(n->nfor.var); 942 cmdputs(" in ..."); 943 break; 944 case NCASE: 945 cmdputs("case "); 946 cmdputs(n->ncase.expr->narg.text); 947 cmdputs(" in ..."); 948 break; 949 case NDEFUN: 950 cmdputs(n->narg.text); 951 cmdputs("() ..."); 952 break; 953 case NCMD: 954 for (np = n->ncmd.args ; np ; np = np->narg.next) { 955 cmdtxt(np); 956 if (np->narg.next) 957 cmdputs(" "); 958 } 959 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { 960 cmdputs(" "); 961 cmdtxt(np); 962 } 963 break; 964 case NARG: 965 cmdputs(n->narg.text); 966 break; 967 case NTO: 968 p = ">"; i = 1; goto redir; 969 case NAPPEND: 970 p = ">>"; i = 1; goto redir; 971 case NTOFD: 972 p = ">&"; i = 1; goto redir; 973 case NFROM: 974 p = "<"; i = 0; goto redir; 975 case NFROMFD: 976 p = "<&"; i = 0; goto redir; 977 redir: 978 if (n->nfile.fd != i) { 979 s[0] = n->nfile.fd + '0'; 980 s[1] = '\0'; 981 cmdputs(s); 982 } 983 cmdputs(p); 984 if (n->type == NTOFD || n->type == NFROMFD) { 985 s[0] = n->ndup.dupfd + '0'; 986 s[1] = '\0'; 987 cmdputs(s); 988 } else { 989 cmdtxt(n->nfile.fname); 990 } 991 break; 992 case NHERE: 993 case NXHERE: 994 cmdputs("<<..."); 995 break; 996 default: 997 cmdputs("???"); 998 break; 999 } 1000 } 1001 1002 1003 1004 STATIC void 1005 cmdputs(s) 1006 char *s; 1007 { 1008 register char *p, *q; 1009 register char c; 1010 int subtype = 0; 1011 1012 if (cmdnleft <= 0) 1013 return; 1014 p = s; 1015 q = cmdnextc; 1016 while ((c = *p++) != '\0') { 1017 if (c == CTLESC) 1018 *q++ = *p++; 1019 else if (c == CTLVAR) { 1020 *q++ = '$'; 1021 if (--cmdnleft > 0) 1022 *q++ = '{'; 1023 subtype = *p++; 1024 } else if (c == '=' && subtype != 0) { 1025 *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; 1026 subtype = 0; 1027 } else if (c == CTLENDVAR) { 1028 *q++ = '}'; 1029 } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE) 1030 cmdnleft++; /* ignore it */ 1031 else 1032 *q++ = c; 1033 if (--cmdnleft <= 0) { 1034 *q++ = '.'; 1035 *q++ = '.'; 1036 *q++ = '.'; 1037 break; 1038 } 1039 } 1040 cmdnextc = q; 1041 } 1042