Use Client_find_by_session() instead of a few open-coded loops.
[umurmur.git] / src / log.c
index f4a0ea117233f9e8bdd7a97dc35861a6d3eaae8c..91ec5b023ed40e0de5d8e407553114fc0f9d78f7 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,5 +1,5 @@
-/* Copyright (C) 2009-2010, Martin Johansson <martin@fatbob.nu>
-   Copyright (C) 2005-2010, Thorvald Natvig <thorvald@natvig.com>
+/* Copyright (C) 2009-2014, Martin Johansson <martin@fatbob.nu>
+   Copyright (C) 2005-2014, Thorvald Natvig <thorvald@natvig.com>
 
    All rights reserved.
 
 #include <stdlib.h>
 #include <syslog.h>
 #include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
 
 #include "log.h"
+#include "conf.h"
+#include "util.h"
 
-#define BUFSIZE 254
+#define STRSIZE 254
 
-static bool_t termprint;
+static bool_t termprint, init;
+static FILE *logfile;
+
+static void openlogfile(const char *logfilename)
+{
+       int fd, flags;
+       logfile = fopen(logfilename, "a");
+       if (logfile == NULL) {
+               Log_fatal("Failed to open log file '%s' for writing: %s\n", logfilename, strerror(errno));
+       }
+
+       /* Set the stream as line buffered */
+       if (setvbuf(logfile, NULL, _IOLBF, 0) < 0)
+               Log_fatal("setvbuf() failed: %s\n", strerror(errno));
+
+       /* XXX - Is it neccessary/appropriate that logging to file is non-blocking?
+        * If not, there's a risk that execution blocks, meaning that voice blocks
+        * as well since uMurmur is single threaded by design. OTOH, what could
+        * cause a block? If the disk causes blocking, it is probably br0ken, but
+        * the log could be on a nfs or smb share, so let's set it up as
+        * non-blocking and we'll see what happens.
+        */
+       fd = fileno(logfile);
+       flags = fcntl(fd, F_GETFL, 0);
+       fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+}
+
+static char *timestring(void)
+{
+       static char timebuf[32];
+       time_t t;
+       struct tm *timespec;
+
+       t= time(NULL);
+       timespec = localtime(&t);
+       strftime(timebuf, 32, "%b %e %T", timespec);
+       return timebuf;
+}
 
 void Log_init(bool_t terminal)
 {
+       const char *logfilename;
+
        termprint = terminal;
-       if (!termprint)
-               openlog("uMurmurd", LOG_PID, LOG_DAEMON);
+       if (termprint)
+               return;
+
+       logfilename = getStrConf(LOGFILE);
+       if (logfilename != NULL) {
+               openlogfile(logfilename);
+       }
+       else openlog("uMurmurd", LOG_PID, LOG_DAEMON);
+       init = true;
 }
 
 void Log_free()
 {
-       if (!termprint)
+       if (termprint)
+               return;
+       else if (logfile)
+               fclose(logfile);
+       else
                closelog();
 }
