]> git.sur5r.net Git - i3/i3/blobdiff - src/log.c
shm-logging: implement i3-dump-log -f (follow)
[i3/i3] / src / log.c
index 05a235fabf59f3ab53b7a119bc242e17f53fab16..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>
 #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>
+#endif
 
 #include "util.h"
 #include "log.h"
 #include "i3.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;
@@ -44,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;
@@ -52,6 +59,18 @@ static int logbuffer_size;
 /* File descriptor for shm_open. */
 static int logbuffer_shm;
 
+/*
+ * Writes the offsets for the next write and for the last wrap to the
+ * shmlog_header.
+ * Necessary to print the i3 SHM log in the correct order.
+ *
+ */
+static void store_log_markers(void) {
+    header->offset_next_write = (logwalk - logbuffer);
+    header->offset_last_wrap = (loglastwrap - logbuffer);
+    header->size = logbuffer_size;
+}
+
 /*
  * Initializes logging by creating an error logfile in /tmp (or
  * XDG_RUNTIME_DIR, see get_process_filename()).
@@ -59,7 +78,7 @@ static int logbuffer_shm;
  * Will be called twice if --shmlog-size is specified.
  *
  */
-void init_logging() {
+void init_logging(void) {
     if (!errorfilename) {
         if (!(errorfilename = get_process_filename("errorlog")))
             ELOG("Could not initialize errorlog\n");
@@ -78,8 +97,15 @@ void init_logging() {
          * For 512 MiB of RAM this will lead to a 5 MiB log buffer.
          * At the moment (2011-12-10), no testcase leads to an i3 log
          * of more than ~ 600 KiB. */
-        long long physical_mem_bytes = (long long)sysconf(_SC_PHYS_PAGES) *
-                                                  sysconf(_SC_PAGESIZE);
+        long long physical_mem_bytes;
+#if defined(__APPLE__)
+        int mib[2] = { CTL_HW, HW_MEMSIZE };
+        size_t length = sizeof(long long);
+        sysctl(mib, 2, &physical_mem_bytes, &length, NULL, 0);
+#else
+        physical_mem_bytes = (long long)sysconf(_SC_PHYS_PAGES) *
+                                        sysconf(_SC_PAGESIZE);
+#endif
         logbuffer_size = min(physical_mem_bytes * 0.01, shmlog_size);
         sasprintf(&shmlogname, "/i3-log-%d", getpid());
         logbuffer_shm = shm_open(shmlogname, O_RDWR | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
@@ -103,9 +129,23 @@ void init_logging() {
             logbuffer = NULL;
             return;
         }
-        logwalk = logbuffer;
+
+        /* 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);
 }
 
 /*
@@ -119,37 +159,11 @@ void set_verbosity(bool _verbose) {
 }
 
 /*
- * Enables the given loglevel.
- *
- */
-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;
-    }
-}
-
-/*
- * Returns the offsets for the next write and for the last wrap.
- * Necessary to print the i3 SHM log in the correct order.
+ * Set debug logging.
  *
  */
-void get_log_markers(int *offset_next_write, int *offset_last_wrap, int *size) {
-    *offset_next_write = (logwalk - logbuffer);
-    *offset_last_wrap = (loglastwrap - logbuffer);
-    *size = logbuffer_size;
+void set_debug_logging(const bool _debug_logging) {
+    debug_logging = _debug_logging;
 }
 
 /*
@@ -194,22 +208,28 @@ static void vlog(const bool print, const char *fmt, va_list args) {
         vprintf(fmt, args);
     } else {
         len += vsnprintf(message + len, sizeof(message) - len, fmt, args);
-        if (len == sizeof(message)) {
+        if (len >= sizeof(message)) {
             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;
+            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);
@@ -252,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);
+    }
+}