]> mj.ucw.cz Git - moe.git/blob - ucw/log-file.c
mo-create-public: rmdir+mkdir
[moe.git] / ucw / log-file.c
1 /*
2  *      UCW Library -- Logging to Files
3  *
4  *      (c) 1997--2009 Martin Mares <mj@ucw.cz>
5  *      (c) 2008 Tomas Gavenciak <gavento@ucw.cz>
6  *
7  *      This software may be freely distributed and used according to the terms
8  *      of the GNU Lesser General Public License.
9  */
10
11 #include "ucw/lib.h"
12 #include "ucw/log.h"
13 #include "ucw/log-internal.h"
14 #include "ucw/lfs.h"
15 #include "ucw/threads.h"
16 #include "ucw/simple-lists.h"
17
18 #include <stdio.h>
19 #include <string.h>
20 #include <fcntl.h>
21 #include <unistd.h>
22 #include <time.h>
23 #include <errno.h>
24
25 struct file_stream {
26   struct log_stream ls;         // ls.name is the current name of the log file
27   int fd;
28   uns flags;                    // FF_xxx
29   char *orig_name;              // Original name with strftime escapes
30 };
31
32 #define MAX_EXPAND 64           // Maximum size of expansion of strftime escapes
33
34 static int log_switch_nest;
35
36 static void
37 do_log_reopen(struct file_stream *fs, const char *name)
38 {
39   int fd = ucw_open(name, O_WRONLY | O_CREAT | O_APPEND, 0666);
40   if (fd < 0)
41     die("Unable to open log file %s: %m", name);
42   if (fs->fd >= 0)
43     close(fs->fd);
44   fs->fd = fd;
45   if (fs->flags & FF_FD2_FOLLOWS)
46     dup2(fd, 2);
47   if (fs->ls.name)
48     {
49       xfree(fs->ls.name);
50       fs->ls.name = NULL;       // We have to keep the stream consistent -- die() below can invoke logging
51     }
52   fs->ls.name = xstrdup(name);
53 }
54
55 static int
56 do_log_switch(struct file_stream *fs, struct tm *tm)
57 {
58   if (!(fs->flags & FF_FORMAT_NAME))
59     {
60       if (fs->fd >= 0)
61         return 1;
62       else
63         {
64           do_log_reopen(fs, fs->orig_name);
65           return 1;
66         }
67     }
68
69   int buflen = strlen(fs->orig_name) + MAX_EXPAND;
70   char name[buflen];
71   int switched = 0;
72
73   ucwlib_lock();
74   if (!log_switch_nest)         // Avoid infinite loops if we die when switching logs
75     {
76       log_switch_nest++;
77       int l = strftime(name, buflen, fs->orig_name, tm);
78       if (l < 0 || l >= buflen)
79         die("Error formatting log file name: %m");
80       if (!fs->ls.name || strcmp(name, fs->ls.name))
81         {
82           do_log_reopen(fs, name);
83           switched = 1;
84         }
85       log_switch_nest--;
86     }
87   ucwlib_unlock();
88   return switched;
89 }
90
91 static void
92 file_close(struct log_stream *ls)
93 {
94   struct file_stream *fs = (struct file_stream *) ls;
95   if ((fs->flags & FF_CLOSE_FD) && fs->fd >= 0)
96     close(fs->fd);
97   xfree(fs->ls.name);
98   xfree(fs->orig_name);
99 }
100
101 static int
102 file_handler(struct log_stream *ls, struct log_msg *m)
103 {
104   struct file_stream *fs = (struct file_stream *) ls;
105   if ((fs->flags & FF_FORMAT_NAME) && m->tm)
106     do_log_switch(fs, m->tm);
107
108   int r = write(fs->fd, m->m, m->m_len);
109   return ((r < 0) ? errno : 0);
110 }
111
112 struct log_stream *
113 log_new_fd(int fd, uns flags)
114 {
115   struct log_stream *ls = log_new_stream(sizeof(struct file_stream));
116   struct file_stream *fs = (struct file_stream *) ls;
117   fs->fd = fd;
118   fs->flags = flags;
119   ls->msgfmt = LSFMT_DEFAULT;
120   ls->handler = file_handler;
121   ls->close = file_close;
122   ls->name = xmalloc(16);
123   snprintf(ls->name, 16, "fd%d", fd);
124   return ls;
125 }
126
127 struct log_stream *
128 log_new_file(const char *path, uns flags)
129 {
130   struct log_stream *ls = log_new_stream(sizeof(struct file_stream));
131   struct file_stream *fs = (struct file_stream *) ls;
132   fs->fd = -1;
133   fs->orig_name = xstrdup(path);
134   if (strchr(path, '%'))
135     fs->flags = FF_FORMAT_NAME;
136   fs->flags |= FF_CLOSE_FD | flags;
137   ls->msgfmt = LSFMT_DEFAULT;
138   ls->handler = file_handler;
139   ls->close = file_close;
140
141   time_t now = time(NULL);
142   struct tm *tm = localtime(&now);
143   ASSERT(tm);
144   do_log_switch(fs, tm);                // die()'s on errors
145   return ls;
146 }
147
148 int
149 log_switch(void)
150 {
151   time_t now = time(NULL);
152   struct tm *tm = localtime(&now);
153   ASSERT(tm);
154
155   int switched = 0;
156   for (int i=0; i < log_streams_after; i++)
157     if (log_streams.ptr[i]->handler == file_handler)
158       switched |= do_log_switch((struct file_stream *) log_streams.ptr[i], tm);
159   return switched;
160 }
161
162 void
163 log_switch_disable(void)
164 {
165   log_switch_nest++;
166 }
167
168 void
169 log_switch_enable(void)
170 {
171   ASSERT(log_switch_nest);
172   log_switch_nest--;
173 }
174
175 void
176 log_file(const char *name)
177 {
178   if (!name)
179     return;
180
181   struct log_stream *ls = log_new_file(name, FF_FD2_FOLLOWS);
182   struct log_stream *def = log_stream_by_flags(0);
183   log_rm_substream(def, NULL);
184   log_add_substream(def, ls);
185   log_close_stream(ls);
186 }
187
188 #ifdef TEST
189
190 int main(int argc, char **argv)
191 {
192   log_init(argv[0]);
193   log_file("/proc/self/fd/1");
194   // struct log_stream *ls = log_new_fd(1, 0);
195   // struct log_stream *ls = log_new_file("/tmp/quork-%Y%m%d-%H%M%S", 0);
196   for (int i=1; i<argc; i++)
197     msg(L_INFO, argv[i]);
198   log_close_all();
199   return 0;
200 }
201
202 #endif