#ident "@(#)sigretro.c 1.3 'attmail mail(1) command'" #ident "@(#)mailx:sigretro.c 1.6.1.1" /* Copyright (c) 1984 AT&T */ /* All Rights Reserved */ /* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */ /* The copyright notice above does not evidence any */ /* actual or intended publication of such source code. */ #ident "@(#)mailx:sigretro.c 1.6" /* * mailx -- a modified version of a University of California at Berkeley * mail program */ /* * This code is only compiled in if SIG_HOLD is not defined! * * Retrofit new signal interface to old signal primitives. * Supported routines: * sigsys(sig, func) * sigset(sig, func) * sighold(sig) * sigrelse(sig) * sigignore(sig) * sigpause(sig) * Also, * sigchild() * to set all held signals to ignored signals in the * child process after fork(2) */ #include <rcv.h> /* find out if we're in USG -- adb */ #include <signal.h> #ifndef USG # include <errno.h> extern int errno; typedef void (*sigtype)(); #ifndef SIG_HOLD /* adb */ #define SIG_HOLD ((sigtype) 2) #endif #ifndef SIG_ERR # define SIG_ERR ((sigtype) -1) #endif sigtype sigdisp(), sighold(), sigignore(); void _Sigtramp(); /* * The following helps us keep the extended signal semantics together. * We remember for each signal the address of the function we're * supposed to call. s_func is SIG_DFL / SIG_IGN if appropriate. */ static struct sigtable { sigtype s_func; /* What to call */ int s_flag; /* Signal flags; see below */ } sigtable[NSIG + 1]; /* * Signal flag values. */ #define SHELD 1 /* Signal is being held */ #define SDEFER 2 /* Signal occured while held */ #define SSET 4 /* s_func is believable */ #define SPAUSE 8 /* are pausing, waiting for sig */ jmp_buf _pause; /* For doing sigpause() */ /* * Approximate sigsys() system call * This is almost useless since one only calls sigsys() * in the child of a fork(). If you have vfork(), you have new signals * anyway. The real sigsys() does all the stuff needed to support * the real sigset() library. We don't bother here, assuming that * you are either ignoring or defaulting a signal in the child. */ sigtype sigsys(sig, func) sigtype func; { sigtype old; old = sigdisp(sig); signal(sig, func); return(old); } /* * Set the (permanent) disposition of a signal. * If the signal is subsequently (or even now) held, * the action you set here can be enabled using sigrelse(). */ sigtype sigset(sig, func) sigtype func; { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(SIG_ERR); } old = sigdisp(sig); /* * Does anyone actually call sigset with SIG_HOLD!? */ if (func == (void (*)())SIG_HOLD) { /* adb */ sighold(sig); return(old); } sigtable[sig].s_flag |= SSET; sigtable[sig].s_func = func; if (func == (void (*)())SIG_DFL) { /* adb */ /* * If signal has been held, must retain * the catch so that we can note occurrance * of signal. */ if ((sigtable[sig].s_flag & SHELD) == 0) signal(sig, SIG_DFL); else signal(sig, _Sigtramp); return(old); } if (func == (void (*)())SIG_IGN) { /* adb */ /* * Clear pending signal */ signal(sig, SIG_IGN); sigtable[sig].s_flag &= ~SDEFER; return(old); } signal(sig, _Sigtramp); return(old); } /* * Hold a signal. * This CAN be tricky if the signal's disposition is SIG_DFL. * In that case, we still catch the signal so we can note it */ sigtype sighold(sig) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(SIG_ERR); } old = sigdisp(sig); if (sigtable[sig].s_flag & SHELD) return(old); /* * When the default action is required, we have to * set up to catch the signal to note signal's occurrance. */ if (old == (void (*)())SIG_DFL) { /* adb */ sigtable[sig].s_flag |= SSET; signal(sig, _Sigtramp); } sigtable[sig].s_flag |= SHELD; return(old); } /* * Release a signal * If the signal occurred while we had it held, cause the signal. */ sigtype sigrelse(sig) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(SIG_ERR); } old = sigdisp(sig); if ((sigtable[sig].s_flag & SHELD) == 0) return(old); sigtable[sig].s_flag &= ~SHELD; if (sigtable[sig].s_flag & SDEFER) _Sigtramp(sig); /* * If disposition was the default, then we can unset the * catch to _Sigtramp() and let the system do the work. */ if (sigtable[sig].s_func == (void (*)())SIG_DFL) /* adb */ signal(sig, SIG_DFL); return(old); } /* * Ignore a signal. */ sigtype sigignore(sig) { return(sigset(sig, SIG_IGN)); } /* * Pause, waiting for sig to occur. * We assume LUSER called us with the signal held. * When we got the signal, mark the signal as having * occurred. It will actually cause something when * the signal is released. */ sigpause(sig) { if (sig < 1 || sig > NSIG) { errno = EINVAL; return; } sigtable[sig].s_flag |= SHELD|SPAUSE; if (setjmp(_pause) == 0) pause(); sigtable[sig].s_flag &= ~SPAUSE; sigtable[sig].s_flag |= SDEFER; } /* * In the child process after fork(2), set the disposition of all held * signals to SIG_IGN. This is a new procedure not in the real sigset() * package, provided for retrofitting purposes. */ void sigchild() { register int i; for (i = 1; i <= NSIG; i++) if (sigtable[i].s_flag & SHELD) signal(i, SIG_IGN); } /* * Return the current disposition of a signal * If we have not set this signal before, we have to * ask the system */ sigtype sigdisp(sig) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(SIG_ERR); } /* * If we have no knowledge of this signal, * ask the system, then save the result for later. */ if ((sigtable[sig].s_flag & SSET) == 0) { old = (void (*)())signal(sig, SIG_IGN); /* adb */ sigtable[sig].s_func = old; sigtable[sig].s_flag |= SSET; signal(sig, old); return(old); } /* * If we have set this signal before, then sigset() * will have been careful to leave something meaningful * in s_func. */ return(sigtable[sig].s_func); } /* * The following routine gets called for any signal * that is to be trapped to a user function. */ void _Sigtramp(sig) { sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return; } top: old = (void (*)())signal(sig, SIG_IGN); /* adb */ /* * If signal being paused on, wakeup sigpause() */ if (sigtable[sig].s_flag & SPAUSE) longjmp(_pause, 1); /* * If signal is being held, mark its table entry * so we can trigger it when signal is released. * Then just return. */ if (sigtable[sig].s_flag & SHELD) { sigtable[sig].s_flag |= SDEFER; signal(sig, _Sigtramp); return; } /* * If the signal is being ignored, just return. * This would make SIGCONT more normal, but of course * any system with SIGCONT also has the new signal pkg, so... */ if (sigtable[sig].s_func == (void (*)())SIG_IGN) /* adb */ return; /* * If the signal is SIG_DFL, then we probably got here * by holding the signal, having it happen, then releasing * the signal. */ if (sigtable[sig].s_func == (void (*)())SIG_DFL) { /* adb */ signal(sig, SIG_DFL); kill(getpid(), sig); /* Will we get back here? */ return; } /* * Looks like we should just cause the signal... * We hold the signal for the duration of the user's * code with the signal re-enabled. If the signal * happens again while in user code, we will recursively * trap here and mark that we had another occurance * and return to the user's trap code. When we return * from there, we can cause the signal again. */ sigtable[sig].s_flag &= ~SDEFER; sigtable[sig].s_flag |= SHELD; signal(sig, _Sigtramp); (*sigtable[sig].s_func)(sig); /* * If the signal re-occurred while in the user's routine, * just go try it again... */ sigtable[sig].s_flag &= ~SHELD; if (sigtable[sig].s_flag & SDEFER) goto top; } #else /* * In the child process after fork(2), set the disposition of all held * signals to SIG_IGN. This is a new procedure not in the real sigset() * package, provided for retrofitting purposes. With the real sigset() * package, this function is unnecessary. */ void sigchild() {} #endif