1 /*
2  * Signal handling for Plan 9 programs.
3  * We stubbornly use the strings from Plan 9 instead
4  * of the enumerated Unix constants.
5  * There are some weird translations.  In particular,
6  * a "kill" note is the same as SIGTERM in Unix.
7  * There is no equivalent note to Unix's SIGKILL, since
8  * it's not a deliverable signal anyway.
9  *
10  * We do not handle SIGABRT or SIGSEGV, mainly because
11  * the thread library queues its notes for later, and we want
12  * to dump core with the state at time of delivery.
13  *
14  * We have to add some extra entry points to provide the
15  * ability to tweak which signals are deliverable and which
16  * are acted upon.  Notifydisable and notifyenable play with
17  * the process signal mask.  Notifyignore enables the signal
18  * but will not call notifyf when it comes in.  This is occasionally
19  * useful.
20  */
21 
22 #include <u.h>
23 #include <signal.h>
24 #define NOPLAN9DEFINES
25 #include <libc.h>
26 
27 extern char *_p9sigstr(int, char*);
28 extern int _p9strsig(char*);
29 
30 typedef struct Sig Sig;
31 struct Sig
32 {
33 	int sig;			/* signal number */
34 	int flags;
35 };
36 
37 enum
38 {
39 	Restart = 1<<0,
40 	Ignore = 1<<1,
41 	NoNotify = 1<<2,
42 };
43 
44 static Sig sigs[] = {
45 	SIGHUP,		0,
46 	SIGINT,		0,
47 	SIGQUIT,		0,
48 	SIGILL,		0,
49 	SIGTRAP,		0,
50 /*	SIGABRT, 		0, 	*/
51 #ifdef SIGEMT
52 	SIGEMT,		0,
53 #endif
54 	SIGFPE,		0,
55 	SIGBUS,		0,
56 /*	SIGSEGV, 		0, 	*/
57 	SIGCHLD,		Restart|Ignore,
58 	SIGSYS,		0,
59 	SIGPIPE,		Ignore,
60 	SIGALRM,		0,
61 	SIGTERM,		0,
62 	SIGTSTP,		Restart|Ignore|NoNotify,
63 /*	SIGTTIN,		Restart|Ignore, */
64 /*	SIGTTOU,		Restart|Ignore, */
65 	SIGXCPU,		0,
66 	SIGXFSZ,		0,
67 	SIGVTALRM,	0,
68 	SIGUSR1,		0,
69 	SIGUSR2,		0,
70 #ifdef SIGWINCH
71 	SIGWINCH,	Restart|Ignore|NoNotify,
72 #endif
73 #ifdef SIGINFO
74 	SIGINFO,		Restart|Ignore|NoNotify,
75 #endif
76 };
77 
78 static Sig*
findsig(int s)79 findsig(int s)
80 {
81 	int i;
82 
83 	for(i=0; i<nelem(sigs); i++)
84 		if(sigs[i].sig == s)
85 			return &sigs[i];
86 	return nil;
87 }
88 
89 /*
90  * The thread library initializes _notejmpbuf to its own
91  * routine which provides a per-pthread jump buffer.
92  * If we're not using the thread library, we assume we are
93  * single-threaded.
94  */
95 typedef struct Jmp Jmp;
96 struct Jmp
97 {
98 	p9jmp_buf b;
99 };
100 
101 static Jmp onejmp;
102 
103 static Jmp*
getonejmp(void)104 getonejmp(void)
105 {
106 	return &onejmp;
107 }
108 
109 Jmp *(*_notejmpbuf)(void) = getonejmp;
110 static void noteinit(void);
111 
112 /*
113  * Actual signal handler.
114  */
115 
116 static void (*notifyf)(void*, char*);	/* Plan 9 handler */
117 
118 static void
signotify(int sig)119 signotify(int sig)
120 {
121 	char tmp[64];
122 	Jmp *j;
123 	Sig *s;
124 
125 	j = (*_notejmpbuf)();
126 	switch(p9setjmp(j->b)){
127 	case 0:
128 		if(notifyf)
129 			(*notifyf)(nil, _p9sigstr(sig, tmp));
130 		/* fall through */
131 	case 1:	/* noted(NDFLT) */
132 		if(0)print("DEFAULT %d\n", sig);
133 		s = findsig(sig);
134 		if(s && (s->flags&Ignore))
135 			return;
136 		signal(sig, SIG_DFL);
137 		raise(sig);
138 		_exit(1);
139 	case 2:	/* noted(NCONT) */
140 		if(0)print("HANDLED %d\n", sig);
141 		return;
142 	}
143 }
144 
145 static void
signonotify(int sig)146 signonotify(int sig)
147 {
148 	USED(sig);
149 }
150 
151 int
noted(int v)152 noted(int v)
153 {
154 	p9longjmp((*_notejmpbuf)()->b, v==NCONT ? 2 : 1);
155 	abort();
156 	return 0;
157 }
158 
159 int
notify(void (* f)(void *,char *))160 notify(void (*f)(void*, char*))
161 {
162 	static int init;
163 
164 	notifyf = f;
165 	if(!init){
166 		init = 1;
167 		noteinit();
168 	}
169 	return 0;
170 }
171 
172 /*
173  * Nonsense about enabling and disabling signals.
174  */
175 typedef void Sighandler(int);
176 static Sighandler*
handler(int s)177 handler(int s)
178 {
179 	struct sigaction sa;
180 
181 	sigaction(s, nil, &sa);
182 	return sa.sa_handler;
183 }
184 
185 static int
notesetenable(int sig,int enabled)186 notesetenable(int sig, int enabled)
187 {
188 	sigset_t mask, omask;
189 
190 	if(sig == 0)
191 		return -1;
192 
193 	sigemptyset(&mask);
194 	sigaddset(&mask, sig);
195 	sigprocmask(enabled ? SIG_UNBLOCK : SIG_BLOCK, &mask, &omask);
196 	return !sigismember(&omask, sig);
197 }
198 
199 int
noteenable(char * msg)200 noteenable(char *msg)
201 {
202 	return notesetenable(_p9strsig(msg), 1);
203 }
204 
205 int
notedisable(char * msg)206 notedisable(char *msg)
207 {
208 	return notesetenable(_p9strsig(msg), 0);
209 }
210 
211 static int
notifyseton(int s,int on)212 notifyseton(int s, int on)
213 {
214 	Sig *sig;
215 	struct sigaction sa, osa;
216 
217 	sig = findsig(s);
218 	if(sig == nil)
219 		return -1;
220 	memset(&sa, 0, sizeof sa);
221 	sa.sa_handler = on ? signotify : signonotify;
222 	if(sig->flags&Restart)
223 		sa.sa_flags |= SA_RESTART;
224 
225 	/*
226 	 * We can't allow signals within signals because there's
227 	 * only one jump buffer.
228 	 */
229 	sigfillset(&sa.sa_mask);
230 
231 	/*
232 	 * Install handler.
233 	 */
234 	sigaction(sig->sig, &sa, &osa);
235 	return osa.sa_handler == signotify;
236 }
237 
238 int
notifyon(char * msg)239 notifyon(char *msg)
240 {
241 	return notifyseton(_p9strsig(msg), 1);
242 }
243 
244 int
notifyoff(char * msg)245 notifyoff(char *msg)
246 {
247 	return notifyseton(_p9strsig(msg), 0);
248 }
249 
250 /*
251  * Initialization follows sigs table.
252  */
253 static void
noteinit(void)254 noteinit(void)
255 {
256 	int i;
257 	Sig *sig;
258 
259 	for(i=0; i<nelem(sigs); i++){
260 		sig = &sigs[i];
261 		/*
262 		 * If someone has already installed a handler,
263 		 * It's probably some ld preload nonsense,
264 		 * like pct (a SIGVTALRM-based profiler).
265 		 * Or maybe someone has already called notifyon/notifyoff.
266 		 * Leave it alone.
267 		 */
268 		if(handler(sig->sig) != SIG_DFL)
269 			continue;
270 		notifyseton(sig->sig, !(sig->flags&NoNotify));
271 	}
272 }
273 
274