1c2536ced2c750143752824227ee5403155e34e5
[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     }
344
345 out:
346     xx = sigrelse(SIGCHLD);
347     return sq->child;
348 }
349
350 /**
351  * Wait for child process to be reaped, and unregister SIGCHLD handler.
352  * @param sq            scriptlet queue element
353  * @return              0 on success
354  */
355 static int rpmsqWaitUnregister(rpmsq sq)
356         /*@globals fileSystem, internalState @*/
357         /*@modifies sq, fileSystem, internalState @*/
358 {
359     int same_thread = 0;
360     int ret = 0;
361     int xx;
362
363     if (same_thread)
364         ret = sighold(SIGCHLD);
365     else
366         ret = pthread_mutex_lock(&sq->mutex);
367
368     /* Start the child. */
369 /*@-bounds@*/
370     if (sq->pipes[0] >= 0)
371         xx = close(sq->pipes[0]);
372     if (sq->pipes[1] >= 0)
373         xx = close(sq->pipes[1]);
374     sq->pipes[0] = sq->pipes[1] = -1;
375 /*@=bounds@*/
376
377     (void) rpmswEnter(&sq->op, -1);
378
379     /*@-infloops@*/
380     while (ret == 0 && sq->reaped != sq->child) {
381         if (same_thread)
382             ret = sigpause(SIGCHLD);
383         else
384             ret = pthread_cond_wait(&sq->cond, &sq->mutex);
385     }
386     /*@=infloops@*/
387
388     sq->ms_scriptlets += rpmswExit(&sq->op, -1)/1000;
389
390     if (same_thread)
391         xx = sigrelse(SIGCHLD);
392     else
393         xx = pthread_mutex_unlock(&sq->mutex);
394
395 #ifdef _RPMSQ_DEBUG
396 /*@-modfilesys@*/
397 if (_rpmsq_debug)
398 fprintf(stderr, "      Wake(%p): %p child %d reaper %d ret %d\n", ME(), sq, sq->child, sq->reaper, ret);
399 /*@=modfilesys@*/
400 #endif
401
402     xx = rpmsqRemove(sq);
403     xx = rpmsqEnable(-SIGCHLD, NULL);
404 #ifdef _RPMSQ_DEBUG
405 /*@-modfilesys@*/
406 if (_rpmsq_debug)
407 fprintf(stderr, "   Disable(%p): %p\n", ME(), sq);
408 /*@=modfilesys@*/
409 #endif
410
411     return ret;
412 }
413
414 pid_t rpmsqWait(rpmsq sq)
415 {
416
417 #ifdef _RPMSQ_DEBUG
418 /*@-modfilesys@*/
419 if (_rpmsq_debug)
420 fprintf(stderr, "      Wait(%p): %p child %d reaper %d\n", ME(), sq, sq->child, sq->reaper);
421 /*@=modfilesys@*/
422 #endif
423
424     if (sq->reaper) {
425         (void) rpmsqWaitUnregister(sq);
426     } else {
427         pid_t reaped;
428         int status;
429         do {
430             reaped = waitpid(sq->child, &status, 0);
431         } while (reaped >= 0 && reaped != sq->child);
432         sq->reaped = reaped;
433         sq->status = status;
434 #ifdef _RPMSQ_DEBUG
435 /*@-modfilesys@*/
436 if (_rpmsq_debug)
437 fprintf(stderr, "   Waitpid(%p): %p child %d reaped %d\n", ME(), sq, sq->child, sq->reaped);
438 /*@=modfilesys@*/
439 #endif
440     }
441
442 #ifdef _RPMSQ_DEBUG
443 /*@-modfilesys@*/
444 if (_rpmsq_debug)
445 fprintf(stderr, "      Fini(%p): %p child %d status 0x%x\n", ME(), sq, sq->child, sq->status);
446 /*@=modfilesys@*/
447 #endif
448
449     return sq->reaped;
450 }
451
452 void * rpmsqThread(void * (*start) (void * arg), void * arg)
453 {
454     pthread_t pth;
455     int ret;
456
457     ret = pthread_create(&pth, NULL, start, arg);
458     return (ret == 0 ? (void *)pth : NULL);
459 }
460
461 int rpmsqJoin(void * thread)
462 {
463     pthread_t pth = (pthread_t) thread;
464     if (thread == NULL)
465         return EINVAL;
466     return pthread_join(pth, NULL);
467 }
468
469 int rpmsqThreadEqual(void * thread)
470 {
471     pthread_t t1 = (pthread_t) thread;
472     pthread_t t2 = pthread_self();
473     return pthread_equal(t1, t2);
474 }
475
476 /**
477  * SIGCHLD cancellation handler.
478  */
479 static void
480 sigchld_cancel (void *arg)
481         /*@globals fileSystem, internalState @*/
482         /*@modifies fileSystem, internalState @*/
483 {
484     pid_t child = *(pid_t *) arg;
485     pid_t result;
486
487     (void) kill(child, SIGKILL);
488
489     do {
490         result = waitpid(child, NULL, 0);
491     } while (result == (pid_t)-1 && errno == EINTR);
492
493     DO_LOCK ();
494     if (SUB_REF (rpmsigTbl_sigchld) == 0) {
495         (void) rpmsqEnable(-SIGQUIT, NULL);
496         (void) rpmsqEnable(-SIGINT, NULL);
497     }
498     DO_UNLOCK ();
499 }
500
501 /**
502  * Execute a command, returning its status.
503  */
504 /*@-bounds@*/
505 int
506 rpmsqExecve (const char ** argv)
507 {
508     int oldtype;
509     int status = -1;
510     pid_t pid;
511     pid_t result;
512     sigset_t newMask, oldMask;
513     rpmsq sq = memset(alloca(sizeof(*sq)), 0, sizeof(*sq));
514
515     DO_LOCK ();
516     if (ADD_REF (rpmsigTbl_sigchld) == 0) {
517         if (rpmsqEnable(SIGINT, NULL) < 0) {
518             SUB_REF (rpmsigTbl_sigchld);
519             goto out;
520         }
521         if (rpmsqEnable(SIGQUIT, NULL) < 0) {
522             SUB_REF (rpmsigTbl_sigchld);
523             goto out_restore_sigint;
524         }
525     }
526     DO_UNLOCK ();
527
528     (void) sigemptyset (&newMask);
529     (void) sigaddset (&newMask, SIGCHLD);
530     if (sigprocmask (SIG_BLOCK, &newMask, &oldMask) < 0) {
531         DO_LOCK ();
532         if (SUB_REF (rpmsigTbl_sigchld) == 0)
533             goto out_restore_sigquit_and_sigint;
534         goto out;
535     }
536
537     CLEANUP_HANDLER(sigchld_cancel, &pid, &oldtype);
538
539     pid = fork ();
540     if (pid < (pid_t) 0) {              /* fork failed.  */
541         goto out;
542     } else if (pid == (pid_t) 0) {      /* Child. */
543
544         /* Restore the signals.  */
545         (void) sigaction (SIGINT, &rpmsigTbl_sigint->oact, NULL);
546         (void) sigaction (SIGQUIT, &rpmsigTbl_sigquit->oact, NULL);
547         (void) sigprocmask (SIG_SETMASK, &oldMask, NULL);
548
549         /* Reset rpmsigTbl lock and refcnt. */
550         INIT_LOCK ();
551
552         (void) execve (argv[0], (char *const *) argv, environ);
553         _exit (127);
554     } else {                            /* Parent. */
555         do {
556             result = waitpid(pid, &status, 0);
557         } while (result == (pid_t)-1 && errno == EINTR);
558         if (result != pid)
559             status = -1;
560     }
561
562     CLEANUP_RESET(0, oldtype);
563
564     DO_LOCK ();
565     if ((SUB_REF (rpmsigTbl_sigchld) == 0 &&
566         (rpmsqEnable(-SIGINT, NULL) < 0 || rpmsqEnable (-SIGQUIT, NULL) < 0))
567       || sigprocmask (SIG_SETMASK, &oldMask, NULL) != 0)
568     {
569         status = -1;
570     }
571     goto out;
572
573 out_restore_sigquit_and_sigint:
574     (void) rpmsqEnable(-SIGQUIT, NULL);
575 out_restore_sigint:
576     (void) rpmsqEnable(-SIGINT, NULL);
577 out:
578     DO_UNLOCK ();
579     return status;
580 }
581 /*@=bounds@*/
582 /*@=unrecog@*/