int main(int argc, char *argv[]) { static struct option long_options[] = { {"port", 1, 0, 'p'}, {"daemonize", 0, 0, 'd'}, {"forks", 1, 0, 'f'}, {0, 0, 0, 0} }; int port = 8080; int daemon_mode = 0; int forks = 0; for (;;) { int option_index = 0; int c = getopt_long(argc, argv, "p:f:d", long_options, &option_index); if (c == -1) break; switch (c) { case 'p': port = atoi(optarg); break; case 'd': daemon_mode = 1; break; case 'f': forks = atoi(optarg); break; } } /* server config */ struct http_server server = { /* port number */ .port = port, /* call simple_file_server upon receiving http request */ .user_func = simple_file_server, /* set idle connection timeout to 60 seconds */ .timeout_handler.timeout = 60000, /* set fiber's stack size to automatic (0) */ .stack_size = 0, /* start the server with 100 stacks */ /* more stacks will be created if necessary */ .num_stacks = 100, /* we expect most of our requests to be less than 8K */ .init_request_size = 8192, /* we expect most of our response headers to be less than 8K */ .init_header_size = 8192, /* we expect most of our response payloads to be less than 8K */ .init_payload_size = 8192, /* no limit on the request size, this should be set to something reasonable if you want to protect your server against denial of service attack */ .max_req_size = 0, /* no additional space is needed in the context to store app specified data (fiber local storage) */ .context_size = 0 }; /* initialize server, but don't accept connections yet */ if (0 > http_server_init(&server)) exit(EXIT_FAILURE); /* run as daemon if specified */ if (daemon_mode) daemonize(), daemon_finalize(); /* assume autoconfiguration if forks is not a positive value */ if (0 >= forks) { forks = sysconf(_SC_NPROCESSORS_CONF); if (0 > forks) exit(EXIT_FAILURE); } for(; forks > 1; --forks){ if (0 >= fork()) { break; } } /* initialize the event loop */ if (0 > epoll_worker_init()) exit(EXIT_FAILURE); /* start accepting connections, must be called after initializing epoll worker */ if (0 > http_server_init_acceptor(&server)) exit(EXIT_FAILURE); epoll_worker_loop(); return 0; }
static int _set_signals(void) { struct sigaction sa = { .sa_handler = signal_handler, .sa_flags = 0 }; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); if (0 > sigprocmask(SIG_BLOCK, &sigset, NULL)) return LOGGER_PERROR("sigprocmask"), -1; sigfd = signalfd(-1, &sigset, SFD_NONBLOCK | SFD_CLOEXEC); if (0 > sigfd) return LOGGER_PERROR("signalfd"), -1; if (NULL == (sigfd_ctx = ribs_context_create(SIG_CHLD_STACK_SIZE, 0, _handle_sig_child))) return -1; return 0; } static int _init_subprocesses(const char *pidfilename, int num_forks) { if (0 >= num_forks) { num_forks = sysconf(_SC_NPROCESSORS_CONF); if (0 > num_forks) exit(EXIT_FAILURE); } LOGGER_INFO("num forks = %d", num_forks); num_instances = num_forks; daemon_instance = 0; children_pids = calloc(num_forks - 1, sizeof(pid_t)); int i; for (i = 1; i < num_forks; ++i) { LOGGER_INFO("starting sub-process %d", i); pid_t pid = fork(); if (0 > pid) return LOGGER_PERROR("fork"), -1; if (0 == pid) { daemon_instance = i; if (0 > prctl(PR_SET_PDEATHSIG, SIGTERM)) return LOGGER_PERROR("prctl"), -1; if (0 > _set_signals()) return LOGGER_ERROR("failed to set signals"), -1; LOGGER_INFO("sub-process %d started", i); return 0; } else children_pids[i-1] = pid; } if (pidfilename && 0 > _set_pidfile(pidfilename)) return LOGGER_ERROR("failed to set pidfile"), -1; if (0 > _set_signals()) return LOGGER_ERROR("failed to set signals"), -1; return 0; } static int ribs_server_init_daemon(const char *pidfilename, const char *logfilename, int num_forks) { if (0 > pipe2(child_is_up_pipe, O_CLOEXEC)) return LOGGER_ERROR("failed to create pipe"), -1; pid_t pid = fork(); if (pid < 0) return LOGGER_PERROR("ribs_server_init_daemon, fork"), -1; if (pid > 0) { close(child_is_up_pipe[1]); /* close the write side */ /* wait for child to come up */ uint8_t t; int res; LOGGER_INFO("waiting for the child process [%d] to start...", pid); if (0 >= (res = read(child_is_up_pipe[0], &t, sizeof(t)))) { if (0 > res) LOGGER_PERROR("pipe"); LOGGER_ERROR("child process failed to start"); return -1; } LOGGER_INFO("child process started successfully"); exit(EXIT_SUCCESS); } close(child_is_up_pipe[0]); /* close the read side */ umask(0); if (0 > setsid()) return LOGGER_PERROR("daemonize, setsid"), -1; int fdnull = open("/dev/null", O_RDWR); if (0 > fdnull) return LOGGER_PERROR("/dev/null"), -1; dup2(fdnull, STDIN_FILENO); if (logfilename) { if (0 > _logger_init(logfilename)) return -1; } else { dup2(fdnull, STDOUT_FILENO); dup2(fdnull, STDERR_FILENO); } close(fdnull); LOGGER_INFO("child process started"); return _init_subprocesses(pidfilename, num_forks); } int ribs_server_init(int daemonize, const char *pidfilename, const char *logfilename, int num_forks) { if (0 > hashtable_init(&ht_pid_to_ctx, 0)) return -1; if (daemonize) return ribs_server_init_daemon(pidfilename, logfilename, num_forks); /* if (logfilename && 0 > _logger_init(logfilename)) */ /* exit(EXIT_FAILURE); */ return _init_subprocesses(pidfilename, num_forks); } int ribs_server_signal_children(int sig) { if (0 >= num_instances || 0 != daemon_instance) return 0; int i, res = 0; for (i = 0; i < num_instances-1; ++i) { if (0 < children_pids[i] && 0 > kill(children_pids[i], sig)) { LOGGER_PERROR("kill [%d] %d", sig, children_pids[i]); res = -1; } } return res; } void ribs_server_start(void) { daemon_finalize(); if (0 <= sigfd && 0 > ribs_epoll_add(sigfd, EPOLLIN, sigfd_ctx)) return LOGGER_ERROR("ribs_epoll_add: sigfd"); epoll_worker_loop(); if (0 >= num_instances || 0 != daemon_instance) return; LOGGER_INFO("sending SIGTERM to sub-processes"); ribs_server_signal_children(SIGTERM); LOGGER_INFO("waiting for sub-processes to exit"); int i, status; for (i = 0; i < num_instances-1; ++i) { if (0 < children_pids[i] && 0 > waitpid(children_pids[i], &status, 0)) LOGGER_PERROR("waitpid %d", children_pids[i]); } LOGGER_INFO("sub-processes terminated"); }
static int _set_signals(void) { struct sigaction sa = { .sa_handler = signal_handler, .sa_flags = 0 }; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); if (0 == daemon_instance) sigaction(SIGCHLD, &sa, NULL); return 0; } static int _init_subprocesses(const char *pidfilename, int num_forks) { if (0 >= num_forks) { num_forks = sysconf(_SC_NPROCESSORS_CONF); if (0 > num_forks) exit(EXIT_FAILURE); } LOGGER_INFO("num forks = %d", num_forks); num_instances = num_forks; daemon_instance = 0; children_pids = calloc(num_forks - 1, sizeof(pid_t)); int i; for (i = 1; i < num_forks; ++i) { LOGGER_INFO("starting sub-process %d", i); pid_t pid = fork(); if (0 > pid) return LOGGER_PERROR("fork"), -1; if (0 == pid) { daemon_instance = i; if (0 > prctl(PR_SET_PDEATHSIG, SIGTERM)) return LOGGER_PERROR("prctl"), -1; if (0 > _set_signals()) return LOGGER_ERROR("failed to set signals"), -1; LOGGER_INFO("sub-process %d started", i); return 0; } else children_pids[i-1] = pid; } if (pidfilename && 0 > _set_pidfile(pidfilename)) return LOGGER_ERROR("failed to set pidfile"), -1; if (0 > _set_signals()) return LOGGER_ERROR("failed to set signals"), -1; return 0; } static int ribs_server_init_daemon(const char *pidfilename, const char *logfilename, int num_forks) { if (0 > pipe(child_is_up_pipe)) return LOGGER_ERROR("failed to create pipe"), -1; pid_t pid = fork(); if (pid < 0) return LOGGER_PERROR("ribs_server_init_daemon, fork"), -1; if (pid > 0) { close(child_is_up_pipe[1]); /* close the write side */ /* wait for child to come up */ uint8_t t; int res; LOGGER_INFO("waiting for the child process [%d] to start...", pid); if (0 >= (res = read(child_is_up_pipe[0], &t, sizeof(t)))) { if (0 > res) LOGGER_PERROR("pipe"); LOGGER_ERROR("child process failed to start"); return -1; } LOGGER_INFO("child process started successfully"); exit(EXIT_SUCCESS); } close(child_is_up_pipe[0]); /* close the read side */ umask(0); if (0 > setsid()) return LOGGER_PERROR("daemonize, setsid"), -1; int fdnull = open("/dev/null", O_RDWR); if (0 > fdnull) return LOGGER_PERROR("/dev/null"), -1; dup2(fdnull, STDIN_FILENO); if (logfilename) { if (0 > _logger_init(logfilename)) return -1; } else { dup2(fdnull, STDOUT_FILENO); dup2(fdnull, STDERR_FILENO); } close(fdnull); LOGGER_INFO("child process started"); return _init_subprocesses(pidfilename, num_forks); } int ribs_server_init(int daemonize, const char *pidfilename, const char *logfilename, int num_forks) { if (daemonize) return ribs_server_init_daemon(pidfilename, logfilename, num_forks); /* if (logfilename && 0 > _logger_init(logfilename)) */ /* exit(EXIT_FAILURE); */ return _init_subprocesses(pidfilename, num_forks); } int ribs_server_signal_children(int sig) { if (0 >= num_instances || 0 != daemon_instance) return 0; int i, res = 0; for (i = 0; i < num_instances-1; ++i) { if (0 > kill(children_pids[i], sig)) { LOGGER_PERROR("kill [%d] %d", sig, children_pids[i]); res = -1; } } return res; } void ribs_server_start(void) { daemon_finalize(); epoll_worker_loop(); if (0 >= num_instances || 0 != daemon_instance) return; struct sigaction sa = { .sa_handler = SIG_DFL, .sa_flags = 0 }; sigaction(SIGCHLD, &sa, NULL); LOGGER_INFO("sending SIGTERM to sub-processes"); ribs_server_signal_children(SIGTERM); LOGGER_INFO("waiting for sub-processes to exit"); int i, status; for (i = 0; i < num_instances-1; ++i) { if (0 > waitpid(children_pids[i], &status, 0)) LOGGER_PERROR("waitpid %d", children_pids[i]); } LOGGER_INFO("sub-processes terminated"); } int ribs_get_daemon_instance(void) { return daemon_instance; } int daemonize(void) { if (0 > pipe(child_is_up_pipe)) return LOGGER_ERROR("failed to create pipe"), -1; pid_t pid = fork(); if (pid < 0) { LOGGER_PERROR("daemonize, fork"); exit(EXIT_FAILURE); } if (pid > 0) { close(child_is_up_pipe[1]); /* close the write side */ /* wait for child to come up */ uint8_t t; int res; LOGGER_INFO("waiting for the child process [%d] to start...", pid); if (0 >= (res = read(child_is_up_pipe[0], &t, sizeof(t)))) { if (0 > res) LOGGER_PERROR("pipe"); LOGGER_ERROR("child process failed to start"); exit(EXIT_FAILURE); } LOGGER_INFO("child process started successfully"); exit(EXIT_SUCCESS); } close(child_is_up_pipe[0]); /* close the read side */ umask(0); if (0 > setsid()) { LOGGER_PERROR("daemonize, setsid"); exit(EXIT_FAILURE); } int fdnull = open("/dev/null", O_RDWR); dup2(fdnull, STDIN_FILENO); dup2(fdnull, STDOUT_FILENO); dup2(fdnull, STDERR_FILENO); close(fdnull); pid = getpid(); LOGGER_INFO("child process started (pid=%d)", pid); return 0; } void daemon_finalize(void) { if (daemon_instance > 0) return; if (0 > child_is_up_pipe[1]) return; uint8_t t = 0; if (0 > write(child_is_up_pipe[1], &t, sizeof(t))) { LOGGER_PERROR("pipe"); exit(EXIT_FAILURE); } close(child_is_up_pipe[1]); } int ribs_logger_init(const char *filename) { return _logger_init(filename); } int ribs_set_signals(void) { return _set_signals(); } int ribs_set_pidfile(const char *filename) { if (0 > _set_pidfile(filename)) return -1; return _set_signals(); }