moving NeonServ into background by default; added a few startup parameters
authorpk910 <philipp@zoelle1.de>
Mon, 30 Jan 2012 22:41:35 +0000 (23:41 +0100)
committerpk910 <philipp@zoelle1.de>
Mon, 30 Jan 2012 23:26:38 +0000 (00:26 +0100)
src/IRCParser.c
src/main.c
src/main.h
src/mysqlConn.c
src/signal.c

index eed9380f8e0dddb242bee17782200f36ab36d987..91b493822ada4d0e89ed042323a5d954ee7b370f 100644 (file)
@@ -58,9 +58,9 @@ static void parse_line(struct ClientSocket *client, char *line) {
     int argc = 0;
     char *argv[MAXNUMPARAMS];
     #ifdef HAVE_THREADS
-    printf("[%d recv %lu] %s\n", getCurrentThreadID(), (unsigned long) strlen(line), line);
+    putlog(LOGLEVEL_RAW, "[%d recv %lu] %s\n", getCurrentThreadID(), (unsigned long) strlen(line), line);
     #else
-    printf("[recv %lu] %s\n", (unsigned long) strlen(line), line);
+    putlog(LOGLEVEL_RAW, "[recv %lu] %s\n", (unsigned long) strlen(line), line);
     #endif
     if(line[0] == ':')
         line++;
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);
+}
+
index b8d260b8ec68be3582645f48b5447b7757f33095..12c689958eb768c0c88c89fd185872a57d4950cf 100644 (file)
 #include "memoryDebug.h"
 #endif
 
+#define PID_FILE "neonserv.pid"
+#define CONF_FILE "neonserv.conf"
+#define LOG_FILE "neonserv.log"
+
 #define SOCKET_SELECT_TIME    1
 #define SOCKET_RECONNECT_TIME 20
 
 #define TOPICLEN        500
 #define CHANNELLEN      200
 #define MAXLEN          512
+#define MAXLOGLEN       1024
 #define TRIGGERLEN      50
 #define MAXNUMPARAMS    200 /* maximum number of parameters in one line */
 #define MAXLANGUAGES    5
 
 #define TEMPUSER_LIST_INDEX VALID_NICK_CHARS_FIRST_LEN
 
+#define LOGLEVEL_INFO  0x01
+#define LOGLEVEL_ERROR 0x02
+#define LOGLEVEL_RAW   0x04
+#define LOGLEVEL_MYSQL 0x08
+
 #define timeval_is_bigger(x,y) ((x.tv_sec > y.tv_sec) || (x.tv_sec == y.tv_sec && x.tv_usec > y.tv_usec))
 
 extern time_t start_time;
@@ -148,6 +158,8 @@ extern pthread_mutex_t whohandler_sync, whohandler_mass_sync;
 int getCurrentThreadID();
 #endif
 
+void exit_daemon();
+
 int stricmp (const char *s1, const char *s2);
 int stricmplen (const char *s1, const char *s2, int len);
 
@@ -159,4 +171,8 @@ void reload_config();
 
 void statistics_update();
 
+void putlog(int loglevel, const char *text, ...) PRINTF_LIKE(2, 3);
+
+#define perror(errmsg) (putlog(LOGLEVEL_ERROR, "ERROR (%s:%d) %s", __FILE__, __LINE__, errmsg))
+
 #endif
\ No newline at end of file
index 3e5dd19dfac03a156ede2bb49a30bfb26f83b5a7..529e8a4347f6ca26a824c4a551be937c2e68ac11 100644 (file)
@@ -204,7 +204,7 @@ void free_mysql() {
 void show_mysql_error() {
     MYSQL *mysql_conn = get_mysql_conn();
     //show mysql_error()
-    printf("MySQL Error: %s\n", mysql_error(mysql_conn));
+    putlog(LOGLEVEL_ERROR, "MySQL Error: %s\n", mysql_error(mysql_conn));
 }
 
 void printf_mysql_query(const char *text, ...) {
@@ -218,7 +218,7 @@ void printf_mysql_query(const char *text, ...) {
     va_end(arg_list);
     if (pos < 0 || pos > (MYSQLMAXLEN - 2)) pos = MYSQLMAXLEN - 2;
     queryBuf[pos] = '\0';
-    printf("MySQL: %s\n", queryBuf);
+    putlog(LOGLEVEL_MYSQL, "MySQL: %s\n", queryBuf);
     if(mysql_query(mysql_conn, queryBuf)) {
         check_mysql();
         if(mysql_query(mysql_conn, queryBuf)) {
@@ -238,7 +238,7 @@ void printf_long_mysql_query(int len, const char *text, ...) {
     va_end(arg_list);
     if (pos < 0 || pos > (len - 2)) pos = len - 2;
     queryBuf[pos] = '\0';
-    printf("MySQL: %s\n", queryBuf);
+    putlog(LOGLEVEL_MYSQL, "MySQL: %s\n", queryBuf);
     if(mysql_query(mysql_conn, queryBuf)) {
         check_mysql();
         if(mysql_query(mysql_conn, queryBuf)) {
index d5431da482bbed62855f2beeec2f6217988269d3..848828c4a27779c95f6fa42c1eeff527aa800abc 100644 (file)
@@ -24,6 +24,7 @@ static void sigcrash();
 static void sigexit();
 
 void sighandler(int signum) {
+    putlog(LOGLEVEL_INFO, "Received Signal %d", signum);
     signal(signum, SIG_DFL);
     switch(signum) {
         case SIGABRT:
@@ -44,12 +45,6 @@ void sighandler(int signum) {
 
 static void sigcrash(int signum) {
     char *coregen = "";
-    #ifndef WIN32
-    char gcore[50];
-    sprintf(gcore, "gcore %u", getpid());
-    system(gcore); //generate core file
-    coregen = "core file generated.";
-    #endif
     char *signame;
     switch(signum) {
         case SIGSEGV:
@@ -65,6 +60,14 @@ static void sigcrash(int signum) {
             signame = "SIGUNKNOWN";
             break;
     }
+    putlog(LOGLEVEL_ERROR, "NeonServ process crashed (%s)", signame);
+    #ifndef WIN32
+    char gcore[50];
+    sprintf(gcore, "gcore %u", getpid());
+    system(gcore); //generate core file
+    coregen = "core file generated.";
+    putlog(LOGLEVEL_ERROR | LOGLEVEL_INFO, "generated core file.");
+    #endif
     char *alertchan = get_string_field("General.alertchan");
     if(alertchan) {
         struct ChanNode *channel = getChanByName(alertchan);
@@ -81,6 +84,8 @@ static void sigcrash(int signum) {
             bot->flags &= SOCKET_FLAG_CONNECTED;
         }
     }
+    putlog(LOGLEVEL_INFO, "hard shutdown...");
+    exit_daemon();
     sleep(1);
     //hard restart
     restart_process();
@@ -88,4 +93,6 @@ static void sigcrash(int signum) {
 
 static void sigexit(int signum) {
     cleanup();
+    exit_daemon();
+    exit(0);
 }