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