]> git.sur5r.net Git - i3/i3/blobdiff - src/log.c
shm-logging: implement i3-dump-log -f (follow)
[i3/i3] / src / log.c
index 92e8f57c758f75c4ca2a98b31a5ab42a35ebf8cc..16fa0beded1acd51cce8bc9faf3fe78370bc103d 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -1,10 +1,12 @@
+#undef I3__FILE__
+#define I3__FILE__ "log.c"
 /*
  * vim:ts=4:sw=4:expandtab
  *
  * i3 - an improved dynamic tiling window manager
  * © 2009-2011 Michael Stapelberg and contributors (see also: LICENSE)
  *
- * log.c: Setting of loglevels, logging functions.
+ * log.c: Logging functions.
  *
  */
 #include <stdarg.h>
@@ -18,6 +20,7 @@
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <errno.h>
+#include <pthread.h>
 #if defined(__APPLE__)
 #include <sys/types.h>
 #include <sys/sysctl.h>
 #include "libi3.h"
 #include "shmlog.h"
 
-/* loglevels.h is autogenerated at make time */
-#include "loglevels.h"
-
-static uint64_t loglevel = 0;
+static bool debug_logging = false;
 static bool verbose = false;
 static FILE *errorfile;
 char *errorfilename;
@@ -49,6 +49,8 @@ int shmlog_size = 0;
 static char *logbuffer;
 /* A pointer (within logbuffer) where data will be written to next. */
 static char *logwalk;
+/* A pointer to the shmlog header */
+static i3_shmlog_header *header;
 /* A pointer to the byte where we last wrapped. Necessary to not print the
  * left-overs at the end of the ringbuffer. */
 static char *loglastwrap;
@@ -64,8 +66,6 @@ static int logbuffer_shm;
  *
  */
 static void store_log_markers(void) {
-    i3_shmlog_header *header = (i3_shmlog_header*)logbuffer;
-
     header->offset_next_write = (logwalk - logbuffer);
     header->offset_last_wrap = (loglastwrap - logbuffer);
     header->size = logbuffer_size;
@@ -129,10 +129,23 @@ void init_logging(void) {
             logbuffer = NULL;
             return;
         }
+
+        /* Initialize with 0-bytes, just to be sure… */
+        memset(logbuffer, '\0', logbuffer_size);
+
+        header = (i3_shmlog_header*)logbuffer;
+
+        pthread_condattr_t cond_attr;
+        pthread_condattr_init(&cond_attr);
+        if (pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED) != 0)
+            ELOG("pthread_condattr_setpshared() failed, i3-dump-log -f will not work!\n");
+        pthread_cond_init(&(header->condvar), &cond_attr);
+
         logwalk = logbuffer + sizeof(i3_shmlog_header);
         loglastwrap = logbuffer + logbuffer_size;
         store_log_markers();
     }
+    atexit(purge_zerobyte_logfile);
 }
 
 /*
@@ -146,26 +159,11 @@ void set_verbosity(bool _verbose) {
 }
 
 /*
- * Enables the given loglevel.
+ * Set debug logging.
  *
  */
-void add_loglevel(const char *level) {
-    /* Handle the special loglevel "all" */
-    if (strcasecmp(level, "all") == 0) {
-        loglevel = UINT64_MAX;
-        return;
-    }
-
-    for (int i = 0; i < sizeof(loglevels) / sizeof(char*); i++) {
-        if (strcasecmp(loglevels[i], level) != 0)
-            continue;
-
-        /* The position in the array (plus one) is the amount of times
-         * which we need to shift 1 to the left to get our bitmask for
-         * the specific loglevel. */
-        loglevel |= (1 << (i+1));
-        break;
-    }
+void set_debug_logging(const bool _debug_logging) {
+    debug_logging = _debug_logging;
 }
 
 /*
@@ -214,22 +212,25 @@ static void vlog(const bool print, const char *fmt, va_list args) {
             fprintf(stderr, "BUG: single log message > 4k\n");
         }
 
-        /* If there is no space for the current message (plus trailing
-         * nullbyte) in the ringbuffer, we need to wrap and write to the
-         * beginning again. */
-        if ((len+1) >= (logbuffer_size - (logwalk - logbuffer))) {
+        /* If there is no space for the current message in the ringbuffer, we
+         * need to wrap and write to the beginning again. */
+        if (len >= (logbuffer_size - (logwalk - logbuffer))) {
             loglastwrap = logwalk;
             logwalk = logbuffer + sizeof(i3_shmlog_header);
+            store_log_markers();
+            header->wrap_count++;
         }
 
-        /* Copy the buffer, terminate it, move the write pointer to the byte after
-         * our current message. */
+        /* Copy the buffer, move the write pointer to the byte after our
+         * current message. */
         strncpy(logwalk, message, len);
-        logwalk[len] = '\0';
-        logwalk += len + 1;
+        logwalk += len;
 
         store_log_markers();
 
+        /* Wake up all (i3-dump-log) processes waiting for condvar. */
+        pthread_cond_broadcast(&(header->condvar));
+
         if (print)
             fwrite(message, len, 1, stdout);
     }
@@ -271,17 +272,44 @@ void errorlog(char *fmt, ...) {
 
 /*
  * Logs the given message to stdout while prefixing the current time to it,
- * but only if the corresponding debug loglevel was activated.
+ * but only if debug logging was activated.
  * This is to be called by DLOG() which includes filename/linenumber
  *
  */
-void debuglog(uint64_t lev, char *fmt, ...) {
+void debuglog(char *fmt, ...) {
     va_list args;
 
-    if (!logbuffer && !(loglevel & lev))
+    if (!logbuffer && !(debug_logging))
         return;
 
     va_start(args, fmt);
-    vlog((loglevel & lev), fmt, args);
+    vlog(debug_logging, fmt, args);
     va_end(args);
 }
+
+/*
+ * Deletes the unused log files. Useful if i3 exits immediately, eg.
+ * because --get-socketpath was called. We don't care for syscall
+ * failures. This function is invoked automatically when exiting.
+ */
+void purge_zerobyte_logfile(void) {
+    struct stat st;
+    char *slash;
+
+    if (!errorfilename)
+        return;
+
+    /* don't delete the log file if it contains something */
+    if ((stat(errorfilename, &st)) == -1 || st.st_size > 0)
+        return;
+
+    if (unlink(errorfilename) == -1)
+        return;
+
+    if ((slash = strrchr(errorfilename, '/')) != NULL) {
+        *slash = '\0';
+        /* possibly fails with ENOTEMPTY if there are files (or
+         * sockets) left. */
+        rmdir(errorfilename);
+    }
+}