LCOV - code coverage report
Current view: top level - lib/process - waitpid.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 40 44 90.9 %
Date: 2021-11-24 03:28:48 Functions: 10 13 76.9 %

          Line data    Source code
       1             : /* Copyright (c) 2003-2004, Roger Dingledine
       2             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       3             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       4             : /* See LICENSE for licensing information */
       5             : 
       6             : /**
       7             :  * \file waitpid.c
       8             :  * \brief Convenience structures for handlers for handling waitpid().
       9             :  **/
      10             : 
      11             : #include "orconfig.h"
      12             : 
      13             : #ifndef _WIN32
      14             : 
      15             : #include "lib/process/waitpid.h"
      16             : #include "lib/log/log.h"
      17             : #include "lib/log/util_bug.h"
      18             : #include "lib/malloc/malloc.h"
      19             : #include "ext/ht.h"
      20             : 
      21             : #ifdef HAVE_SYS_WAIT_H
      22             : #include <sys/wait.h>
      23             : #endif
      24             : 
      25             : #include <string.h>
      26             : 
      27             : /* ================================================== */
      28             : /* Convenience structures for handlers for waitpid().
      29             :  *
      30             :  * The tor_process_monitor*() code above doesn't use them, since it is for
      31             :  * monitoring a non-child process.
      32             :  */
      33             : 
      34             : /** Mapping from a PID to a userfn/userdata pair. */
      35             : struct waitpid_callback_t {
      36             :   HT_ENTRY(waitpid_callback_t) node;
      37             :   pid_t pid;
      38             : 
      39             :   void (*userfn)(int, void *userdata);
      40             :   void *userdata;
      41             : 
      42             :   unsigned running;
      43             : };
      44             : 
      45             : static inline unsigned int
      46          11 : process_map_entry_hash_(const waitpid_callback_t *ent)
      47             : {
      48          11 :   return (unsigned) ent->pid;
      49             : }
      50             : 
      51             : static inline unsigned int
      52           6 : process_map_entries_eq_(const waitpid_callback_t *a,
      53             :                         const waitpid_callback_t *b)
      54             : {
      55           6 :   return a->pid == b->pid;
      56             : }
      57             : 
      58             : static HT_HEAD(process_map, waitpid_callback_t) process_map = HT_INITIALIZER();
      59             : 
      60          22 : HT_PROTOTYPE(process_map, waitpid_callback_t, node, process_map_entry_hash_,
      61             :              process_map_entries_eq_);
      62           2 : HT_GENERATE2(process_map, waitpid_callback_t, node, process_map_entry_hash_,
      63             :              process_map_entries_eq_, 0.6, tor_reallocarray_, tor_free_);
      64             : 
      65             : /**
      66             :  * Begin monitoring the child pid <b>pid</b> to see if we get a SIGCHLD for
      67             :  * it.  If we eventually do, call <b>fn</b>, passing it the exit status (as
      68             :  * yielded by waitpid) and the pointer <b>arg</b>.
      69             :  *
      70             :  * To cancel this, or clean up after it has triggered, call
      71             :  * clear_waitpid_callback().
      72             :  */
      73             : waitpid_callback_t *
      74           6 : set_waitpid_callback(pid_t pid, void (*fn)(int, void *), void *arg)
      75             : {
      76           6 :   waitpid_callback_t *old_ent;
      77           6 :   waitpid_callback_t *ent = tor_malloc_zero(sizeof(waitpid_callback_t));
      78           6 :   ent->pid = pid;
      79           6 :   ent->userfn = fn;
      80           6 :   ent->userdata = arg;
      81           6 :   ent->running = 1;
      82             : 
      83           6 :   old_ent = HT_REPLACE(process_map, &process_map, ent);
      84           6 :   if (old_ent) {
      85           1 :     log_warn(LD_BUG, "Replaced a waitpid monitor on pid %u. That should be "
      86             :              "impossible.", (unsigned) pid);
      87           1 :     old_ent->running = 0;
      88             :   }
      89             : 
      90           6 :   return ent;
      91             : }
      92             : 
      93             : /**
      94             :  * Cancel a waitpid_callback_t, or clean up after one has triggered. Releases
      95             :  * all storage held by <b>ent</b>.
      96             :  */
      97             : void
      98          36 : clear_waitpid_callback(waitpid_callback_t *ent)
      99             : {
     100          36 :   waitpid_callback_t *old_ent;
     101          36 :   if (ent == NULL)
     102             :     return;
     103             : 
     104           6 :   if (ent->running) {
     105           2 :     old_ent = HT_REMOVE(process_map, &process_map, ent);
     106           2 :     if (old_ent != ent) {
     107           0 :       log_warn(LD_BUG, "Couldn't remove waitpid monitor for pid %u.",
     108             :                (unsigned) ent->pid);
     109           0 :       return;
     110             :     }
     111             :   }
     112             : 
     113           6 :   tor_free(ent);
     114             : }
     115             : 
     116             : /** Helper: find the callback for <b>pid</b>; if there is one, run it,
     117             :  * reporting the exit status as <b>status</b>. */
     118             : static void
     119           3 : notify_waitpid_callback_by_pid(pid_t pid, int status)
     120             : {
     121           3 :   waitpid_callback_t search, *ent;
     122             : 
     123           3 :   search.pid = pid;
     124           3 :   ent = HT_REMOVE(process_map, &process_map, &search);
     125           3 :   if (!ent || !ent->running) {
     126           0 :     log_info(LD_GENERAL, "Child process %u has exited; no callback was "
     127             :              "registered", (unsigned)pid);
     128           0 :     return;
     129             :   }
     130             : 
     131           3 :   log_info(LD_GENERAL, "Child process %u has exited; running callback.",
     132             :            (unsigned)pid);
     133             : 
     134           3 :   ent->running = 0;
     135           3 :   ent->userfn(status, ent->userdata);
     136             : }
     137             : 
     138             : /** Use waitpid() to wait for all children that have exited, and invoke any
     139             :  * callbacks registered for them. */
     140             : void
     141           8 : notify_pending_waitpid_callbacks(void)
     142             : {
     143             :   /* I was going to call this function reap_zombie_children(), but
     144             :    * that makes it sound way more exciting than it really is. */
     145           8 :   pid_t child;
     146           8 :   int status = 0;
     147             : 
     148          11 :   while ((child = waitpid(-1, &status, WNOHANG)) > 0) {
     149           3 :     notify_waitpid_callback_by_pid(child, status);
     150           3 :     status = 0; /* should be needless */
     151             :   }
     152           8 : }
     153             : 
     154             : #endif /* !defined(_WIN32) */

Generated by: LCOV version 1.14