summaryrefslogtreecommitdiff
path: root/src/monitor/main.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/monitor/main.cpp')
-rw-r--r--src/monitor/main.cpp217
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;
+ }
+}