8 #include <sys/signal.h>
14 #if defined(HAVE_PTHREAD_H)
18 /* XXX suggested in bugzilla #159024 */
19 #if PTHREAD_MUTEX_DEFAULT != PTHREAD_MUTEX_NORMAL
20 #error RPM expects PTHREAD_MUTEX_DEFAULT == PTHREAD_MUTEX_NORMAL
23 #ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
24 static pthread_mutex_t rpmsigTbl_lock = PTHREAD_MUTEX_INITIALIZER;
26 static pthread_mutex_t rpmsigTbl_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
29 #define DO_LOCK() pthread_mutex_lock(&rpmsigTbl_lock);
30 #define DO_UNLOCK() pthread_mutex_unlock(&rpmsigTbl_lock);
31 #define ADD_REF(__tbl) (__tbl)->active++
32 #define SUB_REF(__tbl) --(__tbl)->active
34 #define ME() ((void *)pthread_self())
40 #define ADD_REF(__tbl) (0)
41 #define SUB_REF(__tbl) (0)
43 #define ME() (((void *)getpid()))
45 #endif /* HAVE_PTHREAD_H */
47 #define _RPMSQ_INTERNAL
48 #include <rpm/rpmsq.h>
52 #define _RPMSQ_DEBUG 0
53 int _rpmsq_debug = _RPMSQ_DEBUG;
55 static struct rpmsqElem rpmsqRock;
57 static rpmsq rpmsqQueue = &rpmsqRock;
60 * Insert node into from queue.
61 * @param elem node to link
62 * @param prev previous node from queue
63 * @return 0 on success
65 static int rpmsqInsert(void * elem, void * prev)
67 rpmsq sq = (rpmsq) elem;
73 fprintf(stderr, " Insert(%p): %p\n", ME(), sq);
75 ret = sighold(SIGCHLD);
81 sq->pipes[0] = sq->pipes[1] = -1;
84 ret = pthread_mutex_init(&sq->mutex, NULL);
85 insque(elem, (prev != NULL ? prev : rpmsqQueue));
86 ret = sigrelse(SIGCHLD);
93 * Remove node from queue.
94 * @param elem node to link
95 * @return 0 on success
97 static int rpmsqRemove(void * elem)
99 rpmsq sq = (rpmsq) elem;
106 fprintf(stderr, " Remove(%p): %p\n", ME(), sq);
108 ret = sighold (SIGCHLD);
112 /* Unlock the mutex and then destroy it */
113 if((ret = pthread_mutex_unlock(&sq->mutex)) == 0)
114 ret = pthread_mutex_destroy(&sq->mutex);
117 if (sq->pipes[1]) ret = close(sq->pipes[1]);
118 if (sq->pipes[0]) ret = close(sq->pipes[0]);
119 sq->pipes[0] = sq->pipes[1] = -1;
120 #ifdef NOTYET /* rpmpsmWait debugging message needs */
126 ret = sigrelse(SIGCHLD);
132 static sigset_t rpmsqCaught;
134 static struct rpmsig_s {
136 rpmsqAction_t handler;
138 struct sigaction oact;
140 { SIGINT, rpmsqAction },
141 #define rpmsigTbl_sigint (&rpmsigTbl[0])
142 { SIGQUIT, rpmsqAction },
143 #define rpmsigTbl_sigquit (&rpmsigTbl[1])
144 { SIGCHLD, rpmsqAction },
145 #define rpmsigTbl_sigchld (&rpmsigTbl[2])
146 { SIGHUP, rpmsqAction },
147 #define rpmsigTbl_sighup (&rpmsigTbl[3])
148 { SIGTERM, rpmsqAction },
149 #define rpmsigTbl_sigterm (&rpmsigTbl[4])
150 { SIGPIPE, rpmsqAction },
151 #define rpmsigTbl_sigpipe (&rpmsigTbl[5])
155 int rpmsqIsCaught(int signum)
157 return sigismember(&rpmsqCaught, signum);
161 void rpmsqAction(int signum, siginfo_t * info, void * context)
163 void rpmsqAction(int signum)
169 for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
170 if (tbl->signum != signum)
173 (void) sigaddset(&rpmsqCaught, signum);
180 pid_t reaped = waitpid(0, &status, WNOHANG);
182 /* XXX errno set to ECHILD/EINVAL/EINTR. */
186 /* XXX insque(3)/remque(3) are dequeue, not ring. */
187 for (sq = rpmsqQueue->q_forw;
188 sq != NULL && sq != rpmsqQueue;
193 if (sq->child != reaped)
198 /* Unlock the mutex. The waiter will then be able to
201 * XXX: jbj, wtd, if this fails?
203 ret = pthread_mutex_unlock(&sq->mutex);
217 int rpmsqEnable(int signum, rpmsqAction_t handler)
219 int tblsignum = (signum >= 0 ? signum : -signum);
225 if (rpmsqQueue->id == NULL)
226 rpmsqQueue->id = ME();
227 for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
228 if (tblsignum != tbl->signum)
231 if (signum >= 0) { /* Enable. */
232 if (ADD_REF(tbl) <= 0) {
233 (void) sigdelset(&rpmsqCaught, tbl->signum);
235 /* XXX Don't set a signal handler if already SIG_IGN */
236 (void) sigaction(tbl->signum, NULL, &tbl->oact);
237 if (tbl->oact.sa_handler == SIG_IGN)
240 (void) sigemptyset (&sa.sa_mask);
242 sa.sa_flags = SA_SIGINFO;
246 sa.sa_sigaction = (handler != NULL ? handler : tbl->handler);
247 if (sigaction(tbl->signum, &sa, &tbl->oact) < 0) {
251 tbl->active = 1; /* XXX just in case */
253 tbl->handler = handler;
255 } else { /* Disable. */
256 if (SUB_REF(tbl) <= 0) {
257 if (sigaction(tbl->signum, &tbl->oact, NULL) < 0)
259 tbl->active = 0; /* XXX just in case */
260 tbl->handler = (handler != NULL ? handler : rpmsqAction);
270 pid_t rpmsqFork(rpmsq sq)
274 int nothreads = 0; /* XXX: Shouldn't this be a global? */
277 xx = rpmsqInsert(sq, NULL);
280 fprintf(stderr, " Enable(%p): %p\n", ME(), sq);
282 xx = rpmsqEnable(SIGCHLD, NULL);
285 xx = pipe(sq->pipes);
287 xx = sighold(SIGCHLD);
290 * Initialize the cond var mutex. We have to aquire the lock we
291 * use for the condition before we fork. Otherwise it is possible for
292 * the child to exit, we get sigchild and the sig handler to send
293 * the condition signal before we are waiting on the condition.
296 if(pthread_mutex_lock(&sq->mutex)) {
297 /* Yack we did not get the lock, lets just give up */
298 xx = close(sq->pipes[0]);
299 xx = close(sq->pipes[1]);
300 sq->pipes[0] = sq->pipes[1] = -1;
306 if (pid < (pid_t) 0) { /* fork failed. */
307 sq->child = (pid_t)-1;
308 xx = close(sq->pipes[0]);
309 xx = close(sq->pipes[1]);
310 sq->pipes[0] = sq->pipes[1] = -1;
312 } else if (pid == (pid_t) 0) { /* Child. */
315 /* Block to permit parent time to wait. */
316 xx = close(sq->pipes[1]);
317 xx = read(sq->pipes[0], &yy, sizeof(yy));
318 xx = close(sq->pipes[0]);
319 sq->pipes[0] = sq->pipes[1] = -1;
323 fprintf(stderr, " Child(%p): %p child %d\n", ME(), sq, getpid());
326 } else { /* Parent. */
332 fprintf(stderr, " Parent(%p): %p child %d\n", ME(), sq, sq->child);
338 xx = sigrelse(SIGCHLD);
343 * Wait for child process to be reaped, and unregister SIGCHLD handler.
344 * @todo Rewrite to use waitpid on helper thread.
345 * @param sq scriptlet queue element
346 * @return 0 on success
348 static int rpmsqWaitUnregister(rpmsq sq)
354 /* Protect sq->reaped from handler changes. */
355 ret = sighold(SIGCHLD);
357 /* Start the child, linux often runs child before parent. */
358 if (sq->pipes[0] >= 0)
359 xx = close(sq->pipes[0]);
360 if (sq->pipes[1] >= 0)
361 xx = close(sq->pipes[1]);
362 sq->pipes[0] = sq->pipes[1] = -1;
364 /* Put a stopwatch on the time spent waiting to measure performance gain. */
365 (void) rpmswEnter(&sq->op, -1);
367 /* Wait for handler to receive SIGCHLD. */
368 while (ret == 0 && sq->reaped != sq->child) {
370 /* Note that sigpause re-enables SIGCHLD. */
371 ret = sigpause(SIGCHLD);
373 xx = sigrelse(SIGCHLD);
376 * We start before the fork with this mutex locked;
377 * The only one that unlocks this the signal handler.
378 * So if we get the lock the child has been reaped.
380 ret = pthread_mutex_lock(&sq->mutex);
381 xx = sighold(SIGCHLD);
385 /* Accumulate stopwatch time spent waiting, potential performance gain. */
386 sq->ms_scriptlets += rpmswExit(&sq->op, -1)/1000;
388 xx = sigrelse(SIGCHLD);
392 fprintf(stderr, " Wake(%p): %p child %d reaper %d ret %d\n", ME(), sq, sq->child, sq->reaper, ret);
395 /* Remove processed SIGCHLD item from queue. */
396 xx = rpmsqRemove(sq);
398 /* Disable SIGCHLD handler on refcount == 0. */
399 xx = rpmsqEnable(-SIGCHLD, NULL);
402 fprintf(stderr, " Disable(%p): %p\n", ME(), sq);
408 pid_t rpmsqWait(rpmsq sq)
413 fprintf(stderr, " Wait(%p): %p child %d reaper %d\n", ME(), sq, sq->child, sq->reaper);
417 (void) rpmsqWaitUnregister(sq);
422 reaped = waitpid(sq->child, &status, 0);
423 } while (reaped >= 0 && reaped != sq->child);
428 fprintf(stderr, " Waitpid(%p): %p child %d reaped %d\n", ME(), sq, sq->child, sq->reaped);
434 fprintf(stderr, " Fini(%p): %p child %d status 0x%x\n", ME(), sq, sq->child, sq->status);