]> mj.ucw.cz Git - libucw.git/blobdiff - ucw/log-file.c
Opt: OPT_MULTIPLE implemented, no tests yet but seems working
[libucw.git] / ucw / log-file.c
index 2df094989cdd45acd000d1a1eb219ee073c39fe2..b00f5b3a054304bc951bdd1cd8f55b5710ffc5d9 100644 (file)
@@ -8,10 +8,12 @@
  *     of the GNU Lesser General Public License.
  */
 
-#include "ucw/lib.h"
-#include "ucw/log.h"
-#include "ucw/lfs.h"
-#include "ucw/threads.h"
+#include <ucw/lib.h>
+#include <ucw/log.h>
+#include <ucw/log-internal.h>
+#include <ucw/io.h>
+#include <ucw/threads.h>
+#include <ucw/simple-lists.h>
 
 #include <stdio.h>
 #include <string.h>
 #include <time.h>
 #include <errno.h>
 
-#if 0 // FIXME
+struct file_stream {
+  struct log_stream ls;                // ls.name is the current name of the log file
+  int fd;
+  uns flags;                   // FF_xxx
+  char *orig_name;             // Original name with strftime escapes
+};
+
+#define MAX_EXPAND 64          // Maximum size of expansion of strftime escapes
 
-static char *log_name_patt;
-static int log_params;
-static int log_filename_size;
 static int log_switch_nest;
 
+static void
+do_log_reopen(struct file_stream *fs, const char *name)
+{
+  int fd = ucw_open(name, O_WRONLY | O_CREAT | O_APPEND, 0666);
+  if (fd < 0)
+    die("Unable to open log file %s: %m", name);
+  if (fs->fd >= 0)
+    close(fs->fd);
+  fs->fd = fd;
+  if (fs->flags & FF_FD2_FOLLOWS)
+    dup2(fd, 2);
+  if (fs->ls.name)
+    {
+      xfree(fs->ls.name);
+      fs->ls.name = NULL;      // We have to keep the stream consistent -- die() below can invoke logging
+    }
+  fs->ls.name = xstrdup(name);
+}
+
 static int
-do_log_switch(struct tm *tm)
+do_log_switch(struct file_stream *fs, struct tm *tm)
 {
-  int fd, l;
-  char name[log_filename_size];
+  if (!(fs->flags & FF_FORMAT_NAME))
+    {
+      if (fs->fd >= 0)
+       return 1;
+      else
+       {
+         do_log_reopen(fs, fs->orig_name);
+         return 1;
+       }
+    }
+
+  int buflen = strlen(fs->orig_name) + MAX_EXPAND;
+  char name[buflen];
   int switched = 0;
 
-  if (!log_name_patt ||
-      log_filename[0] && !log_params)
-    return 0;
   ucwlib_lock();
-  log_switch_nest++;
-  l = strftime(name, log_filename_size, log_name_patt, tm);
-  if (l < 0 || l >= log_filename_size)
-    die("Error formatting log file name: %m");
-  if (strcmp(name, log_filename))
+  if (!log_switch_nest)                // Avoid infinite loops if we die when switching logs
     {
-      strcpy(log_filename, name);
-      fd = ucw_open(name, O_WRONLY | O_CREAT | O_APPEND, 0666);
-      if (fd < 0)
-       die("Unable to open log file %s: %m", name);
-      dup2(fd, 2);
-      close(fd);
-      switched = 1;
+      log_switch_nest++;
+      int l = strftime(name, buflen, fs->orig_name, tm);
+      if (l < 0 || l >= buflen)
+       die("Error formatting log file name: %m");
+      if (!fs->ls.name || strcmp(name, fs->ls.name))
+       {
+         do_log_reopen(fs, name);
+         switched = 1;
+       }
+      log_switch_nest--;
     }
-  log_switch_nest--;
   ucwlib_unlock();
   return switched;
 }
 
-int
-log_switch(void)
+static void
+file_close(struct log_stream *ls)
 {
-  time_t tim = time(NULL);
-  return do_log_switch(localtime(&tim));
+  struct file_stream *fs = (struct file_stream *) ls;
+  if ((fs->flags & FF_CLOSE_FD) && fs->fd >= 0)
+    close(fs->fd);
+  xfree(fs->ls.name);
+  xfree(fs->orig_name);
 }
 
-static void
-internal_log_switch(struct tm *tm)
+static int
+file_handler(struct log_stream *ls, struct log_msg *m)
 {
-  if (!log_switch_nest)
-    do_log_switch(tm);
+  struct file_stream *fs = (struct file_stream *) ls;
+  if ((fs->flags & FF_FORMAT_NAME) && m->tm)
+    do_log_switch(fs, m->tm);
+
+  int r = write(fs->fd, m->m, m->m_len);
+  return ((r < 0) ? errno : 0);
 }
 
