52180b64c6593f81b4d94c329acda192e2587664
[platform/upstream/rpm.git] / rpmio / rpmsq.c
1 /** \ingroup rpmio
2  * \file rpmio/rpmsq.c
3  */
4
5 #include "system.h"
6                                                                                 
7 #if defined(HAVE_PTHREAD_H) && !defined(__LCLINT__)
8 #include <pthread.h>
9 #endif
10
11 #include <rpmsq.h>
12
13 #include "debug.h"
14
15 /*@unchecked@*/
16 static struct rpmsqElem rpmsqRock;
17 /*@unchecked@*/
18 rpmsq rpmsqQueue = &rpmsqRock;
19
20 void Insque(void * elem, void * prev)
21 {
22     if (elem != NULL)
23         insque(elem, (prev ? prev : rpmsqQueue));
24 }
25
26 void Remque(void * elem)
27 {
28     if (elem != NULL)
29         remque(elem);
30 }
31
32 /*@unchecked@*/
33 sigset_t rpmsqCaught;
34
35 /*@unchecked@*/
36 static pthread_mutex_t rpmsigTbl_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
37
38 /*@unchecked@*/
39 /*@-fullinitblock@*/
40 static struct rpmsig_s {
41     int signum;
42     void (*handler) (int signum);
43     int active;
44     struct sigaction oact;
45 } rpmsigTbl[] = {
46     { SIGINT,   rpmsqHandler },
47 #define rpmsigTbl_sigint        (&rpmsigTbl[0])
48     { SIGQUIT,  rpmsqHandler },
49 #define rpmsigTbl_sigquit       (&rpmsigTbl[1])
50     { SIGCHLD,  rpmsqHandler },
51 #define rpmsigTbl_sigchld       (&rpmsigTbl[2])
52
53 #define DO_LOCK()       pthread_mutex_lock(&rpmsigTbl_lock);
54 #define DO_UNLOCK()     pthread_mutex_unlock(&rpmsigTbl_lock);
55 #define INIT_LOCK()     \
56      {  pthread_mutexattr_t attr; \
57         pthread_mutexattr_init(&attr); \
58         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
59         pthread_mutex_init (&rpmsigTbl_lock, &attr); \
60         pthread_mutexattr_destroy(&attr); \
61         rpmsigTbl_sigchld->active = 0; \
62      }
63 #define ADD_REF(__tbl)  (__tbl)->active++
64 #define SUB_REF(__tbl)  --(__tbl)->active
65
66 #define CLEANUP_HANDLER(__handler, __arg, __oldtypeptr) \
67         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, (__oldtypeptr)); \
68         pthread_cleanup_push((__handler), (__arg));
69 #define CLEANUP_RESET(__execute, __oldtype) \
70         pthread_cleanup_pop(__execute); \
71         pthread_setcanceltype ((__oldtype), &(__oldtype));
72
73     { SIGHUP,   rpmsqHandler },
74 #define rpmsigTbl_sighup        (&rpmsigTbl[3])
75     { SIGTERM,  rpmsqHandler },
76 #define rpmsigTbl_sigterm       (&rpmsigTbl[4])
77     { SIGPIPE,  rpmsqHandler },
78 #define rpmsigTbl_sigpipe       (&rpmsigTbl[5])
79     { -1,       NULL },
80 };
81 /*@=fullinitblock@*/
82
83 /**
84  */
85 /*@-incondefs@*/
86 void rpmsqHandler(int signum)
87 {
88     rpmsig tbl;
89
90     for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
91         if (tbl->signum != signum)
92             continue;
93
94         (void) sigaddset(&rpmsqCaught, signum);
95
96         switch (signum) {
97         case SIGCHLD:
98             while (1) {
99                 rpmsq sq;
100                 int status = 0;
101                 pid_t reaped = waitpid(0, &status, WNOHANG);
102
103                 if (reaped <= 0)
104                     /*@innerbreak@*/ break;
105
106                 for (sq = rpmsqQueue->q_forw;
107                      sq != NULL && sq != rpmsqQueue;
108                      sq = sq->q_forw)
109                 {
110                     if (sq->child != reaped)
111                         /*@innercontinue@*/ continue;
112                     sq->reaped = reaped;
113                     sq->status = status;
114                     /*@innerbreak@*/ break;
115                 }
116             }
117             /*@switchbreak@*/ break;
118         default:
119             /*@switchbreak@*/ break;
120         }
121         break;
122     }
123 }
124 /*@=incondefs@*/
125
126 /**
127  * Enable or disable a signal handler.
128  * @param signum        signal to enable (or disable if negative)
129  * @param handler       signal handler (or NULL to use rpmsqHandler())
130  * @return              no. of refs, -1 on error
131  */
132 int rpmsqEnable(int signum, /*@null@*/ sighandler_t handler)
133         /*@globals rpmsqCaught, rpmsigTbl @*/
134         /*@modifies rpmsqCaught, rpmsigTbl @*/
135 {
136     int tblsignum = (signum >= 0 ? signum : -signum);
137     struct sigaction sa;
138     rpmsig tbl;
139     int ret = -1;
140
141     DO_LOCK ();
142     for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
143         if (tblsignum != tbl->signum)
144             continue;
145
146         if (signum >= 0) {                      /* Enable. */
147             if (ADD_REF(tbl) <= 0) {
148                 tbl->active = 1;                /* XXX just in case */
149                 (void) sigdelset(&rpmsqCaught, tbl->signum);
150                 sa.sa_flags = 0;
151                 sigemptyset (&sa.sa_mask);
152                 sa.sa_handler = (handler != NULL ? handler : tbl->handler);
153                 if (sigaction(tbl->signum, &sa, &tbl->oact) < 0) {
154                     SUB_REF(tbl);
155                     break;
156                 }
157             }
158         } else {                                /* Disable. */
159             if (SUB_REF(tbl) <= 0) {
160                 tbl->active = 0;                /* XXX just in case */
161                 if (sigaction(tbl->signum, &tbl->oact, NULL) < 0)
162                     break;
163             }
164         }
165         ret = tbl->active;
166         break;
167     }
168     DO_UNLOCK ();
169     return ret;
170 }
171
172 /**
173  * SIGCHLD cancellation handler.
174  */
175 static void
176 sigchld_cancel (void *arg)
177 {
178     pid_t child = *(pid_t *) arg;
179     pid_t result;
180
181     (void) kill(child, SIGKILL);
182
183     do {
184         result = waitpid(child, NULL, 0);
185     } while (result == (pid_t)-1 && errno == EINTR);
186
187     DO_LOCK ();
188     if (SUB_REF (rpmsigTbl_sigchld) == 0) {
189         (void) rpmsqEnable(-SIGQUIT, NULL);
190         (void) rpmsqEnable(-SIGINT, NULL);
191     }
192     DO_UNLOCK ();
193 }
194
195 /**
196  * Execute a command, returning its status.
197  */
198 int
199 rpmsqExecve (const char ** argv)
200 {
201     int oldtype;
202     int status = -1;
203     pid_t pid;
204     pid_t result;
205     sigset_t newMask, oldMask;
206
207     DO_LOCK ();
208     if (ADD_REF (rpmsigTbl_sigchld) == 0) {
209         if (rpmsqEnable(SIGINT, NULL) < 0) {
210             SUB_REF (rpmsigTbl_sigchld);
211             goto out;
212         }
213         if (rpmsqEnable(SIGQUIT, NULL) < 0) {
214             SUB_REF (rpmsigTbl_sigchld);
215             goto out_restore_sigint;
216         }
217     }
218     DO_UNLOCK ();
219
220     sigemptyset (&newMask);
221     sigaddset (&newMask, SIGCHLD);
222     if (sigprocmask (SIG_BLOCK, &newMask, &oldMask) < 0) {
223         DO_LOCK ();
224         if (SUB_REF (rpmsigTbl_sigchld) == 0)
225             goto out_restore_sigquit_and_sigint;
226         goto out;
227     }
228
229     CLEANUP_HANDLER(sigchld_cancel, &pid, &oldtype);
230
231     pid = fork ();
232     if (pid < (pid_t) 0) {              /* fork failed.  */
233         goto out;
234     } else if (pid == (pid_t) 0) {      /* Child. */
235
236         /* Restore the signals.  */
237         (void) sigaction (SIGINT, &rpmsigTbl_sigint->oact, NULL);
238         (void) sigaction (SIGQUIT, &rpmsigTbl_sigquit->oact, NULL);
239         (void) sigprocmask (SIG_SETMASK, &oldMask, NULL);
240
241         /* Reset rpmsigTbl lock and refcnt. */
242         INIT_LOCK ();
243
244         (void) execve (argv[0], (char *const *) argv, environ);
245         _exit (127);
246     } else {                            /* Parent. */
247         do {
248             result = waitpid(pid, &status, 0);
249         } while (result == (pid_t)-1 && errno == EINTR);
250         if (result != pid)
251             status = -1;
252     }
253
254     CLEANUP_RESET(0, oldtype);
255
256     DO_LOCK ();
257     if ((SUB_REF (rpmsigTbl_sigchld) == 0 &&
258         (rpmsqEnable(-SIGINT, NULL) < 0 || rpmsqEnable (-SIGQUIT, NULL) < 0))
259       || sigprocmask (SIG_SETMASK, &oldMask, (sigset_t *) NULL) != 0)
260     {
261         status = -1;
262     }
263     goto out;
264
265 out_restore_sigquit_and_sigint:
266     (void) rpmsqEnable(-SIGQUIT, NULL);
267 out_restore_sigint:
268     (void) rpmsqEnable(-SIGINT, NULL);
269 out:
270     DO_UNLOCK ();
271     return status;
272 }