waitpid.c 4.0 KB

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