-void
-log_file(const char *name)
+struct log_stream *
+log_new_fd(int fd, uns flags)
 {
-  if (name)
-    {
-      if (log_name_patt)
-       xfree(log_name_patt);
-      if (log_filename)
-       {
-         xfree(log_filename);
-         log_filename = NULL;
-       }
-      log_name_patt = xstrdup(name);
-      log_params = !!strchr(name, '%');
-      log_filename_size = strlen(name) + 64;   /* 63 is an upper bound on expansion of % escapes */
-      log_filename = xmalloc(log_filename_size);
-      log_filename[0] = 0;
-      log_switch();
-      log_switch_hook = internal_log_switch;
-    }
+  struct log_stream *ls = log_new_stream(sizeof(struct file_stream));
+  struct file_stream *fs = (struct file_stream *) ls;
+  fs->fd = fd;
+  fs->flags = flags;
+  ls->msgfmt = LSFMT_DEFAULT;
+  ls->handler = file_handler;
+  ls->close = file_close;
+  ls->name = xmalloc(16);
+  snprintf(ls->name, 16, "fd%d", fd);
+  return ls;
 }
 
-void
-log_fork(void)
+struct log_stream *
+log_new_file(const char *path, uns flags)
 {
-  log_pid = getpid();
+  struct log_stream *ls = log_new_stream(sizeof(struct file_stream));
+  struct file_stream *fs = (struct file_stream *) ls;
+  fs->fd = -1;
+  fs->orig_name = xstrdup(path);
+  if (strchr(path, '%'))
+    fs->flags = FF_FORMAT_NAME;
+  fs->flags |= FF_CLOSE_FD | flags;
+  ls->msgfmt = LSFMT_DEFAULT;
+  ls->handler = file_handler;
+  ls->close = file_close;
+
+  time_t now = time(NULL);
+  struct tm *tm = localtime(&now);
+  ASSERT(tm);
+  do_log_switch(fs, tm);               // die()'s on errors
+  return ls;
+}
+
+int
+log_switch(void)
+{
+  time_t now = time(NULL);
+  struct tm *tm = localtime(&now);
+  ASSERT(tm);
+
+  int switched = 0;
+  for (int i=0; i < log_streams_after; i++)
+    if (log_streams.ptr[i]->handler == file_handler)
+      switched |= do_log_switch((struct file_stream *) log_streams.ptr[i], tm);
+  return switched;
 }
 
 void
@@ -112,57 +172,13 @@ log_switch_enable(void)
   log_switch_nest--;
 }
 
-#endif
-
-/* destructor for standard files */
-static void ls_fdfile_close(struct log_stream *ls)
-{
-  ASSERT(ls);
-  close(ls->idata);
-  if(ls->name)
-    xfree(ls->name);
-}
-
-/* handler for standard files */
-static int ls_fdfile_handler(struct log_stream* ls, const char *m, uns cat UNUSED)
-{
-  int len = strlen(m);
-  int r = write(ls->idata, m, len);
-  /* TODO: check the errors here? */
-  if (r!=len)
-    return errno;
-  return 0;
-}
-
-/* assign log to a file descriptor */
-/* initialize with the default formatting, does NOT close the descriptor */
-struct log_stream *ls_fdfile_new(int fd)
+void
+log_file(const char *name)
 {
-  struct log_stream *ls=log_new_stream();
-  ls->idata=fd;
-  ls->msgfmt=LSFMT_DEFAULT;
-  ls->handler=ls_fdfile_handler;
-  return ls;
-}
+  if (!name)
+    return;
 
-/* open() a file (append mode) */
-/* initialize with the default formatting */
-struct log_stream *ls_file_new(const char *path)
-{
-  struct log_stream *ls;
-  int fd = open(path, O_WRONLY | O_CREAT | O_APPEND, 0666);
-  if (fd<0)
-  {
-    msg(L_ERROR, "Opening logfile '%s' failed: %m.", path);
-    return NULL;
-  }
-  ls = log_new_stream();
-  ls->name = xstrdup(path);
-  ls->idata = fd;
-  ls->msgfmt = LSFMT_DEFAULT;
-  ls->handler = ls_fdfile_handler;
-  ls->close = ls_fdfile_close;
-  return ls;
+  log_set_default_stream(log_new_file(name, FF_FD2_FOLLOWS));
 }
 
 #ifdef TEST
@@ -171,8 +187,11 @@ int main(int argc, char **argv)
 {
   log_init(argv[0]);
   log_file("/proc/self/fd/1");
+  // struct log_stream *ls = log_new_fd(1, 0);
+  // struct log_stream *ls = log_new_file("/tmp/quork-%Y%m%d-%H%M%S", 0);
   for (int i=1; i<argc; i++)
     msg(L_INFO, argv[i]);
+  log_close_all();
   return 0;
 }