-               
+
+void Log_reset()
+{
+       const char *logfilename;
+
+       if (logfile) {
+               logfilename = getStrConf(LOGFILE);
+               fclose(logfile);
+               openlogfile(logfilename);
+       }
+}
 
 void logthis(const char *logstring, ...)
 {
        va_list argp;
-       char buf[BUFSIZE + 2];
-       
+       char buf[STRSIZE + 1];
+
        va_start(argp, logstring);
-       vsnprintf(&buf[0], BUFSIZE, logstring, argp);
+       vsnprintf(&buf[0], STRSIZE, logstring, argp);
        va_end(argp);
-       strcat(buf, "\n");
+
        if (termprint)
-               fprintf(stderr, "%s", buf); /* XXX - other targets for logging */
+               fprintf(stderr, "%s\n", buf);
+       else if (logfile)
+               fprintf(logfile, "%s %s\n", timestring(), buf);
        else
-               syslog(LOG_INFO, buf);
+               syslog(LOG_INFO, "%s", buf);
 }
 
 void Log_warn(const char *logstring, ...)
 {
        va_list argp;
-       char buf[BUFSIZE + 2];
+       char buf[STRSIZE + 1];
        int offset = 0;
-       
+
+       if (termprint || logfile)
+               offset = sprintf(buf, "WARN: ");
+
        va_start(argp, logstring);
-       offset = sprintf(buf, "WARN: ");
-       vsnprintf(&buf[offset], BUFSIZE - offset, logstring, argp);
+       vsnprintf(&buf[offset], STRSIZE - offset, logstring, argp);
        va_end(argp);
-       strcat(buf, "\n");
+
        if (termprint)
-               fprintf(stderr, "%s", buf); /* XXX - other targets for logging */
+               fprintf(stderr, "%s\n", buf);
+       else if (logfile)
+               fprintf(logfile, "%s %s\n", timestring(), buf);
        else
-               syslog(LOG_WARNING, buf);
+               syslog(LOG_WARNING, "%s", buf);
 }
 
 void Log_info(const char *logstring, ...)
 {
        va_list argp;
-       char buf[BUFSIZE + 2];
+       char buf[STRSIZE + 1];
        int offset = 0;
-       
+
+       if (termprint || logfile)
+               offset = sprintf(buf, "INFO: ");
+
        va_start(argp, logstring);
-       offset = sprintf(buf, "INFO: ");
-       vsnprintf(&buf[offset], BUFSIZE - offset, logstring, argp);
+       vsnprintf(&buf[offset], STRSIZE - offset, logstring, argp);
        va_end(argp);
-       strcat(buf, "\n");
+
        if (termprint)
-               fprintf(stderr, "%s", buf); /* XXX - other targets for logging */
+               fprintf(stderr, "%s\n", buf);
+       else if (logfile)
+               fprintf(logfile, "%s %s\n", timestring(), buf);
        else
-               syslog(LOG_INFO, buf);
+               syslog(LOG_INFO, "%s", buf);
+}
+
+void Log_info_client(client_t *client, const char *logstring, ...)
+{
+       va_list argp;
+       char buf[STRSIZE + 1];
+       int offset = 0;
+
+       if (termprint || logfile)
+               offset = sprintf(buf, "INFO: ");
+
+       va_start(argp, logstring);
+       offset += vsnprintf(&buf[offset], STRSIZE - offset, logstring, argp);
+       va_end(argp);
+
+       char *clientAddressString = Util_clientAddressToString(client);
+       offset += snprintf(&buf[offset], STRSIZE - offset, " - [%d] %s@%s:%d",
+               client->sessionId,
+               client->username == NULL ? "" : client->username,
+               clientAddressString,
+               Util_clientAddressToPortTCP(client));
+       free(clientAddressString);
+
+       if (termprint)
+               fprintf(stderr, "%s\n", buf);
+       else if (logfile)
+               fprintf(logfile, "%s %s\n", timestring(), buf);
+       else
+               syslog(LOG_INFO, "%s", buf);
 }
 
 #ifdef DEBUG
 void Log_debug(const char *logstring, ...)
 {
        va_list argp;
-       char buf[BUFSIZE + 2];
+       char buf[STRSIZE + 1];
        int offset = 0;
-       
+
+       if (termprint || logfile)
+               offset = sprintf(buf, "DEBUG: ");
+
        va_start(argp, logstring);
-       offset = sprintf(buf, "DEBUG: ");
-       vsnprintf(&buf[offset], BUFSIZE - offset, logstring, argp);
+       vsnprintf(&buf[offset], STRSIZE - offset, logstring, argp);
        va_end(argp);
-       strcat(buf, "\n");
        if (termprint)
-               fprintf(stderr, "%s", buf); /* XXX - other targets for logging */
+               fprintf(stderr, "%s\n", buf);
+       else if (logfile)
+               fprintf(logfile, "%s %s\n", timestring(), buf);
        else
-               syslog(LOG_DEBUG, buf);
+               syslog(LOG_DEBUG, "%s", buf);
 }
 #endif
 
 void Log_fatal(const char *logstring, ...)
 {
        va_list argp;
-       char buf[BUFSIZE + 2];
+       char buf[STRSIZE + 1];
        int offset = 0;
+
+       if (termprint || logfile)
+               offset = sprintf(buf, "FATAL: ");
+
        va_start(argp, logstring);
-       offset = sprintf(buf, "FATAL: ");
-       vsnprintf(&buf[offset], BUFSIZE - offset, logstring, argp);
+       vsnprintf(&buf[offset], STRSIZE - offset, logstring, argp);
        va_end(argp);
-       strcat(buf, "\n");
+
        if (termprint)
-               fprintf(stderr, "%s", buf); /* XXX - other targets for logging */
-       else
-               syslog(LOG_CRIT, buf);
+               fprintf(stderr, "%s\n", buf);
+       else if (logfile)
+               fprintf(logfile, "%s %s\n", timestring(), buf);
+       else { /* If logging subsystem is not initialized, fall back to stderr +
+                       * syslog logging for fatal errors.
+                       */
+               if (!init) {
+                       openlog("uMurmurd", LOG_PID, LOG_DAEMON);
+                       fprintf(stderr, "%s\n", buf);
+               }
+               syslog(LOG_CRIT, "%s", buf);
+       }
+
        exit(1);
 }