| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243 |
- /*
- * bg.c -- handles:
- * moving the process to the background, i.e. forking, while keeping threads
- * happy.
- *
- */
- #include "main.h"
- #include <signal.h>
- #include "bg.h"
- #ifdef HAVE_TCL_THREADS
- # define SUPPORT_THREADS
- #endif
- extern char pid_file[], botnetnick[];
- #ifdef SUPPORT_THREADS
- /* When threads are started during eggdrop's init phase, we can't simply
- * fork() later on, because that only copies the VM space over and
- * doesn't actually duplicate the threads.
- *
- * To work around this, we fork() very early and let the parent process
- * wait in an event loop. As soon as the init phase is completed and we
- * would normally move to the background, the child process simply
- * messages it's parent that it may now quit. This allows us to control
- * the terminal long enough to, e.g. properly feed error messages to
- * cron scripts and let the user abort the loading process by hitting
- * CTRL+C.
- *
- *
- * [ Parent process ] [ Child process ]
- *
- * main()
- * ...
- * bg_prepare_split()
- * fork() - created -
- * waiting in event loop. continuing execution in main()
- * ...
- * completed init.
- * bg_do_detach()
- * message parent new PID file-
- * name.
- * receives new PID filename
- * message.
- * message parent to quit.
- * receives quit message. continues to main loop.
- * writes PID to file. ...
- * exits.
- */
- /* Format of messages sent from the newly forked process to the
- original process, connected to the terminal. */
- typedef struct {
- enum {
- BG_COMM_QUIT, /* Quit original process. Write
- PID file, etc. i.e. detach. */
- BG_COMM_ABORT, /* Quit original process. */
- BG_COMM_TRANSFERPF /* Sending pid_file. */
- } comm_type;
- union {
- struct { /* Data for BG_COMM_TRANSFERPF. */
- int len; /* Length of the file name. */
- } transferpf;
- } comm_data;
- } bg_comm_t;
- typedef enum {
- BG_NONE = 0, /* No forking has taken place
- yet. */
- BG_SPLIT, /* I'm the newly forked process. */
- BG_PARENT /* I'm the original process. */
- } bg_state_t;
- typedef struct {
- int comm_recv; /* Receives messages from the
- child process. */
- int comm_send; /* Sends messages to the parent
- process. */
- bg_state_t state; /* Current state, see above
- enum descriptions. */
- pid_t child_pid; /* PID of split process. */
- } bg_t;
- static bg_t bg = { 0 };
- #endif /* SUPPORT_THREADS */
- /* Do everything we normally do after we have split off a new
- * process to the background. This includes writing a PID file
- * and informing the user of the split.
- */
- static void bg_do_detach(pid_t p)
- {
- FILE *fp;
- /* Need to attempt to write pid now, not later. */
- unlink(pid_file);
- fp = fopen(pid_file, "w");
- if (fp != NULL) {
- fprintf(fp, "%u\n", p);
- if (fflush(fp)) {
- /* Kill bot incase a botchk is run from crond. */
- printf(EGG_NOWRITE, pid_file);
- printf(" Try freeing some disk space\n");
- fclose(fp);
- unlink(pid_file);
- exit(1);
- }
- fclose(fp);
- } else
- printf(EGG_NOWRITE, pid_file);
- #ifdef HUB
- printf("Launched into the background (pid: %d)\n\n", p);
- #else
- printf("%s launched into the background (pid: %d)\n\n", botnetnick, p);
- #endif
- #if HAVE_SETPGID
- setpgid(p, p);
- #endif
- exit(0);
- }
- void bg_prepare_split(void)
- {
- #ifdef SUPPORT_THREADS
- /* Create a pipe between parent and split process, fork to create a
- parent and a split process and wait for messages on the pipe. */
- pid_t p;
- bg_comm_t message;
- {
- int comm_pair[2];
- if (pipe(comm_pair) < 0)
- fatal("CANNOT OPEN PIPE.", 0);
- bg.comm_recv = comm_pair[0];
- bg.comm_send = comm_pair[1];
- }
- p = fork();
- if (p == -1)
- fatal("CANNOT FORK PROCESS.", 0);
- if (p == 0) {
- bg.state = BG_SPLIT;
- return;
- } else {
- bg.child_pid = p;
- bg.state = BG_PARENT;
- }
- while (read(bg.comm_recv, &message, sizeof(message)) > 0) {
- switch (message.comm_type) {
- case BG_COMM_QUIT:
- bg_do_detach(p);
- break;
- case BG_COMM_ABORT:
- exit(1);
- break;
- case BG_COMM_TRANSFERPF:
- /* Now transferring file from split process.
- */
- if (message.comm_data.transferpf.len >= 40)
- message.comm_data.transferpf.len = 40 - 1;
- /* Next message contains data. */
- if (read(bg.comm_recv, pid_file, message.comm_data.transferpf.len) <= 0)
- goto error;
- pid_file[message.comm_data.transferpf.len] = 0;
- break;
- }
- }
- error:
- /* We only reach this point in case of an error.
- */
- fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
- #endif
- }
- #ifdef SUPPORT_THREADS
- /* Transfer contents of pid_file to parent process. This is necessary,
- * as the pid_file[] buffer has changed in this fork by now, but the
- * parent needs an up-to-date version.
- */
- static void bg_send_pidfile(void)
- {
- bg_comm_t message;
- message.comm_type = BG_COMM_TRANSFERPF;
- message.comm_data.transferpf.len = strlen(pid_file);
- /* Send type message. */
- if (write(bg.comm_send, &message, sizeof(message)) < 0)
- goto error;
- /* Send data. */
- if (write(bg.comm_send, pid_file, message.comm_data.transferpf.len) < 0)
- goto error;
- return;
- error:
- fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
- }
- #endif
- void bg_send_quit(bg_quit_t q)
- {
- #ifdef SUPPORT_THREADS
- if (bg.state == BG_PARENT) {
- kill(bg.child_pid, SIGKILL);
- } else if (bg.state == BG_SPLIT) {
- bg_comm_t message;
- if (q == BG_QUIT) {
- bg_send_pidfile();
- message.comm_type = BG_COMM_QUIT;
- } else
- message.comm_type = BG_COMM_ABORT;
- /* Send message. */
- if (write(bg.comm_send, &message, sizeof(message)) < 0)
- fatal("COMMUNICATION THROUGH PIPE BROKE.", 0);
- }
- #endif
- }
- void bg_do_split(void)
- {
- #ifdef SUPPORT_THREADS
- /* Tell our parent process to go away now, as we don't need it
- anymore. */
- bg_send_quit(BG_QUIT);
- #else
- /* Split off a new process.
- */
- int xx = fork();
- if (xx == -1)
- fatal("CANNOT FORK PROCESS.", 0);
- if (xx != 0)
- bg_do_detach(xx);
- #endif
- }
|