386BSD 0.1 development
[unix-history] / usr / othersrc / public / zsh-2.2 / src / jobs.c
CommitLineData
dbf02a84
WJ
1/*
2 *
3 * jobs.c - job control
4 *
5 * This file is part of zsh, the Z shell.
6 *
7 * This software is Copyright 1992 by Paul Falstad
8 *
9 * Permission is hereby granted to copy, reproduce, redistribute or otherwise
10 * use this software as long as: there is no monetary profit gained
11 * specifically from the use or reproduction of this software, it is not
12 * sold, rented, traded or otherwise marketed, and this copyright notice is
13 * included prominently in any copy made.
14 *
15 * The author make no claims as to the fitness or correctness of this software
16 * for any use whatsoever, and it is provided as is. Any use of this software
17 * is at the user's own risk.
18 *
19 */
20
21#include "zsh.h"
22#include <sys/errno.h>
23
24/* != 0 means the handler is active */
25
26static int handling = 0;
27
28#ifdef INTHANDTYPE
29#define RETURN return 0
30#else
31#define RETURN return
32#endif
33
34/* the signal handler */
35
36HANDTYPE handler(sig,code) /**/
37int sig;int code;
38{
39long pid;
40int statusp;
41Job jn;
42struct process *pn;
43#ifdef HAS_RUSAGE
44struct rusage ru;
45#else
46long chlds,chldu;
47#endif
48
49#ifdef RESETHANDNEEDED
50 signal(sig,handler);
51#endif
52 if (sig == SIGINT)
53 {
54 if (sigtrapped[SIGINT])
55 dotrap(SIGINT);
56 else
57 errflag = 1;
58 RETURN;
59 }
60#ifdef SIGWINCH
61 if (sig == SIGWINCH)
62 adjustwinsize();
63#endif
64 if (sig != SIGCHLD)
65 {
66 dotrap(sig);
67 if (sig == SIGALRM && !sigtrapped[SIGALRM])
68 {
69 zerr("timeout",NULL,0);
70 exit(1);
71 }
72 RETURN;
73 }
74 for (;;)
75 {
76#ifdef HAS_RUSAGE
77 pid = wait3((vptr) &statusp,WNOHANG|WUNTRACED,&ru);
78#else
79#ifndef WNOHANG
80 pid = wait(&statusp);
81#else
82 pid = wait3((vptr) &statusp,WNOHANG|WUNTRACED,NULL);
83#endif
84 chlds = shtms.tms_cstime;
85 chldu = shtms.tms_cutime;
86 times(&shtms);
87#endif
88 if (pid == -1)
89 {
90 if (errno != ECHILD)
91 zerr("wait failed: %e",NULL,errno);
92 RETURN;
93 }
94 if (!pid)
95 RETURN;
96 findproc(pid,&jn,&pn); /* find the process of this pid */
97 if (jn)
98 {
99 pn->statusp = statusp;
100 handling = 1;
101#ifdef HAS_RUSAGE
102 pn->ti.ru = ru;
103#else
104 pn->ti.st = shtms.tms_cstime-chlds;
105 pn->ti.ut = shtms.tms_cutime-chldu;
106#endif
107 pn->endtime = time(NULL);
108 updatestatus(jn);
109 handling = 0;
110 }
111#if 0
112 else if (WIFSTOPPED(statusp))
113 kill(pid,SIGKILL); /* kill stopped untraced children */
114#endif
115 }
116}
117
118/* change job table entry from stopped to running */
119
120void makerunning(jn) /**/
121Job jn;
122{
123struct process *pn;
124
125 jn->stat &= ~STAT_STOPPED;
126 for (pn = jn->procs; pn; pn = pn->next)
127 if (WIFSTOPPED(pn->statusp))
128 pn->statusp = SP_RUNNING;
129}
130
131/* update status of job, possibly printing it */
132
133void updatestatus(jn) /**/
134Job jn;
135{
136struct process *pn;
137int notrunning = 1,alldone = 1,val,job = jn-jobtab,somestopped = 0;
138
139 for (pn = jn->procs; pn; pn = pn->next)
140 {
141 if (pn->statusp == SP_RUNNING)
142 notrunning = 0;
143 if (pn->statusp == SP_RUNNING || WIFSTOPPED(pn->statusp))
144 alldone = 0;
145 if (WIFSTOPPED(pn->statusp))
146 somestopped = 1;
147 if (!pn->next && jn)
148 val = (WIFSIGNALED(pn->statusp)) ?
149 0200 | WTERMSIG(pn->statusp) : WEXITSTATUS(pn->statusp);
150 }
151 if (!notrunning)
152 return;
153 if (somestopped && (jn->stat & STAT_STOPPED))
154 return;
155 jn->stat |= (alldone) ? STAT_CHANGED|STAT_DONE :
156 STAT_CHANGED|STAT_STOPPED;
157 if (alldone && job == thisjob)
158 {
159 if (!ttyfrozen && !val) {
160 gettyinfo(&shttyinfo);
161 if (interact && isset(SHINSTDIN) && SHTTY != -1 && isset(USEZLE))
162 sanetty(&shttyinfo);
163#ifdef TIOCSWINSZ
164 if (!(columns = shttyinfo.winsize.ws_col))
165 columns = 80;
166 lines = shttyinfo.winsize.ws_row;
167#endif
168 } else
169 settyinfo(&shttyinfo);
170 lastval = val;
171 }
172 if ((jn->stat & (STAT_DONE|STAT_STOPPED)) == STAT_STOPPED) {
173 prevjob = curjob;
174 curjob = job;
175 }
176 if ((isset(NOTIFY) || job == thisjob) && jn->stat & STAT_LOCKED) {
177 printjob(jn,!!isset(LONGLISTJOBS));
178 if (zleactive) refresh();
179 }
180 if (sigtrapped[SIGCHLD] && job != thisjob)
181 dotrap(SIGCHLD);
182}
183
184/* find process and job associated with pid */
185
186void findproc(pid,jptr,pptr) /**/
187int pid;Job *jptr;struct process **pptr;
188{
189struct process *pn;
190int jn;
191
192 for (jn = 1; jn != MAXJOB; jn++)
193 for (pn = jobtab[jn].procs; pn; pn = pn->next)
194 if (pn->pid == pid)
195 {
196 *pptr = pn;
197 *jptr = jobtab+jn;
198 return;
199 }
200 *pptr = NULL;
201 *jptr = NULL;
202}
203
204/*
205 lng = 0 means jobs
206 lng = 1 means jobs -l
207 lng = 2 means jobs -p
208*/
209
210void printjob(jn,lng) /**/
211Job jn;int lng;
212{
213int job = jn-jobtab,len = 9,sig = -1,sflag = 0,llen,printed = 0;
214int conted = 0,lineleng = getlineleng(),skip = 0,doputnl = 0;
215struct process *pn;
216
217 if (lng < 0)
218 {
219 conted = 1;
220 lng = 0;
221 }
222
223 /* find length of longest signame, check to see if we
224 really need to print this job */
225
226 for (pn = jn->procs; pn; pn = pn->next)
227 {
228 if (pn->statusp != SP_RUNNING)
229 if (WIFSIGNALED(pn->statusp))
230 {
231 sig = WTERMSIG(pn->statusp);
232 llen = strlen(sigmsg[sig]);
233 if (WCOREDUMPED(pn->statusp))
234 llen += 14;
235 if (llen > len)
236 len = llen;
237 if (sig != SIGINT && sig != SIGPIPE)
238 sflag = 1;
239 else if (sig == SIGINT)
240 errflag = 1;
241 if (job == thisjob && sig == SIGINT)
242 doputnl = 1;
243 }
244 else if (WIFSTOPPED(pn->statusp))
245 {
246 sig = WSTOPSIG(pn->statusp);
247 if (strlen(sigmsg[sig]) > len)
248 len = strlen(sigmsg[sig]);
249 if (job == thisjob && sig == SIGTSTP)
250 doputnl = 1;
251 }
252 else if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) &&
253 WEXITSTATUS(pn->statusp))
254 sflag = 1;
255 }
256
257 /* print if necessary */
258
259 if (interact && jobbing && ((jn->stat & STAT_STOPPED) || sflag ||
260 job != thisjob))
261 {
262 int len2,fline = 1;
263 struct process *qn;
264
265 trashzle();
266 if (doputnl)
267 putc('\n',stderr);
268 for (pn = jn->procs; pn;)
269 {
270 len2 = ((job == thisjob) ? 5 : 10)+len; /* 2 spaces */
271 if (lng)
272 qn = pn->next;
273 else for (qn = pn->next; qn; qn = qn->next)
274 {
275 if (qn->statusp != pn->statusp)
276 break;
277 if (strlen(qn->text)+len2+((qn->next) ? 3 : 0) > lineleng)
278 break;
279 len2 += strlen(qn->text)+2;
280 }
281 if (job != thisjob)
282 if (fline)
283 fprintf(stderr,"[%d] %c ",jn-jobtab,(job == curjob) ? '+' :
284 (job == prevjob) ? '-' : ' ');
285 else
286 fprintf(stderr,(job > 9) ? " " : " ");
287 else
288 fprintf(stderr,"zsh: ");
289 if (lng)
290 if (lng == 1)
291 fprintf(stderr,"%d ",pn->pid);
292 else
293 {
294 int x = jn->gleader;
295
296 fprintf(stderr,"%d ",x);
297 do skip++; while (x /= 10);
298 skip++;
299 lng = 0;
300 }
301 else
302 fprintf(stderr,"%*s",skip,"");
303 if (pn->statusp == SP_RUNNING)
304 if (!conted)
305 fprintf(stderr,"running%*s",len-7+2,"");
306 else
307 fprintf(stderr,"continued%*s",len-9+2,"");
308 else if (WIFEXITED(pn->statusp))
309 if (WEXITSTATUS(pn->statusp))
310 fprintf(stderr,"exit %-4d%*s",WEXITSTATUS(pn->statusp),
311 len-9+2,"");
312 else
313 fprintf(stderr,"done%*s",len-4+2,"");
314 else if (WIFSTOPPED(pn->statusp))
315 fprintf(stderr,"%-*s",len+2,sigmsg[WSTOPSIG(pn->statusp)]);
316 else if (WCOREDUMPED(pn->statusp))
317 fprintf(stderr,"%s (core dumped)%*s",
318 sigmsg[WTERMSIG(pn->statusp)],
319 len-14+2-strlen(sigmsg[WTERMSIG(pn->statusp)]),"");
320 else
321 fprintf(stderr,"%-*s",len+2,sigmsg[WTERMSIG(pn->statusp)]);
322 for (; pn != qn; pn = pn->next)
323 fprintf(stderr,(pn->next) ? "%s | " : "%s",pn->text);
324 putc('\n',stderr);
325 fline = 0;
326 }
327 printed = 1;
328 }
329 else if (doputnl && interact)
330 putc('\n',stderr);
331 fflush(stderr);
332
333 /* print "(pwd now: foo)" messages */
334
335 if (interact && job==thisjob && strcmp(jn->pwd,pwd))
336 {
337 printf("(pwd now: ");
338 printdir(pwd);
339 printf(")\n");
340 fflush(stdout);
341 }
342
343 /* delete job if done */
344
345 if (jn->stat & STAT_DONE)
346 {
347 static struct job zero;
348 struct process *nx;
349 char *s;
350
351 if ((jn->stat & STAT_TIMED) || (reporttime != -1 && report(jn))) {
352 dumptime(jn);
353 printed = 1;
354 }
355 for (pn = jn->procs; pn; pn = nx)
356 {
357 nx = pn->next;
358 free(pn);
359 }
360 free(jn->pwd);
361 if (jn->filelist)
362 {
363 while (s = getnode(jn->filelist))
364 {
365 unlink(s);
366 free(s);
367 }
368 free(jn->filelist);
369 }
370 *jn = zero;
371 if (job == curjob)
372 {
373 curjob = prevjob;
374 prevjob = job;
375 }
376 if (job == prevjob)
377 setprevjob();
378 }
379 else
380 jn->stat &= ~STAT_CHANGED;
381}
382
383/* set the previous job to something reasonable */
384
385void setprevjob() /**/
386{
387int t0;
388
389 for (t0 = MAXJOB-1; t0; t0--)
390 if ((jobtab[t0].stat & STAT_INUSE) && (jobtab[t0].stat & STAT_STOPPED) &&
391 t0 != curjob && t0 != thisjob)
392 break;
393 if (!t0)
394 for (t0 = MAXJOB-1; t0; t0--)
395 if ((jobtab[t0].stat & STAT_INUSE) && t0 != curjob && t0 != thisjob)
396 break;
397 prevjob = (t0) ? t0 : -1;
398}
399
400/* initialize a job table entry */
401
402void initjob() /**/
403{
404 jobtab[thisjob].pwd = ztrdup(pwd);
405 jobtab[thisjob].stat = STAT_INUSE;
406 jobtab[thisjob].gleader = 0;
407}
408
409/* add a process to the current job */
410
411struct process *addproc(pid,text) /**/
412long pid;char *text;
413{
414struct process *process;
415
416 if (!jobtab[thisjob].gleader) jobtab[thisjob].gleader = pid;
417 process = zcalloc(sizeof *process);
418 process->pid = pid;
419 if (text) strcpy(process->text,text);
420 else *process->text = '\0';
421 process->next = NULL;
422 process->statusp = SP_RUNNING;
423 process->bgtime = time(NULL);
424 if (jobtab[thisjob].procs) {
425 struct process *n;
426 for (n = jobtab[thisjob].procs; n->next; n = n->next);
427 process->next = NULL;
428 n->next = process;
429 } else jobtab[thisjob].procs = process;
430 return process;
431}
432
433/* determine if it's all right to exec a command without
434 forking in last component of subshells; it's not ok if we have files
435 to delete */
436
437int execok() /**/
438{
439Job jn;
440
441 if (!exiting)
442 return 0;
443 for (jn = jobtab+1; jn != jobtab+MAXJOB; jn++)
444 if (jn->stat && jn->filelist)
445 return 0;
446 return 1;
447
448}
449
450void waitforpid(pid) /**/
451long pid;
452{
453 while (!errflag && (kill(pid,0) >= 0 || errno != ESRCH)) chldsuspend();
454}
455
456/* wait for a job to finish */
457
458void waitjob(job) /**/
459int job;
460{
461static struct job zero;
462Job jn;
463
464 if (jobtab[job].procs) /* if any forks were done */
465 {
466 jobtab[job].stat |= STAT_LOCKED;
467 if (jobtab[job].stat & STAT_CHANGED)
468 printjob(jobtab+job,!!isset(LONGLISTJOBS));
469 while (jobtab[job].stat &&
470 !(jobtab[job].stat & (STAT_DONE|STAT_STOPPED)))
471 chldsuspend();
472 }
473 else /* else do what printjob() usually does */
474 {
475 char *s;
476
477 jn = jobtab+job;
478 free(jn->pwd);
479 if (jn->filelist)
480 {
481 while (s = getnode(jn->filelist))
482 {
483 unlink(s);
484 free(s);
485 }
486 free(jn->filelist);
487 }
488 *jn = zero;
489 }
490}
491
492/* wait for running job to finish */
493
494void waitjobs() /**/
495{
496 waitjob(thisjob);
497 thisjob = -1;
498}
499
500/* clear job table when entering subshells */
501
502void clearjobtab() /**/
503{
504static struct job zero;
505int t0;
506
507 for (t0 = 1; t0 != MAXJOB; t0++)
508 jobtab[thisjob] = zero;
509}
510
511/* get a free entry in the job table to use */
512
513int getfreejob() /**/
514{
515int t0;
516
517 for (t0 = 1; t0 != MAXJOB; t0++)
518 if (!jobtab[t0].stat) {
519 jobtab[t0].stat |= STAT_INUSE;
520 return t0;
521 }
522 zerr("job table full or recursion limit exceeded",NULL,0);
523 return -1;
524}
525
526/* print pids for & */
527
528void spawnjob() /**/
529{
530struct process *pn;
531
532 if (!subsh)
533 {
534 if (curjob == -1 || !(jobtab[curjob].stat & STAT_STOPPED))
535 {
536 curjob = thisjob;
537 setprevjob();
538 }
539 else if (prevjob == -1 || !(jobtab[prevjob].stat & STAT_STOPPED))
540 prevjob = thisjob;
541 if (interact && jobbing && jobtab[thisjob].procs)
542 {
543 fprintf(stderr,"[%d]",thisjob);
544 for (pn = jobtab[thisjob].procs; pn; pn = pn->next)
545 fprintf(stderr," %d",pn->pid);
546 fprintf(stderr,"\n");
547 fflush(stderr);
548 }
549 }
550 if (!jobtab[thisjob].procs)
551 {
552 char *s;
553 static struct job zero;
554 struct job *jn;
555
556 jn = jobtab+thisjob;
557 free(jn->pwd);
558 if (jn->filelist)
559 {
560 while (s = getnode(jn->filelist))
561 {
562 unlink(s);
563 free(s);
564 }
565 free(jn->filelist);
566 }
567 *jn = zero;
568 }
569 else
570 jobtab[thisjob].stat |= STAT_LOCKED;
571 thisjob = -1;
572}
573
574void fixsigs() /**/
575{
576 unblockchld();
577}
578
579int report(j) /**/
580Job j;
581{
582 if (!j->procs) return 0;
583#ifdef HAS_RUSAGE
584 return (j->procs->ti.ru.ru_utime.tv_sec+j->procs->ti.ru.ru_stime.tv_sec)
585 >= reporttime;
586#else
587 return (j->procs->ti.ut+j->procs->ti.st)/HZ >= reporttime;
588#endif
589}
590
591void printtime(real,ti,desc) /**/
592time_t real;struct timeinfo *ti;char *desc;
593{
594char *s;
595#ifdef sun
596long ticks = 1;
597int pk = getpagesize()/1024;
598#else
599long sec;
600#endif
601#ifdef HAS_RUSAGE
602struct rusage *ru = &ti->ru;
603#endif
604
605 if (!desc) desc = "";
606#ifdef HAS_RUSAGE
607#ifdef sun
608 ticks = (ru->ru_utime.tv_sec+ru->ru_stime.tv_sec)*HZ+
609 (ru->ru_utime.tv_usec+ru->ru_stime.tv_usec)*HZ/1000000;
610 if (!ticks) ticks = 1;
611#else
612 sec = ru->ru_utime.tv_sec + ru->ru_stime.tv_sec;
613 if (!sec) sec = 1;
614#endif
615#endif
616 for (s = timefmt; *s; s++)
617 if (*s == '%')
618 switch(s++,*s)
619 {
620 case 'E': fprintf(stderr,"%lds",real); break;
621#ifndef HAS_RUSAGE
622 case 'U': fprintf(stderr,"%ld.%03lds",
623 ti->ut/HZ,ti->ut*1000/60%1000); break;
624 case 'S': fprintf(stderr,"%ld.%03lds",
625 ti->st/HZ,ti->st*1000/60%1000); break;
626 case 'P':
627 if (real)
628 fprintf(stderr,"%d%%",
629 (int) (100*((ti->ut+ti->st)/HZ))/real);
630 break;
631#else
632 case 'U': fprintf(stderr,"%ld.%03lds",
633 ru->ru_utime.tv_sec,ru->ru_utime.tv_usec/1000); break;
634 case 'S': fprintf(stderr,"%ld.%03lds",
635 ru->ru_stime.tv_sec,ru->ru_stime.tv_usec/1000); break;
636 case 'P':
637 if (real)
638 fprintf(stderr,"%d%%",
639 (int) (100*(ru->ru_utime.tv_sec+ru->ru_stime.tv_sec))
640 / real);
641 break;
642 case 'W': fprintf(stderr,"%ld",ru->ru_nswap); break;
643#ifdef sun
644 case 'K': case 'D':
645 fprintf(stderr,"%ld",ru->ru_idrss/ticks*pk); break;
646 case 'M': fprintf(stderr,"%ld",ru->ru_maxrss*pk); break;
647#else
648 case 'X': fprintf(stderr,"%ld",ru->ru_ixrss/sec); break;
649 case 'D': fprintf(stderr,"%ld",
650 (ru->ru_idrss+ru->ru_isrss)/sec); break;
651 case 'K': fprintf(stderr,"%ld",
652 (ru->ru_ixrss+ru->ru_idrss+ru->ru_isrss)/sec); break;
653 case 'M': fprintf(stderr,"%ld",ru->ru_maxrss/1024); break;
654#endif
655 case 'F': fprintf(stderr,"%ld",ru->ru_majflt); break;
656 case 'R': fprintf(stderr,"%ld",ru->ru_minflt); break;
657 case 'I': fprintf(stderr,"%ld",ru->ru_inblock); break;
658 case 'O': fprintf(stderr,"%ld",ru->ru_oublock); break;
659 case 'r': fprintf(stderr,"%ld",ru->ru_msgrcv); break;
660 case 's': fprintf(stderr,"%ld",ru->ru_msgsnd); break;
661 case 'k': fprintf(stderr,"%ld",ru->ru_nsignals); break;
662 case 'w': fprintf(stderr,"%ld",ru->ru_nvcsw); break;
663 case 'c': fprintf(stderr,"%ld",ru->ru_nivcsw); break;
664#endif
665 case 'J': fprintf(stderr,"%s",desc); break;
666 default: fprintf(stderr,"%%%c",*s); break;
667 }
668 else
669 putc(*s,stderr);
670 putc('\n',stderr);
671 fflush(stderr);
672}
673
674void dumptime(jn) /**/
675Job jn;
676{
677struct process *pn = jn->procs;
678
679 if (!jn->procs)
680 return;
681 for (pn = jn->procs; pn; pn = pn->next)
682 printtime(pn->endtime-pn->bgtime,&pn->ti,pn->text);
683}
684
685void shelltime() /**/
686{
687struct timeinfo ti;
688#ifdef HAS_RUSAGE
689struct rusage ru;
690
691 getrusage(RUSAGE_SELF,&ru);
692 memcpy(&ti.ru,&ru,sizeof(ru));
693 printtime(time(NULL)-shtimer,&ti,"shell");
694
695 getrusage(RUSAGE_CHILDREN,&ru);
696 memcpy(&ti.ru,&ru,sizeof(ru));
697 printtime(time(NULL)-shtimer,&ti,"children");
698#else
699struct tms buf;
700
701 times(&buf);
702 ti.ut = buf.tms_utime;
703 ti.st = buf.tms_stime;
704 printtime(time(NULL)-shtimer,&ti,"shell");
705 ti.ut = buf.tms_cutime;
706 ti.st = buf.tms_cstime;
707 printtime(time(NULL)-shtimer,&ti,"children");
708#endif
709}
710
711/* SIGHUP any jobs left running */
712
713void killrunjobs() /**/
714{
715int t0,killed = 0;
716
717 if (isset(NOHUP)) return;
718 for (t0 = 1; t0 != MAXJOB; t0++)
719 if (t0 != thisjob && (jobtab[t0].stat & STAT_LOCKED) &&
720 !(jobtab[t0].stat & STAT_STOPPED)) {
721 if (killpg(jobtab[t0].gleader,SIGHUP) != -1) killed++;
722 }
723 if (killed) zerr("warning: %d jobs SIGHUPed",NULL,killed);
724}
725
726/* check to see if user has jobs running/stopped */
727
728void checkjobs() /**/
729{
730int t0;
731
732 scanjobs();
733 for (t0 = 1; t0 != MAXJOB; t0++)
734 if (t0 != thisjob && jobtab[t0].stat & STAT_LOCKED)
735 break;
736 if (t0 != MAXJOB) {
737 if (jobtab[t0].stat & STAT_STOPPED) {
738#ifdef USE_SUSPENDED
739 zerr("you have suspended jobs.",NULL,0);
740#else
741 zerr("you have stopped jobs.",NULL,0);
742#endif
743 } else
744 zerr("you have running jobs.",NULL,0);
745 stopmsg = 1;
746 }
747}
748
749/* send a signal to a job (simply involves kill if monitoring is on) */
750
751int killjb(jn,sig) /**/
752Job jn;int sig;
753{
754struct process *pn;
755int err;
756
757 if (jobbing)
758 return(killpg(jn->gleader,sig));
759 for (pn = jn->procs; pn; pn = pn->next)
760 if ((err = kill(pn->pid,sig)) == -1 && errno != ESRCH)
761 return -1;
762 return err;
763}
764