diff options
Diffstat (limited to 'src/monitor/main.cpp')
-rw-r--r-- | src/monitor/main.cpp | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/src/monitor/main.cpp b/src/monitor/main.cpp new file mode 100644 index 0000000..f6e8271 --- /dev/null +++ b/src/monitor/main.cpp @@ -0,0 +1,217 @@ +/** + * Name: eAthena processes monitor + * Original Author: Bartosz Waszak <waszi@evil.org.pl> + * Rewrite Author: Ben Longbons <b.r.longbons@gmail.com> + * License: GPL + * Compilation: + * gcc -o eathena-monitor eathena-monitor.c + */ + +#include <sys/wait.h> + +#include <fcntl.h> +#include <unistd.h> + +#include <csignal> + +#include <fstream> + +#include "../common/cxxstdio.hpp" +#include "../common/io.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 +FString workdir; +//the rest are relative to workdir +static +FString login_server = LOGIN_SERVER; +static +FString map_server = MAP_SERVER; +static +FString char_server = CHAR_SERVER; + +static +pid_t pid_login, pid_map, pid_char; + +static +FString make_path(XString base, XString path) +{ + MString m; + m += base; + m += '/'; + m += path; + return FString(m); +} + +static +void parse_option(XString name, ZString 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 + { + FString name_ = name; + FPRINTF(stderr, "WARNING: ingnoring invalid option '%s' : '%s'\n", + name_, value); + } +} + +static +void read_config(ZString filename) +{ + std::ifstream in(filename.c_str()); + if (!in.is_open()) + { + FPRINTF(stderr, "Monitor config file not found: %s\n", filename); + exit(1); + } + + FString line; + while (io::getline(in, line)) + { + SString name; + TString value; + if (!split_key_value(line, &name, &value)) + continue; + + parse_option(name, value); + } +} + +static +pid_t start_process(ZString 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<char **>(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(ZString(ZString::really_construct_from_a_pointer, getenv("HOME"), nullptr), "tmwserver"); + + ZString config = CONFIG; + if (argc > 1) + config = ZString(ZString::really_construct_from_a_pointer, argv[1], nullptr); + 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<unsigned long>(pid_login)); + } + if (!pid_char) + { + pid_char = start_process(char_server); + FPRINTF(stderr, "[%s] forked char server: %lu\n", + timestamp, static_cast<unsigned long>(pid_char)); + } + if (!pid_map) + { + pid_map = start_process(map_server); + FPRINTF(stderr, "[%s] forked map server: %lu\n", + timestamp, static_cast<unsigned long>(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; + } +} |