util_process.c 4.1 KB

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