moving NeonServ into background by default; added a few startup parameters
[NeonServV5.git] / src / main.c
index 600c519f3b4fa722fad902fbfaf83e1312e69951..1730e38026f4389f721f33b8d15891190034e812 100644 (file)
@@ -38,6 +38,7 @@
 #include "ConfigParser.h"
 #include "ssl.h"
 #include "QServer.h"
+#include "version.h"
 
 time_t start_time;
 static int running, hard_restart;
@@ -45,12 +46,16 @@ static int statistics_requested_lusers = 0;
 int statistics_enabled;
 TIMEQ_CALLBACK(main_statistics);
 TIMEQ_CALLBACK(main_checkauths);
+static int daemonized = 0;
+static int print_loglevel = 0;
+static FILE *log_fptr = NULL;
 static int process_argc;
 static char **process_argv;
 #ifdef HAVE_THREADS
 int running_threads;
 pthread_mutex_t cache_sync;
 pthread_mutex_t whohandler_sync, whohandler_mass_sync;
+static pthread_mutex_t log_sync;
 #endif
 
 void cleanup() {
@@ -71,32 +76,28 @@ void cleanup() {
 static int load_mysql_config() {
     char *mysql_host, *mysql_user, *mysql_pass, *mysql_base;
     int mysql_serverport;
-    if(loadConfig("neonserv.conf")) {
-        mysql_host = get_string_field("MySQL.host");
-        if(!mysql_host) {
-            perror("invalid neonserv.conf: missing MySQL.host");
-            return 0;
-        }
-        mysql_serverport = get_int_field("MySQL.port");
-        if(!mysql_serverport)
-            mysql_serverport = 3306;
-        mysql_user = get_string_field("MySQL.user");
-        if(!mysql_user) {
-            perror("invalid neonserv.conf: missing MySQL.user");
-            return 0;
-        }
-        mysql_pass = get_string_field("MySQL.pass");
-        if(!mysql_pass) {
-            perror("invalid neonserv.conf: missing MySQL.pass");
-            return 0;
-        }
-        mysql_base = get_string_field("MySQL.base");
-        if(!mysql_base) {
-            perror("invalid neonserv.conf: missing MySQL base");
-            return 0;
-        }
-    } else {
-        perror("Unable to load neonserv.conf");
+    
+    mysql_host = get_string_field("MySQL.host");
+    if(!mysql_host) {
+        perror("invalid neonserv.conf: missing MySQL.host");
+        return 0;
+    }
+    mysql_serverport = get_int_field("MySQL.port");
+    if(!mysql_serverport)
+        mysql_serverport = 3306;
+    mysql_user = get_string_field("MySQL.user");
+    if(!mysql_user) {
+        perror("invalid neonserv.conf: missing MySQL.user");
+        return 0;
+    }
+    mysql_pass = get_string_field("MySQL.pass");
+    if(!mysql_pass) {
+        perror("invalid neonserv.conf: missing MySQL.pass");
+        return 0;
+    }
+    mysql_base = get_string_field("MySQL.base");
+    if(!mysql_base) {
+        perror("invalid neonserv.conf: missing MySQL base");
         return 0;
     }
     init_mysql(mysql_host, mysql_serverport, mysql_user, mysql_pass, mysql_base);
@@ -137,11 +138,76 @@ int getCurrentThreadID() {
 
 #endif
 
+void exit_daemon() {
+    if(daemonized) {
+        remove(PID_FILE);
+    }
+}
+
 int main(int argc, char *argv[]) {
-main:
+    int run_as_daemon = 1;
+    int argi;
     process_argv = argv;
     process_argc = argc;
+    printf("NeonServ v%s\n\n", NEONSERV_VERSION);
+    for(argi = 1; argi < argc; argi++) {
+        if(!strcmp(argv[argi], "-f") || !strcmp(argv[argi], "--foreground")) {
+            run_as_daemon = 0;
+        } else if(!strcmp(argv[argi], "-s") || !strcmp(argv[argi], "--show")) {
+            if(argi+1 == argc) break;
+            argi++;
+            print_loglevel = atoi(argv[argi]);
+        } else if(!strcmp(argv[argi], "-v") || !strcmp(argv[argi], "--version")) {
+            printf("Version: %s.%d (%s)\n", NEONSERV_VERSION, patchlevel, (strcmp(revision, "") ? revision : "-"));
+            printf("Build: #%s %s (%s lines, " COMPILER ")\n", compilation, creation, codelines);
+            exit(0);
+        } else if(!strcmp(argv[argi], "-h") || !strcmp(argv[argi], "--help")) {
+            printf("Usage: ./neonserv [-s loglevel] [-f] [-h|-v]\n");
+            printf(" -s, --show           show log lines matching loglevel in stdout.\n");
+            printf(" -f, --foreground     run NeonServ in the foreground.\n");
+            printf(" -h, --help           prints this usage message.\n");
+            printf(" -v, --version        prints this program's version.\n");
+            exit(0);
+        }
+    }
+    if(geteuid() == 0 || getuid() == 0) {
+        fprintf(stderr, "NeonServ may not be run with super user privileges.\n");
+        exit(0);
+    }
+    if(!loadConfig(CONF_FILE)) {
+        fprintf(stderr, "Unable to load " CONF_FILE "\n");
+        exit(0);
+    }
+    #if HAVE_THREADS
+    THREAD_MUTEX_INIT(log_sync);
+    #endif
+    if (run_as_daemon) {
+        /* Attempt to fork into the background if daemon mode is on. */
+        pid_t pid = fork();
+        if (pid < 0) {
+            fprintf(stderr, "Unable to fork: %s\n", strerror(errno));
+        } else if (pid > 0) {
+            printf("Forking into the background (pid: %d)...\n", pid);
+            putlog(LOGLEVEL_INFO, "Forking into the background (pid: %d)...\n", pid);
+            exit(0);
+        }
+        setsid();
+        daemonized = 1;
+        atexit(exit_daemon);
+        FILE *pidfile = fopen(PID_FILE, "w");
+        if (pidfile == NULL) {
+            fprintf(stderr, "Unable to create PID file: %s", strerror(errno));
+            putlog(LOGLEVEL_ERROR, "Unable to create PID file: %s", strerror(errno));
+        } else {
+            fprintf(pidfile, "%i\n", (int)getpid());
+            fclose(pidfile);
+        }
+        fclose(stdin);
+        fclose(stdout);
+        fclose(stderr);
+    }
     
+main:
     signal(SIGABRT, sighandler);
     signal(SIGFPE, sighandler);
     signal(SIGILL, sighandler);
@@ -298,7 +364,7 @@ void stop_bot() {
 }
 
 void reload_config() {
-    loadConfig("neonserv.conf");
+    loadConfig(CONF_FILE);
 }
 
 static int getCurrentSecondsOfDay() {
@@ -419,3 +485,42 @@ void statistics_update() {
         system(command);
     }
 }
+
+void write_log(int loglevel, const char *line, int len) {
+    SYNCHRONIZE(log_sync);
+    if(!daemonized && (print_loglevel & loglevel)) {
+        printf("%s", line);
+    } else if(!daemonized && loglevel == LOGLEVEL_ERROR) {
+        fprintf(stderr, "%s", line);
+    }
+    if(get_int_field("log.loglevel") & loglevel) {
+        if(!log_fptr) {
+            log_fptr = fopen(LOG_FILE, "a");
+            if(!log_fptr) goto write_log_end;
+        }
+        time_t rawtime;
+        struct tm *timeinfo;
+        time(&rawtime);
+        timeinfo = localtime(&rawtime);
+        char timestr[20];
+        int timepos = strftime(timestr, 20, "%x %X ", timeinfo);
+        fwrite(timestr, 1, timepos, log_fptr);
+        fwrite(line, 1, len, log_fptr);
+    }
+    write_log_end:
+    DESYNCHRONIZE(log_sync);
+}
+
+void putlog(int loglevel, const char *text, ...) {
+    va_list arg_list;
+    char logBuf[MAXLOGLEN];
+    int pos;
+    logBuf[0] = '\0';
+    va_start(arg_list, text);
+    pos = vsnprintf(logBuf, MAXLOGLEN - 1, text, arg_list);
+    va_end(arg_list);
+    if (pos < 0 || pos > (MAXLOGLEN - 1)) pos = MAXLOGLEN - 1;
+    logBuf[pos] = '\0';
+    write_log(loglevel, logBuf, pos);
+}
+