|
1 // jobs.c - job control |
|
2 // |
|
3 // © Portions Copyright (c) Symbian Software Ltd 2007. All rights reserved. |
|
4 // |
|
5 /* |
|
6 * This file is part of zsh, the Z shell. |
|
7 * |
|
8 * Copyright (c) 1992-1997 Paul Falstad |
|
9 * All rights reserved. |
|
10 * |
|
11 * Permission is hereby granted, without written agreement and without |
|
12 * license or royalty fees, to use, copy, modify, and distribute this |
|
13 * software and to distribute modified versions of this software for any |
|
14 * purpose, provided that the above copyright notice and the following |
|
15 * two paragraphs appear in all copies of this software. |
|
16 * |
|
17 * In no event shall Paul Falstad or the Zsh Development Group be liable |
|
18 * to any party for direct, indirect, special, incidental, or consequential |
|
19 * damages arising out of the use of this software and its documentation, |
|
20 * even if Paul Falstad and the Zsh Development Group have been advised of |
|
21 * the possibility of such damage. |
|
22 * |
|
23 * Paul Falstad and the Zsh Development Group specifically disclaim any |
|
24 * warranties, including, but not limited to, the implied warranties of |
|
25 * merchantability and fitness for a particular purpose. The software |
|
26 * provided hereunder is on an "as is" basis, and Paul Falstad and the |
|
27 * Zsh Development Group have no obligation to provide maintenance, |
|
28 * support, updates, enhancements, or modifications. |
|
29 * |
|
30 */ |
|
31 #include "zsh.mdh" |
|
32 #include "jobs.pro" |
|
33 |
|
34 #ifdef __SYMBIAN32__ |
|
35 #ifdef __WINSCW__ |
|
36 #pragma warn_unusedarg off |
|
37 #pragma warn_possunwant off |
|
38 #endif//__WINSCW__ |
|
39 #endif//__SYMBIAN32__ |
|
40 |
|
41 #ifdef __SYMBIAN32__ |
|
42 #include "dummy.h" |
|
43 #endif //__SYMBIAN32__ |
|
44 |
|
45 /* the process group of the shell at startup (equal to mypgprp, except |
|
46 when we started without being process group leader */ |
|
47 |
|
48 /**/ |
|
49 mod_export pid_t origpgrp; |
|
50 |
|
51 /* the process group of the shell */ |
|
52 |
|
53 /**/ |
|
54 mod_export pid_t mypgrp; |
|
55 |
|
56 /* the job we are working on */ |
|
57 |
|
58 /**/ |
|
59 mod_export int thisjob; |
|
60 |
|
61 /* the current job (+) */ |
|
62 |
|
63 /**/ |
|
64 mod_export int curjob; |
|
65 |
|
66 /* the previous job (-) */ |
|
67 |
|
68 /**/ |
|
69 mod_export int prevjob; |
|
70 |
|
71 /* the job table */ |
|
72 |
|
73 /**/ |
|
74 mod_export struct job *jobtab; |
|
75 |
|
76 /* Size of the job table. */ |
|
77 |
|
78 /**/ |
|
79 mod_export int jobtabsize; |
|
80 |
|
81 /* The highest numbered job in the jobtable */ |
|
82 |
|
83 /**/ |
|
84 mod_export int maxjob; |
|
85 |
|
86 /* If we have entered a subshell, the original shell's job table. */ |
|
87 static struct job *oldjobtab; |
|
88 |
|
89 /* The size of that. */ |
|
90 static int oldmaxjob; |
|
91 |
|
92 /* shell timings */ |
|
93 |
|
94 /**/ |
|
95 #ifdef HAVE_GETRUSAGE |
|
96 /**/ |
|
97 static struct rusage child_usage; |
|
98 /**/ |
|
99 #else |
|
100 /**/ |
|
101 static struct tms shtms; |
|
102 /**/ |
|
103 #endif |
|
104 |
|
105 /* 1 if ttyctl -f has been executed */ |
|
106 |
|
107 /**/ |
|
108 int ttyfrozen; |
|
109 |
|
110 /* Previous values of errflag and breaks if the signal handler had to |
|
111 * change them. And a flag saying if it did that. */ |
|
112 |
|
113 /**/ |
|
114 int prev_errflag, prev_breaks, errbrk_saved; |
|
115 |
|
116 /**/ |
|
117 int numpipestats, pipestats[MAX_PIPESTATS]; |
|
118 |
|
119 /* Diff two timevals for elapsed-time computations */ |
|
120 |
|
121 /**/ |
|
122 static struct timeval * |
|
123 dtime(struct timeval *dt, struct timeval *t1, struct timeval *t2) |
|
124 { |
|
125 dt->tv_sec = t2->tv_sec - t1->tv_sec; |
|
126 dt->tv_usec = t2->tv_usec - t1->tv_usec; |
|
127 if (dt->tv_usec < 0) { |
|
128 dt->tv_usec += 1000000.0; |
|
129 dt->tv_sec -= 1.0; |
|
130 } |
|
131 return dt; |
|
132 } |
|
133 |
|
134 /* change job table entry from stopped to running */ |
|
135 |
|
136 /**/ |
|
137 void |
|
138 makerunning(Job jn) |
|
139 { |
|
140 Process pn; |
|
141 |
|
142 jn->stat &= ~STAT_STOPPED; |
|
143 for (pn = jn->procs; pn; pn = pn->next) |
|
144 #if 0 |
|
145 if (WIFSTOPPED(pn->status) && |
|
146 (!(jn->stat & STAT_SUPERJOB) || pn->next)) |
|
147 pn->status = SP_RUNNING; |
|
148 #endif |
|
149 if (WIFSTOPPED(pn->status)) |
|
150 pn->status = SP_RUNNING; |
|
151 |
|
152 if (jn->stat & STAT_SUPERJOB) |
|
153 makerunning(jobtab + jn->other); |
|
154 } |
|
155 |
|
156 /* Find process and job associated with pid. * |
|
157 * Return 1 if search was successful, else return 0. */ |
|
158 |
|
159 /**/ |
|
160 int |
|
161 findproc(pid_t pid, Job *jptr, Process *pptr, int aux) |
|
162 { |
|
163 Process pn; |
|
164 int i; |
|
165 |
|
166 for (i = 1; i <= maxjob; i++) |
|
167 { |
|
168 for (pn = aux ? jobtab[i].auxprocs : jobtab[i].procs; |
|
169 pn; pn = pn->next) |
|
170 if (pn->pid == pid) { |
|
171 *pptr = pn; |
|
172 *jptr = jobtab + i; |
|
173 return 1; |
|
174 } |
|
175 } |
|
176 |
|
177 return 0; |
|
178 } |
|
179 |
|
180 /* Does the given job number have any processes? */ |
|
181 |
|
182 /**/ |
|
183 int |
|
184 hasprocs(int job) |
|
185 { |
|
186 Job jn = jobtab + job; |
|
187 |
|
188 return jn->procs || jn->auxprocs; |
|
189 } |
|
190 |
|
191 /* Find the super-job of a sub-job. */ |
|
192 |
|
193 /**/ |
|
194 static int |
|
195 super_job(int sub) |
|
196 { |
|
197 int i; |
|
198 |
|
199 for (i = 1; i <= maxjob; i++) |
|
200 if ((jobtab[i].stat & STAT_SUPERJOB) && |
|
201 jobtab[i].other == sub && |
|
202 jobtab[i].gleader) |
|
203 return i; |
|
204 return 0; |
|
205 } |
|
206 |
|
207 /**/ |
|
208 static int |
|
209 handle_sub(int job, int fg) |
|
210 { |
|
211 Job jn = jobtab + job, sj = jobtab + jn->other; |
|
212 |
|
213 if ((sj->stat & STAT_DONE) || (!sj->procs && !sj->auxprocs)) { |
|
214 struct process *p; |
|
215 |
|
216 for (p = sj->procs; p; p = p->next) |
|
217 if (WIFSIGNALED(p->status)) { |
|
218 if (jn->gleader != mypgrp && jn->procs->next) |
|
219 killpg(jn->gleader, WTERMSIG(p->status)); |
|
220 else |
|
221 kill(jn->procs->pid, WTERMSIG(p->status)); |
|
222 kill(sj->other, SIGCONT); |
|
223 kill(sj->other, WTERMSIG(p->status)); |
|
224 break; |
|
225 } |
|
226 if (!p) { |
|
227 int cp; |
|
228 |
|
229 jn->stat &= ~STAT_SUPERJOB; |
|
230 jn->stat |= STAT_WASSUPER; |
|
231 |
|
232 if ((cp = ((WIFEXITED(jn->procs->status) || |
|
233 WIFSIGNALED(jn->procs->status)) && |
|
234 killpg(jn->gleader, 0) == -1))) { |
|
235 Process p; |
|
236 for (p = jn->procs; p->next; p = p->next); |
|
237 jn->gleader = p->pid; |
|
238 } |
|
239 /* This deleted the job too early if the parent |
|
240 shell waited for a command in a list that will |
|
241 be executed by the sub-shell (e.g.: if we have |
|
242 `ls|if true;then sleep 20;cat;fi' and ^Z the |
|
243 sleep, the rest will be executed by a sub-shell, |
|
244 but the parent shell gets notified for the |
|
245 sleep. |
|
246 deletejob(sj); */ |
|
247 /* If this super-job contains only the sub-shell, |
|
248 we have to attach the tty to its process group |
|
249 now. */ |
|
250 if ((fg || thisjob == job) && |
|
251 (!jn->procs->next || cp || jn->procs->pid != jn->gleader)) |
|
252 attachtty(jn->gleader); |
|
253 kill(sj->other, SIGCONT); |
|
254 } |
|
255 curjob = jn - jobtab; |
|
256 } else if (sj->stat & STAT_STOPPED) { |
|
257 struct process *p; |
|
258 |
|
259 jn->stat |= STAT_STOPPED; |
|
260 for (p = jn->procs; p; p = p->next) |
|
261 if (p->status == SP_RUNNING || |
|
262 (!WIFEXITED(p->status) && !WIFSIGNALED(p->status))) |
|
263 p->status = sj->procs->status; |
|
264 curjob = jn - jobtab; |
|
265 printjob(jn, !!isset(LONGLISTJOBS), 1); |
|
266 return 1; |
|
267 } |
|
268 return 0; |
|
269 } |
|
270 |
|
271 |
|
272 /* Get the latest usage information */ |
|
273 |
|
274 /**/ |
|
275 void |
|
276 get_usage(void) |
|
277 { |
|
278 #ifdef HAVE_GETRUSAGE |
|
279 getrusage(RUSAGE_CHILDREN, &child_usage); |
|
280 #else |
|
281 times(&shtms); |
|
282 #endif |
|
283 } |
|
284 |
|
285 |
|
286 #ifndef HAVE_GETRUSAGE |
|
287 /* Update status of process that we have just WAIT'ed for */ |
|
288 |
|
289 /**/ |
|
290 void |
|
291 update_process(Process pn, int status) |
|
292 { |
|
293 struct timezone dummy_tz; |
|
294 long childs, childu; |
|
295 |
|
296 childs = shtms.tms_cstime; |
|
297 childu = shtms.tms_cutime; |
|
298 /* get time-accounting info */ |
|
299 get_usage(); |
|
300 gettimeofday(&pn->endtime, &dummy_tz); /* record time process exited */ |
|
301 |
|
302 pn->status = status; /* save the status returned by WAIT */ |
|
303 pn->ti.st = shtms.tms_cstime - childs; /* compute process system space time */ |
|
304 pn->ti.ut = shtms.tms_cutime - childu; /* compute process user space time */ |
|
305 } |
|
306 #endif |
|
307 |
|
308 /* Update status of job, possibly printing it */ |
|
309 |
|
310 /**/ |
|
311 void |
|
312 update_job(Job jn) |
|
313 { |
|
314 Process pn; |
|
315 int job; |
|
316 int val = 0, status = 0; |
|
317 int somestopped = 0, inforeground = 0; |
|
318 |
|
319 for (pn = jn->auxprocs; pn; pn = pn->next) |
|
320 if (pn->status == SP_RUNNING) |
|
321 return; |
|
322 |
|
323 for (pn = jn->procs; pn; pn = pn->next) { |
|
324 if (pn->status == SP_RUNNING) /* some processes in this job are running */ |
|
325 return; /* so no need to update job table entry */ |
|
326 if (WIFSTOPPED(pn->status)) /* some processes are stopped */ |
|
327 somestopped = 1; /* so job is not done, but entry needs updating */ |
|
328 if (!pn->next) /* last job in pipeline determines exit status */ |
|
329 val = (WIFSIGNALED(pn->status)) ? 0200 | WTERMSIG(pn->status) : |
|
330 WEXITSTATUS(pn->status); |
|
331 if (pn->pid == jn->gleader) /* if this process is process group leader */ |
|
332 status = pn->status; |
|
333 } |
|
334 |
|
335 job = jn - jobtab; /* compute job number */ |
|
336 |
|
337 if (somestopped) { |
|
338 if (jn->stty_in_env && !jn->ty) { |
|
339 jn->ty = (struct ttyinfo *) zalloc(sizeof(struct ttyinfo)); |
|
340 gettyinfo(jn->ty); |
|
341 } |
|
342 if (jn->stat & STAT_STOPPED) { |
|
343 if (jn->stat & STAT_SUBJOB) { |
|
344 /* If we have `cat foo|while read a; grep $a bar;done' |
|
345 * and have hit ^Z, the sub-job is stopped, but the |
|
346 * super-job may still be running, waiting to be stopped |
|
347 * or to exit. So we have to send it a SIGTSTP. */ |
|
348 int i; |
|
349 |
|
350 if ((i = super_job(job))) |
|
351 killpg(jobtab[i].gleader, SIGTSTP); |
|
352 } |
|
353 return; |
|
354 } |
|
355 } |
|
356 { /* job is done or stopped, remember return value */ |
|
357 lastval2 = val; |
|
358 /* If last process was run in the current shell, keep old status |
|
359 * and let it handle its own traps, but always allow the test |
|
360 * for the pgrp. |
|
361 */ |
|
362 if (jn->stat & STAT_CURSH) |
|
363 inforeground = 1; |
|
364 else if (job == thisjob) { |
|
365 lastval = val; |
|
366 inforeground = 2; |
|
367 } |
|
368 } |
|
369 |
|
370 if (shout && shout != stderr && !ttyfrozen && !jn->stty_in_env && |
|
371 !zleactive && job == thisjob && !somestopped && |
|
372 !(jn->stat & STAT_NOSTTY)) |
|
373 gettyinfo(&shttyinfo); |
|
374 |
|
375 if (isset(MONITOR)) { |
|
376 pid_t pgrp = gettygrp(); /* get process group of tty */ |
|
377 |
|
378 /* is this job in the foreground of an interactive shell? */ |
|
379 if (mypgrp != pgrp && inforeground && |
|
380 (jn->gleader == pgrp || (pgrp > 1 && kill(-pgrp, 0) == -1))) { |
|
381 if (list_pipe) { |
|
382 if (somestopped || (pgrp > 1 && kill(-pgrp, 0) == -1)) { |
|
383 attachtty(mypgrp); |
|
384 /* check window size and adjust if necessary */ |
|
385 adjustwinsize(0); |
|
386 } else { |
|
387 /* |
|
388 * Oh, dear, we're right in the middle of some confusion |
|
389 * of shell jobs on the righthand side of a pipeline, so |
|
390 * it's death to call attachtty() just yet. Mark the |
|
391 * fact in the job, so that the attachtty() will be called |
|
392 * when the job is finally deleted. |
|
393 */ |
|
394 jn->stat |= STAT_ATTACH; |
|
395 } |
|
396 /* If we have `foo|while true; (( x++ )); done', and hit |
|
397 * ^C, we have to stop the loop, too. */ |
|
398 if ((val & 0200) && inforeground == 1) { |
|
399 if (!errbrk_saved) { |
|
400 errbrk_saved = 1; |
|
401 prev_breaks = breaks; |
|
402 prev_errflag = errflag; |
|
403 } |
|
404 breaks = loops; |
|
405 errflag = 1; |
|
406 inerrflush(); |
|
407 } |
|
408 } else { |
|
409 attachtty(mypgrp); |
|
410 /* check window size and adjust if necessary */ |
|
411 adjustwinsize(0); |
|
412 } |
|
413 } |
|
414 } else if (list_pipe && (val & 0200) && inforeground == 1) { |
|
415 if (!errbrk_saved) { |
|
416 errbrk_saved = 1; |
|
417 prev_breaks = breaks; |
|
418 prev_errflag = errflag; |
|
419 } |
|
420 breaks = loops; |
|
421 errflag = 1; |
|
422 inerrflush(); |
|
423 } |
|
424 if (somestopped && jn->stat & STAT_SUPERJOB) |
|
425 return; |
|
426 jn->stat |= (somestopped) ? STAT_CHANGED | STAT_STOPPED : |
|
427 STAT_CHANGED | STAT_DONE; |
|
428 if (job == thisjob && (jn->stat & STAT_DONE)) { |
|
429 int i; |
|
430 Process p; |
|
431 |
|
432 for (p = jn->procs, i = 0; p && i < MAX_PIPESTATS; p = p->next, i++) |
|
433 pipestats[i] = ((WIFSIGNALED(p->status)) ? |
|
434 0200 | WTERMSIG(p->status) : |
|
435 WEXITSTATUS(p->status)); |
|
436 if ((jn->stat & STAT_CURSH) && i < MAX_PIPESTATS) |
|
437 pipestats[i++] = lastval; |
|
438 numpipestats = i; |
|
439 } |
|
440 if (!inforeground && |
|
441 (jn->stat & (STAT_SUBJOB | STAT_DONE)) == (STAT_SUBJOB | STAT_DONE)) { |
|
442 int su; |
|
443 |
|
444 if ((su = super_job(jn - jobtab))) |
|
445 handle_sub(su, 0); |
|
446 } |
|
447 if ((jn->stat & (STAT_DONE | STAT_STOPPED)) == STAT_STOPPED) { |
|
448 prevjob = curjob; |
|
449 curjob = job; |
|
450 } |
|
451 if ((isset(NOTIFY) || job == thisjob) && (jn->stat & STAT_LOCKED)) { |
|
452 printjob(jn, !!isset(LONGLISTJOBS), 0); |
|
453 if (zleactive) |
|
454 zrefresh(); |
|
455 } |
|
456 if (sigtrapped[SIGCHLD] && job != thisjob) |
|
457 dotrap(SIGCHLD); |
|
458 |
|
459 /* When MONITOR is set, the foreground process runs in a different * |
|
460 * process group from the shell, so the shell will not receive * |
|
461 * terminal signals, therefore we we pretend that the shell got * |
|
462 * the signal too. */ |
|
463 if (inforeground == 2 && isset(MONITOR) && WIFSIGNALED(status)) { |
|
464 int sig = WTERMSIG(status); |
|
465 |
|
466 if (sig == SIGINT || sig == SIGQUIT) { |
|
467 if (sigtrapped[sig]) { |
|
468 dotrap(sig); |
|
469 /* We keep the errflag as set or not by dotrap. |
|
470 * This is to fulfil the promise to carry on |
|
471 * with the jobs if trap returns zero. |
|
472 * Setting breaks = loops ensures a consistent return |
|
473 * status if inside a loop. Maybe the code in loops |
|
474 * should be changed. |
|
475 */ |
|
476 if (errflag) |
|
477 breaks = loops; |
|
478 } else { |
|
479 breaks = loops; |
|
480 errflag = 1; |
|
481 } |
|
482 } |
|
483 } |
|
484 } |
|
485 |
|
486 /* set the previous job to something reasonable */ |
|
487 |
|
488 /**/ |
|
489 static void |
|
490 setprevjob(void) |
|
491 { |
|
492 int i; |
|
493 |
|
494 for (i = maxjob; i; i--) |
|
495 if ((jobtab[i].stat & STAT_INUSE) && (jobtab[i].stat & STAT_STOPPED) && |
|
496 !(jobtab[i].stat & STAT_SUBJOB) && i != curjob && i != thisjob) { |
|
497 prevjob = i; |
|
498 return; |
|
499 } |
|
500 |
|
501 for (i = maxjob; i; i--) |
|
502 if ((jobtab[i].stat & STAT_INUSE) && !(jobtab[i].stat & STAT_SUBJOB) && |
|
503 i != curjob && i != thisjob) { |
|
504 prevjob = i; |
|
505 return; |
|
506 } |
|
507 |
|
508 prevjob = -1; |
|
509 } |
|
510 |
|
511 /**/ |
|
512 #ifndef HAVE_GETRUSAGE |
|
513 static long clktck = 0; |
|
514 |
|
515 /**/ |
|
516 static void |
|
517 set_clktck(void) |
|
518 { |
|
519 #ifdef _SC_CLK_TCK |
|
520 if (!clktck) |
|
521 /* fetch clock ticks per second from * |
|
522 * sysconf only the first time */ |
|
523 clktck = sysconf(_SC_CLK_TCK); |
|
524 #else |
|
525 # ifdef __NeXT__ |
|
526 /* NeXTStep 3.3 defines CLK_TCK wrongly */ |
|
527 clktck = 60; |
|
528 # else |
|
529 # ifdef CLK_TCK |
|
530 clktck = CLK_TCK; |
|
531 # else |
|
532 # ifdef HZ |
|
533 clktck = HZ; |
|
534 # else |
|
535 clktck = 60; |
|
536 # endif |
|
537 # endif |
|
538 # endif |
|
539 #endif |
|
540 } |
|
541 /**/ |
|
542 #endif |
|
543 |
|
544 /**/ |
|
545 static void |
|
546 printhhmmss(double secs) |
|
547 { |
|
548 int mins = (int) secs / 60; |
|
549 int hours = mins / 60; |
|
550 |
|
551 secs -= 60 * mins; |
|
552 mins -= 60 * hours; |
|
553 if (hours) |
|
554 fprintf(stderr, "%d:%02d:%05.2f", hours, mins, secs); |
|
555 else if (mins) |
|
556 fprintf(stderr, "%d:%05.2f", mins, secs); |
|
557 else |
|
558 fprintf(stderr, "%.3f", secs); |
|
559 } |
|
560 |
|
561 static void |
|
562 printtime(struct timeval *real, child_times_t *ti, char *desc) |
|
563 { |
|
564 char *s; |
|
565 double elapsed_time, user_time, system_time; |
|
566 #ifdef HAVE_GETRUSAGE |
|
567 double total_time; |
|
568 #endif |
|
569 int percent; |
|
570 |
|
571 if (!desc) |
|
572 desc = ""; |
|
573 |
|
574 /* go ahead and compute these, since almost every TIMEFMT will have them */ |
|
575 elapsed_time = real->tv_sec + real->tv_usec / 1000000.0; |
|
576 |
|
577 #ifdef HAVE_GETRUSAGE |
|
578 user_time = ti->ru_utime.tv_sec + ti->ru_utime.tv_usec / 1000000.0; |
|
579 system_time = ti->ru_stime.tv_sec + ti->ru_stime.tv_usec / 1000000.0; |
|
580 total_time = user_time + system_time; |
|
581 percent = 100.0 * total_time |
|
582 / (real->tv_sec + real->tv_usec / 1000000.0); |
|
583 #else |
|
584 set_clktck(); |
|
585 user_time = ti->ut / (double) clktck; |
|
586 system_time = ti->st / (double) clktck; |
|
587 percent = 100.0 * (ti->ut + ti->st) |
|
588 / (clktck * real->tv_sec + clktck * real->tv_usec / 1000000.0); |
|
589 #endif |
|
590 |
|
591 queue_signals(); |
|
592 if (!(s = getsparam("TIMEFMT"))) |
|
593 s = DEFAULT_TIMEFMT; |
|
594 |
|
595 for (; *s; s++) |
|
596 if (*s == '%') |
|
597 switch (*++s) { |
|
598 case 'E': |
|
599 fprintf(stderr, "%4.2fs", elapsed_time); |
|
600 break; |
|
601 case 'U': |
|
602 fprintf(stderr, "%4.2fs", user_time); |
|
603 break; |
|
604 case 'S': |
|
605 fprintf(stderr, "%4.2fs", system_time); |
|
606 break; |
|
607 case '*': |
|
608 switch (*++s) { |
|
609 case 'E': |
|
610 printhhmmss(elapsed_time); |
|
611 break; |
|
612 case 'U': |
|
613 printhhmmss(user_time); |
|
614 break; |
|
615 case 'S': |
|
616 printhhmmss(system_time); |
|
617 break; |
|
618 default: |
|
619 fprintf(stderr, "%%*"); |
|
620 s--; |
|
621 break; |
|
622 } |
|
623 break; |
|
624 case 'P': |
|
625 fprintf(stderr, "%d%%", percent); |
|
626 break; |
|
627 #ifdef HAVE_STRUCT_RUSAGE_RU_NSWAP |
|
628 case 'W': |
|
629 fprintf(stderr, "%ld", ti->ru_nswap); |
|
630 break; |
|
631 #endif |
|
632 #ifdef HAVE_STRUCT_RUSAGE_RU_IXRSS |
|
633 case 'X': |
|
634 fprintf(stderr, "%ld", (long)(ti->ru_ixrss / total_time)); |
|
635 break; |
|
636 #endif |
|
637 #ifdef HAVE_STRUCT_RUSAGE_RU_IDRSS |
|
638 case 'D': |
|
639 fprintf(stderr, "%ld", |
|
640 (long) ((ti->ru_idrss |
|
641 #ifdef HAVE_STRUCT_RUSAGE_RU_ISRSS |
|
642 + ti->ru_isrss |
|
643 #endif |
|
644 ) / total_time)); |
|
645 break; |
|
646 #endif |
|
647 #if defined(HAVE_STRUCT_RUSAGE_RU_IDRSS) || \ |
|
648 defined(HAVE_STRUCT_RUSAGE_RU_ISRSS) || \ |
|
649 defined(HAVE_STRUCT_RUSAGE_RU_IXRSS) |
|
650 case 'K': |
|
651 /* treat as D if X not available */ |
|
652 fprintf(stderr, "%ld", |
|
653 (long) (( |
|
654 #ifdef HAVE_STRUCT_RUSAGE_RU_IXRSS |
|
655 ti->ru_ixrss |
|
656 #else |
|
657 0 |
|
658 #endif |
|
659 #ifdef HAVE_STRUCT_RUSAGE_RU_IDRSS |
|
660 + ti->ru_idrss |
|
661 #endif |
|
662 #ifdef HAVE_STRUCT_RUSAGE_RU_ISRSS |
|
663 + ti->ru_isrss |
|
664 #endif |
|
665 ) / total_time)); |
|
666 break; |
|
667 #endif |
|
668 #ifdef HAVE_STRUCT_RUSAGE_RU_MAXRSS |
|
669 case 'M': |
|
670 fprintf(stderr, "%ld", ti->ru_maxrss / 1024); |
|
671 break; |
|
672 #endif |
|
673 #ifdef HAVE_STRUCT_RUSAGE_RU_MAJFLT |
|
674 case 'F': |
|
675 fprintf(stderr, "%ld", ti->ru_majflt); |
|
676 break; |
|
677 #endif |
|
678 #ifdef HAVE_STRUCT_RUSAGE_RU_MINFLT |
|
679 case 'R': |
|
680 fprintf(stderr, "%ld", ti->ru_minflt); |
|
681 break; |
|
682 #endif |
|
683 #ifdef HAVE_STRUCT_RUSAGE_RU_INBLOCK |
|
684 case 'I': |
|
685 fprintf(stderr, "%ld", ti->ru_inblock); |
|
686 break; |
|
687 #endif |
|
688 #ifdef HAVE_STRUCT_RUSAGE_RU_OUBLOCK |
|
689 case 'O': |
|
690 fprintf(stderr, "%ld", ti->ru_oublock); |
|
691 break; |
|
692 #endif |
|
693 #ifdef HAVE_STRUCT_RUSAGE_RU_MSGRCV |
|
694 case 'r': |
|
695 fprintf(stderr, "%ld", ti->ru_msgrcv); |
|
696 break; |
|
697 #endif |
|
698 #ifdef HAVE_STRUCT_RUSAGE_RU_MSGSND |
|
699 case 's': |
|
700 fprintf(stderr, "%ld", ti->ru_msgsnd); |
|
701 break; |
|
702 #endif |
|
703 #ifdef HAVE_STRUCT_RUSAGE_RU_NSIGNALS |
|
704 case 'k': |
|
705 fprintf(stderr, "%ld", ti->ru_nsignals); |
|
706 break; |
|
707 #endif |
|
708 #ifdef HAVE_STRUCT_RUSAGE_RU_NVCSW |
|
709 case 'w': |
|
710 fprintf(stderr, "%ld", ti->ru_nvcsw); |
|
711 break; |
|
712 #endif |
|
713 #ifdef HAVE_STRUCT_RUSAGE_RU_NIVCSW |
|
714 case 'c': |
|
715 fprintf(stderr, "%ld", ti->ru_nivcsw); |
|
716 break; |
|
717 #endif |
|
718 case 'J': |
|
719 fprintf(stderr, "%s", desc); |
|
720 break; |
|
721 case '%': |
|
722 putc('%', stderr); |
|
723 break; |
|
724 case '\0': |
|
725 s--; |
|
726 break; |
|
727 default: |
|
728 fprintf(stderr, "%%%c", *s); |
|
729 break; |
|
730 } else |
|
731 putc(*s, stderr); |
|
732 unqueue_signals(); |
|
733 putc('\n', stderr); |
|
734 fflush(stderr); |
|
735 } |
|
736 |
|
737 /**/ |
|
738 static void |
|
739 dumptime(Job jn) |
|
740 { |
|
741 Process pn; |
|
742 struct timeval dtimeval; |
|
743 |
|
744 if (!jn->procs) |
|
745 return; |
|
746 for (pn = jn->procs; pn; pn = pn->next) |
|
747 printtime(dtime(&dtimeval, &pn->bgtime, &pn->endtime), &pn->ti, |
|
748 pn->text); |
|
749 } |
|
750 |
|
751 /* Check whether shell should report the amount of time consumed * |
|
752 * by job. This will be the case if we have preceded the command * |
|
753 * with the keyword time, or if REPORTTIME is non-negative and the * |
|
754 * amount of time consumed by the job is greater than REPORTTIME */ |
|
755 |
|
756 /**/ |
|
757 static int |
|
758 should_report_time(Job j) |
|
759 { |
|
760 struct value vbuf; |
|
761 Value v; |
|
762 char *s = "REPORTTIME"; |
|
763 zlong reporttime; |
|
764 |
|
765 /* if the time keyword was used */ |
|
766 if (j->stat & STAT_TIMED) |
|
767 return 1; |
|
768 |
|
769 queue_signals(); |
|
770 if (!(v = getvalue(&vbuf, &s, 0)) || |
|
771 (reporttime = getintvalue(v)) < 0) { |
|
772 unqueue_signals(); |
|
773 return 0; |
|
774 } |
|
775 unqueue_signals(); |
|
776 /* can this ever happen? */ |
|
777 if (!j->procs) |
|
778 return 0; |
|
779 |
|
780 #ifdef HAVE_GETRUSAGE |
|
781 reporttime -= j->procs->ti.ru_utime.tv_sec + j->procs->ti.ru_stime.tv_sec; |
|
782 if (j->procs->ti.ru_utime.tv_usec + |
|
783 j->procs->ti.ru_stime.tv_usec >= 1000000) |
|
784 reporttime--; |
|
785 return reporttime <= 0; |
|
786 #else |
|
787 set_clktck(); |
|
788 return ((j->procs->ti.ut + j->procs->ti.st) / clktck >= reporttime); |
|
789 #endif |
|
790 } |
|
791 |
|
792 /* !(lng & 3) means jobs * |
|
793 * (lng & 1) means jobs -l * |
|
794 * (lng & 2) means jobs -p |
|
795 * (lng & 4) means jobs -d |
|
796 * |
|
797 * synch = 0 means asynchronous |
|
798 * synch = 1 means synchronous |
|
799 * synch = 2 means called synchronously from jobs |
|
800 */ |
|
801 |
|
802 /**/ |
|
803 void |
|
804 printjob(Job jn, int lng, int synch) |
|
805 { |
|
806 Process pn; |
|
807 int job, len = 9, sig, sflag = 0, llen; |
|
808 int conted = 0, lineleng = columns, skip = 0, doputnl = 0; |
|
809 FILE *fout = (synch == 2) ? stdout : shout; |
|
810 |
|
811 if (jn->stat & STAT_NOPRINT) |
|
812 return; |
|
813 |
|
814 /* |
|
815 * Wow, what a hack. Did I really write this? --- pws |
|
816 */ |
|
817 if (jn < jobtab || jn >= jobtab + jobtabsize) |
|
818 job = jn - oldjobtab; |
|
819 else |
|
820 job = jn - jobtab; |
|
821 |
|
822 if (lng < 0) { |
|
823 conted = 1; |
|
824 lng = 0; |
|
825 } |
|
826 |
|
827 /* find length of longest signame, check to see */ |
|
828 /* if we really need to print this job */ |
|
829 |
|
830 for (pn = jn->procs; pn; pn = pn->next) { |
|
831 if (jn->stat & STAT_SUPERJOB && |
|
832 jn->procs->status == SP_RUNNING && !pn->next) |
|
833 pn->status = SP_RUNNING; |
|
834 if (pn->status != SP_RUNNING) { |
|
835 if (WIFSIGNALED(pn->status)) { |
|
836 sig = WTERMSIG(pn->status); |
|
837 llen = strlen(sigmsg(sig)); |
|
838 if (WCOREDUMP(pn->status)) |
|
839 llen += 14; |
|
840 if (llen > len) |
|
841 len = llen; |
|
842 if (sig != SIGINT && sig != SIGPIPE) |
|
843 sflag = 1; |
|
844 if (job == thisjob && sig == SIGINT) |
|
845 doputnl = 1; |
|
846 } else if (WIFSTOPPED(pn->status)) { |
|
847 sig = WSTOPSIG(pn->status); |
|
848 if ((int)strlen(sigmsg(sig)) > len) |
|
849 len = strlen(sigmsg(sig)); |
|
850 if (job == thisjob && sig == SIGTSTP) |
|
851 doputnl = 1; |
|
852 } else if (isset(PRINTEXITVALUE) && isset(SHINSTDIN) && |
|
853 WEXITSTATUS(pn->status)) |
|
854 sflag = 1; |
|
855 } |
|
856 } |
|
857 |
|
858 /* print if necessary: ignore option state on explicit call to `jobs'. */ |
|
859 |
|
860 if (synch == 2 || |
|
861 (interact && jobbing && |
|
862 ((jn->stat & STAT_STOPPED) || sflag || job != thisjob))) { |
|
863 int len2, fline = 1; |
|
864 /* use special format for current job, except in `jobs' */ |
|
865 int thisfmt = job == thisjob && synch != 2; |
|
866 Process qn; |
|
867 |
|
868 if (!synch) |
|
869 trashzle(); |
|
870 if (doputnl && !synch) |
|
871 putc('\n', fout); |
|
872 for (pn = jn->procs; pn;) { |
|
873 len2 = (thisfmt ? 5 : 10) + len; /* 2 spaces */ |
|
874 if (lng & 3) |
|
875 qn = pn->next; |
|
876 else |
|
877 for (qn = pn->next; qn; qn = qn->next) { |
|
878 if (qn->status != pn->status) |
|
879 break; |
|
880 if ((int)strlen(qn->text) + len2 + ((qn->next) ? 3 : 0) > lineleng) |
|
881 break; |
|
882 len2 += strlen(qn->text) + 2; |
|
883 } |
|
884 if (!thisfmt) { |
|
885 if (fline) |
|
886 fprintf(fout, "[%ld] %c ", |
|
887 (long)job, |
|
888 (job == curjob) ? '+' |
|
889 : (job == prevjob) ? '-' : ' '); |
|
890 else |
|
891 fprintf(fout, (job > 9) ? " " : " "); |
|
892 } else |
|
893 fprintf(fout, "zsh: "); |
|
894 if (lng & 1) |
|
895 fprintf(fout, "%ld ", (long) pn->pid); |
|
896 else if (lng & 2) { |
|
897 pid_t x = jn->gleader; |
|
898 |
|
899 fprintf(fout, "%ld ", (long) x); |
|
900 do |
|
901 skip++; |
|
902 while ((x /= 10)); |
|
903 skip++; |
|
904 lng &= ~3; |
|
905 } else |
|
906 fprintf(fout, "%*s", skip, ""); |
|
907 if (pn->status == SP_RUNNING) { |
|
908 if (!conted) |
|
909 fprintf(fout, "running%*s", len - 7 + 2, ""); |
|
910 else |
|
911 fprintf(fout, "continued%*s", len - 9 + 2, ""); |
|
912 } |
|
913 else if (WIFEXITED(pn->status)) { |
|
914 if (WEXITSTATUS(pn->status)) |
|
915 fprintf(fout, "exit %-4d%*s", WEXITSTATUS(pn->status), |
|
916 len - 9 + 2, ""); |
|
917 else |
|
918 fprintf(fout, "done%*s", len - 4 + 2, ""); |
|
919 } else if (WIFSTOPPED(pn->status)) |
|
920 fprintf(fout, "%-*s", len + 2, sigmsg(WSTOPSIG(pn->status))); |
|
921 else if (WCOREDUMP(pn->status)) |
|
922 fprintf(fout, "%s (core dumped)%*s", |
|
923 sigmsg(WTERMSIG(pn->status)), |
|
924 (int)(len - 14 + 2 - strlen(sigmsg(WTERMSIG(pn->status)))), ""); |
|
925 else |
|
926 fprintf(fout, "%-*s", len + 2, sigmsg(WTERMSIG(pn->status))); |
|
927 for (; pn != qn; pn = pn->next) |
|
928 fprintf(fout, (pn->next) ? "%s | " : "%s", pn->text); |
|
929 putc('\n', fout); |
|
930 fline = 0; |
|
931 } |
|
932 fflush(fout); |
|
933 } else if (doputnl && interact && !synch) { |
|
934 putc('\n', fout); |
|
935 fflush(fout); |
|
936 } |
|
937 |
|
938 /* print "(pwd now: foo)" messages: with (lng & 4) we are printing |
|
939 * the directory where the job is running, otherwise the current directory |
|
940 */ |
|
941 |
|
942 if ((lng & 4) || (interact && job == thisjob && |
|
943 jn->pwd && strcmp(jn->pwd, pwd))) { |
|
944 fprintf(shout, "(pwd %s: ", (lng & 4) ? "" : "now"); |
|
945 fprintdir(((lng & 4) && jn->pwd) ? jn->pwd : pwd, shout); |
|
946 fprintf(shout, ")\n"); |
|
947 fflush(shout); |
|
948 } |
|
949 /* delete job if done */ |
|
950 |
|
951 if (jn->stat & STAT_DONE) { |
|
952 if (should_report_time(jn)) |
|
953 dumptime(jn); |
|
954 deletejob(jn); |
|
955 if (job == curjob) { |
|
956 curjob = prevjob; |
|
957 prevjob = job; |
|
958 } |
|
959 if (job == prevjob) |
|
960 setprevjob(); |
|
961 } else |
|
962 jn->stat &= ~STAT_CHANGED; |
|
963 } |
|
964 |
|
965 /**/ |
|
966 void |
|
967 deletefilelist(LinkList file_list) |
|
968 { |
|
969 char *s; |
|
970 if (file_list) { |
|
971 while ((s = (char *)getlinknode(file_list))) { |
|
972 unlink(s); |
|
973 zsfree(s); |
|
974 } |
|
975 zfree(file_list, sizeof(struct linklist)); |
|
976 } |
|
977 } |
|
978 |
|
979 /**/ |
|
980 void |
|
981 freejob(Job jn, int deleting) |
|
982 { |
|
983 struct process *pn, *nx; |
|
984 |
|
985 pn = jn->procs; |
|
986 jn->procs = NULL; |
|
987 for (; pn; pn = nx) { |
|
988 nx = pn->next; |
|
989 zfree(pn, sizeof(struct process)); |
|
990 } |
|
991 |
|
992 pn = jn->auxprocs; |
|
993 jn->auxprocs = NULL; |
|
994 for (; pn; pn = nx) { |
|
995 nx = pn->next; |
|
996 zfree(pn, sizeof(struct process)); |
|
997 } |
|
998 |
|
999 if (jn->ty) |
|
1000 zfree(jn->ty, sizeof(struct ttyinfo)); |
|
1001 if (jn->pwd) |
|
1002 zsfree(jn->pwd); |
|
1003 jn->pwd = NULL; |
|
1004 if (jn->stat & STAT_WASSUPER) { |
|
1005 /* careful in case we shrink and move the job table */ |
|
1006 int job = jn - jobtab; |
|
1007 if (deleting) |
|
1008 deletejob(jobtab + jn->other); |
|
1009 else |
|
1010 freejob(jobtab + jn->other, 0); |
|
1011 jn = jobtab + job; |
|
1012 } |
|
1013 jn->gleader = jn->other = 0; |
|
1014 jn->stat = jn->stty_in_env = 0; |
|
1015 jn->filelist = NULL; |
|
1016 jn->ty = NULL; |
|
1017 |
|
1018 /* Find the new highest job number. */ |
|
1019 if (maxjob == jn - jobtab) { |
|
1020 while (maxjob && !(jobtab[maxjob].stat & STAT_INUSE)) |
|
1021 maxjob--; |
|
1022 } |
|
1023 } |
|
1024 |
|
1025 /* |
|
1026 * We are actually finished with this job, rather |
|
1027 * than freeing it to make space. |
|
1028 */ |
|
1029 |
|
1030 /**/ |
|
1031 void |
|
1032 deletejob(Job jn) |
|
1033 { |
|
1034 deletefilelist(jn->filelist); |
|
1035 if (jn->stat & STAT_ATTACH) { |
|
1036 attachtty(mypgrp); |
|
1037 adjustwinsize(0); |
|
1038 } |
|
1039 |
|
1040 freejob(jn, 1); |
|
1041 } |
|
1042 |
|
1043 /* |
|
1044 * Add a process to the current job. |
|
1045 * The third argument is 1 if we are adding a process which is not |
|
1046 * part of the main pipeline but an auxiliary process used for |
|
1047 * handling MULTIOS or process substitution. We will wait for it |
|
1048 * but not display job information about it. |
|
1049 */ |
|
1050 |
|
1051 /**/ |
|
1052 void |
|
1053 addproc(pid_t pid, char *text, int aux, struct timeval *bgtime) |
|
1054 { |
|
1055 Process pn, *pnlist; |
|
1056 |
|
1057 DPUTS(thisjob == -1, "No valid job in addproc."); |
|
1058 pn = (Process) zshcalloc(sizeof *pn); |
|
1059 pn->pid = pid; |
|
1060 if (text) |
|
1061 strcpy(pn->text, text); |
|
1062 else |
|
1063 *pn->text = '\0'; |
|
1064 pn->status = SP_RUNNING; |
|
1065 pn->next = NULL; |
|
1066 |
|
1067 if (!aux) |
|
1068 { |
|
1069 pn->bgtime = *bgtime; |
|
1070 /* if this is the first process we are adding to * |
|
1071 * the job, then it's the group leader. */ |
|
1072 if (!jobtab[thisjob].gleader) |
|
1073 jobtab[thisjob].gleader = pid; |
|
1074 /* attach this process to end of process list of current job */ |
|
1075 pnlist = &jobtab[thisjob].procs; |
|
1076 } |
|
1077 else |
|
1078 pnlist = &jobtab[thisjob].auxprocs; |
|
1079 |
|
1080 if (*pnlist) { |
|
1081 Process n; |
|
1082 |
|
1083 for (n = *pnlist; n->next; n = n->next); |
|
1084 n->next = pn; |
|
1085 } else { |
|
1086 /* first process for this job */ |
|
1087 *pnlist = pn; |
|
1088 } |
|
1089 /* If the first process in the job finished before any others were * |
|
1090 * added, maybe STAT_DONE got set incorrectly. This can happen if * |
|
1091 * a $(...) was waited for and the last existing job in the * |
|
1092 * pipeline was already finished. We need to be very careful that * |
|
1093 * there was no call to printjob() between then and now, else * |
|
1094 * the job will already have been deleted from the table. */ |
|
1095 jobtab[thisjob].stat &= ~STAT_DONE; |
|
1096 } |
|
1097 |
|
1098 /* Check if we have files to delete. We need to check this to see * |
|
1099 * if it's all right to exec a command without forking in the last * |
|
1100 * component of subshells or after the `-c' option. */ |
|
1101 |
|
1102 /**/ |
|
1103 int |
|
1104 havefiles(void) |
|
1105 { |
|
1106 int i; |
|
1107 |
|
1108 for (i = 1; i <= maxjob; i++) |
|
1109 if (jobtab[i].stat && jobtab[i].filelist) |
|
1110 return 1; |
|
1111 return 0; |
|
1112 |
|
1113 } |
|
1114 |
|
1115 /* wait for a particular process */ |
|
1116 |
|
1117 /**/ |
|
1118 void |
|
1119 waitforpid(pid_t pid) |
|
1120 { |
|
1121 int first = 1, q = queue_signal_level(); |
|
1122 |
|
1123 /* child_block() around this loop in case #ifndef WNOHANG */ |
|
1124 dont_queue_signals(); |
|
1125 child_block(); /* unblocked in signal_suspend() */ |
|
1126 while (!errflag && (kill(pid, 0) >= 0 || errno != ESRCH)) { |
|
1127 if (first) |
|
1128 first = 0; |
|
1129 else |
|
1130 kill(pid, SIGCONT); |
|
1131 |
|
1132 signal_suspend(SIGCHLD, SIGINT); |
|
1133 child_block(); |
|
1134 } |
|
1135 child_unblock(); |
|
1136 restore_queue_signals(q); |
|
1137 } |
|
1138 |
|
1139 /* wait for a job to finish */ |
|
1140 |
|
1141 /**/ |
|
1142 static void |
|
1143 zwaitjob(int job, int sig) |
|
1144 { |
|
1145 int q = queue_signal_level(); |
|
1146 Job jn = jobtab + job; |
|
1147 |
|
1148 dont_queue_signals(); |
|
1149 child_block(); /* unblocked during signal_suspend() */ |
|
1150 if (jn->procs || jn->auxprocs) { /* if any forks were done */ |
|
1151 jn->stat |= STAT_LOCKED; |
|
1152 if (jn->stat & STAT_CHANGED) |
|
1153 printjob(jn, !!isset(LONGLISTJOBS), 1); |
|
1154 while (!errflag && jn->stat && |
|
1155 !(jn->stat & STAT_DONE) && |
|
1156 !(interact && (jn->stat & STAT_STOPPED))) { |
|
1157 signal_suspend(SIGCHLD, sig); |
|
1158 /* Commenting this out makes ^C-ing a job started by a function |
|
1159 stop the whole function again. But I guess it will stop |
|
1160 something else from working properly, we have to find out |
|
1161 what this might be. --oberon |
|
1162 |
|
1163 errflag = 0; */ |
|
1164 if (subsh) { |
|
1165 killjb(jn, SIGCONT); |
|
1166 jn->stat &= ~STAT_STOPPED; |
|
1167 } |
|
1168 if (jn->stat & STAT_SUPERJOB) |
|
1169 if (handle_sub(jn - jobtab, 1)) |
|
1170 break; |
|
1171 child_block(); |
|
1172 } |
|
1173 } else { |
|
1174 deletejob(jn); |
|
1175 pipestats[0] = lastval; |
|
1176 numpipestats = 1; |
|
1177 } |
|
1178 child_unblock(); |
|
1179 restore_queue_signals(q); |
|
1180 } |
|
1181 |
|
1182 /* wait for running job to finish */ |
|
1183 |
|
1184 /**/ |
|
1185 void |
|
1186 waitjobs(void) |
|
1187 { |
|
1188 Job jn = jobtab + thisjob; |
|
1189 DPUTS(thisjob == -1, "No valid job in waitjobs."); |
|
1190 |
|
1191 if (jn->procs || jn->auxprocs) |
|
1192 zwaitjob(thisjob, 0); |
|
1193 else { |
|
1194 deletejob(jn); |
|
1195 pipestats[0] = lastval; |
|
1196 numpipestats = 1; |
|
1197 } |
|
1198 thisjob = -1; |
|
1199 } |
|
1200 |
|
1201 /* clear job table when entering subshells */ |
|
1202 |
|
1203 /**/ |
|
1204 mod_export void |
|
1205 clearjobtab(int monitor) |
|
1206 { |
|
1207 int i; |
|
1208 |
|
1209 for (i = 1; i <= maxjob; i++) { |
|
1210 /* |
|
1211 * See if there is a jobtable worth saving. |
|
1212 * We never free the saved version; it only happens |
|
1213 * once for each subshell of a shell with job control, |
|
1214 * so doesn't create a leak. |
|
1215 */ |
|
1216 if (monitor && jobtab[i].stat) |
|
1217 oldmaxjob = i+1; |
|
1218 else if (jobtab[i].stat & STAT_INUSE) |
|
1219 freejob(jobtab + i, 0); |
|
1220 } |
|
1221 |
|
1222 if (monitor && oldmaxjob) { |
|
1223 int sz = oldmaxjob * sizeof(struct job); |
|
1224 oldjobtab = (struct job *)zalloc(sz); |
|
1225 memcpy(oldjobtab, jobtab, sz); |
|
1226 |
|
1227 /* Don't report any job we're part of */ |
|
1228 if (thisjob != -1 && thisjob < oldmaxjob) |
|
1229 memset(oldjobtab+thisjob, 0, sizeof(struct job)); |
|
1230 } |
|
1231 |
|
1232 memset(jobtab, 0, jobtabsize * sizeof(struct job)); /* zero out table */ |
|
1233 maxjob = 0; |
|
1234 } |
|
1235 |
|
1236 static int initnewjob(int i) |
|
1237 { |
|
1238 jobtab[i].stat = STAT_INUSE; |
|
1239 if (jobtab[i].pwd) { |
|
1240 zsfree(jobtab[i].pwd); |
|
1241 jobtab[i].pwd = NULL; |
|
1242 } |
|
1243 jobtab[i].gleader = 0; |
|
1244 |
|
1245 if (i > maxjob) |
|
1246 maxjob = i; |
|
1247 |
|
1248 return i; |
|
1249 } |
|
1250 |
|
1251 /* Get a free entry in the job table and initialize it. */ |
|
1252 |
|
1253 /**/ |
|
1254 int |
|
1255 initjob(void) |
|
1256 { |
|
1257 int i; |
|
1258 |
|
1259 for (i = 1; i <= maxjob; i++) |
|
1260 if (!jobtab[i].stat) |
|
1261 return initnewjob(i); |
|
1262 if (maxjob + 1 < jobtabsize) |
|
1263 return initnewjob(maxjob+1); |
|
1264 |
|
1265 if (expandjobtab()) |
|
1266 return initnewjob(i); |
|
1267 |
|
1268 zerr("job table full or recursion limit exceeded", NULL, 0); |
|
1269 return -1; |
|
1270 } |
|
1271 |
|
1272 /**/ |
|
1273 void |
|
1274 setjobpwd(void) |
|
1275 { |
|
1276 int i; |
|
1277 |
|
1278 for (i = 1; i <= maxjob; i++) |
|
1279 if (jobtab[i].stat && !jobtab[i].pwd) |
|
1280 jobtab[i].pwd = ztrdup(pwd); |
|
1281 } |
|
1282 |
|
1283 /* print pids for & */ |
|
1284 |
|
1285 /**/ |
|
1286 void |
|
1287 spawnjob(void) |
|
1288 { |
|
1289 Process pn; |
|
1290 |
|
1291 DPUTS(thisjob == -1, "No valid job in spawnjob."); |
|
1292 /* if we are not in a subshell */ |
|
1293 if (!subsh) { |
|
1294 if (curjob == -1 || !(jobtab[curjob].stat & STAT_STOPPED)) { |
|
1295 curjob = thisjob; |
|
1296 setprevjob(); |
|
1297 } else if (prevjob == -1 || !(jobtab[prevjob].stat & STAT_STOPPED)) |
|
1298 prevjob = thisjob; |
|
1299 if (interact && jobbing && jobtab[thisjob].procs) { |
|
1300 fprintf(stderr, "[%d]", thisjob); |
|
1301 for (pn = jobtab[thisjob].procs; pn; pn = pn->next) |
|
1302 fprintf(stderr, " %ld", (long) pn->pid); |
|
1303 fprintf(stderr, "\n"); |
|
1304 fflush(stderr); |
|
1305 } |
|
1306 } |
|
1307 if (!hasprocs(thisjob)) |
|
1308 deletejob(jobtab + thisjob); |
|
1309 else |
|
1310 jobtab[thisjob].stat |= STAT_LOCKED; |
|
1311 thisjob = -1; |
|
1312 } |
|
1313 |
|
1314 /**/ |
|
1315 void |
|
1316 shelltime(void) |
|
1317 { |
|
1318 struct timezone dummy_tz; |
|
1319 struct timeval dtimeval, now; |
|
1320 child_times_t ti; |
|
1321 #ifndef HAVE_GETRUSAGE |
|
1322 struct tms buf; |
|
1323 #endif |
|
1324 |
|
1325 gettimeofday(&now, &dummy_tz); |
|
1326 |
|
1327 #ifdef HAVE_GETRUSAGE |
|
1328 getrusage(RUSAGE_SELF, &ti); |
|
1329 #else |
|
1330 times(&buf); |
|
1331 |
|
1332 ti.ut = buf.tms_utime; |
|
1333 ti.st = buf.tms_stime; |
|
1334 #endif |
|
1335 printtime(dtime(&dtimeval, &shtimer, &now), &ti, "shell"); |
|
1336 |
|
1337 #ifdef HAVE_GETRUSAGE |
|
1338 getrusage(RUSAGE_CHILDREN, &ti); |
|
1339 #else |
|
1340 ti.ut = buf.tms_cutime; |
|
1341 ti.st = buf.tms_cstime; |
|
1342 #endif |
|
1343 printtime(&dtimeval, &ti, "children"); |
|
1344 |
|
1345 } |
|
1346 |
|
1347 /* see if jobs need printing */ |
|
1348 |
|
1349 /**/ |
|
1350 void |
|
1351 scanjobs(void) |
|
1352 { |
|
1353 int i; |
|
1354 |
|
1355 for (i = 1; i <= maxjob; i++) |
|
1356 if (jobtab[i].stat & STAT_CHANGED) |
|
1357 printjob(jobtab + i, 0, 1); |
|
1358 } |
|
1359 |
|
1360 /**** job control builtins ****/ |
|
1361 |
|
1362 /* This simple function indicates whether or not s may represent * |
|
1363 * a number. It returns true iff s consists purely of digits and * |
|
1364 * minuses. Note that minus may appear more than once, and the empty * |
|
1365 * string will produce a `true' response. */ |
|
1366 |
|
1367 /**/ |
|
1368 static int |
|
1369 isanum(char *s) |
|
1370 { |
|
1371 while (*s == '-' || idigit(*s)) |
|
1372 s++; |
|
1373 return *s == '\0'; |
|
1374 } |
|
1375 |
|
1376 /* Make sure we have a suitable current and previous job set. */ |
|
1377 |
|
1378 /**/ |
|
1379 static void |
|
1380 setcurjob(void) |
|
1381 { |
|
1382 if (curjob == thisjob || |
|
1383 (curjob != -1 && !(jobtab[curjob].stat & STAT_INUSE))) { |
|
1384 curjob = prevjob; |
|
1385 setprevjob(); |
|
1386 if (curjob == thisjob || |
|
1387 (curjob != -1 && !((jobtab[curjob].stat & STAT_INUSE) && |
|
1388 curjob != thisjob))) { |
|
1389 curjob = prevjob; |
|
1390 setprevjob(); |
|
1391 } |
|
1392 } |
|
1393 } |
|
1394 |
|
1395 /* Convert a job specifier ("%%", "%1", "%foo", "%?bar?", etc.) * |
|
1396 * to a job number. */ |
|
1397 |
|
1398 /**/ |
|
1399 static int |
|
1400 getjob(char *s, char *prog) |
|
1401 { |
|
1402 int jobnum, returnval; |
|
1403 |
|
1404 /* if there is no %, treat as a name */ |
|
1405 if (*s != '%') |
|
1406 goto jump; |
|
1407 s++; |
|
1408 /* "%%", "%+" and "%" all represent the current job */ |
|
1409 if (*s == '%' || *s == '+' || !*s) { |
|
1410 if (curjob == -1) { |
|
1411 zwarnnam(prog, "no current job", NULL, 0); |
|
1412 returnval = -1; |
|
1413 goto done; |
|
1414 } |
|
1415 returnval = curjob; |
|
1416 goto done; |
|
1417 } |
|
1418 /* "%-" represents the previous job */ |
|
1419 if (*s == '-') { |
|
1420 if (prevjob == -1) { |
|
1421 zwarnnam(prog, "no previous job", NULL, 0); |
|
1422 returnval = -1; |
|
1423 goto done; |
|
1424 } |
|
1425 returnval = prevjob; |
|
1426 goto done; |
|
1427 } |
|
1428 /* a digit here means we have a job number */ |
|
1429 if (idigit(*s)) { |
|
1430 jobnum = atoi(s); |
|
1431 if (jobnum && jobnum <= maxjob && jobtab[jobnum].stat && |
|
1432 !(jobtab[jobnum].stat & STAT_SUBJOB) && jobnum != thisjob) { |
|
1433 returnval = jobnum; |
|
1434 goto done; |
|
1435 } |
|
1436 zwarnnam(prog, "%%%s: no such job", s, 0); |
|
1437 returnval = -1; |
|
1438 goto done; |
|
1439 } |
|
1440 /* "%?" introduces a search string */ |
|
1441 if (*s == '?') { |
|
1442 struct process *pn; |
|
1443 |
|
1444 for (jobnum = maxjob; jobnum >= 0; jobnum--) |
|
1445 if (jobtab[jobnum].stat && !(jobtab[jobnum].stat & STAT_SUBJOB) && |
|
1446 jobnum != thisjob) |
|
1447 for (pn = jobtab[jobnum].procs; pn; pn = pn->next) |
|
1448 if (strstr(pn->text, s + 1)) { |
|
1449 returnval = jobnum; |
|
1450 goto done; |
|
1451 } |
|
1452 zwarnnam(prog, "job not found: %s", s, 0); |
|
1453 returnval = -1; |
|
1454 goto done; |
|
1455 } |
|
1456 jump: |
|
1457 /* anything else is a job name, specified as a string that begins the |
|
1458 job's command */ |
|
1459 if ((jobnum = findjobnam(s)) != -1) { |
|
1460 returnval = jobnum; |
|
1461 goto done; |
|
1462 } |
|
1463 /* if we get here, it is because none of the above succeeded and went |
|
1464 to done */ |
|
1465 zwarnnam(prog, "job not found: %s", s, 0); |
|
1466 returnval = -1; |
|
1467 done: |
|
1468 return returnval; |
|
1469 } |
|
1470 |
|
1471 /* For jobs -Z (which modifies the shell's name as seen in ps listings). * |
|
1472 * hackzero is the start of the safely writable space, and hackspace is * |
|
1473 * its length, excluding a final NUL terminator that will always be left. */ |
|
1474 |
|
1475 static char *hackzero; |
|
1476 static int hackspace; |
|
1477 |
|
1478 |
|
1479 /* Initialise job handling. */ |
|
1480 |
|
1481 /**/ |
|
1482 void |
|
1483 init_jobs(char **argv, char **envp) |
|
1484 { |
|
1485 char *p, *q; |
|
1486 size_t init_bytes = MAXJOBS_ALLOC*sizeof(struct job); |
|
1487 |
|
1488 /* |
|
1489 * Initialise the job table. If this fails, we're in trouble. |
|
1490 */ |
|
1491 jobtab = (struct job *)zalloc(init_bytes); |
|
1492 if (!jobtab) { |
|
1493 zerr("failed to allocate job table, aborting.", NULL, 0); |
|
1494 exit(1); |
|
1495 } |
|
1496 jobtabsize = MAXJOBS_ALLOC; |
|
1497 memset(jobtab, 0, init_bytes); |
|
1498 |
|
1499 /* |
|
1500 * Initialise the jobs -Z system. The technique is borrowed from |
|
1501 * perl: check through the argument and environment space, to see |
|
1502 * how many of the strings are in contiguous space. This determines |
|
1503 * the value of hackspace. |
|
1504 */ |
|
1505 hackzero = *argv; |
|
1506 p = strchr(hackzero, 0); |
|
1507 while(*++argv) { |
|
1508 q = *argv; |
|
1509 if(q != p+1) |
|
1510 goto done; |
|
1511 p = strchr(q, 0); |
|
1512 } |
|
1513 for(; *envp; envp++) { |
|
1514 q = *envp; |
|
1515 if(q != p+1) |
|
1516 goto done; |
|
1517 p = strchr(q, 0); |
|
1518 } |
|
1519 done: |
|
1520 hackspace = p - hackzero; |
|
1521 } |
|
1522 |
|
1523 |
|
1524 /* |
|
1525 * We have run out of space in the job table. |
|
1526 * Expand it by an additional MAXJOBS_ALLOC slots. |
|
1527 */ |
|
1528 |
|
1529 /* |
|
1530 * An arbitrary limit on the absolute maximum size of the job table. |
|
1531 * This prevents us taking over the entire universe. |
|
1532 * Ought to be a multiple of MAXJOBS_ALLOC, but doesn't need to be. |
|
1533 */ |
|
1534 #define MAX_MAXJOBS 1000 |
|
1535 |
|
1536 /**/ |
|
1537 int |
|
1538 expandjobtab(void) |
|
1539 { |
|
1540 int newsize = jobtabsize + MAXJOBS_ALLOC; |
|
1541 struct job *newjobtab; |
|
1542 |
|
1543 if (newsize > MAX_MAXJOBS) |
|
1544 return 0; |
|
1545 |
|
1546 newjobtab = (struct job *)zrealloc(jobtab, newsize * sizeof(struct job)); |
|
1547 if (!newjobtab) |
|
1548 return 0; |
|
1549 |
|
1550 /* |
|
1551 * Clear the new section of the table; this is necessary for |
|
1552 * the jobs to appear unused. |
|
1553 */ |
|
1554 memset(newjobtab + jobtabsize, 0, MAXJOBS_ALLOC * sizeof(struct job)); |
|
1555 |
|
1556 jobtab = newjobtab; |
|
1557 jobtabsize = newsize; |
|
1558 |
|
1559 return 1; |
|
1560 } |
|
1561 |
|
1562 |
|
1563 /* |
|
1564 * See if we can reduce the job table. We can if we go over |
|
1565 * a MAXJOBS_ALLOC boundary. However, we leave a boundary, |
|
1566 * currently 20 jobs, so that we have a place for immediate |
|
1567 * expansion and don't play ping pong with the job table size. |
|
1568 */ |
|
1569 |
|
1570 /**/ |
|
1571 void |
|
1572 maybeshrinkjobtab(void) |
|
1573 { |
|
1574 int jobbound; |
|
1575 |
|
1576 queue_signals(); |
|
1577 jobbound = maxjob + MAXJOBS_ALLOC - (maxjob % MAXJOBS_ALLOC); |
|
1578 if (jobbound < jobtabsize && jobbound > maxjob + 20) { |
|
1579 struct job *newjobtab; |
|
1580 |
|
1581 /* Hope this can't fail, but anyway... */ |
|
1582 newjobtab = (struct job *)zrealloc(jobtab, |
|
1583 jobbound*sizeof(struct job)); |
|
1584 |
|
1585 if (newjobtab) { |
|
1586 jobtab = newjobtab; |
|
1587 jobtabsize = jobbound; |
|
1588 } |
|
1589 } |
|
1590 unqueue_signals(); |
|
1591 } |
|
1592 |
|
1593 #ifndef __SYMBIAN32__ |
|
1594 /* bg, disown, fg, jobs, wait: most of the job control commands are * |
|
1595 * here. They all take the same type of argument. Exception: wait can * |
|
1596 * take a pid or a job specifier, whereas the others only work on jobs. */ |
|
1597 |
|
1598 /**/ |
|
1599 int |
|
1600 bin_fg(char *name, char **argv, Options ops, int func) |
|
1601 { |
|
1602 int job, lng, firstjob = -1, retval = 0, ofunc = func; |
|
1603 |
|
1604 if (OPT_ISSET(ops,'Z')) { |
|
1605 int len; |
|
1606 |
|
1607 if(isset(RESTRICTED)) { |
|
1608 zwarnnam(name, "-Z is restricted", NULL, 0); |
|
1609 return 1; |
|
1610 } |
|
1611 if(!argv[0] || argv[1]) { |
|
1612 zwarnnam(name, "-Z requires one argument", NULL, 0); |
|
1613 return 1; |
|
1614 } |
|
1615 queue_signals(); |
|
1616 unmetafy(*argv, &len); |
|
1617 if(len > hackspace) |
|
1618 len = hackspace; |
|
1619 memcpy(hackzero, *argv, len); |
|
1620 memset(hackzero + len, 0, hackspace - len); |
|
1621 unqueue_signals(); |
|
1622 return 0; |
|
1623 } |
|
1624 |
|
1625 lng = (OPT_ISSET(ops,'l')) ? 1 : (OPT_ISSET(ops,'p')) ? 2 : 0; |
|
1626 if (OPT_ISSET(ops,'d')) |
|
1627 lng |= 4; |
|
1628 |
|
1629 if ((func == BIN_FG || func == BIN_BG) && !jobbing) { |
|
1630 /* oops... maybe bg and fg should have been disabled? */ |
|
1631 zwarnnam(name, "no job control in this shell.", NULL, 0); |
|
1632 return 1; |
|
1633 } |
|
1634 |
|
1635 queue_signals(); |
|
1636 /* If necessary, update job table. */ |
|
1637 if (unset(NOTIFY)) |
|
1638 scanjobs(); |
|
1639 |
|
1640 if (func != BIN_JOBS || isset(MONITOR) || !oldmaxjob) |
|
1641 setcurjob(); |
|
1642 |
|
1643 if (func == BIN_JOBS) |
|
1644 /* If you immediately type "exit" after "jobs", this * |
|
1645 * will prevent zexit from complaining about stopped jobs */ |
|
1646 stopmsg = 2; |
|
1647 if (!*argv) { |
|
1648 /* This block handles all of the default cases (no arguments). bg, |
|
1649 fg and disown act on the current job, and jobs and wait act on all the |
|
1650 jobs. */ |
|
1651 if (func == BIN_FG || func == BIN_BG || func == BIN_DISOWN) { |
|
1652 /* W.r.t. the above comment, we'd better have a current job at this |
|
1653 point or else. */ |
|
1654 if (curjob == -1 || (jobtab[curjob].stat & STAT_NOPRINT)) { |
|
1655 zwarnnam(name, "no current job", NULL, 0); |
|
1656 unqueue_signals(); |
|
1657 return 1; |
|
1658 } |
|
1659 firstjob = curjob; |
|
1660 } else if (func == BIN_JOBS) { |
|
1661 /* List jobs. */ |
|
1662 struct job *jobptr; |
|
1663 int curmaxjob, ignorejob; |
|
1664 if (unset(MONITOR) && oldmaxjob) { |
|
1665 jobptr = oldjobtab; |
|
1666 curmaxjob = oldmaxjob ? oldmaxjob - 1 : 0; |
|
1667 ignorejob = 0; |
|
1668 } else { |
|
1669 jobptr = jobtab; |
|
1670 curmaxjob = maxjob; |
|
1671 ignorejob = thisjob; |
|
1672 } |
|
1673 for (job = 0; job <= curmaxjob; job++, jobptr++) |
|
1674 if (job != ignorejob && jobptr->stat) { |
|
1675 if ((!OPT_ISSET(ops,'r') && !OPT_ISSET(ops,'s')) || |
|
1676 (OPT_ISSET(ops,'r') && OPT_ISSET(ops,'s')) || |
|
1677 (OPT_ISSET(ops,'r') && |
|
1678 !(jobptr->stat & STAT_STOPPED)) || |
|
1679 (OPT_ISSET(ops,'s') && jobptr->stat & STAT_STOPPED)) |
|
1680 printjob(jobptr, lng, 2); |
|
1681 } |
|
1682 unqueue_signals(); |
|
1683 return 0; |
|
1684 } else { /* Must be BIN_WAIT, so wait for all jobs */ |
|
1685 for (job = 0; job <= maxjob; job++) |
|
1686 if (job != thisjob && jobtab[job].stat) |
|
1687 zwaitjob(job, SIGINT); |
|
1688 unqueue_signals(); |
|
1689 return 0; |
|
1690 } |
|
1691 } |
|
1692 |
|
1693 /* Defaults have been handled. We now have an argument or two, or three... |
|
1694 In the default case for bg, fg and disown, the argument will be provided by |
|
1695 the above routine. We now loop over the arguments. */ |
|
1696 for (; (firstjob != -1) || *argv; (void)(*argv && argv++)) { |
|
1697 int stopped, ocj = thisjob; |
|
1698 |
|
1699 func = ofunc; |
|
1700 |
|
1701 if (func == BIN_WAIT && isanum(*argv)) { |
|
1702 /* wait can take a pid; the others can't. */ |
|
1703 pid_t pid = (long)atoi(*argv); |
|
1704 Job j; |
|
1705 Process p; |
|
1706 |
|
1707 if (findproc(pid, &j, &p, 0)) |
|
1708 waitforpid(pid); |
|
1709 else |
|
1710 zwarnnam(name, "pid %d is not a child of this shell", 0, pid); |
|
1711 retval = lastval2; |
|
1712 thisjob = ocj; |
|
1713 continue; |
|
1714 } |
|
1715 /* The only type of argument allowed now is a job spec. Check it. */ |
|
1716 job = (*argv) ? getjob(*argv, name) : firstjob; |
|
1717 firstjob = -1; |
|
1718 if (job == -1) { |
|
1719 retval = 1; |
|
1720 break; |
|
1721 } |
|
1722 if (!(jobtab[job].stat & STAT_INUSE) || |
|
1723 (jobtab[job].stat & STAT_NOPRINT)) { |
|
1724 zwarnnam(name, "no such job: %d", 0, job); |
|
1725 unqueue_signals(); |
|
1726 return 1; |
|
1727 } |
|
1728 /* If AUTO_CONTINUE is set (automatically make stopped jobs running |
|
1729 * on disown), we actually do a bg and then delete the job table entry. */ |
|
1730 |
|
1731 if (isset(AUTOCONTINUE) && func == BIN_DISOWN && |
|
1732 jobtab[job].stat & STAT_STOPPED) |
|
1733 func = BIN_BG; |
|
1734 |
|
1735 /* We have a job number. Now decide what to do with it. */ |
|
1736 switch (func) { |
|
1737 case BIN_FG: |
|
1738 case BIN_BG: |
|
1739 case BIN_WAIT: |
|
1740 if (func == BIN_BG) |
|
1741 jobtab[job].stat |= STAT_NOSTTY; |
|
1742 if ((stopped = (jobtab[job].stat & STAT_STOPPED))) |
|
1743 makerunning(jobtab + job); |
|
1744 else if (func == BIN_BG) { |
|
1745 /* Silly to bg a job already running. */ |
|
1746 zwarnnam(name, "job already in background", NULL, 0); |
|
1747 thisjob = ocj; |
|
1748 unqueue_signals(); |
|
1749 return 1; |
|
1750 } |
|
1751 /* It's time to shuffle the jobs around! Reset the current job, |
|
1752 and pick a sensible secondary job. */ |
|
1753 if (curjob == job) { |
|
1754 curjob = prevjob; |
|
1755 prevjob = (func == BIN_BG) ? -1 : job; |
|
1756 } |
|
1757 if (prevjob == job || prevjob == -1) |
|
1758 setprevjob(); |
|
1759 if (curjob == -1) { |
|
1760 curjob = prevjob; |
|
1761 setprevjob(); |
|
1762 } |
|
1763 if (func != BIN_WAIT) |
|
1764 /* for bg and fg -- show the job we are operating on */ |
|
1765 printjob(jobtab + job, (stopped) ? -1 : 0, 1); |
|
1766 if (func != BIN_BG) { /* fg or wait */ |
|
1767 if (jobtab[job].pwd && strcmp(jobtab[job].pwd, pwd)) { |
|
1768 fprintf(shout, "(pwd : "); |
|
1769 fprintdir(jobtab[job].pwd, shout); |
|
1770 fprintf(shout, ")\n"); |
|
1771 } |
|
1772 fflush(shout); |
|
1773 if (func != BIN_WAIT) { /* fg */ |
|
1774 thisjob = job; |
|
1775 if ((jobtab[job].stat & STAT_SUPERJOB) && |
|
1776 ((!jobtab[job].procs->next || |
|
1777 (jobtab[job].stat & STAT_SUBLEADER) || |
|
1778 killpg(jobtab[job].gleader, 0) == -1)) && |
|
1779 jobtab[jobtab[job].other].gleader) |
|
1780 attachtty(jobtab[jobtab[job].other].gleader); |
|
1781 else |
|
1782 attachtty(jobtab[job].gleader); |
|
1783 } |
|
1784 } |
|
1785 if (stopped) { |
|
1786 if (func != BIN_BG && jobtab[job].ty) |
|
1787 settyinfo(jobtab[job].ty); |
|
1788 killjb(jobtab + job, SIGCONT); |
|
1789 } |
|
1790 if (func == BIN_WAIT) |
|
1791 zwaitjob(job, SIGINT); |
|
1792 if (func != BIN_BG) { |
|
1793 waitjobs(); |
|
1794 retval = lastval2; |
|
1795 } else if (ofunc == BIN_DISOWN) |
|
1796 deletejob(jobtab + job); |
|
1797 break; |
|
1798 case BIN_JOBS: |
|
1799 printjob(job + jobtab, lng, 2); |
|
1800 break; |
|
1801 case BIN_DISOWN: |
|
1802 if (jobtab[job].stat & STAT_STOPPED) { |
|
1803 char buf[20], *pids = ""; |
|
1804 |
|
1805 if (jobtab[job].stat & STAT_SUPERJOB) { |
|
1806 Process pn; |
|
1807 |
|
1808 for (pn = jobtab[jobtab[job].other].procs; pn; pn = pn->next) { |
|
1809 sprintf(buf, " -%d", pn->pid); |
|
1810 pids = dyncat(pids, buf); |
|
1811 } |
|
1812 for (pn = jobtab[job].procs; pn->next; pn = pn->next) { |
|
1813 sprintf(buf, " %d", pn->pid); |
|
1814 pids = dyncat(pids, buf); |
|
1815 } |
|
1816 if (!jobtab[jobtab[job].other].procs && pn) { |
|
1817 sprintf(buf, " %d", pn->pid); |
|
1818 pids = dyncat(pids, buf); |
|
1819 } |
|
1820 } else { |
|
1821 sprintf(buf, " -%d", jobtab[job].gleader); |
|
1822 pids = buf; |
|
1823 } |
|
1824 zwarnnam(name, |
|
1825 #ifdef USE_SUSPENDED |
|
1826 "warning: job is suspended, use `kill -CONT%s' to resume", |
|
1827 #else |
|
1828 "warning: job is stopped, use `kill -CONT%s' to resume", |
|
1829 #endif |
|
1830 pids, 0); |
|
1831 } |
|
1832 deletejob(jobtab + job); |
|
1833 break; |
|
1834 } |
|
1835 thisjob = ocj; |
|
1836 } |
|
1837 unqueue_signals(); |
|
1838 return retval; |
|
1839 } |
|
1840 #endif //__SYMBIAN32__ |
|
1841 #if defined(SIGCHLD) && defined(SIGCLD) |
|
1842 #if SIGCHLD == SIGCLD |
|
1843 #define ALT_SIGS 1 |
|
1844 #endif |
|
1845 #endif |
|
1846 #if defined(SIGPOLL) && defined(SIGIO) |
|
1847 #if SIGPOLL == SIGIO |
|
1848 #define ALT_SIGS 1 |
|
1849 #endif |
|
1850 #endif |
|
1851 |
|
1852 #ifdef ALT_SIGS |
|
1853 const struct { |
|
1854 const char *name; |
|
1855 int num; |
|
1856 } alt_sigs[] = { |
|
1857 #if defined(SIGCHLD) && defined(SIGCLD) |
|
1858 #if SIGCHLD == SIGCLD |
|
1859 { "CLD", SIGCLD }, |
|
1860 #endif |
|
1861 #endif |
|
1862 #if defined(SIGPOLL) && defined(SIGIO) |
|
1863 #if SIGPOLL == SIGIO |
|
1864 { "IO", SIGIO }, |
|
1865 #endif |
|
1866 #endif |
|
1867 { NULL, 0 } |
|
1868 }; |
|
1869 #endif |
|
1870 |
|
1871 #ifndef __SYMBIAN32__ |
|
1872 /* kill: send a signal to a process. The process(es) may be specified * |
|
1873 * by job specifier (see above) or pid. A signal, defaulting to * |
|
1874 * SIGTERM, may be specified by name or number, preceded by a dash. */ |
|
1875 |
|
1876 /**/ |
|
1877 int |
|
1878 bin_kill(char *nam, char **argv, UNUSED(Options ops), UNUSED(int func)) |
|
1879 { |
|
1880 int sig = SIGTERM; |
|
1881 int returnval = 0; |
|
1882 |
|
1883 /* check for, and interpret, a signal specifier */ |
|
1884 if (*argv && **argv == '-') { |
|
1885 if (idigit((*argv)[1])) |
|
1886 /* signal specified by number */ |
|
1887 sig = atoi(*argv + 1); |
|
1888 else if ((*argv)[1] != '-' || (*argv)[2]) { |
|
1889 char *signame = NULL; |
|
1890 |
|
1891 /* with argument "-l" display the list of signal names */ |
|
1892 if ((*argv)[1] == 'l' && (*argv)[2] == '\0') { |
|
1893 if (argv[1]) { |
|
1894 while (*++argv) { |
|
1895 sig = zstrtol(*argv, &signame, 10); |
|
1896 if (signame == *argv) { |
|
1897 for (sig = 1; sig <= SIGCOUNT; sig++) |
|
1898 if (!cstrpcmp(sigs + sig, &signame)) |
|
1899 break; |
|
1900 #ifdef ALT_SIGS |
|
1901 if (sig > SIGCOUNT) { |
|
1902 int i; |
|
1903 |
|
1904 for (i = 0; alt_sigs[i].name; i++) |
|
1905 if (!cstrpcmp(&alt_sigs[i].name, &signame)) |
|
1906 { |
|
1907 sig = alt_sigs[i].num; |
|
1908 break; |
|
1909 } |
|
1910 } |
|
1911 #endif |
|
1912 if (sig > SIGCOUNT) { |
|
1913 zwarnnam(nam, "unknown signal: SIG%s", |
|
1914 signame, 0); |
|
1915 returnval++; |
|
1916 } else |
|
1917 printf("%d\n", sig); |
|
1918 } else { |
|
1919 if (*signame) { |
|
1920 zwarnnam(nam, "unknown signal: SIG%s", |
|
1921 signame, 0); |
|
1922 returnval++; |
|
1923 } else { |
|
1924 if (WIFSIGNALED(sig)) |
|
1925 sig = WTERMSIG(sig); |
|
1926 else if (WIFSTOPPED(sig)) |
|
1927 sig = WSTOPSIG(sig); |
|
1928 if (1 <= sig && sig <= SIGCOUNT) |
|
1929 printf("%s\n", sigs[sig]); |
|
1930 else |
|
1931 printf("%d\n", sig); |
|
1932 } |
|
1933 } |
|
1934 } |
|
1935 return returnval; |
|
1936 } |
|
1937 printf("%s", sigs[1]); |
|
1938 for (sig = 2; sig <= SIGCOUNT; sig++) |
|
1939 printf(" %s", sigs[sig]); |
|
1940 putchar('\n'); |
|
1941 return 0; |
|
1942 } |
|
1943 |
|
1944 if ((*argv)[1] == 'n' && (*argv)[2] == '\0') { |
|
1945 char *endp; |
|
1946 |
|
1947 if (!*++argv) { |
|
1948 zwarnnam(nam, "-n: argument expected", NULL, 0); |
|
1949 return 1; |
|
1950 } |
|
1951 sig = zstrtol(*argv, &endp, 10); |
|
1952 if (*endp) { |
|
1953 zwarnnam(nam, "invalid signal number", signame, 0); |
|
1954 return 1; |
|
1955 } |
|
1956 } else { |
|
1957 if (!((*argv)[1] == 's' && (*argv)[2] == '\0')) |
|
1958 signame = *argv + 1; |
|
1959 else if (!(*++argv)) { |
|
1960 zwarnnam(nam, "-s: argument expected", NULL, 0); |
|
1961 return 1; |
|
1962 } else |
|
1963 signame = *argv; |
|
1964 makeuppercase(&signame); |
|
1965 if (!strncmp(signame, "SIG", 3)) signame+=3; |
|
1966 |
|
1967 /* check for signal matching specified name */ |
|
1968 for (sig = 1; sig <= SIGCOUNT; sig++) |
|
1969 if (!strcmp(*(sigs + sig), signame)) |
|
1970 break; |
|
1971 if (*signame == '0' && !signame[1]) |
|
1972 sig = 0; |
|
1973 #ifdef ALT_SIGS |
|
1974 if (sig > SIGCOUNT) { |
|
1975 int i; |
|
1976 |
|
1977 for (i = 0; alt_sigs[i].name; i++) |
|
1978 if (!strcmp(alt_sigs[i].name, signame)) |
|
1979 { |
|
1980 sig = alt_sigs[i].num; |
|
1981 break; |
|
1982 } |
|
1983 } |
|
1984 #endif |
|
1985 if (sig > SIGCOUNT) { |
|
1986 zwarnnam(nam, "unknown signal: SIG%s", signame, 0); |
|
1987 zwarnnam(nam, "type kill -l for a List of signals", NULL, 0); |
|
1988 return 1; |
|
1989 } |
|
1990 } |
|
1991 } |
|
1992 argv++; |
|
1993 } |
|
1994 |
|
1995 if (!*argv) { |
|
1996 zwarnnam(nam, "not enough arguments", NULL, 0); |
|
1997 return 1; |
|
1998 } |
|
1999 |
|
2000 queue_signals(); |
|
2001 setcurjob(); |
|
2002 |
|
2003 /* Remaining arguments specify processes. Loop over them, and send the |
|
2004 signal (number sig) to each process. */ |
|
2005 for (; *argv; argv++) { |
|
2006 if (**argv == '%') { |
|
2007 /* job specifier introduced by '%' */ |
|
2008 int p; |
|
2009 |
|
2010 if ((p = getjob(*argv, nam)) == -1) { |
|
2011 returnval++; |
|
2012 continue; |
|
2013 } |
|
2014 if (killjb(jobtab + p, sig) == -1) { |
|
2015 zwarnnam("kill", "kill %s failed: %e", *argv, errno); |
|
2016 returnval++; |
|
2017 continue; |
|
2018 } |
|
2019 /* automatically update the job table if sending a SIGCONT to a |
|
2020 job, and send the job a SIGCONT if sending it a non-stopping |
|
2021 signal. */ |
|
2022 if (jobtab[p].stat & STAT_STOPPED) { |
|
2023 if (sig == SIGCONT) |
|
2024 jobtab[p].stat &= ~STAT_STOPPED; |
|
2025 if (sig != SIGKILL && sig != SIGCONT && sig != SIGTSTP |
|
2026 && sig != SIGTTOU && sig != SIGTTIN && sig != SIGSTOP) |
|
2027 killjb(jobtab + p, SIGCONT); |
|
2028 } |
|
2029 } else if (!isanum(*argv)) { |
|
2030 zwarnnam("kill", "illegal pid: %s", *argv, 0); |
|
2031 returnval++; |
|
2032 } else if (kill(atoi(*argv), sig) == -1) { |
|
2033 zwarnnam("kill", "kill %s failed: %e", *argv, errno); |
|
2034 returnval++; |
|
2035 } |
|
2036 } |
|
2037 unqueue_signals(); |
|
2038 |
|
2039 return returnval < 126 ? returnval : 1; |
|
2040 } |
|
2041 #endif //__SYMBIAN32__ |
|
2042 /* Get a signal number from a string */ |
|
2043 |
|
2044 /**/ |
|
2045 mod_export int |
|
2046 getsignum(char *s) |
|
2047 { |
|
2048 int x, i; |
|
2049 |
|
2050 /* check for a signal specified by number */ |
|
2051 x = atoi(s); |
|
2052 if (idigit(*s) && x >= 0 && x < VSIGCOUNT) |
|
2053 return x; |
|
2054 |
|
2055 /* search for signal by name */ |
|
2056 for (i = 0; i < VSIGCOUNT; i++) |
|
2057 if (!strcmp(s, sigs[i])) |
|
2058 return i; |
|
2059 |
|
2060 #ifdef ALT_SIGS |
|
2061 for (i = 0; alt_sigs[i].name; i++) |
|
2062 { |
|
2063 if (!strcmp(s, alt_sigs[i].name)) |
|
2064 return alt_sigs[i].num; |
|
2065 } |
|
2066 #endif |
|
2067 |
|
2068 /* no matching signal */ |
|
2069 return -1; |
|
2070 } |
|
2071 |
|
2072 /* Get the function node for a trap, taking care about alternative names */ |
|
2073 /**/ |
|
2074 HashNode |
|
2075 gettrapnode(int sig, int ignoredisable) |
|
2076 { |
|
2077 char fname[20]; |
|
2078 HashNode hn; |
|
2079 HashNode (*getptr)(HashTable ht, char *name); |
|
2080 #ifdef ALT_SIGS |
|
2081 int i; |
|
2082 #endif |
|
2083 if (ignoredisable) |
|
2084 getptr = shfunctab->getnode2; |
|
2085 else |
|
2086 getptr = shfunctab->getnode; |
|
2087 |
|
2088 sprintf(fname, "TRAP%s", sigs[sig]); |
|
2089 if ((hn = getptr(shfunctab, fname))) |
|
2090 return hn; |
|
2091 |
|
2092 #ifdef ALT_SIGS |
|
2093 for (i = 0; alt_sigs[i].name; i++) { |
|
2094 if (alt_sigs[i].num == sig) { |
|
2095 sprintf(fname, "TRAP%s", alt_sigs[i].name); |
|
2096 if ((hn = getptr(shfunctab, fname))) |
|
2097 return hn; |
|
2098 } |
|
2099 } |
|
2100 #endif |
|
2101 |
|
2102 return NULL; |
|
2103 } |
|
2104 |
|
2105 /* Remove a TRAP function under any name for the signal */ |
|
2106 |
|
2107 /**/ |
|
2108 void |
|
2109 removetrapnode(int sig) |
|
2110 { |
|
2111 HashNode hn = gettrapnode(sig, 1); |
|
2112 if (hn) { |
|
2113 shfunctab->removenode(shfunctab, hn->nam); |
|
2114 shfunctab->freenode(hn); |
|
2115 } |
|
2116 } |
|
2117 |
|
2118 /* Suspend this shell */ |
|
2119 |
|
2120 /**/ |
|
2121 int |
|
2122 bin_suspend(char *name, UNUSED(char **argv), Options ops, UNUSED(int func)) |
|
2123 { |
|
2124 /* won't suspend a login shell, unless forced */ |
|
2125 if (islogin && !OPT_ISSET(ops,'f')) { |
|
2126 zwarnnam(name, "can't suspend login shell", NULL, 0); |
|
2127 return 1; |
|
2128 } |
|
2129 if (jobbing) { |
|
2130 /* stop ignoring signals */ |
|
2131 signal_default(SIGTTIN); |
|
2132 signal_default(SIGTSTP); |
|
2133 signal_default(SIGTTOU); |
|
2134 |
|
2135 /* Move ourselves back to the process group we came from */ |
|
2136 release_pgrp(); |
|
2137 } |
|
2138 |
|
2139 /* suspend ourselves with a SIGTSTP */ |
|
2140 killpg(origpgrp, SIGTSTP); |
|
2141 |
|
2142 if (jobbing) { |
|
2143 acquire_pgrp(); |
|
2144 /* restore signal handling */ |
|
2145 signal_ignore(SIGTTOU); |
|
2146 signal_ignore(SIGTSTP); |
|
2147 signal_ignore(SIGTTIN); |
|
2148 } |
|
2149 return 0; |
|
2150 } |
|
2151 |
|
2152 /* find a job named s */ |
|
2153 |
|
2154 /**/ |
|
2155 int |
|
2156 findjobnam(char *s) |
|
2157 { |
|
2158 int jobnum; |
|
2159 |
|
2160 for (jobnum = maxjob; jobnum >= 0; jobnum--) |
|
2161 if (!(jobtab[jobnum].stat & (STAT_SUBJOB | STAT_NOPRINT)) && |
|
2162 jobtab[jobnum].stat && jobtab[jobnum].procs && jobnum != thisjob && |
|
2163 jobtab[jobnum].procs->text && strpfx(s, jobtab[jobnum].procs->text)) |
|
2164 return jobnum; |
|
2165 return -1; |
|
2166 } |
|
2167 |
|
2168 |
|
2169 /* make sure we are a process group leader by creating a new process |
|
2170 group if necessary */ |
|
2171 |
|
2172 /**/ |
|
2173 void |
|
2174 acquire_pgrp(void) |
|
2175 { |
|
2176 long ttpgrp; |
|
2177 sigset_t blockset, oldset; |
|
2178 |
|
2179 if ((mypgrp = GETPGRP()) > 0) { |
|
2180 sigemptyset(&blockset); |
|
2181 sigaddset(&blockset, SIGTTIN); |
|
2182 sigaddset(&blockset, SIGTTOU); |
|
2183 sigaddset(&blockset, SIGTSTP); |
|
2184 oldset = signal_block(blockset); |
|
2185 while ((ttpgrp = gettygrp()) != -1 && ttpgrp != mypgrp) { |
|
2186 mypgrp = GETPGRP(); |
|
2187 if (mypgrp == mypid) { |
|
2188 signal_setmask(oldset); |
|
2189 attachtty(mypgrp); /* Might generate SIGT* */ |
|
2190 signal_block(blockset); |
|
2191 } |
|
2192 if (mypgrp == gettygrp()) |
|
2193 break; |
|
2194 signal_setmask(oldset); |
|
2195 read(0, NULL, 0); /* Might generate SIGT* */ |
|
2196 signal_block(blockset); |
|
2197 mypgrp = GETPGRP(); |
|
2198 } |
|
2199 if (mypgrp != mypid) { |
|
2200 if (setpgrp(0, 0) == 0) { |
|
2201 mypgrp = mypid; |
|
2202 attachtty(mypgrp); |
|
2203 } else |
|
2204 opts[MONITOR] = 0; |
|
2205 } |
|
2206 signal_setmask(oldset); |
|
2207 } else |
|
2208 opts[MONITOR] = 0; |
|
2209 } |
|
2210 |
|
2211 /* revert back to the process group we came from (before acquire_pgrp) */ |
|
2212 |
|
2213 /**/ |
|
2214 void |
|
2215 release_pgrp(void) |
|
2216 { |
|
2217 if (origpgrp != mypgrp) { |
|
2218 attachtty(origpgrp); |
|
2219 setpgrp(0, origpgrp); |
|
2220 mypgrp = origpgrp; |
|
2221 } |
|
2222 } |