fe58d0983be60af1b7c3f296dcfc6e0cf7f9c024
[platform/upstream/rpm.git] / rpmio / rpmsq.c
1 /*@-unrecog@*/
2 /** \ingroup rpmio
3  * \file rpmio/rpmsq.c
4  */
5
6 #include "system.h"
7
8 #if defined(__LCLINT__)
9 struct qelem;
10 /*@-exportheader@*/
11 extern  void insque(struct qelem * __elem, struct qelem * __prev)
12         /*@modifies  __elem, prev @*/;
13 extern  void remque(struct qelem * __elem)
14         /*@modifies  __elem @*/;
15 /*@=exportheader@*/
16 #endif
17
18 #include <signal.h>
19 #include <sys/signal.h>
20 #include <sys/wait.h>
21 #include <search.h>
22
23 #if defined(HAVE_PTHREAD_H) && !defined(__LCLINT__)
24
25 #include <pthread.h>
26
27 /*@unchecked@*/
28 static pthread_mutex_t rpmsigTbl_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
29
30 #define DO_LOCK()       pthread_mutex_lock(&rpmsigTbl_lock);
31 #define DO_UNLOCK()     pthread_mutex_unlock(&rpmsigTbl_lock);
32 #define INIT_LOCK()     \
33      {  pthread_mutexattr_t attr; \
34         pthread_mutexattr_init(&attr); \
35         pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); \
36         pthread_mutex_init (&rpmsigTbl_lock, &attr); \
37         pthread_mutexattr_destroy(&attr); \
38         rpmsigTbl_sigchld->active = 0; \
39      }
40 #define ADD_REF(__tbl)  (__tbl)->active++
41 #define SUB_REF(__tbl)  --(__tbl)->active
42 #define CLEANUP_HANDLER(__handler, __arg, __oldtypeptr) \
43         pthread_setcanceltype (PTHREAD_CANCEL_ASYNCHRONOUS, (__oldtypeptr)); \
44         pthread_cleanup_push((__handler), (__arg));
45 #define CLEANUP_RESET(__execute, __oldtype) \
46         pthread_cleanup_pop(__execute); \
47         pthread_setcanceltype ((__oldtype), &(__oldtype));
48
49 #define SAME_THREAD(_a, _b)     pthread_equal(((pthread_t)_a), ((pthread_t)_b))
50
51 #define ME()    ((void *)pthread_self())
52
53 #else
54
55 #define DO_LOCK()
56 #define DO_UNLOCK()
57 #define INIT_LOCK()
58 #define ADD_REF(__tbl)  /*@-noeffect@*/ (0) /*@=noeffect@*/
59 #define SUB_REF(__tbl)  /*@-noeffect@*/ (0) /*@=noeffect@*/
60 #define CLEANUP_HANDLER(__handler, __arg, __oldtypeptr)
61 #define CLEANUP_RESET(__execute, __oldtype)
62
63 #define SAME_THREAD(_a, _b)     (42)
64
65 #define ME()    (((void *)getpid()))
66
67 #endif  /* HAVE_PTHREAD_H */
68
69 #include <rpmsq.h>
70
71 #include "debug.h"
72
73 #define _RPMSQ_DEBUG    0
74 /*@unchecked@*/
75 int _rpmsq_debug = _RPMSQ_DEBUG;
76
77 /*@unchecked@*/
78 static struct rpmsqElem rpmsqRock;
79
80 /*@-compmempass@*/
81 /*@unchecked@*/
82 rpmsq rpmsqQueue = &rpmsqRock;
83 /*@=compmempass@*/
84
85 /*@-mustmod@*/
86 int rpmsqInsert(void * elem, /*@unused@*/ void * prev)
87 {
88     rpmsq sq = (rpmsq) elem;
89     int ret = -1;
90
91     if (sq != NULL) {
92 #ifdef _RPMSQ_DEBUG
93 /*@-modfilesys@*/
94 if (_rpmsq_debug)
95 fprintf(stderr, "    Insert(%p): %p\n", ME(), sq);
96 /*@=modfilesys@*/
97 #endif
98         ret = sighold(SIGCHLD);
99         if (ret == 0) {
100             sq->child = 0;
101             sq->reaped = 0;
102             sq->status = 0;
103             sq->reaper = 1;
104 /*@-bounds@*/
105             sq->pipes[0] = sq->pipes[1] = -1;
106 /*@=bounds@*/
107
108 /*@-unqualifiedtrans@*/
109             sq->id = ME();
110 /*@=unqualifiedtrans@*/
111             ret = pthread_mutex_init(&sq->mutex, NULL);
112             ret = pthread_cond_init(&sq->cond, NULL);
113 #if !defined(__LCLINT__)        /* XXX FIXME */
114             insque(elem, (prev ? prev : rpmsqQueue));
115 #endif
116             ret = sigrelse(SIGCHLD);
117         }
118     }
119     return ret;
120 }
121 /*@=mustmod@*/
122
123 int rpmsqRemove(void * elem)
124 {
125     rpmsq sq = (rpmsq) elem;
126     int ret = -1;
127
128     if (elem != NULL) {
129
130 #ifdef _RPMSQ_DEBUG
131 /*@-modfilesys@*/
132 if (_rpmsq_debug)
133 fprintf(stderr, "    Remove(%p): %p\n", ME(), sq);
134 /*@=modfilesys@*/
135 #endif
136         ret = sighold (SIGCHLD);
137         if (ret == 0) {
138             remque(elem);
139             ret = pthread_cond_destroy(&sq->cond);
140             ret = pthread_mutex_destroy(&sq->mutex);
141             sq->id = NULL;
142 /*@-bounds@*/
143             if (sq->pipes[1])   ret = close(sq->pipes[1]);
144             if (sq->pipes[0])   ret = close(sq->pipes[0]);
145             sq->pipes[0] = sq->pipes[1] = -1;
146 /*@=bounds@*/
147 #ifdef  NOTYET  /* rpmpsmWait debugging message needs */
148             sq->reaper = 1;
149             sq->status = 0;
150             sq->reaped = 0;
151             sq->child = 0;
152 #endif
153             ret = sigrelse(SIGCHLD);
154         }
155     }
156     return ret;
157 }
158
159 /*@unchecked@*/
160 sigset_t rpmsqCaught;
161
162 /*@unchecked@*/
163 /*@-fullinitblock@*/
164 static struct rpmsig_s {
165     int signum;
166     void (*handler) (int signum, void * info, void * context);
167     int active;
168     struct sigaction oact;
169 } rpmsigTbl[] = {
170     { SIGINT,   rpmsqAction },
171 #define rpmsigTbl_sigint        (&rpmsigTbl[0])
172     { SIGQUIT,  rpmsqAction },
173 #define rpmsigTbl_sigquit       (&rpmsigTbl[1])
174     { SIGCHLD,  rpmsqAction },
175 #define rpmsigTbl_sigchld       (&rpmsigTbl[2])
176     { SIGHUP,   rpmsqAction },
177 #define rpmsigTbl_sighup        (&rpmsigTbl[3])
178     { SIGTERM,  rpmsqAction },
179 #define rpmsigTbl_sigterm       (&rpmsigTbl[4])
180     { SIGPIPE,  rpmsqAction },
181 #define rpmsigTbl_sigpipe       (&rpmsigTbl[5])
182     { -1,       NULL },
183 };
184 /*@=fullinitblock@*/
185
186 /*@-incondefs@*/
187 void rpmsqAction(int signum,
188                 /*@unused@*/ void * info, /*@unused@*/ void * context)
189         /*@globals rpmsqQueue @*/
190         /*@modifies rpmsqQueue @*/
191 {
192     int save = errno;
193     rpmsig tbl;
194
195     for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
196         if (tbl->signum != signum)
197             continue;
198
199         (void) sigaddset(&rpmsqCaught, signum);
200
201         switch (signum) {
202         case SIGCHLD:
203             while (1) {
204                 rpmsq sq;
205                 int status = 0;
206                 pid_t reaped = waitpid(0, &status, WNOHANG);
207
208                 /* XXX errno set to ECHILD/EINVAL/EINTR. */
209                 if (reaped <= 0)
210                     /*@innerbreak@*/ break;
211
212                 /* XXX insque(3)/remque(3) are dequeue, not ring. */
213                 for (sq = rpmsqQueue->q_forw;
214                      sq != NULL && sq != rpmsqQueue;
215                      sq = sq->q_forw)
216                 {
217                     if (sq->child != reaped)
218                         /*@innercontinue@*/ continue;
219                     sq->reaped = reaped;
220                     sq->status = status;
221 #if defined(HAVE_PTHREAD_H) && !defined(__LCLINT__)
222                     (void) pthread_cond_signal(&sq->cond);
223 #endif
224                     /*@innerbreak@*/ break;
225                 }
226             }
227             /*@switchbreak@*/ break;
228         default:
229             /*@switchbreak@*/ break;
230         }
231         break;
232     }
233     errno = save;
234 }
235 /*@=incondefs@*/
236
237 int rpmsqEnable(int signum, /*@null@*/ rpmsqAction_t handler)
238         /*@globals rpmsigTbl @*/
239         /*@modifies rpmsigTbl @*/
240 {
241     int tblsignum = (signum >= 0 ? signum : -signum);
242     struct sigaction sa;
243     rpmsig tbl;
244     int ret = -1;
245
246     DO_LOCK ();
247 #if !defined(__LCLINT__)
248     if (rpmsqQueue->id == NULL)
249         rpmsqQueue->id = ME();
250 #endif
251     for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) {
252         if (tblsignum != tbl->signum)
253             continue;
254
255         if (signum >= 0) {                      /* Enable. */
256             if (ADD_REF(tbl) <= 0) {
257                 (void) sigdelset(&rpmsqCaught, tbl->signum);
258                 (void) sigemptyset (&sa.sa_mask);
259 /*@-compdef -type @*/
260                 sa.sa_flags = SA_SIGINFO;
261                 sa.sa_sigaction = (handler != NULL ? handler : tbl->handler);
262                 if (sigaction(tbl->signum, &sa, &tbl->oact) < 0) {
263                     SUB_REF(tbl);
264                     break;
265                 }
266 /*@=compdef =type @*/
267                 tbl->active = 1;                /* XXX just in case */
268                 if (handler != NULL)
269                     tbl->handler = handler;
270             }
271         } else {                                /* Disable. */
272             if (SUB_REF(tbl) <= 0) {
273                 if (sigaction(tbl->signum, &tbl->oact, NULL) < 0)
274                     break;
275                 tbl->active = 0;                /* XXX just in case */
276                 tbl->handler = (handler != NULL ? handler : rpmsqAction);
277             }
278         }
279         ret = tbl->active;
280         break;
281     }
282     DO_UNLOCK ();
283     return ret;
284 }
285
286 pid_t rpmsqFork(rpmsq sq)
287 {
288     pid_t pid;
289     int xx;
290
291     if (sq->reaper) {
292         xx = rpmsqInsert(sq, NULL);
293 #ifdef _RPMSQ_DEBUG
294 /*@-modfilesys@*/
295 if (_rpmsq_debug)
296 fprintf(stderr, "    Enable(%p): %p\n", ME(), sq);
297 /*@=modfilesys@*/
298 #endif
299         xx = rpmsqEnable(SIGCHLD, NULL);
300     }
301
302     xx = pipe(sq->pipes);
303
304     xx = sighold(SIGCHLD);
305
306     pid = fork();
307     if (pid < (pid_t) 0) {              /* fork failed.  */
308 /*@-bounds@*/
309         xx = close(sq->pipes[0]);
310         xx = close(sq->pipes[1]);
311         sq->pipes[0] = sq->pipes[1] = -1;
312 /*@=bounds@*/
313         goto out;
314     } else if (pid == (pid_t) 0) {      /* Child. */
315         int yy;
316
317         /* Block to permit parent to wait. */
318 /*@-bounds@*/
319         xx = close(sq->pipes[1]);
320         xx = read(sq->pipes[0], &yy, sizeof(yy));
321         xx = close(sq->pipes[0]);
322         sq->pipes[0] = sq->pipes[1] = -1;
323 /*@=bounds@*/
324
325 #ifdef _RPMSQ_DEBUG
326 /*@-modfilesys@*/
327 if (_rpmsq_debug)
328 fprintf(stderr, "     Child(%p): %p child %d\n", ME(), sq, getpid());
329 /*@=modfilesys@*/
330 #endif
331
332     } else {                            /* Parent. */
333
334         sq->child = pid;
335
336 #ifdef _RPMSQ_DEBUG
337 /*@-modfilesys@*/
338 if (_rpmsq_debug)
339 fprintf(stderr, "    Parent(%p): %p child %d\n", ME(), sq, sq->child);
340 /*@=modfilesys@*/
341 #endif
342
343 #ifdef  DYING
344 /*@-bounds@*/
345         /* Unblock child. */
346         xx = close(sq->pipes[0]);
347         xx = close(sq->pipes[1]);
348         sq->pipes[0] = sq->pipes[1] = -1;
349 /*@=bounds@*/
350 #endif
351
352     }
353
354 out:
355     xx = sigrelse(SIGCHLD);
356     return sq->child;
357 }
358
359 /**
360  * Wait for child process to be reaped, and unregister SIGCHLD handler.
361  * @param sq            scriptlet queue element
362  * @return              0 on success
363  */
364 static int rpmsqWaitUnregister(rpmsq sq)
365         /*@globals fileSystem, internalState @*/
366         /*@modifies sq, fileSystem, internalState @*/
367 {
368     struct rpmsw_s end;
369     int same_thread = 0;
370     int ret = 0;
371     int xx;
372
373     if (same_thread)
374         ret = sighold(SIGCHLD);
375     else
376         ret = pthread_mutex_lock(&sq->mutex);
377
378     /* Start the child. */
379 /*@-bounds@*/
380     if (sq->pipes[0] >= 0)
381         xx = close(sq->pipes[0]);
382     if (sq->pipes[1] >= 0)
383         xx = close(sq->pipes[1]);
384     sq->pipes[0] = sq->pipes[1] = -1;
385 /*@=bounds@*/
386
387     (void) rpmswNow(&sq->begin);
388
389     /*@-infloops@*/
390     while (ret == 0 && sq->reaped != sq->child) {
391         if (same_thread)
392             ret = sigpause(SIGCHLD);
393         else
394             ret = pthread_cond_wait(&sq->cond, &sq->mutex);
395     }
396     /*@=infloops@*/
397
398 /*@-uniondef@*/
399     sq->msecs = rpmswDiff(rpmswNow(&end), &sq->begin)/1000;
400 /*@=uniondef@*/
401     sq->script_msecs += sq->msecs;
402
403     if (same_thread)
404         xx = sigrelse(SIGCHLD);
405     else
406         xx = pthread_mutex_unlock(&sq->mutex);
407
408 #ifdef _RPMSQ_DEBUG
409 /*@-modfilesys@*/
410 if (_rpmsq_debug)
411 fprintf(stderr, "      Wake(%p): %p child %d reaper %d ret %d\n", ME(), sq, sq->child, sq->reaper, ret);
412 /*@=modfilesys@*/
413 #endif
414
415     xx = rpmsqRemove(sq);
416     xx = rpmsqEnable(-SIGCHLD, NULL);
417 #ifdef _RPMSQ_DEBUG
418 /*@-modfilesys@*/
419 if (_rpmsq_debug)
420 fprintf(stderr, "   Disable(%p): %p\n", ME(), sq);
421 /*@=modfilesys@*/
422 #endif
423
424     return ret;
425 }
426
427 pid_t rpmsqWait(rpmsq sq)
428 {
429
430 #ifdef _RPMSQ_DEBUG
431 /*@-modfilesys@*/
432 if (_rpmsq_debug)
433 fprintf(stderr, "      Wait(%p): %p child %d reaper %d\n", ME(), sq, sq->child, sq->reaper);
434 /*@=modfilesys@*/
435 #endif
436
437     if (sq->reaper) {
438         (void) rpmsqWaitUnregister(sq);
439     } else {
440         pid_t reaped;
441         int status;
442         do {
443             reaped = waitpid(sq->child, &status, 0);
444         } while (reaped >= 0 && reaped != sq->child);
445         sq->reaped = reaped;
446         sq->status = status;
447 #ifdef _RPMSQ_DEBUG
448 /*@-modfilesys@*/
449 if (_rpmsq_debug)
450 fprintf(stderr, "   Waitpid(%p): %p child %d reaped %d\n", ME(), sq, sq->child, sq->reaped);
451 /*@=modfilesys@*/
452 #endif
453     }
454
455 #ifdef _RPMSQ_DEBUG
456 /*@-modfilesys@*/
457 if (_rpmsq_debug)
458 fprintf(stderr, "      Fini(%p): %p child %d status 0x%x\n", ME(), sq, sq->child, sq->status);
459 /*@=modfilesys@*/
460 #endif
461
462     return sq->reaped;
463 }
464
465 int rpmsqThread(void * (*start) (void * arg), void * arg)
466 {
467     pthread_t pth;
468     int ret;
469
470     ret = pthread_create(&pth, NULL, start, arg);
471     if (ret == 0) {
472 #if 0
473 fprintf(stderr, "    Thread(%p): %p\n", ME(), pth);
474 #endif
475         ret = pthread_join(pth, NULL);
476     }
477     return ret;
478 }
479
480 /**
481  * SIGCHLD cancellation handler.
482  */
483 static void
484 sigchld_cancel (void *arg)
485         /*@globals fileSystem, internalState @*/
486         /*@modifies fileSystem, internalState @*/
487 {
488     pid_t child = *(pid_t *) arg;
489     pid_t result;
490
491     (void) kill(child, SIGKILL);
492
493     do {
494         result = waitpid(child, NULL, 0);
495     } while (result == (pid_t)-1 && errno == EINTR);
496
497     DO_LOCK ();
498     if (SUB_REF (rpmsigTbl_sigchld) == 0) {
499         (void) rpmsqEnable(-SIGQUIT, NULL);
500         (void) rpmsqEnable(-SIGINT, NULL);
501     }
502     DO_UNLOCK ();
503 }
504
505 /**
506  * Execute a command, returning its status.
507  */
508 /*@-bounds@*/
509 int
510 rpmsqExecve (const char ** argv)
511 {
512     int oldtype;
513     int status = -1;
514     pid_t pid;
515     pid_t result;
516     sigset_t newMask, oldMask;
517     rpmsq sq = memset(alloca(sizeof(*sq)), 0, sizeof(*sq));
518
519     DO_LOCK ();
520     if (ADD_REF (rpmsigTbl_sigchld) == 0) {
521         if (rpmsqEnable(SIGINT, NULL) < 0) {
522             SUB_REF (rpmsigTbl_sigchld);
523             goto out;
524         }
525         if (rpmsqEnable(SIGQUIT, NULL) < 0) {
526             SUB_REF (rpmsigTbl_sigchld);
527             goto out_restore_sigint;
528         }
529     }
530     DO_UNLOCK ();
531
532     (void) sigemptyset (&newMask);
533     (void) sigaddset (&newMask, SIGCHLD);
534     if (sigprocmask (SIG_BLOCK, &newMask, &oldMask) < 0) {
535         DO_LOCK ();
536         if (SUB_REF (rpmsigTbl_sigchld) == 0)
537             goto out_restore_sigquit_and_sigint;
538         goto out;
539     }
540
541     CLEANUP_HANDLER(sigchld_cancel, &pid, &oldtype);
542
543     pid = fork ();
544     if (pid < (pid_t) 0) {              /* fork failed.  */
545         goto out;
546     } else if (pid == (pid_t) 0) {      /* Child. */
547
548         /* Restore the signals.  */
549         (void) sigaction (SIGINT, &rpmsigTbl_sigint->oact, NULL);
550         (void) sigaction (SIGQUIT, &rpmsigTbl_sigquit->oact, NULL);
551         (void) sigprocmask (SIG_SETMASK, &oldMask, NULL);
552
553         /* Reset rpmsigTbl lock and refcnt. */
554         INIT_LOCK ();
555
556         (void) execve (argv[0], (char *const *) argv, environ);
557         _exit (127);
558     } else {                            /* Parent. */
559         do {
560             result = waitpid(pid, &status, 0);
561         } while (result == (pid_t)-1 && errno == EINTR);
562         if (result != pid)
563             status = -1;
564     }
565
566     CLEANUP_RESET(0, oldtype);
567
568     DO_LOCK ();
569     if ((SUB_REF (rpmsigTbl_sigchld) == 0 &&
570         (rpmsqEnable(-SIGINT, NULL) < 0 || rpmsqEnable (-SIGQUIT, NULL) < 0))
571       || sigprocmask (SIG_SETMASK, &oldMask, NULL) != 0)
572     {
573         status = -1;
574     }
575     goto out;
576
577 out_restore_sigquit_and_sigint:
578     (void) rpmsqEnable(-SIGQUIT, NULL);
579 out_restore_sigint:
580     (void) rpmsqEnable(-SIGINT, NULL);
581 out:
582     DO_UNLOCK ();
583     return status;
584 }
585 /*@=bounds@*/
586 /*@=unrecog@*/