/* * Copyright (c) 1980 Regents of the University of California. * All rights reserved. * * Redistribution and use in source and binary forms are permitted * provided that this notice is preserved and that due credit is given * to the University of California at Berkeley. The name of the University * may not be used to endorse or promote products derived from this * software without specific prior written permission. This software * is provided ``as is'' without express or implied warranty. */ #ifdef notdef static char sccsid[] = "@(#)sigretro.c 5.3 (Berkeley) 2/18/88"; #endif /* notdef */ #include <signal.h> #include <errno.h> #include <setjmp.h> #include "sigretro.h" /* * 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) */ typedef int (*sigtype)(); sigtype sigdisp(), sighold(), sigignore(); /* * 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. */ 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 vfork(). 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; int _sigtramp(); extern int errno; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(BADSIG); } old = sigdisp(sig); /* * Does anyone actually call sigset with SIG_HOLD!? */ if (func == SIG_HOLD) { sighold(sig); return(old); } sigtable[sig].s_flag |= SSET; sigtable[sig].s_func = func; if (func == SIG_DFL) { /* * 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 == SIG_IGN) { /* * 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 * happened and do something crazy later. */ sigtype sighold(sig) { sigtype old; extern int errno; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(BADSIG); } 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 == SIG_DFL) { 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; extern int errno; int _sigtramp(); if (sig < 1 || sig > NSIG) { errno = EINVAL; return(BADSIG); } 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 == SIG_DFL) 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. * * This is probably useless without job control anyway. */ sigpause(sig) { extern int errno; 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. */ 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) { extern int errno; sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return(BADSIG); } /* * 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 = signal(sig, SIG_IGN); 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. */ _sigtramp(sig) { extern int errno; sigtype old; if (sig < 1 || sig > NSIG) { errno = EINVAL; return; } top: old = signal(sig, SIG_IGN); /* * If signal being paused on, wakeup sigpause() */ if (sigtable[sig].s_flag & SPAUSE) longjmp(_pause, 1); /* * If signal being held, mark its table entry * so we can trigger it when signal 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 == SIG_IGN) return; /* * If the signal is SIG_DFL, then we probably got here * by holding the signal, having it happen, then releasing * the signal. I wonder if a process is allowed to send * a signal to itself? */ if (sigtable[sig].s_func == SIG_DFL) { 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; }