bg.c 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. * bg.c -- handles:
  3. * moving the process to the background, i.e. forking, while keeping threads
  4. * happy.
  5. *
  6. */
  7. #include "main.h"
  8. #include <signal.h>
  9. #include "bg.h"
  10. #ifdef HAVE_TCL_THREADS
  11. # define SUPPORT_THREADS
  12. #endif
  13. extern char pid_file[], botnetnick[];
  14. #ifdef SUPPORT_THREADS
  15. /* When threads are started during eggdrop's init phase, we can't simply
  16. * fork() later on, because that only copies the VM space over and
  17. * doesn't actually duplicate the threads.
  18. *
  19. * To work around this, we fork() very early and let the parent process
  20. * wait in an event loop. As soon as the init phase is completed and we
  21. * would normally move to the background, the child process simply
  22. * messages it's parent that it may now quit. This allows us to control
  23. * the terminal long enough to, e.g. properly feed error messages to
  24. * cron scripts and let the user abort the loading process by hitting
  25. * CTRL+C.
  26. *
  27. *
  28. * [ Parent process ] [ Child process ]
  29. *
  30. * main()
  31. * ...
  32. * bg_prepare_split()
  33. * fork() - created -
  34. * waiting in event loop. continuing execution in main()
  35. * ...
  36. * completed init.
  37. * bg_do_detach()
  38. * message parent new PID file-
  39. * name.
  40. * receives new PID filename
  41. * message.
  42. * message parent to quit.
  43. * receives quit message. continues to main loop.
  44. * writes PID to file. ...
  45. * exits.
  46. */
  47. /* Format of messages sent from the newly forked process to the
  48. original process, connected to the terminal. */
  49. typedef struct {
  50. enum {
  51. BG_COMM_QUIT, /* Quit original process. Write
  52. PID file, etc. i.e. detach. */
  53. BG_COMM_ABORT, /* Quit original process. */
  54. BG_COMM_TRANSFERPF /* Sending pid_file. */
  55. } comm_type;
  56. union {
  57. struct { /* Data for BG_COMM_TRANSFERPF. */
  58. int len; /* Length of the file name. */
  59. } transferpf;
  60. } comm_data;
  61. } bg_comm_t;
  62. typedef enum {
  63. BG_NONE = 0, /* No forking has taken place
  64. yet. */
  65. BG_SPLIT, /* I'm the newly forked process. */
  66. BG_PARENT /* I'm the original process. */
  67. } bg_state_t;
  68. typedef struct {
  69. int comm_recv; /* Receives messages from the
  70. child process. */
  71. int comm_send; /* Sends messages to the parent
  72. process. */
  73. bg_state_t state; /* Current state, see above
  74. enum descriptions. */
  75. pid_t child_pid; /* PID of split process. */
  76. } bg_t;
  77. static bg_t bg = { 0 };
  78. #endif /* SUPPORT_THREADS */
  79. /* Do everything we normally do after we have split off a new
  80. * process to the background. This includes writing a PID file
  81. * and informing the user of the split.
  82. */
  83. static void bg_do_detach(pid_t p)
  84. {
  85. FILE *fp;
  86. /* Need to attempt to write pid now, not later. */
  87. unlink(pid_file);
  88. fp = fopen(pid_file, "w");
  89. if (fp != NULL) {
  90. fprintf(fp, "%u\n", p);
  91. if (fflush(fp)) {
  92. /* Kill bot incase a botchk is run from crond. */
  93. printf(EGG_NOWRITE, pid_file);
  94. printf(" Try freeing some disk space\n");
  95. fclose(fp);
  96. unlink(pid_file);
  97. exit(1);
  98. }
  99. fclose(fp);
  100. } else
  101. printf(EGG_NOWRITE, pid_file);
  102. #ifdef HUB
  103. printf("Launched into the background (pid: %d)\n\n", p);
  104. #else
  105. printf("%s launched into the background (pid: %d)\n\n", botnetnick, p);
  106. #endif
  107. #if HAVE_SETPGID
  108. setpgid(p, p);
  109. #endif
  110. exit(0);
  111. }
  112. void bg_prepare_split(void)
  113. {
  114. #ifdef SUPPORT_THREADS
  115. /* Create a pipe between parent and split process, fork to create a
  116. parent and a split process and wait for messages on the pipe. */
  117. pid_t p;
  118. bg_comm_t message;
  119. {
  120. int comm_pair[2];
  121. if (pipe(comm_pair) < 0)
  122. fatal("CANNOT OPEN PIPE.", 0);
  123. bg.comm_recv = comm_pair[0];
  124. bg.comm_send = comm_pair[1];
  125. }
  126. p = fork();
  127. if (p == -1)
  128. fatal("CANNOT FORK PROCESS.", 0);
  129. if (p == 0) {
  130. bg.state = BG_SPLIT;
  131. return;
  132. } else {
  133. bg.child_pid = p;
  134. bg.state = BG_PARENT;
  135. }
  136. while (read(bg.comm_recv, &message, sizeof(message)) > 0) {
  137. switch (message.comm_type) {
  138. case BG_COMM_QUIT:
  139. bg_do_detach(p);
  140. break;
  141. case BG_COMM_ABORT:
  142. exit(1);
  143. break;
  144. case BG_COMM_TRANSFERPF:
  145. /* Now transferring file from split process.
  146. */
  147. if (message.comm_data.transferpf.len >= 40)
  148. message.comm_data.transferpf.len = 40 - 1;
  149. /* Next message contains data. */
  150. if (read(bg.comm_recv, pid_file, message.comm_data.transferpf.len) <= 0)
  151. goto error;
  152. pid_file[message.comm_data.transferpf.len] = 0;
  153. break;
  154. }
  155. }
  156. error:
  157. /* We only reach this point in case of an error.
  158. */
  159. fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
  160. #endif
  161. }
  162. #ifdef SUPPORT_THREADS
  163. /* Transfer contents of pid_file to parent process. This is necessary,
  164. * as the pid_file[] buffer has changed in this fork by now, but the
  165. * parent needs an up-to-date version.
  166. */
  167. static void bg_send_pidfile(void)
  168. {
  169. bg_comm_t message;
  170. message.comm_type = BG_COMM_TRANSFERPF;
  171. message.comm_data.transferpf.len = strlen(pid_file);
  172. /* Send type message. */
  173. if (write(bg.comm_send, &message, sizeof(message)) < 0)
  174. goto error;
  175. /* Send data. */
  176. if (write(bg.comm_send, pid_file, message.comm_data.transferpf.len) < 0)
  177. goto error;
  178. return;
  179. error:
  180. fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
  181. }
  182. #endif
  183. void bg_send_quit(bg_quit_t q)
  184. {
  185. #ifdef SUPPORT_THREADS
  186. if (bg.state == BG_PARENT) {
  187. kill(bg.child_pid, SIGKILL);
  188. } else if (bg.state == BG_SPLIT) {
  189. bg_comm_t message;
  190. if (q == BG_QUIT) {
  191. bg_send_pidfile();
  192. message.comm_type = BG_COMM_QUIT;
  193. } else
  194. message.comm_type = BG_COMM_ABORT;
  195. /* Send message. */
  196. if (write(bg.comm_send, &message, sizeof(message)) < 0)
  197. fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
  198. }
  199. #endif
  200. }
  201. void bg_do_split(void)
  202. {
  203. #ifdef SUPPORT_THREADS
  204. /* Tell our parent process to go away now, as we don't need it
  205. anymore. */
  206. bg_send_quit(BG_QUIT);
  207. #else
  208. /* Split off a new process.
  209. */
  210. int xx = fork();
  211. if (xx == -1)
  212. fatal("CANNOT FORK PROCESS.", 0);
  213. if (xx != 0)
  214. bg_do_detach(xx);
  215. #endif
  216. }