1/*
2 * signals.c - signals handling code
3 *
4 * This file is part of zsh, the Z shell.
5 *
6 * Copyright (c) 1992-1997 Paul Falstad
7 * All rights reserved.
8 *
9 * Permission is hereby granted, without written agreement and without
10 * license or royalty fees, to use, copy, modify, and distribute this
11 * software and to distribute modified versions of this software for any
12 * purpose, provided that the above copyright notice and the following
13 * two paragraphs appear in all copies of this software.
14 *
15 * In no event shall Paul Falstad or the Zsh Development Group be liable
16 * to any party for direct, indirect, special, incidental, or consequential
17 * damages arising out of the use of this software and its documentation,
18 * even if Paul Falstad and the Zsh Development Group have been advised of
19 * the possibility of such damage.
20 *
21 * Paul Falstad and the Zsh Development Group specifically disclaim any
22 * warranties, including, but not limited to, the implied warranties of
23 * merchantability and fitness for a particular purpose.  The software
24 * provided hereunder is on an "as is" basis, and Paul Falstad and the
25 * Zsh Development Group have no obligation to provide maintenance,
26 * support, updates, enhancements, or modifications.
27 *
28 */
29
30#include "zsh.mdh"
31#include "signals.pro"
32
33/* Array describing the state of each signal: an element contains *
34 * 0 for the default action or some ZSIG_* flags ored together.   */
35
36/**/
37mod_export int sigtrapped[VSIGCOUNT];
38
39/*
40 * Trap programme lists for each signal.
41 *
42 * If (sigtrapped[sig] & ZSIG_FUNC) is set, this isn't used.
43 * The corresponding shell function is used instead.
44 *
45 * Otherwise, if sigtrapped[sig] is not zero, this is NULL when a signal
46 * is to be ignored, and if not NULL contains the programme list to be
47 * eval'd.
48 */
49
50/**/
51mod_export Eprog siglists[VSIGCOUNT];
52
53/* Total count of trapped signals */
54
55/**/
56mod_export int nsigtrapped;
57
58/* Variables used by signal queueing */
59
60/**/
61mod_export int queueing_enabled, queue_front, queue_rear;
62/**/
63mod_export int signal_queue[MAX_QUEUE_SIZE];
64/**/
65mod_export sigset_t signal_mask_queue[MAX_QUEUE_SIZE];
66
67/* Variables used by trap queueing */
68
69/**/
70mod_export int trap_queueing_enabled, trap_queue_front, trap_queue_rear;
71/**/
72mod_export int trap_queue[MAX_QUEUE_SIZE];
73
74/* This is only used on machines that don't understand signal sets.  *
75 * On SYSV machines this will represent the signals that are blocked *
76 * (held) using sighold.  On machines which can't block signals at   *
77 * all, we will simulate this by ignoring them and remembering them  *
78 * in this variable.                                                 */
79#if !defined(POSIX_SIGNALS) && !defined(BSD_SIGNALS)
80static sigset_t blocked_set;
81#endif
82
83#ifdef POSIX_SIGNALS
84# define signal_jmp_buf       sigjmp_buf
85# define signal_setjmp(b)     sigsetjmp((b),1)
86# define signal_longjmp(b,n)  siglongjmp((b),(n))
87#else
88# define signal_jmp_buf       jmp_buf
89# define signal_setjmp(b)     setjmp(b)
90# define signal_longjmp(b,n)  longjmp((b),(n))
91#endif
92
93#ifdef NO_SIGNAL_BLOCKING
94# define signal_process(sig)  signal_ignore(sig)
95# define signal_reset(sig)    install_handler(sig)
96#else
97# define signal_process(sig)  ;
98# define signal_reset(sig)    ;
99#endif
100
101/* Install signal handler for given signal.           *
102 * If possible, we want to make sure that interrupted *
103 * system calls are not restarted.                    */
104
105/**/
106mod_export void
107install_handler(int sig)
108{
109#ifdef POSIX_SIGNALS
110    struct sigaction act;
111
112    act.sa_handler = (SIGNAL_HANDTYPE) zhandler;
113    sigemptyset(&act.sa_mask);        /* only block sig while in handler */
114    act.sa_flags = 0;
115# ifdef SA_INTERRUPT                  /* SunOS 4.x */
116    if (interact)
117        act.sa_flags |= SA_INTERRUPT; /* make sure system calls are not restarted */
118# endif
119    sigaction(sig, &act, (struct sigaction *)NULL);
120#else
121# ifdef BSD_SIGNALS
122    struct sigvec vec;
123
124    vec.sv_handler = (SIGNAL_HANDTYPE) zhandler;
125    vec.sv_mask = sigmask(sig);    /* mask out this signal while in handler    */
126#  ifdef SV_INTERRUPT
127    vec.sv_flags = SV_INTERRUPT;   /* make sure system calls are not restarted */
128#  endif
129    sigvec(sig, &vec, (struct sigvec *)NULL);
130# else
131#  ifdef SYSV_SIGNALS
132    /* we want sigset rather than signal because it will   *
133     * block sig while in handler.  signal usually doesn't */
134    sigset(sig, zhandler);
135#  else  /* NO_SIGNAL_BLOCKING (bummer) */
136    signal(sig, zhandler);
137
138#  endif /* SYSV_SIGNALS  */
139# endif  /* BSD_SIGNALS   */
140#endif   /* POSIX_SIGNALS */
141}
142
143/* enable ^C interrupts */
144
145/**/
146mod_export void
147intr(void)
148{
149    if (interact)
150        install_handler(SIGINT);
151}
152
153/* disable ^C interrupts */
154
155#if 0 /**/
156void
157nointr(void)
158{
159    if (interact)
160        signal_ignore(SIGINT);
161}
162#endif
163
164/* temporarily block ^C interrupts */
165
166/**/
167mod_export void
168holdintr(void)
169{
170    if (interact)
171        signal_block(signal_mask(SIGINT));
172}
173
174/* release ^C interrupts */
175
176/**/
177mod_export void
178noholdintr(void)
179{
180    if (interact)
181        signal_unblock(signal_mask(SIGINT));
182}
183
184/* create a signal mask containing *
185 * only the given signal           */
186
187/**/
188mod_export sigset_t
189signal_mask(int sig)
190{
191    sigset_t set;
192
193    sigemptyset(&set);
194    if (sig)
195        sigaddset(&set, sig);
196    return set;
197}
198
199/* Block the signals in the given signal *
200 * set. Return the old signal set.       */
201
202/**/
203#ifndef BSD_SIGNALS
204
205/**/
206mod_export sigset_t
207signal_block(sigset_t set)
208{
209    sigset_t oset;
210
211#ifdef POSIX_SIGNALS
212    sigprocmask(SIG_BLOCK, &set, &oset);
213
214#else
215# ifdef SYSV_SIGNALS
216    int i;
217
218    oset = blocked_set;
219    for (i = 1; i <= NSIG; ++i) {
220        if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
221            sigaddset(&blocked_set, i);
222            sighold(i);
223        }
224    }
225# else  /* NO_SIGNAL_BLOCKING */
226/* We will just ignore signals if the system doesn't have *
227 * the ability to block them.                             */
228    int i;
229
230    oset = blocked_set;
231    for (i = 1; i <= NSIG; ++i) {
232        if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
233            sigaddset(&blocked_set, i);
234            signal_ignore(i);
235        }
236   }
237# endif /* SYSV_SIGNALS */
238#endif /* POSIX_SIGNALS */
239
240    return oset;
241}
242
243/**/
244#endif /* BSD_SIGNALS */
245
246/* Unblock the signals in the given signal *
247 * set. Return the old signal set.         */
248
249/**/
250mod_export sigset_t
251signal_unblock(sigset_t set)
252{
253    sigset_t oset;
254
255#ifdef POSIX_SIGNALS
256    sigprocmask(SIG_UNBLOCK, &set, &oset);
257#else
258# ifdef BSD_SIGNALS
259    sigfillset(&oset);
260    oset = sigsetmask(oset);
261    sigsetmask(oset & ~set);
262# else
263#  ifdef SYSV_SIGNALS
264    int i;
265
266    oset = blocked_set;
267    for (i = 1; i <= NSIG; ++i) {
268        if (sigismember(&set, i) && sigismember(&blocked_set, i)) {
269            sigdelset(&blocked_set, i);
270            sigrelse(i);
271        }
272    }
273#  else  /* NO_SIGNAL_BLOCKING */
274/* On systems that can't block signals, we are just ignoring them.  So *
275 * to unblock signals, we just reenable the signal handler for them.   */
276    int i;
277
278    oset = blocked_set;
279    for (i = 1; i <= NSIG; ++i) {
280        if (sigismember(&set, i) && sigismember(&blocked_set, i)) {
281            sigdelset(&blocked_set, i);
282            install_handler(i);
283        }
284   }
285#  endif /* SYSV_SIGNALS  */
286# endif  /* BSD_SIGNALS   */
287#endif   /* POSIX_SIGNALS */
288
289    return oset;
290}
291
292/* set the process signal mask to *
293 * be the given signal mask       */
294
295/**/
296mod_export sigset_t
297signal_setmask(sigset_t set)
298{
299    sigset_t oset;
300
301#ifdef POSIX_SIGNALS
302    sigprocmask(SIG_SETMASK, &set, &oset);
303#else
304# ifdef BSD_SIGNALS
305    oset = sigsetmask(set);
306# else
307#  ifdef SYSV_SIGNALS
308    int i;
309
310    oset = blocked_set;
311    for (i = 1; i <= NSIG; ++i) {
312        if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
313            sigaddset(&blocked_set, i);
314            sighold(i);
315        } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) {
316            sigdelset(&blocked_set, i);
317            sigrelse(i);
318        }
319    }
320#  else  /* NO_SIGNAL_BLOCKING */
321    int i;
322
323    oset = blocked_set;
324    for (i = 1; i < NSIG; ++i) {
325        if (sigismember(&set, i) && !sigismember(&blocked_set, i)) {
326            sigaddset(&blocked_set, i);
327            signal_ignore(i);
328        } else if (!sigismember(&set, i) && sigismember(&blocked_set, i)) {
329            sigdelset(&blocked_set, i);
330            install_handler(i);
331        }
332    }
333#  endif /* SYSV_SIGNALS  */
334# endif  /* BSD_SIGNALS   */
335#endif   /* POSIX_SIGNALS */
336
337    return oset;
338}
339
340#if defined(NO_SIGNAL_BLOCKING)
341static int suspend_longjmp = 0;
342static signal_jmp_buf suspend_jmp_buf;
343#endif
344
345/**/
346int
347signal_suspend(UNUSED(int sig), int wait_cmd)
348{
349    int ret;
350
351#if defined(POSIX_SIGNALS) || defined(BSD_SIGNALS)
352    sigset_t set;
353# if defined(POSIX_SIGNALS) && defined(BROKEN_POSIX_SIGSUSPEND)
354    sigset_t oset;
355# endif
356
357    sigemptyset(&set);
358
359    /* SIGINT from the terminal driver needs to interrupt "wait"
360     * and to cause traps to fire, but otherwise should not be
361     * handled by the shell until after any foreground job has
362     * a chance to decide whether to exit on that signal.
363     */
364    if (!(wait_cmd || isset(TRAPSASYNC) ||
365	  (sigtrapped[SIGINT] & ~ZSIG_IGNORED)))
366	sigaddset(&set, SIGINT);
367#endif /* POSIX_SIGNALS || BSD_SIGNALS */
368
369#ifdef POSIX_SIGNALS
370# ifdef BROKEN_POSIX_SIGSUSPEND
371    sigprocmask(SIG_SETMASK, &set, &oset);
372    pause();
373    sigprocmask(SIG_SETMASK, &oset, NULL);
374# else /* not BROKEN_POSIX_SIGSUSPEND */
375    ret = sigsuspend(&set);
376# endif /* BROKEN_POSIX_SIGSUSPEND */
377#else /* not POSIX_SIGNALS */
378# ifdef BSD_SIGNALS
379    ret = sigpause(set);
380# else
381#  ifdef SYSV_SIGNALS
382    ret = sigpause(sig);
383
384#  else  /* NO_SIGNAL_BLOCKING */
385    /* need to use signal_longjmp to make this race-free *
386     * between the child_unblock() and pause()           */
387    if (signal_setjmp(suspend_jmp_buf) == 0) {
388        suspend_longjmp = 1;   /* we want to signal_longjmp after catching signal */
389        child_unblock();       /* do we need to do wait_cmd stuff as well?             */
390        ret = pause();
391    }
392    suspend_longjmp = 0;       /* turn off using signal_longjmp since we are past *
393                                * the pause() function.                           */
394#  endif /* SYSV_SIGNALS  */
395# endif  /* BSD_SIGNALS   */
396#endif   /* POSIX_SIGNALS */
397
398    return ret;
399}
400
401/* last signal we handled: race prone, or what? */
402/**/
403int last_signal;
404
405/*
406 * Wait for any processes that have changed state.
407 *
408 * The main use for this is in the SIGCHLD handler.  However,
409 * we also use it to pick up status changes of jobs when
410 * updating jobs.
411 */
412/**/
413void
414wait_for_processes(void)
415{
416    /* keep WAITING until no more child processes to reap */
417    for (;;) {
418	/* save the errno, since WAIT may change it */
419	int old_errno = errno;
420	int status;
421	Job jn;
422	Process pn;
423	pid_t pid;
424	pid_t *procsubpid = &cmdoutpid;
425	int *procsubval = &cmdoutval;
426	int cont = 0;
427	struct execstack *es = exstack;
428
429	/*
430	 * Reap the child process.
431	 * If we want usage information, we need to use wait3.
432	 */
433#if defined(HAVE_WAIT3) || defined(HAVE_WAITPID)
434# ifdef WCONTINUED
435# define WAITFLAGS (WNOHANG|WUNTRACED|WCONTINUED)
436# else
437# define WAITFLAGS (WNOHANG|WUNTRACED)
438# endif
439#endif
440#ifdef HAVE_WAIT3
441# ifdef HAVE_GETRUSAGE
442	struct rusage ru;
443
444	pid = wait3((void *)&status, WAITFLAGS, &ru);
445# else
446	pid = wait3((void *)&status, WAITFLAGS, NULL);
447# endif
448#else
449# ifdef HAVE_WAITPID
450	pid = waitpid(-1, &status, WAITFLAGS);
451# else
452	pid = wait(&status);
453# endif
454#endif
455
456	if (!pid)  /* no more children to reap */
457	    break;
458
459	/* check if child returned was from process substitution */
460	for (;;) {
461	    if (pid == *procsubpid) {
462		*procsubpid = 0;
463		if (WIFSIGNALED(status))
464		    *procsubval = (0200 | WTERMSIG(status));
465		else
466		    *procsubval = WEXITSTATUS(status);
467		use_cmdoutval = 1;
468		get_usage();
469		cont = 1;
470		break;
471	    }
472	    if (!es)
473		break;
474	    procsubpid = &es->cmdoutpid;
475	    procsubval = &es->cmdoutval;
476	    es = es->next;
477	}
478	if (cont)
479	    continue;
480
481	/* check for WAIT error */
482	if (pid == -1) {
483	    if (errno != ECHILD)
484		zerr("wait failed: %e", errno);
485	    /* WAIT changed errno, so restore the original */
486	    errno = old_errno;
487	    break;
488	}
489
490	/*
491	 * Find the process and job containing this pid and
492	 * update it.
493	 */
494	if (findproc(pid, &jn, &pn, 0)) {
495	    if (((jn->stat & STAT_BUILTIN) ||
496		 (list_pipe &&
497		  (thisjob == -1 ||
498		   (jobtab[thisjob].stat & STAT_BUILTIN)))) &&
499		WIFSTOPPED(status) && WSTOPSIG(status) == SIGTSTP) {
500		killjb(jn, SIGCONT);
501		zwarn("job can't be suspended");
502	    } else {
503#if defined(HAVE_WAIT3) && defined(HAVE_GETRUSAGE)
504		struct timezone dummy_tz;
505		gettimeofday(&pn->endtime, &dummy_tz);
506		pn->status = status;
507		pn->ti = ru;
508#else
509		update_process(pn, status);
510#endif
511	    }
512	    update_job(jn);
513	} else if (findproc(pid, &jn, &pn, 1)) {
514	    pn->status = status;
515	    update_job(jn);
516	} else {
517	    /* If not found, update the shell record of time spent by
518	     * children in sub processes anyway:  otherwise, this
519	     * will get added on to the next found process that
520	     * terminates.
521	     */
522	    get_usage();
523	}
524	/*
525	 * Remember the status associated with $!, so we can
526	 * wait for it even if it's exited.  This value is
527	 * only used if we can't find the PID in the job table,
528	 * so it doesn't matter that the value we save here isn't
529	 * useful until the process has exited.
530	 */
531	if (pn != NULL && pid == lastpid && lastpid_status != -1L)
532	    lastpid_status = lastval2;
533    }
534}
535
536/* the signal handler */
537
538/**/
539mod_export RETSIGTYPE
540zhandler(int sig)
541{
542    sigset_t newmask, oldmask;
543
544#if defined(NO_SIGNAL_BLOCKING)
545    int do_jump;
546    signal_jmp_buf jump_to;
547#endif
548
549    last_signal = sig;
550    signal_process(sig);
551
552    sigfillset(&newmask);
553    /* Block all signals temporarily           */
554    oldmask = signal_block(newmask);
555
556#if defined(NO_SIGNAL_BLOCKING)
557    /* do we need to longjmp to signal_suspend */
558    do_jump = suspend_longjmp;
559    /* In case a SIGCHLD somehow arrives       */
560    suspend_longjmp = 0;
561
562    /* Traps can cause nested signal_suspend()  */
563    if (sig == SIGCHLD) {
564        if (do_jump) {
565	    /* Copy suspend_jmp_buf                    */
566            jump_to = suspend_jmp_buf;
567	}
568    }
569#endif
570
571    /* Are we queueing signals now?      */
572    if (queueing_enabled) {
573        int temp_rear = ++queue_rear % MAX_QUEUE_SIZE;
574
575	DPUTS(temp_rear == queue_front, "BUG: signal queue full");
576	/* Make sure it's not full (extremely unlikely) */
577        if (temp_rear != queue_front) {
578	    /* ok, not full, so add to queue   */
579            queue_rear = temp_rear;
580	    /* save signal caught              */
581            signal_queue[queue_rear] = sig;
582	    /* save current signal mask        */
583            signal_mask_queue[queue_rear] = oldmask;
584        }
585        signal_reset(sig);
586        return;
587    }
588
589    /* Reset signal mask, signal traps ok now */
590    signal_setmask(oldmask);
591
592    switch (sig) {
593    case SIGCHLD:
594	wait_for_processes();
595        break;
596
597    case SIGHUP:
598        if (!handletrap(SIGHUP)) {
599            stopmsg = 1;
600            zexit(SIGHUP, 1);
601        }
602        break;
603
604    case SIGINT:
605        if (!handletrap(SIGINT)) {
606	    if ((isset(PRIVILEGED) || isset(RESTRICTED)) &&
607		isset(INTERACTIVE) && noerrexit < 0)
608		zexit(SIGINT, 1);
609            if (list_pipe || chline || simple_pline) {
610                breaks = loops;
611                errflag = 1;
612		inerrflush();
613		check_cursh_sig(SIGINT);
614            }
615        }
616        break;
617
618#ifdef SIGWINCH
619    case SIGWINCH:
620        adjustwinsize(1);  /* check window size and adjust */
621	(void) handletrap(SIGWINCH);
622        break;
623#endif
624
625    case SIGALRM:
626        if (!handletrap(SIGALRM)) {
627	    int idle = ttyidlegetfn(NULL);
628	    int tmout = getiparam("TMOUT");
629	    if (idle >= 0 && idle < tmout)
630		alarm(tmout - idle);
631	    else {
632		errflag = noerrs = 0;
633		zwarn("timeout");
634		stopmsg = 1;
635		zexit(SIGALRM, 1);
636	    }
637        }
638        break;
639
640    default:
641        (void) handletrap(sig);
642        break;
643    }   /* end of switch(sig) */
644
645    signal_reset(sig);
646
647/* This is used to make signal_suspend() race-free */
648#if defined(NO_SIGNAL_BLOCKING)
649    if (do_jump)
650        signal_longjmp(jump_to, 1);
651#endif
652
653} /* handler */
654
655
656/* SIGHUP any jobs left running */
657
658/**/
659void
660killrunjobs(int from_signal)
661{
662    int i, killed = 0;
663
664    if (unset(HUP))
665        return;
666    for (i = 1; i <= maxjob; i++)
667        if ((from_signal || i != thisjob) && (jobtab[i].stat & STAT_LOCKED) &&
668            !(jobtab[i].stat & STAT_NOPRINT) &&
669            !(jobtab[i].stat & STAT_STOPPED)) {
670            if (jobtab[i].gleader != getpid() &&
671		killpg(jobtab[i].gleader, SIGHUP) != -1)
672                killed++;
673        }
674    if (killed)
675        zwarn("warning: %d jobs SIGHUPed", killed);
676}
677
678
679/* send a signal to a job (simply involves kill if monitoring is on) */
680
681/**/
682int
683killjb(Job jn, int sig)
684{
685    Process pn;
686    int err = 0;
687
688    if (jobbing) {
689        if (jn->stat & STAT_SUPERJOB) {
690            if (sig == SIGCONT) {
691                for (pn = jobtab[jn->other].procs; pn; pn = pn->next)
692                    if (killpg(pn->pid, sig) == -1)
693			if (kill(pn->pid, sig) == -1 && errno != ESRCH)
694			    err = -1;
695
696                for (pn = jn->procs; pn->next; pn = pn->next)
697                    if (kill(pn->pid, sig) == -1 && errno != ESRCH)
698			err = -1;
699
700		if (!jobtab[jn->other].procs && pn)
701		    if (kill(pn->pid, sig) == -1 && errno != ESRCH)
702			err = -1;
703
704                return err;
705            }
706            if (killpg(jobtab[jn->other].gleader, sig) == -1 && errno != ESRCH)
707		err = -1;
708
709	    if (killpg(jn->gleader, sig) == -1 && errno != ESRCH)
710		err = -1;
711
712	    return err;
713        }
714        else
715	    return killpg(jn->gleader, sig);
716    }
717    for (pn = jn->procs; pn; pn = pn->next)
718        if ((err = kill(pn->pid, sig)) == -1 && errno != ESRCH && sig != 0)
719            return -1;
720    return err;
721}
722
723/*
724 * List for saving traps.  We don't usually have that many traps
725 * at once, so just use a linked list.
726 */
727struct savetrap {
728    int sig, flags, local;
729    void *list;
730};
731
732static LinkList savetraps;
733static int dontsavetrap;
734
735/*
736 * Save the current trap by copying it.  This does nothing to
737 * the existing value of sigtrapped or siglists.
738 */
739
740static void
741dosavetrap(int sig, int level)
742{
743    struct savetrap *st;
744    st = (struct savetrap *)zalloc(sizeof(*st));
745    st->sig = sig;
746    st->local = level;
747    if ((st->flags = sigtrapped[sig]) & ZSIG_FUNC) {
748	/*
749	 * Get the old function: this assumes we haven't added
750	 * the new one yet.
751	 */
752	Shfunc shf, newshf = NULL;
753	if ((shf = (Shfunc)gettrapnode(sig, 1))) {
754	    /* Copy the node for saving */
755	    newshf = (Shfunc) zalloc(sizeof(*newshf));
756	    newshf->node.nam = ztrdup(shf->node.nam);
757	    newshf->node.flags = shf->node.flags;
758	    newshf->funcdef = dupeprog(shf->funcdef, 0);
759	    newshf->filename = ztrdup(shf->filename);
760	    if (shf->sticky) {
761		newshf->sticky = sticky_emulation_dup(shf->sticky, 0);
762	    } else
763		newshf->sticky = 0;
764	    if (shf->node.flags & PM_UNDEFINED)
765		newshf->funcdef->shf = newshf;
766	}
767#ifdef DEBUG
768	else dputs("BUG: no function present with function trap flag set.");
769#endif
770	DPUTS(siglists[sig], "BUG: function signal has eval list, too.");
771	st->list = newshf;
772    } else if (sigtrapped[sig]) {
773	st->list = siglists[sig] ? dupeprog(siglists[sig], 0) : NULL;
774    } else {
775	DPUTS(siglists[sig], "BUG: siglists not null for untrapped signal");
776	st->list = NULL;
777    }
778    if (!savetraps)
779	savetraps = znewlinklist();
780    /*
781     * Put this at the front of the list
782     */
783    zinsertlinknode(savetraps, (LinkNode)savetraps, st);
784}
785
786
787/*
788 * Set a trap:  note this does not handle manipulation of
789 * the function table for TRAPNAL functions.
790 *
791 * sig is the signal number.
792 *
793 * l is the list to be eval'd for a trap defined with the "trap"
794 * builtin and should be NULL for a function trap.
795 *
796 * flags includes any additional flags to be or'd into sigtrapped[sig],
797 * in particular ZSIG_FUNC; the basic flags will be assigned within
798 * settrap.
799 */
800
801/**/
802mod_export int
803settrap(int sig, Eprog l, int flags)
804{
805    if (sig == -1)
806        return 1;
807    if (jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN)) {
808        zerr("can't trap SIG%s in interactive shells", sigs[sig]);
809        return 1;
810    }
811
812    /*
813     * Call unsettrap() unconditionally, to make sure trap is saved
814     * if necessary.
815     */
816    queue_signals();
817    unsettrap(sig);
818
819    DPUTS((flags & ZSIG_FUNC) && l,
820	  "BUG: trap function has passed eval list, too");
821    siglists[sig] = l;
822    if (!(flags & ZSIG_FUNC) && empty_eprog(l)) {
823	sigtrapped[sig] = ZSIG_IGNORED;
824        if (sig && sig <= SIGCOUNT &&
825#ifdef SIGWINCH
826            sig != SIGWINCH &&
827#endif
828            sig != SIGCHLD)
829            signal_ignore(sig);
830    } else {
831	nsigtrapped++;
832        sigtrapped[sig] = ZSIG_TRAPPED;
833        if (sig && sig <= SIGCOUNT &&
834#ifdef SIGWINCH
835            sig != SIGWINCH &&
836#endif
837            sig != SIGCHLD)
838            install_handler(sig);
839    }
840    /*
841     * Note that introducing the locallevel does not affect whether
842     * sigtrapped[sig] is zero or not, i.e. a test without a mask
843     * works just the same.
844     */
845    sigtrapped[sig] |= (locallevel << ZSIG_SHIFT) | flags;
846    unqueue_signals();
847    return 0;
848}
849
850/**/
851void
852unsettrap(int sig)
853{
854    HashNode hn;
855
856    queue_signals();
857    hn = removetrap(sig);
858    if (hn)
859	shfunctab->freenode(hn);
860    unqueue_signals();
861}
862
863/**/
864HashNode
865removetrap(int sig)
866{
867    int trapped;
868
869    if (sig == -1 ||
870	(jobbing && (sig == SIGTTOU || sig == SIGTSTP || sig == SIGTTIN)))
871	return NULL;
872
873    queue_signals();
874    trapped = sigtrapped[sig];
875    /*
876     * Note that we save the trap here even if there isn't an existing
877     * one, to aid in removing this one.  However, if there's
878     * already one at the current locallevel we just overwrite it.
879     */
880    if (!dontsavetrap &&
881	(sig == SIGEXIT ? !isset(POSIXTRAPS) : isset(LOCALTRAPS)) &&
882	locallevel &&
883	(!trapped || locallevel > (sigtrapped[sig] >> ZSIG_SHIFT)))
884	dosavetrap(sig, locallevel);
885
886    if (!trapped) {
887	unqueue_signals();
888        return NULL;
889    }
890    if (sigtrapped[sig] & ZSIG_TRAPPED)
891	nsigtrapped--;
892    sigtrapped[sig] = 0;
893    if (sig == SIGINT && interact) {
894	/* PWS 1995/05/16:  added test for interactive, also noholdintr() *
895	 * as subshells ignoring SIGINT have it blocked from delivery     */
896        intr();
897	noholdintr();
898    } else if (sig == SIGHUP)
899        install_handler(sig);
900    else if (sig && sig <= SIGCOUNT &&
901#ifdef SIGWINCH
902             sig != SIGWINCH &&
903#endif
904             sig != SIGCHLD)
905        signal_default(sig);
906
907    /*
908     * At this point we free the appropriate structs.  If we don't
909     * want that to happen then either the function should already have been
910     * removed from shfunctab, or the entry in siglists should have been set
911     * to NULL.  This is no longer necessary for saving traps as that
912     * copies the structures, so here we are remove the originals.
913     * That causes a little inefficiency, but a good deal more reliability.
914     */
915    if (trapped & ZSIG_FUNC) {
916	HashNode node = gettrapnode(sig, 1);
917
918	/*
919	 * As in dosavetrap(), don't call removeshfuncnode() because
920	 * that calls back into unsettrap();
921	 */
922	if (node)
923	    removehashnode(shfunctab, node->nam);
924	unqueue_signals();
925
926	return node;
927    } else if (siglists[sig]) {
928	freeeprog(siglists[sig]);
929	siglists[sig] = NULL;
930    }
931    unqueue_signals();
932
933    return NULL;
934}
935
936/**/
937void
938starttrapscope(void)
939{
940    /* No special SIGEXIT behaviour inside another trap. */
941    if (intrap)
942	return;
943
944    /*
945     * SIGEXIT needs to be restored at the current locallevel,
946     * so give it the next higher one. dosavetrap() is called
947     * automatically where necessary.
948     */
949    if (sigtrapped[SIGEXIT] && !isset(POSIXTRAPS)) {
950	locallevel++;
951	unsettrap(SIGEXIT);
952	locallevel--;
953    }
954}
955
956/*
957 * Reset traps after the end of a function: must be called after
958 * endparamscope() so that the locallevel has been decremented.
959 */
960
961/**/
962void
963endtrapscope(void)
964{
965    LinkNode ln;
966    struct savetrap *st;
967    int exittr = 0;
968    void *exitfn = NULL;
969
970    /*
971     * Remember the exit trap, but don't run it until
972     * after all the other traps have been put back.
973     * Don't do this inside another trap.
974     */
975    if (!intrap &&
976	!isset(POSIXTRAPS) && (exittr = sigtrapped[SIGEXIT])) {
977	if (exittr & ZSIG_FUNC) {
978	    exitfn = removehashnode(shfunctab, "TRAPEXIT");
979	} else {
980	    exitfn = siglists[SIGEXIT];
981	    siglists[SIGEXIT] = NULL;
982	}
983	if (sigtrapped[SIGEXIT] & ZSIG_TRAPPED)
984	    nsigtrapped--;
985	sigtrapped[SIGEXIT] = 0;
986    }
987
988    if (savetraps) {
989	while ((ln = firstnode(savetraps)) &&
990	       (st = (struct savetrap *) ln->dat) &&
991	       st->local > locallevel) {
992	    int sig = st->sig;
993
994	    remnode(savetraps, ln);
995
996	    if (st->flags && (st->list != NULL)) {
997		/* prevent settrap from saving this */
998		dontsavetrap++;
999		if (st->flags & ZSIG_FUNC)
1000		    settrap(sig, NULL, ZSIG_FUNC);
1001		else
1002		    settrap(sig, (Eprog) st->list, 0);
1003		dontsavetrap--;
1004		/*
1005		 * counting of nsigtrapped should presumably be handled
1006		 * in settrap...
1007		 */
1008		DPUTS((sigtrapped[sig] ^ st->flags) & ZSIG_TRAPPED,
1009		      "BUG: settrap didn't restore correct ZSIG_TRAPPED");
1010		if ((sigtrapped[sig] = st->flags) & ZSIG_FUNC)
1011		    shfunctab->addnode(shfunctab, ((Shfunc)st->list)->node.nam,
1012				       (Shfunc) st->list);
1013	    } else if (sigtrapped[sig])
1014		unsettrap(sig);
1015
1016	    zfree(st, sizeof(*st));
1017	}
1018    }
1019
1020    if (exittr) {
1021	if (!isset(POSIXTRAPS))
1022	    dotrapargs(SIGEXIT, &exittr, exitfn);
1023	if (exittr & ZSIG_FUNC)
1024	    shfunctab->freenode((HashNode)exitfn);
1025	else
1026	    freeeprog(exitfn);
1027    }
1028    DPUTS(!locallevel && savetraps && firstnode(savetraps),
1029	  "BUG: still saved traps outside all function scope");
1030}
1031
1032
1033/*
1034 * Decide whether a trap needs handling.
1035 * If so, see if the trap should be run now or queued.
1036 * Return 1 if the trap has been or will be handled.
1037 * This only needs to be called in place of dotrap() in the
1038 * signal handler, since it's only while waiting for children
1039 * to exit that we queue traps.
1040 */
1041/**/
1042static int
1043handletrap(int sig)
1044{
1045    if (!sigtrapped[sig])
1046	return 0;
1047
1048    if (trap_queueing_enabled)
1049    {
1050	/* Code borrowed from signal queueing */
1051	int temp_rear = ++trap_queue_rear % MAX_QUEUE_SIZE;
1052
1053	DPUTS(temp_rear == trap_queue_front, "BUG: trap queue full");
1054	/* If queue is not full... */
1055	if (temp_rear != trap_queue_front) {
1056	    trap_queue_rear = temp_rear;
1057	    trap_queue[trap_queue_rear] = sig;
1058	}
1059	return 1;
1060    }
1061
1062    dotrap(sig);
1063
1064    if (sig == SIGALRM)
1065    {
1066	int tmout;
1067	/*
1068	 * Reset the alarm.
1069	 * It seems slightly more natural to do this when the
1070	 * trap is run, rather than when it's queued, since
1071	 * the user doesn't see the latter.
1072	 */
1073	if ((tmout = getiparam("TMOUT")))
1074	    alarm(tmout);
1075    }
1076
1077    return 1;
1078}
1079
1080
1081/*
1082 * Queue traps if they shouldn't be run asynchronously, i.e.
1083 * we're not in the wait builtin and TRAPSASYNC isn't set, when
1084 * waiting for children to exit.
1085 *
1086 * Note that unlike signal queuing this should only be called
1087 * in single matching pairs and can't be nested.  It is
1088 * only needed when waiting for a job or process to finish.
1089 *
1090 * There is presumably a race setting this up: we shouldn't be running
1091 * traps between forking a foreground process and this point, either.
1092 */
1093/**/
1094void
1095queue_traps(int wait_cmd)
1096{
1097    if (!isset(TRAPSASYNC) && !wait_cmd) {
1098	/*
1099	 * Traps need to be handled synchronously, so
1100	 * enable queueing.
1101	 */
1102	trap_queueing_enabled = 1;
1103    }
1104}
1105
1106
1107/*
1108 * Disable trap queuing and run the traps.
1109 */
1110/**/
1111void
1112unqueue_traps(void)
1113{
1114    trap_queueing_enabled = 0;
1115    while (trap_queue_front != trap_queue_rear) {
1116	trap_queue_front = (trap_queue_front + 1) % MAX_QUEUE_SIZE;
1117	(void) handletrap(trap_queue[trap_queue_front]);
1118    }
1119}
1120
1121
1122/* Execute a trap function for a given signal, possibly
1123 * with non-standard sigtrapped & siglists values
1124 */
1125
1126/* Are we already executing a trap? */
1127/**/
1128int intrap;
1129
1130/* Is the current trap a function? */
1131
1132/**/
1133int trapisfunc;
1134
1135/*
1136 * If the current trap is not a function, at what function depth
1137 * did the trap get called?
1138 */
1139/**/
1140int traplocallevel;
1141
1142/*
1143 * sig is the signal number.
1144 * *sigtr is the value to be taken as the field in sigtrapped (since
1145 *   that may have changed by this point if we are exiting).
1146 * sigfn is an Eprog with a non-function eval list, or a Shfunc
1147 *   with a function trap.  It may be NULL with an ignored signal.
1148 */
1149
1150/**/
1151static void
1152dotrapargs(int sig, int *sigtr, void *sigfn)
1153{
1154    LinkList args;
1155    char *name, num[4];
1156    int obreaks = breaks;
1157    int oretflag = retflag;
1158    int isfunc;
1159    int traperr, new_trap_state, new_trap_return;
1160
1161    /* if signal is being ignored or the trap function		      *
1162     * is NULL, then return					      *
1163     *								      *
1164     * Also return if errflag is set.  In fact, the code in the       *
1165     * function will test for this, but this way we keep status flags *
1166     * intact without working too hard.  Special cases (e.g. calling  *
1167     * a trap for SIGINT after the error flag was set) are handled    *
1168     * by the calling code.  (PWS 1995/06/08).			      *
1169     *                                                                *
1170     * This test is now replicated in dotrap().                       */
1171    if ((*sigtr & ZSIG_IGNORED) || !sigfn || errflag)
1172        return;
1173
1174    /*
1175     * Never execute special (synchronous) traps inside other traps.
1176     * This can cause unexpected code execution when more than one
1177     * of these is set.
1178     *
1179     * The down side is that it's harder to debug traps.  I don't think
1180     * that's a big issue.
1181     */
1182    if (intrap) {
1183	switch (sig) {
1184	case SIGEXIT:
1185	case SIGDEBUG:
1186	case SIGZERR:
1187	    return;
1188	}
1189    }
1190
1191    intrap++;
1192    *sigtr |= ZSIG_IGNORED;
1193
1194    lexsave();
1195    /* execsave will save the old trap_return and trap_state */
1196    execsave();
1197    breaks = retflag = 0;
1198    traplocallevel = locallevel;
1199    runhookdef(BEFORETRAPHOOK, NULL);
1200    if (*sigtr & ZSIG_FUNC) {
1201	int osc = sfcontext, old_incompfunc = incompfunc;
1202	HashNode hn = gettrapnode(sig, 0);
1203
1204	args = znewlinklist();
1205	/*
1206	 * In case of multiple names, try to get
1207	 * a hint of the name in use from the function table.
1208	 * In special cases, e.g. EXIT traps, the function
1209	 * has already been removed.  Then it's OK to
1210	 * use the standard name.
1211	 */
1212	if (hn) {
1213	    name = ztrdup(hn->nam);
1214	} else {
1215	    name = (char *) zalloc(5 + strlen(sigs[sig]));
1216	    sprintf(name, "TRAP%s", sigs[sig]);
1217	}
1218	zaddlinknode(args, name);
1219	sprintf(num, "%d", sig);
1220	zaddlinknode(args, num);
1221
1222	trap_return = -1;	/* incremented by doshfunc */
1223	trap_state = TRAP_STATE_PRIMED;
1224	trapisfunc = isfunc = 1;
1225
1226	sfcontext = SFC_SIGNAL;
1227	incompfunc = 0;
1228	doshfunc((Shfunc)sigfn, args, 1);
1229	sfcontext = osc;
1230	incompfunc= old_incompfunc;
1231	freelinklist(args, (FreeFunc) NULL);
1232	zsfree(name);
1233    } else {
1234	trap_return = -2;	/* not incremented, used at current level */
1235	trap_state = TRAP_STATE_PRIMED;
1236	trapisfunc = isfunc = 0;
1237
1238	execode((Eprog)sigfn, 1, 0, "trap");
1239    }
1240    runhookdef(AFTERTRAPHOOK, NULL);
1241
1242    traperr = errflag;
1243
1244    /* Grab values before they are restored */
1245    new_trap_state = trap_state;
1246    new_trap_return = trap_return;
1247
1248    execrestore();
1249    lexrestore();
1250
1251    if (new_trap_state == TRAP_STATE_FORCE_RETURN &&
1252	/* zero return from function isn't special */
1253	!(isfunc && new_trap_return == 0)) {
1254	if (isfunc) {
1255	    breaks = loops;
1256	    errflag = 1;
1257	}
1258	lastval = new_trap_return;
1259	/* return triggered */
1260	retflag = 1;
1261    } else {
1262	if (traperr && !EMULATION(EMULATE_SH))
1263	    lastval = 1;
1264	if (try_tryflag)
1265	    errflag = traperr;
1266	breaks += obreaks;
1267	/* return not triggered: restore old flag */
1268	retflag = oretflag;
1269	if (breaks > loops)
1270	    breaks = loops;
1271    }
1272
1273    /*
1274     * If zle was running while the trap was executed, see if we
1275     * need to restore the display.
1276     */
1277    if (zleactive && resetneeded)
1278	zleentry(ZLE_CMD_REFRESH);
1279
1280    if (*sigtr != ZSIG_IGNORED)
1281	*sigtr &= ~ZSIG_IGNORED;
1282    intrap--;
1283}
1284
1285/* Standard call to execute a trap for a given signal. */
1286
1287/**/
1288void
1289dotrap(int sig)
1290{
1291    void *funcprog;
1292
1293    if (sigtrapped[sig] & ZSIG_FUNC) {
1294	HashNode hn = gettrapnode(sig, 0);
1295	if (hn)
1296	    funcprog = hn;
1297	else {
1298#ifdef DEBUG
1299	    dputs("BUG: running function trap which has escaped.");
1300#endif
1301	    funcprog = NULL;
1302	}
1303    } else
1304	funcprog = siglists[sig];
1305
1306    /*
1307     * Copied from dotrapargs().
1308     * (In fact, the gain from duplicating this appears to be virtually
1309     * zero.  Not sure why it's here.)
1310     */
1311    if ((sigtrapped[sig] & ZSIG_IGNORED) || !funcprog || errflag)
1312	return;
1313
1314    dotrapargs(sig, sigtrapped+sig, funcprog);
1315}
1316