/** * Name: eAthena processes monitor * Original Author: Bartosz Waszak * Rewrite Author: Ben Longbons * License: GPL * Compilation: * gcc -o eathena-monitor eathena-monitor.c */ #include #include #include #include #include #include "../common/cxxstdio.hpp" #include "../common/utils.hpp" #include "../poison.hpp" #define LOGIN_SERVER "./login-server" #define MAP_SERVER "./map-server" #define CHAR_SERVER "./char-server" #define CONFIG "conf/eathena-monitor.conf" // initialiized to $HOME/tmwserver static std::string workdir; //the rest are relative to workdir static std::string login_server = LOGIN_SERVER; static std::string map_server = MAP_SERVER; static std::string char_server = CHAR_SERVER; static pid_t pid_login, pid_map, pid_char; static std::string make_path(const std::string& base, const std::string& path) { std::string out(base.size() + 1 + path.size(), '\0'); std::string::iterator it = std::copy(base.begin(), base.end(), out.begin()); *it++ = '/'; std::copy(path.begin(), path.end(), it); return out; } static void parse_option(const std::string& name, const std::string& value) { if (name == "login_server") login_server = value; else if (name == "map_server") map_server = value; else if (name == "char_server") char_server = value; else if (name == "workdir") workdir = value; else FPRINTF(stderr, "WARNING: ingnoring invalid option '%s' : '%s'\n", name, value); } static void read_config(const std::string& filename) { std::ifstream in(filename); if (!in.is_open()) { FPRINTF(stderr, "Monitor config file not found: %s\n", filename); exit(1); } std::string line; while (std::getline(in, line)) { std::string name, value; if (!split_key_value(line, &name, &value)) continue; parse_option(name, value); } } static pid_t start_process(const std::string& exec) { const char *args[2] = {exec.c_str(), NULL}; pid_t pid = fork(); if (pid == -1) { FPRINTF(stderr, "Failed to fork"); return 0; } if (pid == 0) { #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wcast-qual" execv(exec.c_str(), const_cast(args)); #pragma GCC diagnostic pop perror("Failed to exec"); kill(getppid(), SIGABRT); exit(1); } return pid; } // Kill all children with the same signal we got, then ourself. static void stop_process(int sig) { if (pid_map) kill(pid_map, sig); if (pid_login) kill(pid_login, sig); if (pid_char) kill(pid_char, sig); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wold-style-cast" signal(sig, SIG_DFL); #pragma GCC diagnostic pop raise(sig); } int main(int argc, char *argv[]) { // These are all the signals we are likely to get // The shell handles stop/cont signal(SIGTERM, stop_process); signal(SIGINT, stop_process); signal(SIGQUIT, stop_process); signal(SIGABRT, stop_process); workdir = make_path(getenv("HOME"), "tmwserver"); std::string config = CONFIG; if (argc > 1) config = argv[1]; read_config(config); if (chdir(workdir.c_str()) < 0) { perror("Failed to change directory"); exit(1); } PRINTF("Starting:\n"); PRINTF("* workdir: %s\n", workdir); PRINTF("* login_server: %s\n", login_server); PRINTF("* map_server: %s\n", map_server); PRINTF("* char_server: %s\n", char_server); { //make sure all possible file descriptors are free for use by the servers //if there are file descriptors higher than the max open from before the limit dropped, that's not our problem int fd = sysconf(_SC_OPEN_MAX); while (--fd > 2) if (close(fd) == 0) FPRINTF(stderr, "close fd %d\n", fd); fd = open("/dev/null", O_RDWR); if (fd < 0) { perror("open /dev/null"); exit(1); } dup2(fd, 0); dup2(fd, 1); close(fd); } while (1) { // write stuff to stderr timestamp_seconds_buffer timestamp; stamp_time(timestamp); if (!pid_login) { pid_login = start_process(login_server); FPRINTF(stderr, "[%s] forked login server: %lu\n", timestamp, static_cast(pid_login)); } if (!pid_char) { pid_char = start_process(char_server); FPRINTF(stderr, "[%s] forked char server: %lu\n", timestamp, static_cast(pid_char)); } if (!pid_map) { pid_map = start_process(map_server); FPRINTF(stderr, "[%s] forked map server: %lu\n", timestamp, static_cast(pid_map)); } pid_t dead = wait(NULL); if (dead == -1) { perror("Failed to wait for child"); exit(1); } if (pid_login == dead) pid_login = 0; if (pid_char == dead) pid_char = 0; if (pid_map == dead) pid_map = 0; } }