/*
* UCW Library -- Logging to Files
*
- * (c) 1997--2009 Martin Mares <mj@ucw.cz>
+ * (c) 1997--2015 Martin Mares <mj@ucw.cz>
* (c) 2008 Tomas Gavenciak <gavento@ucw.cz>
*
* This software may be freely distributed and used according to the terms
* 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/simple-lists.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 <fcntl.h>
#include <unistd.h>
#include <time.h>
+#include <errno.h>
struct file_stream {
struct log_stream ls; // ls.name is the current name of the log file
int fd;
- uns flags; // FF_xxx
+ uint 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 int log_switch_nest;
+static bool log_stderr_replaced;
static void
do_log_reopen(struct file_stream *fs, const char *name)
close(fs->fd);
fs->fd = fd;
if (fs->flags & FF_FD2_FOLLOWS)
- dup2(fd, 2);
+ {
+ dup2(fd, 2);
+ log_stderr_replaced = 1;
+ }
if (fs->ls.name)
{
xfree(fs->ls.name);
return switched;
}
-/* destructor for standard files */
static void
file_close(struct log_stream *ls)
{
xfree(fs->orig_name);
}
-/* handler for standard files */
static int
file_handler(struct log_stream *ls, struct log_msg *m)
{
do_log_switch(fs, m->tm);
int r = write(fs->fd, m->m, m->m_len);
- /* FIXME: check for errors here? */
- return 0;
+ return ((r < 0) ? errno : 0);
}
-/* assign log to a file descriptor */
-/* initialize with the default formatting, does NOT close the descriptor */
struct log_stream *
-log_new_fd(int fd)
+log_new_fd(int fd, uint flags)
{
struct log_stream *ls = log_new_stream(sizeof(struct file_stream));
struct file_stream *fs = (struct file_stream *) ls;
fs->fd = fd;
+ if (fd == 2)
+ log_stderr_replaced = 1;
+ fs->flags = flags;
ls->msgfmt = LSFMT_DEFAULT;
ls->handler = file_handler;
ls->close = file_close;
return ls;
}
-static struct log_stream *
-do_log_new_file(const char *path, uns more_flags)
+struct log_stream *
+log_new_file(const char *path, uint flags)
{
struct log_stream *ls = log_new_stream(sizeof(struct file_stream));
struct file_stream *fs = (struct file_stream *) ls;
fs->fd = -1;
+ fs->flags = FF_CLOSE_FD | flags;
fs->orig_name = xstrdup(path);
if (strchr(path, '%'))
- fs->flags = FF_FORMAT_NAME;
- fs->flags |= FF_CLOSE_FD | more_flags;
+ fs->flags |= FF_FORMAT_NAME;
ls->msgfmt = LSFMT_DEFAULT;
ls->handler = file_handler;
ls->close = file_close;
return ls;
}
-/* open() a file (append mode) */
-/* initialize with the default formatting */
-struct log_stream *
-log_new_file(const char *path)
-{
- return do_log_new_file(path, 0);
-}
-
int
log_switch(void)
{
log_switch_nest--;
}
-/* Emulate the old single-file interface: close the existing log file and open a new one. */
void
log_file(const char *name)
{
if (!name)
return;
- 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);
+ log_set_default_stream(log_new_file(name, FF_FD2_FOLLOWS));
+}
+
+void
+log_drop_stderr(void)
+{
+ if (!log_stderr_replaced)
+ {
+ if (dup2(1, 2) < 0)
+ die("Cannot get rid of stderr: %m");
+ log_stderr_replaced = 1;
+ }
}
#ifdef TEST
{
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");
+ // 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;
}