void daemonize (int cdroot) { /* Become seesion leader and lose controlling terminal */ pid_t pid = fork(); if (pid < 0) { debug(D_DEBUG, "could not fork: %s", strerror(errno)); exit(EXIT_FAILURE); } else if (pid > 0) { exit(EXIT_SUCCESS); /* exit parent */ } pid_t group = setsid(); if (group == (pid_t) -1) { debug(D_DEBUG, "could not create session: %s", strerror(errno)); exit(EXIT_FAILURE); } /* Second fork ensures process cannot acquire controlling terminal */ pid = fork(); if (pid < 0) { debug(D_DEBUG, "could not fork: %s", strerror(errno)); exit(EXIT_FAILURE); } else if (pid > 0) { exit(EXIT_SUCCESS); /* exit parent */ } if (cdroot){ int status = chdir("/"); if (status == -1) { debug(D_DEBUG, "could not chdir to `/': %s", strerror(errno)); exit(EXIT_FAILURE); } } umask(0); int fd, max = fd_max(); for (fd = 0; fd < max; fd++) { if (close(fd) == -1 && errno != EBADF) { debug(D_DEBUG, "could not close open file descriptor: %s", strerror(errno)); exit(EXIT_FAILURE); } } int fd0 = open("/dev/null", O_RDONLY); int fd1 = open("/dev/null", O_WRONLY); int fd2 = open("/dev/null", O_WRONLY); if (!(fd0 == STDIN_FILENO && fd1 == STDOUT_FILENO && fd2 == STDERR_FILENO)) { debug(D_DEBUG, "could not open `/dev/null': %d %d %d: %s", fd0, fd1, fd2, strerror(errno)); exit(EXIT_FAILURE); } }
void impl_init(void) { const char* mode_env = getenv("HUPTIME_MODE"); const char* multi_env = getenv("HUPTIME_MULTI"); const char* revive_env = getenv("HUPTIME_REVIVE"); const char* debug_env = getenv("HUPTIME_DEBUG"); const char* pipe_env = getenv("HUPTIME_PIPE"); const char* wait_env = getenv("HUPTIME_WAIT"); if( debug_env != NULL && strlen(debug_env) > 0 ) { debug_enabled = !strcasecmp(debug_env, "true") ? TRUE: FALSE; } DEBUG("Initializing..."); /* Initialize our lock. */ impl_init_lock(); /* Save this pid as our master pid. * This is done to handle processes that use * process pools. We remember the master pid and * will do the full fork()/exec() only when we are * the master. Otherwise, we will simply shutdown * gracefully, and all the master to restart. */ master_pid = getpid(); /* Grab our exit strategy. */ if( mode_env != NULL && strlen(mode_env) > 0 ) { if( !strcasecmp(mode_env, "fork") ) { exit_strategy = FORK; DEBUG("Exit strategy is fork."); } else if( !strcasecmp(mode_env, "exec") ) { exit_strategy = EXEC; DEBUG("Exit strategy is exec."); } else { fprintf(stderr, "Unknown exit strategy."); libc.exit(1); } } /* Check if we have something to unlink. */ to_unlink = getenv("HUPTIME_UNLINK"); if( to_unlink != NULL && strlen(to_unlink) > 0 ) { DEBUG("Unlink is '%s'.", to_unlink); } /* Clear up any outstanding child processes. * Because we may have exited before the process * could do appropriate waitpid()'s, we try to * clean up children here. Note that we may have * some zombies that hang around during the life * of the program, but at every restart they will * be cleaned up (so at least they won't grow * without bound). */ int status = 0; while( waitpid((pid_t)-1, &status, WNOHANG) > 0 ); /* Check if we're in multi mode. */ if( multi_env != NULL && strlen(multi_env) > 0 ) { multi_mode = !strcasecmp(multi_env, "true") ? TRUE: FALSE; } #ifndef SO_REUSEPORT if( multi_mode == TRUE ) { fprintf(stderr, "WARNING: Multi mode not supported.\n"); fprintf(stderr, "(Requires at least Linux 3.9 and recent headers).\n"); } #endif /* Check if we're in revive mode. */ if( revive_env != NULL && strlen(revive_env) > 0 ) { revive_mode = !strcasecmp(revive_env, "true") ? TRUE : FALSE; } /* Check if we are in wait mode. */ if( wait_env != NULL && strlen(wait_env) > 0 ) { wait_mode = !strcasecmp(wait_env, "true") ? TRUE : FALSE; } /* Check if we're a respawn. */ if( pipe_env != NULL && strlen(pipe_env) > 0 ) { int fd = -1; fdinfo_t *info = NULL; int pipefd = strtol(pipe_env, NULL, 10); DEBUG("Loading all file descriptors."); /* Decode all passed information. */ while( !info_decode(pipefd, &fd, &info) ) { fd_save(fd, info); DEBUG("Decoded fd %d (type %d).", fd, info->type); info = NULL; } if( info != NULL ) { dec_ref(info); } /* Finished with the pipe. */ libc.close(pipefd); unsetenv("HUPTIME_PIPE"); DEBUG("Finished decoding."); /* Close all non-encoded descriptors. */ for( fd = 0; fd < fd_max(); fd += 1 ) { info = fd_lookup(fd); if( info == NULL ) { DEBUG("Closing fd %d.", fd); libc.close(fd); } } /* Restore all given file descriptors. */ for( fd = 0; fd < fd_limit(); fd += 1 ) { info = fd_lookup(fd); if( info != NULL && info->type == SAVED ) { fdinfo_t *orig_info = fd_lookup(info->saved.fd); if( orig_info != NULL ) { /* Uh-oh, conflict. Move the original (best effort). */ do_dup(info->saved.fd); do_close(info->saved.fd); } /* Return the offset (ignore failure). */ if( info->saved.offset != (off_t)-1 ) { lseek(fd, info->saved.offset, SEEK_SET); } /* Move the SAVED fd back. */ libc.dup2(fd, info->saved.fd); DEBUG("Restored fd %d.", info->saved.fd); } } } else { DEBUG("Saving all initial file descriptors."); /* Save all of our initial files. These are used * for re-execing the process. These are persisted * effectively forever, and on restarts we close * everything that is not a BOUND socket or a SAVED * file descriptor. */ for( int fd = 0; fd < fd_max(); fd += 1 ) { fdinfo_t *info = fd_lookup(fd); if( info != NULL ) { /* Encoded earlier. */ continue; } /* Make a new SAVED FD. */ int newfd = libc.dup(fd); if( newfd >= 0 ) { fdinfo_t *saved_info = alloc_info(SAVED); if( saved_info != NULL ) { saved_info->saved.fd = fd; saved_info->saved.offset = lseek(fd, 0, SEEK_CUR); fd_save(newfd, saved_info); DEBUG("Saved fd %d (offset %lld).", fd, (long long int)saved_info->saved.offset); } } } } /* Save the environment. * * NOTE: We reserve extra space in the environment * for our special start-up parameters, which will be added * in impl_exec() below. (The encoded BOUND/SAVED sockets). * * We also filter out the special variables above that were * used to pass in information about sockets that were bound. */ free(environ_copy); environ_copy = (char**)read_nul_sep("/proc/self/environ"); DEBUG("Saved environment."); /* Save the arguments. */ free(args_copy); args_copy = (char**)read_nul_sep("/proc/self/cmdline"); DEBUG("Saved args."); for( int i = 0; args_copy[i] != NULL; i += 1 ) { DEBUG(" arg%d=%s", i, args_copy[i]); } /* Save the cwd & exe. */ free(cwd_copy); cwd_copy = (char*)read_link("/proc/self/cwd"); DEBUG("Saved cwd."); free(exe_copy); exe_copy = (char*)read_link("/proc/self/exe"); DEBUG("Saved exe."); /* Install our signal handlers. */ impl_install_sighandlers(); /* Initialize our thread. */ impl_init_thread(); /* Unblock our signals. * Note that we have specifically masked the * signals prior to the exec() below, to cover * the race between program start and having * installed the appropriate handlers. */ sigset_t set; sigemptyset(&set); sigaddset(&set, SIGHUP); sigprocmask(SIG_UNBLOCK, &set, NULL); /* Done. */ DEBUG("Initialization complete."); }