jobs.c

Go to the documentation of this file.
00001 /*-
00002  * Copyright (c) 1991 The Regents of the University of California.
00003  * All rights reserved.
00004  *
00005  * This code is derived from software contributed to Berkeley by
00006  * Kenneth Almquist.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions
00010  * are met:
00011  * 1. Redistributions of source code must retain the above copyright
00012  *    notice, this list of conditions and the following disclaimer.
00013  * 2. Redistributions in binary form must reproduce the above copyright
00014  *    notice, this list of conditions and the following disclaimer in the
00015  *    documentation and/or other materials provided with the distribution.
00016  * 3. All advertising materials mentioning features or use of this software
00017  *    must display the following acknowledgement:
00018  *      This product includes software developed by the University of
00019  *      California, Berkeley and its contributors.
00020  * 4. Neither the name of the University nor the names of its contributors
00021  *    may be used to endorse or promote products derived from this software
00022  *    without specific prior written permission.
00023  *
00024  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
00025  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00026  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00027  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
00028  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00029  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00030  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00031  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00032  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00033  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00034  * SUCH DAMAGE.
00035  */
00036 
00037 #ifndef lint
00038 static char sccsid[] = "@(#)jobs.c      5.1 (Berkeley) 3/7/91";
00039 #endif /* not lint */
00040 
00041 #include "shell.h"
00042 #if JOBS
00043 #include "sgtty.h"
00044 #undef CEOF                     /* syntax.h redefines this */
00045 #endif
00046 #include "main.h"
00047 #include "parser.h"
00048 #include "nodes.h"
00049 #include "jobs.h"
00050 #include "options.h"
00051 #include "trap.h"
00052 #include "signames.h"
00053 #include "syntax.h"
00054 #include "input.h"
00055 #include "output.h"
00056 #include "memalloc.h"
00057 #include "error.h"
00058 #include "mystring.h"
00059 #include "redir.h"
00060 #include <sys/types.h>
00061 #include <fcntl.h>
00062 #include <signal.h>
00063 #include <errno.h>
00064 #ifdef BSD
00065 #include <sys/types.h>
00066 #include <sys/wait.h>
00067 #include <sys/time.h>
00068 #include <sys/resource.h>
00069 #endif
00070 #if POSIX
00071 #include <sys/wait.h>
00072 #endif
00073 
00074 
00075 
00076 struct job *jobtab;             /* array of jobs */
00077 int njobs;                      /* size of array */
00078 MKINIT pid_t backgndpid = -1;   /* pid of last background process */
00079 #if JOBS
00080 int initialpgrp;                /* pgrp of shell on invocation */
00081 pid_t curjob;                   /* current job */
00082 #endif
00083 
00084 #ifdef __STDC__
00085 STATIC void restartjob(struct job *);
00086 STATIC struct job *getjob(char *);
00087 STATIC void freejob(struct job *);
00088 STATIC int procrunning(int);
00089 STATIC int dowait(int, struct job *);
00090 STATIC int waitproc(int, int *);
00091 STATIC char *commandtext(union node *);
00092 #else
00093 STATIC void restartjob();
00094 STATIC struct job *getjob();
00095 STATIC void freejob();
00096 STATIC int procrunning();
00097 STATIC int dowait();
00098 STATIC int waitproc();
00099 STATIC char *commandtext();
00100 #endif
00101 
00102 
00103  
00104 #if JOBS
00105 /*
00106  * Turn job control on and off.
00107  *
00108  * Note:  This code assumes that the third arg to ioctl is a character
00109  * pointer, which is true on Berkeley systems but not System V.  Since
00110  * System V doesn't have job control yet, this isn't a problem now.
00111  */
00112 
00113 MKINIT int jobctl;
00114 
00115 void
00116 setjobctl(on) {
00117         int ldisc;
00118 
00119         if (on == jobctl || rootshell == 0)
00120                 return;
00121         if (on) {
00122                 do { /* while we are in the background */
00123                         if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) {
00124                                 out2str("ash: can't access tty; job control turned off\n");
00125                                 jflag = 0;
00126                                 return;
00127                         }
00128                         if (initialpgrp == -1)
00129                                 initialpgrp = getpgrp(0);
00130                         else if (initialpgrp != getpgrp(0)) {
00131                                 killpg(initialpgrp, SIGTTIN);
00132                                 continue;
00133                         }
00134                 } while (0);
00135                 if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) {
00136                         out2str("ash: need new tty driver to run job control; job control turned off\n");
00137                         jflag = 0;
00138                         return;
00139                 }
00140                 setsignal(SIGTSTP);
00141                 setsignal(SIGTTOU);
00142                 setpgrp(0, rootpid);
00143                 ioctl(2, TIOCSPGRP, (char *)&rootpid);
00144         } else { /* turning job control off */
00145                 setpgrp(0, initialpgrp);
00146                 ioctl(2, TIOCSPGRP, (char *)&initialpgrp);
00147                 setsignal(SIGTSTP);
00148                 setsignal(SIGTTOU);
00149         }
00150         jobctl = on;
00151 }
00152 #endif
00153 
00154 
00155 #ifdef mkinit
00156 
00157 SHELLPROC {
00158         backgndpid = -1;
00159 #if JOBS
00160         jobctl = 0;
00161 #endif
00162 }
00163 
00164 #endif
00165 
00166 
00167 
00168 #if JOBS
00169 fgcmd(argc, argv)  char **argv; {
00170         struct job *jp;
00171         int pgrp;
00172         int status;
00173 
00174         jp = getjob(argv[1]);
00175         if (jp->jobctl == 0)
00176                 error("job not created under job control");
00177         pgrp = jp->ps[0].pid;
00178         ioctl(2, TIOCSPGRP, (char *)&pgrp);
00179         restartjob(jp);
00180         INTOFF;
00181         status = waitforjob(jp);
00182         INTON;
00183         return status;
00184 }
00185 
00186 
00187 bgcmd(argc, argv)  char **argv; {
00188         struct job *jp;
00189 
00190         do {
00191                 jp = getjob(*++argv);
00192                 if (jp->jobctl == 0)
00193                         error("job not created under job control");
00194                 restartjob(jp);
00195         } while (--argc > 1);
00196         return 0;
00197 }
00198 
00199 
00200 STATIC void
00201 restartjob(jp)
00202         struct job *jp;
00203         {
00204         struct procstat *ps;
00205         int i;
00206 
00207         if (jp->state == JOBDONE)
00208                 return;
00209         INTOFF;
00210         killpg(jp->ps[0].pid, SIGCONT);
00211         for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) {
00212                 if ((ps->status & 0377) == 0177) {
00213                         ps->status = -1;
00214                         jp->state = 0;
00215                 }
00216         }
00217         INTON;
00218 }
00219 #endif
00220 
00221 
00222 int
00223 jobscmd(argc, argv)  char **argv; {
00224         showjobs(0);
00225         return 0;
00226 }
00227 
00228 
00229 /*
00230  * Print a list of jobs.  If "change" is nonzero, only print jobs whose
00231  * statuses have changed since the last call to showjobs.
00232  *
00233  * If the shell is interrupted in the process of creating a job, the
00234  * result may be a job structure containing zero processes.  Such structures
00235  * will be freed here.
00236  */
00237 
00238 void
00239 showjobs(change) {
00240         int jobno;
00241         int procno;
00242         int i;
00243         struct job *jp;
00244         struct procstat *ps;
00245         int col;
00246         char s[64];
00247 
00248         TRACE(("showjobs(%d) called\n", change));
00249         while (dowait(0, (struct job *)NULL) > 0);
00250         for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) {
00251                 if (! jp->used)
00252                         continue;
00253                 if (jp->nprocs == 0) {
00254                         freejob(jp);
00255                         continue;
00256                 }
00257                 if (change && ! jp->changed)
00258                         continue;
00259                 procno = jp->nprocs;
00260                 for (ps = jp->ps ; ; ps++) {    /* for each process */
00261                         if (ps == jp->ps)
00262                                 fmtstr(s, 64, "[%d] %d ", jobno, ps->pid);
00263                         else
00264                                 fmtstr(s, 64, "    %d ", ps->pid);
00265                         out1str(s);
00266                         col = strlen(s);
00267                         s[0] = '\0';
00268                         if (ps->status == -1) {
00269                                 /* don't print anything */
00270                         } else if ((ps->status & 0xFF) == 0) {
00271                                 fmtstr(s, 64, "Exit %d", ps->status >> 8);
00272                         } else {
00273                                 i = ps->status;
00274 #if JOBS
00275                                 if ((i & 0xFF) == 0177)
00276                                         i >>= 8;
00277 #endif
00278                                 if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F])
00279                                         scopy(sigmesg[i & 0x7F], s);
00280                                 else
00281                                         fmtstr(s, 64, "Signal %d", i & 0x7F);
00282                                 if (i & 0x80)
00283                                         strcat(s, " (core dumped)");
00284                         }
00285                         out1str(s);
00286                         col += strlen(s);
00287                         do {
00288                                 out1c(' ');
00289                                 col++;
00290                         } while (col < 30);
00291                         out1str(ps->cmd);
00292                         out1c('\n');
00293                         if (--procno <= 0)
00294                                 break;
00295                 }
00296                 jp->changed = 0;
00297                 if (jp->state == JOBDONE) {
00298                         freejob(jp);
00299                 }
00300         }
00301 }
00302 
00303 
00304 /*
00305  * Mark a job structure as unused.
00306  */
00307 
00308 STATIC void
00309 freejob(jp)
00310         struct job *jp;
00311         {
00312         struct procstat *ps;
00313         int i;
00314 
00315         INTOFF;
00316         for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) {
00317                 if (ps->cmd != nullstr)
00318                         ckfree(ps->cmd);
00319         }
00320         if (jp->ps != &jp->ps0)
00321                 ckfree(jp->ps);
00322         jp->used = 0;
00323 #if JOBS
00324         if (curjob == jp - jobtab + 1)
00325                 curjob = 0;
00326 #endif
00327         INTON;
00328 }
00329 
00330 
00331 
00332 int
00333 waitcmd(argc, argv)  char **argv; {
00334         struct job *job;
00335         int status;
00336         struct job *jp;
00337 
00338         if (argc > 1) {
00339                 job = getjob(argv[1]);
00340         } else {
00341                 job = NULL;
00342         }
00343         for (;;) {      /* loop until process terminated or stopped */
00344                 if (job != NULL) {
00345                         if (job->state) {
00346                                 status = job->ps[job->nprocs - 1].status;
00347                                 if ((status & 0xFF) == 0)
00348                                         status = status >> 8 & 0xFF;
00349 #if JOBS
00350                                 else if ((status & 0xFF) == 0177)
00351                                         status = (status >> 8 & 0x7F) + 128;
00352 #endif
00353                                 else
00354                                         status = (status & 0x7F) + 128;
00355                                 if (! iflag)
00356                                         freejob(job);
00357                                 return status;
00358                         }
00359                 } else {
00360                         for (jp = jobtab ; ; jp++) {
00361                                 if (jp >= jobtab + njobs) {     /* no running procs */
00362                                         return 0;
00363                                 }
00364                                 if (jp->used && jp->state == 0)
00365                                         break;
00366                         }
00367                 }
00368                 dowait(1, (struct job *)NULL);
00369         }
00370 }
00371 
00372 
00373 
00374 jobidcmd(argc, argv)  char **argv; {
00375         struct job *jp;
00376         int i;
00377 
00378         jp = getjob(argv[1]);
00379         for (i = 0 ; i < jp->nprocs ; ) {
00380                 out1fmt("%d", jp->ps[i].pid);
00381                 out1c(++i < jp->nprocs? ' ' : '\n');
00382         }
00383         return 0;
00384 }
00385 
00386 
00387 
00388 /*
00389  * Convert a job name to a job structure.
00390  */
00391 
00392 STATIC struct job *
00393 getjob(name)
00394         char *name;
00395         {
00396         int jobno;
00397         register struct job *jp;
00398         int pid;
00399         int i;
00400 
00401         if (name == NULL) {
00402 #if JOBS
00403 currentjob:
00404                 if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0)
00405                         error("No current job");
00406                 return &jobtab[jobno - 1];
00407 #else
00408                 error("No current job");
00409 #endif
00410         } else if (name[0] == '%') {
00411                 if (is_digit(name[1])) {
00412                         jobno = number(name + 1);
00413                         if (jobno > 0 && jobno <= njobs
00414                          && jobtab[jobno - 1].used != 0)
00415                                 return &jobtab[jobno - 1];
00416 #if JOBS
00417                 } else if (name[1] == '%' && name[2] == '\0') {
00418                         goto currentjob;
00419 #endif
00420                 } else {
00421                         register struct job *found = NULL;
00422                         for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
00423                                 if (jp->used && jp->nprocs > 0
00424                                  && prefix(name + 1, jp->ps[0].cmd)) {
00425                                         if (found)
00426                                                 error("%s: ambiguous", name);
00427                                         found = jp;
00428                                 }
00429                         }
00430                         if (found)
00431                                 return found;
00432                 }
00433         } else if (is_number(name)) {
00434                 pid = number(name);
00435                 for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) {
00436                         if (jp->used && jp->nprocs > 0
00437                          && jp->ps[jp->nprocs - 1].pid == pid)
00438                                 return jp;
00439                 }
00440         }
00441         error("No such job: %s", name);
00442 }
00443 
00444 
00445 
00446 /*
00447  * Return a new job structure,
00448  */
00449 
00450 struct job *
00451 makejob(node, nprocs)
00452         union node *node;
00453         {
00454         int i;
00455         struct job *jp;
00456 
00457         for (i = njobs, jp = jobtab ; ; jp++) {
00458                 if (--i < 0) {
00459                         INTOFF;
00460                         if (njobs == 0) {
00461                                 jobtab = ckmalloc(4 * sizeof jobtab[0]);
00462                         } else {
00463                                 jp = ckmalloc((njobs + 4) * sizeof jobtab[0]);
00464                                 bcopy(jobtab, jp, njobs * sizeof jp[0]);
00465                                 for (i= 0; i<njobs; i++)
00466                                 {
00467                                         if (jobtab[i].ps == &jobtab[i].ps0)
00468                                                 jp[i].ps= &jp[i].ps0;
00469                                 }
00470                                 ckfree(jobtab);
00471                                 jobtab = jp;
00472                         }
00473                         jp = jobtab + njobs;
00474                         for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0);
00475                         INTON;
00476                         break;
00477                 }
00478                 if (jp->used == 0)
00479                         break;
00480         }
00481         INTOFF;
00482         jp->state = 0;
00483         jp->used = 1;
00484         jp->changed = 0;
00485         jp->nprocs = 0;
00486 #if JOBS
00487         jp->jobctl = jobctl;
00488 #endif
00489         if (nprocs > 1) {
00490                 jp->ps = ckmalloc(nprocs * sizeof (struct procstat));
00491         } else {
00492                 jp->ps = &jp->ps0;
00493         }
00494         INTON;
00495         TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1));
00496         return jp;
00497 }       
00498 
00499 
00500 /*
00501  * Fork of a subshell.  If we are doing job control, give the subshell its
00502  * own process group.  Jp is a job structure that the job is to be added to.
00503  * N is the command that will be evaluated by the child.  Both jp and n may
00504  * be NULL.  The mode parameter can be one of the following:
00505  *      FORK_FG - Fork off a foreground process.
00506  *      FORK_BG - Fork off a background process.
00507  *      FORK_NOJOB - Like FORK_FG, but don't give the process its own
00508  *                   process group even if job control is on.
00509  *
00510  * When job control is turned off, background processes have their standard
00511  * input redirected to /dev/null (except for the second and later processes
00512  * in a pipeline).
00513  */
00514 
00515 int
00516 forkshell(jp, n, mode)
00517         union node *n;
00518         struct job *jp;
00519         {
00520         int pid;
00521         int pgrp;
00522 
00523         TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode));
00524         INTOFF;
00525         pid = fork();
00526         if (pid == -1) {
00527                 TRACE(("Fork failed, errno=%d\n", errno));
00528                 INTON;
00529                 error("Cannot fork");
00530         }
00531         if (pid == 0) {
00532                 struct job *p;
00533                 int wasroot;
00534                 int i;
00535 
00536                 TRACE(("Child shell %d\n", getpid()));
00537                 wasroot = rootshell;
00538                 rootshell = 0;
00539                 for (i = njobs, p = jobtab ; --i >= 0 ; p++)
00540                         if (p->used)
00541                                 freejob(p);
00542                 closescript();
00543                 INTON;
00544                 clear_traps();
00545 #if JOBS
00546                 jobctl = 0;             /* do job control only in root shell */
00547                 if (wasroot && mode != FORK_NOJOB && jflag) {
00548                         if (jp == NULL || jp->nprocs == 0)
00549                                 pgrp = getpid();
00550                         else
00551                                 pgrp = jp->ps[0].pid;
00552                         setpgrp(0, pgrp);
00553                         if (mode == FORK_FG) {
00554                                 /*** this causes superfluous TIOCSPGRPS ***/
00555                                 if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0)
00556                                         error("TIOCSPGRP failed, errno=%d\n", errno);
00557                         }
00558                         setsignal(SIGTSTP);
00559                         setsignal(SIGTTOU);
00560                 } else if (mode == FORK_BG) {
00561                         ignoresig(SIGINT);
00562                         ignoresig(SIGQUIT);
00563                         if ((jp == NULL || jp->nprocs == 0)
00564                             && ! fd0_redirected_p ()) {
00565                                 close(0);
00566                                 if (open("/dev/null", O_RDONLY) != 0)
00567                                         error("Can't open /dev/null");
00568                         }
00569                 }
00570 #else
00571                 if (mode == FORK_BG) {
00572                         ignoresig(SIGINT);
00573                         ignoresig(SIGQUIT);
00574                         if ((jp == NULL || jp->nprocs == 0)
00575                             && ! fd0_redirected_p ()) {
00576                                 close(0);
00577                                 if (open("/dev/null", O_RDONLY) != 0)
00578                                         error("Can't open /dev/null");
00579                         }
00580                 }
00581 #endif
00582                 if (wasroot && iflag) {
00583                         setsignal(SIGINT);
00584                         setsignal(SIGQUIT);
00585                         setsignal(SIGTERM);
00586                 }
00587                 return pid;
00588         }
00589         if (rootshell && mode != FORK_NOJOB && jflag) {
00590                 if (jp == NULL || jp->nprocs == 0)
00591                         pgrp = pid;
00592                 else
00593                         pgrp = jp->ps[0].pid;
00594 #if JOBS
00595                 setpgrp(pid, pgrp);
00596 #endif
00597         }
00598         if (mode == FORK_BG)
00599                 backgndpid = pid;               /* set $! */
00600         if (jp) {
00601                 struct procstat *ps = &jp->ps[jp->nprocs++];
00602                 ps->pid = pid;
00603                 ps->status = -1;
00604                 ps->cmd = nullstr;
00605                 if (iflag && rootshell && n)
00606                         ps->cmd = commandtext(n);
00607         }
00608         INTON;
00609         TRACE(("In parent shell:  child = %d\n", pid));
00610         return pid;
00611 }
00612 
00613 
00614 
00615 /*
00616  * Wait for job to finish.
00617  *
00618  * Under job control we have the problem that while a child process is
00619  * running interrupts generated by the user are sent to the child but not
00620  * to the shell.  This means that an infinite loop started by an inter-
00621  * active user may be hard to kill.  With job control turned off, an
00622  * interactive user may place an interactive program inside a loop.  If
00623  * the interactive program catches interrupts, the user doesn't want
00624  * these interrupts to also abort the loop.  The approach we take here
00625  * is to have the shell ignore interrupt signals while waiting for a
00626  * forground process to terminate, and then send itself an interrupt
00627  * signal if the child process was terminated by an interrupt signal.
00628  * Unfortunately, some programs want to do a bit of cleanup and then
00629  * exit on interrupt; unless these processes terminate themselves by
00630  * sending a signal to themselves (instead of calling exit) they will
00631  * confuse this approach.
00632  */
00633 
00634 int
00635 waitforjob(jp)
00636         register struct job *jp;
00637         {
00638 #if JOBS
00639         int mypgrp = getpgrp(0);
00640 #endif
00641         int status;
00642         int st;
00643 
00644         INTOFF;
00645         TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1));
00646         while (jp->state == 0 && dowait(1, jp) != -1) ;
00647 #if JOBS
00648         if (jp->jobctl) {
00649                 if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0)
00650                         error("TIOCSPGRP failed, errno=%d\n", errno);
00651         }
00652         if (jp->state == JOBSTOPPED)
00653                 curjob = jp - jobtab + 1;
00654 #endif
00655         status = jp->ps[jp->nprocs - 1].status;
00656         /* convert to 8 bits */
00657         if ((status & 0xFF) == 0)
00658                 st = status >> 8 & 0xFF;
00659 #if JOBS
00660         else if ((status & 0xFF) == 0177)
00661                 st = (status >> 8 & 0x7F) + 128;
00662 #endif
00663         else
00664                 st = (status & 0x7F) + 128;
00665         if (! JOBS || jp->state == JOBDONE)
00666                 freejob(jp);
00667         CLEAR_PENDING_INT;
00668         if ((status & 0x7F) == SIGINT)
00669                 kill(getpid(), SIGINT);
00670         INTON;
00671         return st;
00672 }
00673 
00674 
00675 
00676 /*
00677  * Wait for a process to terminate.
00678  */
00679 
00680 STATIC int
00681 dowait(block, job)
00682         struct job *job;
00683         {
00684         int pid;
00685         int status;
00686         struct procstat *sp;
00687         struct job *jp;
00688         struct job *thisjob;
00689         int done;
00690         int stopped;
00691         int core;
00692 
00693         TRACE(("dowait(%d) called\n", block));
00694         do {
00695                 pid = waitproc(block, &status);
00696                 TRACE(("wait returns %d, status=%d, errno=%d\n",
00697                                 pid, status, errno));
00698         } while (pid == -1 && errno == EINTR);
00699         if (pid <= 0)
00700                 return pid;
00701         INTOFF;
00702         thisjob = NULL;
00703         for (jp = jobtab ; jp < jobtab + njobs ; jp++) {
00704                 if (jp->used) {
00705                         done = 1;
00706                         stopped = 1;
00707                         for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) {
00708                                 if (sp->pid == -1)
00709                                         continue;
00710                                 if (sp->pid == pid) {
00711                                         TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status));
00712                                         sp->status = status;
00713                                         thisjob = jp;
00714                                 }
00715                                 if (sp->status == -1)
00716                                         stopped = 0;
00717                                 else if ((sp->status & 0377) == 0177)
00718                                         done = 0;
00719                         }
00720                         if (stopped) {          /* stopped or done */
00721                                 int state = done? JOBDONE : JOBSTOPPED;
00722                                 if (jp->state != state) {
00723                                         TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state));
00724                                         jp->state = state;
00725 #if JOBS
00726                                         if (done && curjob == jp - jobtab + 1)
00727                                                 curjob = 0;             /* no current job */
00728 #endif
00729                                 }
00730                         }
00731                 }
00732         }
00733         INTON;
00734         if (! rootshell || ! iflag || (job && thisjob == job)) {
00735 #if JOBS
00736                 if ((status & 0xFF) == 0177)
00737                         status >>= 8;
00738 #endif
00739                 core = status & 0x80;
00740                 status &= 0x7F;
00741                 if (status != 0 && status != SIGINT && status != SIGPIPE) {
00742                         if (thisjob != job)
00743                                 outfmt(out2, "%d: ", pid);
00744 #if JOBS
00745                         if (status == SIGTSTP && rootshell && iflag)
00746                                 outfmt(out2, "%%%d ", job - jobtab + 1);
00747 #endif
00748                         if (status <= MAXSIG && sigmesg[status])
00749                                 out2str(sigmesg[status]);
00750                         else
00751                                 outfmt(out2, "Signal %d", status);
00752                         if (core)
00753                                 out2str(" - core dumped");
00754                         out2c('\n');
00755                         flushout(&errout);
00756                 } else {
00757                         TRACE(("Not printing status: status=%d\n", status));
00758                 }
00759         } else {
00760                 TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job));
00761                 if (thisjob)
00762                         thisjob->changed = 1;
00763         }
00764         return pid;
00765 }
00766 
00767 
00768 
00769 /*
00770  * Do a wait system call.  If job control is compiled in, we accept
00771  * stopped processes.  If block is zero, we return a value of zero
00772  * rather than blocking.
00773  *
00774  * System V doesn't have a non-blocking wait system call.  It does
00775  * have a SIGCLD signal that is sent to a process when one of it's
00776  * children dies.  The obvious way to use SIGCLD would be to install
00777  * a handler for SIGCLD which simply bumped a counter when a SIGCLD
00778  * was received, and have waitproc bump another counter when it got
00779  * the status of a process.  Waitproc would then know that a wait
00780  * system call would not block if the two counters were different.
00781  * This approach doesn't work because if a process has children that
00782  * have not been waited for, System V will send it a SIGCLD when it
00783  * installs a signal handler for SIGCLD.  What this means is that when
00784  * a child exits, the shell will be sent SIGCLD signals continuously
00785  * until is runs out of stack space, unless it does a wait call before
00786  * restoring the signal handler.  The code below takes advantage of
00787  * this (mis)feature by installing a signal handler for SIGCLD and
00788  * then checking to see whether it was called.  If there are any
00789  * children to be waited for, it will be.
00790  *
00791  * If neither SYSV nor BSD is defined, we don't implement nonblocking
00792  * waits at all.  In this case, the user will not be informed when
00793  * a background process until the next time she runs a real program
00794  * (as opposed to running a builtin command or just typing return),
00795  * and the jobs command may give out of date information.
00796  */
00797 
00798 #ifdef SYSV
00799 STATIC int gotsigchild;
00800 
00801 STATIC int onsigchild() {
00802         gotsigchild = 1;
00803 }
00804 #endif
00805 
00806 
00807 STATIC int
00808 waitproc(block, status)
00809         int *status;
00810         {
00811 #ifdef BSD
00812         int flags;
00813 
00814 #if JOBS
00815         flags = WUNTRACED;
00816 #else
00817         flags = 0;
00818 #endif
00819         if (block == 0)
00820                 flags |= WNOHANG;
00821         return wait3((union wait *)status, flags, (struct rusage *)NULL);
00822 #else
00823 #ifdef SYSV
00824         int (*save)();
00825 
00826         if (block == 0) {
00827                 gotsigchild = 0;
00828                 save = signal(SIGCLD, onsigchild);
00829                 signal(SIGCLD, save);
00830                 if (gotsigchild == 0)
00831                         return 0;
00832         }
00833         return wait(status);
00834 #else
00835 #if POSIX
00836         return waitpid(-1, status, block == 0 ? WNOHANG : 0);
00837 #else
00838         if (block == 0)
00839                 return 0;
00840         return wait(status);
00841 #endif
00842 #endif
00843 #endif
00844 }
00845 
00846 
00847 
00848 /*
00849  * Return a string identifying a command (to be printed by the
00850  * jobs command.
00851  */
00852 
00853 STATIC char *cmdnextc;
00854 STATIC int cmdnleft;
00855 STATIC void cmdtxt(), cmdputs();
00856 
00857 STATIC char *
00858 commandtext(n)
00859         union node *n;
00860         {
00861         char *name;
00862 
00863         cmdnextc = name = ckmalloc(50);
00864         cmdnleft = 50 - 4;
00865         cmdtxt(n);
00866         *cmdnextc = '\0';
00867         return name;
00868 }
00869 
00870 
00871 STATIC void
00872 cmdtxt(n)
00873         union node *n;
00874         {
00875         union node *np;
00876         struct nodelist *lp;
00877         char *p;
00878         int i;
00879         char s[2];
00880 
00881         if (n == NULL) return;
00882 
00883         switch (n->type) {
00884         case NSEMI:
00885                 cmdtxt(n->nbinary.ch1);
00886                 cmdputs("; ");
00887                 cmdtxt(n->nbinary.ch2);
00888                 break;
00889         case NAND:
00890                 cmdtxt(n->nbinary.ch1);
00891                 cmdputs(" && ");
00892                 cmdtxt(n->nbinary.ch2);
00893                 break;
00894         case NOR:
00895                 cmdtxt(n->nbinary.ch1);
00896                 cmdputs(" || ");
00897                 cmdtxt(n->nbinary.ch2);
00898                 break;
00899         case NPIPE:
00900                 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
00901                         cmdtxt(lp->n);
00902                         if (lp->next)
00903                                 cmdputs(" | ");
00904                 }
00905                 break;
00906         case NSUBSHELL:
00907                 cmdputs("(");
00908                 cmdtxt(n->nredir.n);
00909                 cmdputs(")");
00910                 break;
00911         case NREDIR:
00912         case NBACKGND:
00913                 cmdtxt(n->nredir.n);
00914                 break;
00915         case NIF:
00916                 cmdputs("if ");
00917                 cmdtxt(n->nif.test);
00918                 cmdputs("; then ");
00919                 cmdtxt(n->nif.ifpart);
00920                 cmdputs("...");
00921                 break;
00922         case NWHILE:
00923                 cmdputs("while ");
00924                 goto until;
00925         case NUNTIL:
00926                 cmdputs("until ");
00927 until:
00928                 cmdtxt(n->nbinary.ch1);
00929                 cmdputs("; do ");
00930                 cmdtxt(n->nbinary.ch2);
00931                 cmdputs("; done");
00932                 break;
00933         case NFOR:
00934                 cmdputs("for ");
00935                 cmdputs(n->nfor.var);
00936                 cmdputs(" in ...");
00937                 break;
00938         case NCASE:
00939                 cmdputs("case ");
00940                 cmdputs(n->ncase.expr->narg.text);
00941                 cmdputs(" in ...");
00942                 break;
00943         case NDEFUN:
00944                 cmdputs(n->narg.text);
00945                 cmdputs("() ...");
00946                 break;
00947         case NCMD:
00948                 for (np = n->ncmd.args ; np ; np = np->narg.next) {
00949                         cmdtxt(np);
00950                         if (np->narg.next)
00951                                 cmdputs(" ");
00952                 }
00953                 for (np = n->ncmd.redirect ; np ; np = np->nfile.next) {
00954                         cmdputs(" ");
00955                         cmdtxt(np);
00956                 }
00957                 break;
00958         case NARG:
00959                 cmdputs(n->narg.text);
00960                 break;
00961         case NTO:
00962                 p = ">";  i = 1;  goto redir;
00963         case NAPPEND:
00964                 p = ">>";  i = 1;  goto redir;
00965         case NTOFD:
00966                 p = ">&";  i = 1;  goto redir;
00967         case NFROM:
00968                 p = "<";  i = 0;  goto redir;
00969         case NFROMFD:
00970                 p = "<&";  i = 0;  goto redir;
00971 redir:
00972                 if (n->nfile.fd != i) {
00973                         s[0] = n->nfile.fd + '0';
00974                         s[1] = '\0';
00975                         cmdputs(s);
00976                 }
00977                 cmdputs(p);
00978                 if (n->type == NTOFD || n->type == NFROMFD) {
00979                         s[0] = n->ndup.dupfd + '0';
00980                         s[1] = '\0';
00981                         cmdputs(s);
00982                 } else {
00983                         cmdtxt(n->nfile.fname);
00984                 }
00985                 break;
00986         case NHERE:
00987         case NXHERE:
00988                 cmdputs("<<...");
00989                 break;
00990         default:
00991                 cmdputs("???");
00992                 break;
00993         }
00994 }
00995 
00996 
00997 
00998 STATIC void
00999 cmdputs(s)
01000         char *s;
01001         {
01002         register char *p, *q;
01003         register char c;
01004         int subtype = 0;
01005 
01006         if (cmdnleft <= 0)
01007                 return;
01008         p = s;
01009         q = cmdnextc;
01010         while ((c = *p++) != '\0') {
01011                 if (c == CTLESC)
01012                         *q++ = *p++;
01013                 else if (c == CTLVAR) {
01014                         *q++ = '$';
01015                         if (--cmdnleft > 0)
01016                                 *q++ = '{';
01017                         subtype = *p++;
01018                 } else if (c == '=' && subtype != 0) {
01019                         *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL];
01020                         subtype = 0;
01021                 } else if (c == CTLENDVAR) {
01022                         *q++ = '}';
01023                 } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE)
01024                         cmdnleft++;             /* ignore it */
01025                 else
01026                         *q++ = c;
01027                 if (--cmdnleft <= 0) {
01028                         *q++ = '.';
01029                         *q++ = '.';
01030                         *q++ = '.';
01031                         break;
01032                 }
01033         }
01034         cmdnextc = q;
01035 }

Generated on Fri Apr 14 22:56:40 2006 for minix by  doxygen 1.4.6