summaryrefslogtreecommitdiff
path: root/src/tool/eathena-monitor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/tool/eathena-monitor.cpp')
-rw-r--r--src/tool/eathena-monitor.cpp223
1 files changed, 223 insertions, 0 deletions
diff --git a/src/tool/eathena-monitor.cpp b/src/tool/eathena-monitor.cpp
new file mode 100644
index 0000000..1b1abd5
--- /dev/null
+++ b/src/tool/eathena-monitor.cpp
@@ -0,0 +1,223 @@
+/**
+ * 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 <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+#include <time.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#define HOME getenv("HOME")
+#define LOGIN_SERVER "./login-server"
+#define MAP_SERVER "./map-server"
+#define CHAR_SERVER "./char-server"
+#define CONFIG "conf/eathena-monitor.conf"
+#define LOGFILE "log/eathena-monitor.log"
+
+
+#define SKIP_BLANK(ptr) ptr += skip_blank(ptr)
+static inline size_t skip_blank(const char* ptr) {
+ size_t i = 0;
+ while (
+ (ptr[i] == ' ') ||
+ (ptr[i] == '\b') ||
+ (ptr[i] == '\n') ||
+ (ptr[i] == '\r')
+ ) ptr++;
+ return i;
+}
+
+#define GOTO_EQL(ptr) ptr += goto_eql(ptr)
+static inline size_t goto_eql(const char* ptr) {
+ size_t i = 0;
+ while (
+ (ptr[i] != '\0') &&
+ (ptr[i] != '=') &&
+ (ptr[i] != '\n') &&
+ (ptr[i] != '\r')
+ ) ptr++;
+ return i;
+}
+
+#define GOTO_EOL(ptr) ptr += goto_newline(ptr)
+static inline size_t goto_newline(const char* ptr) {
+ size_t i = 0;
+ while (
+ (ptr[i] != '\0') &&
+ (ptr[i] != '\n') &&
+ (ptr[i] != '\r')
+ ) ptr++;
+ return i;
+}
+
+// initialiized to $HOME/tmwserver
+const char *workdir;
+//the rest are relative to workdir
+const char *login_server = LOGIN_SERVER;
+const char *map_server = MAP_SERVER;
+const char *char_server = CHAR_SERVER;
+const char *logfile = LOGFILE;
+// this variable is hard-coded, but the command-line is checked first
+const char *config = CONFIG;
+
+pid_t pid_login, pid_map, pid_char;
+
+const char* make_path (const char* base, const char* path) {
+ size_t base_len = strlen(base);
+ size_t path_len = strlen(path);
+ char* out = (char *)malloc(base_len + 1 + path_len + 1);
+ memcpy(out, base, base_len);
+ out[base_len] = '/';
+ memcpy(out + base_len + 1, path, path_len);
+ out[base_len + 1 + path_len] = '\0';
+ return out;
+}
+
+void parse_option (char *name, char *value) {
+ if (!strcasecmp(name, "login_server")) {
+ login_server = strdup(value);
+ } else if (!strcasecmp(name, "map_server")) {
+ map_server = strdup(value);
+ } else if (!strcasecmp(name, "char_server")) {
+ char_server = strdup(value);
+ } else if (!strcasecmp(name, "workdir")) {
+ workdir = strdup(value);
+ } else if (!strcasecmp(name, "logfile")) {
+ logfile = strdup(value);
+ } else {
+ fprintf(stderr, "WARNING: ingnoring invalid option '%s' = '%s'\n", name, value);
+ }
+}
+
+void read_config(const char *filename) {
+ FILE *input;
+ char string[1000];
+
+ if (!(input = fopen(filename,"r")) && !(input = fopen (config, "r"))) {
+ perror("Unable to load config file");
+ return;
+ }
+
+ while (1) {
+ if (fgets (string, sizeof (string) - 1, input) == NULL)
+ break;
+ char *str = string, *name, *value;
+ SKIP_BLANK(str);
+ string[sizeof (string) - 1] = '\0';
+ if (*str == '#')
+ continue;
+ if (*str == '\0')
+ continue;
+ name = str;
+
+ GOTO_EQL (str);
+
+ if (*str != '=') {
+ continue;
+ }
+
+ *str++ = '\0';
+ SKIP_BLANK(str);
+ value = str;
+ GOTO_EOL(str);
+ *str = '\0';
+ parse_option(name, value);
+ }
+
+ fclose (input);
+}
+
+pid_t start_process(const char *exec) {
+ const char *args[2] = {exec, NULL};
+ pid_t pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "Failed to fork");
+ return 0;
+ }
+ if (pid == 0) {
+ execv(exec, (char**)args);
+ perror("Failed to exec");
+ kill(getppid(), SIGABRT);
+ exit(1);
+ }
+ return pid;
+}
+
+// Kill all children with the same signal we got, then ourself.
+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);
+ signal(sig, SIG_DFL);
+ 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(HOME, "tmwserver");
+
+ read_config(argc>1 ? argv[1] : NULL);
+
+ if (chdir(workdir) < 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
+ time_t t = time(NULL);
+ struct tm *tmp = localtime(&t);
+ char timestamp[256];
+ strftime(timestamp, sizeof(timestamp), "%F %T", tmp);
+
+ if (!pid_login) {
+ pid_login = start_process(login_server);
+ fprintf (stderr, "[%s] forked login server: %lu\n", timestamp, (unsigned long)pid_login);
+ }
+ if (!pid_char) {
+ pid_char = start_process(char_server);
+ fprintf (stderr, "[%s] forked char server: %lu\n", timestamp, (unsigned long)pid_char);
+ }
+ if (!pid_map) {
+ pid_map = start_process(map_server);
+ fprintf (stderr, "[%s] forked map server: %lu\n", timestamp, (unsigned long)pid_map);
+ }
+ pid_t dead = wait(NULL);
+ if (dead < 0) 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;
+ }
+}