1 /*
2  * Copyright (C) 2003  Sam Horrocks
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  */
19 
20 #include "speedy.h"
21 
22 /*
23  * Signal handling routines
24  */
25 
26 static volatile int got_sig[SPEEDY_MAXSIG];
27 static sigset_t blockall_save;
28 static int all_blocked;
29 
speedy_sig_blockall(void)30 void speedy_sig_blockall(void) {
31     sigset_t full_set;
32 
33     sigfillset(&full_set);
34     sigprocmask(SIG_BLOCK, &full_set, &blockall_save);
35     all_blocked = 1;
36 }
37 
speedy_sig_blockall_undo(void)38 void speedy_sig_blockall_undo(void) {
39     sigprocmask(SIG_SETMASK, &blockall_save, NULL);
40     all_blocked = 0;
41 }
42 
sig_find(const volatile int sig_rcvd[SPEEDY_MAXSIG],int sig)43 static int sig_find(const volatile int sig_rcvd[SPEEDY_MAXSIG], int sig) {
44     register int i;
45 
46     for (i = 0; i < SPEEDY_MAXSIG && sig_rcvd[i]; ++i) {
47 	if (sig_rcvd[i] == sig)
48 	    return -1;
49     }
50     return i;
51 }
52 
sig_handler(int sig)53 static void sig_handler(int sig) {
54     int i;
55 
56     if ((i = sig_find(got_sig, sig)) >= 0 && i < SPEEDY_MAXSIG) {
57 	got_sig[i++] = sig;
58 	if (i < SPEEDY_MAXSIG)
59 	    got_sig[i] = 0;
60     }
61 }
62 
sig_wait_basic(const SigList * sl)63 static void sig_wait_basic(const SigList *sl) {
64     for (got_sig[0] = 0; !got_sig[0];)
65 	sigsuspend(&sl->unblock_sigs);
66 }
67 
speedy_sig_wait(SigList * sl)68 void speedy_sig_wait(SigList *sl) {
69     sig_wait_basic(sl);
70     speedy_util_time_invalidate();
71     speedy_memcpy(sl->sig_rcvd, got_sig, sizeof(got_sig));
72 }
73 
speedy_sig_got(const SigList * sl,int sig)74 int speedy_sig_got(const SigList *sl, int sig) {
75     return sig_find(sl->sig_rcvd, sig) == -1;
76 }
77 
sig_init2(SigList * sl,int how)78 static void sig_init2(SigList *sl, int how) {
79     int i;
80 
81     /* Set up handlers and save old action setting */
82     {
83 	struct sigaction sigact;
84 	sigact.sa_handler = &sig_handler;
85 	sigact.sa_flags = 0;
86 	sigemptyset(&sigact.sa_mask);
87 	for (i = 0; i < sl->numsigs; ++i)
88 	    sigaction(sl->signum[i],  &sigact, &(sl->sigact_save[i]));
89     }
90 
91     /* Block or unblock our signals.  Save original mask */
92     if (all_blocked) {
93 	sl->sigset_save = blockall_save;
94 	for (i = 0; i < sl->numsigs; ++i) {
95 	    if (how == SIG_BLOCK)
96 		sigaddset(&blockall_save, sl->signum[i]);
97 	    else
98 		sigdelset(&blockall_save, sl->signum[i]);
99 	}
100     } else {
101 	sigset_t block_sigs;
102 	sigemptyset(&block_sigs);
103 	for (i = 0; i < sl->numsigs; ++i)
104 	    sigaddset(&block_sigs, sl->signum[i]);
105 	sigprocmask(how, &block_sigs, &sl->sigset_save);
106     }
107 
108     /* Make an unblock mask for our signals */
109     sl->unblock_sigs = sl->sigset_save;
110     for (i = 0; i < sl->numsigs; ++i)
111 	sigdelset(&sl->unblock_sigs, sl->signum[i]);
112 }
113 
speedy_sig_init(SigList * sl,const int * sigs,int numsigs,int how)114 void speedy_sig_init(SigList *sl, const int *sigs, int numsigs, int how) {
115 
116     /* Copy in args */
117     if (numsigs > SPEEDY_MAXSIG)
118 	DIE_QUIET("Too many sigs passed to sig_init");
119     speedy_memcpy(sl->signum, sigs, numsigs * sizeof(int));
120     sl->numsigs = numsigs;
121 
122     /* Finish init */
123     sig_init2(sl, how);
124 }
125 
speedy_sig_free(const SigList * sl)126 void speedy_sig_free(const SigList *sl) {
127     int i;
128 
129     /* Get rid of any pending signals.  On Sun/apache-2 we don't get pending
130      * signals as soon as they are unblocked - instead they get delivered
131      * after the action is restored, which is not what we want.
132      */
133     do {
134 	sigset_t set;
135 
136 	/* Bug in Mac OS X 10.1 and earlier - sigpending is essentially a
137 	 * no-op, so we get garbage, and get stuck in sigsuspend.
138 	 * Workaround by clearing out the set initially so we get no pending
139 	 * signals back.
140 	 */
141 	sigemptyset(&set);
142 
143 	if (sigpending(&set) == -1)
144 	    break;
145 	for (i = 0; i < sl->numsigs; ++i) {
146 	    if (sigismember(&set, sl->signum[i])) {
147 		sig_wait_basic(sl);
148 		break;
149 	    }
150 	}
151     } while (i < sl->numsigs);
152 
153     /* Unblock sigs */
154     if (all_blocked)
155 	blockall_save = sl->sigset_save;
156     else
157 	sigprocmask(SIG_SETMASK, &sl->sigset_save, NULL);
158 
159     /* Install old handlers */
160     for (i = 0; i < sl->numsigs; ++i)
161 	sigaction(sl->signum[i], &(sl->sigact_save[i]), NULL);
162 }
163