X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;ds=sidebyside;f=ucw%2Flog-file.c;h=cd0d037c478e0e09ffe7a58c90ae9b35055d6c0c;hb=dbe3b315edac25079fcbfe4df20e80b534f2a7a1;hp=2df094989cdd45acd000d1a1eb219ee073c39fe2;hpb=50f3497ac9b5b0178de03522b2cfcb5893e7ac2f;p=libucw.git diff --git a/ucw/log-file.c b/ucw/log-file.c index 2df09498..cd0d037c 100644 --- a/ucw/log-file.c +++ b/ucw/log-file.c @@ -10,93 +10,164 @@ #include "ucw/lib.h" #include "ucw/log.h" +#include "ucw/log-internal.h" #include "ucw/lfs.h" #include "ucw/threads.h" +#include "ucw/simple-lists.h" #include #include #include #include #include -#include -#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 +}; + +enum log_file_flag { + FF_FORMAT_NAME = 1, // Name contains strftime escapes + FF_CLOSE_FD = 2, // Close the fd with the stream + FF_FD2_FOLLOWS = 4, // Maintain stderr as a clone of this stream +}; + +#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); + /* FIXME: check for errors here? */ + return 0; } -void -log_file(const char *name) +struct log_stream * +log_new_fd(int fd) { - 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; + 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) +static struct log_stream * +do_log_new_file(const char *path, uns more_flags) +{ + 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 | more_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; +} + +struct log_stream * +log_new_file(const char *path) +{ + return do_log_new_file(path, 0); +} + +int +log_switch(void) { - log_pid = getpid(); + 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 +183,16 @@ 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; + struct log_stream *ls = do_log_new_file(name, FF_FD2_FOLLOWS); + struct log_stream *def = log_stream_by_flags(0); + log_rm_substream(def, NULL); + log_add_substream(def, ls); } #ifdef TEST @@ -171,6 +201,8 @@ int main(int argc, char **argv) { log_init(argv[0]); log_file("/proc/self/fd/1"); + // struct log_stream *ls = log_new_fd(1); + // struct log_stream *ls = log_new_file("/tmp/quork-%Y%m%d-%H%M%S"); for (int i=1; i