]> mj.ucw.cz Git - libucw.git/blob - ucw/log-file.c
Logging: Rewritten the log-file module to implement log switching.
[libucw.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/lfs.h"
14 #include "ucw/threads.h"
15
16 #include <stdio.h>
17 #include <string.h>
18 #include <fcntl.h>
19 #include <unistd.h>
20 #include <time.h>
21
22 /*
23  *  Use of the private fields of struct log_stream:
24  *
25  *      idata   file descriptor
26  *      udata   various flags (FF_xxx)
27  *      pdata   original name with strftime escapes
28  *      name    current name of the log file
29  *              (a dynamically allocated buffer)
30  */
31
32 enum log_file_flag {
33   FF_FORMAT_NAME = 1,           // Name contains strftime escapes
34   FF_CLOSE_FD = 2,              // Close the fd with the stream
35 };
36
37 #define MAX_EXPAND 64           // Maximum size of expansion of strftime escapes
38
39 static int log_switch_nest;
40
41 static void
42 do_log_reopen(struct log_stream *ls, const char *name)
43 {
44   int fd = ucw_open(name, O_WRONLY | O_CREAT | O_APPEND, 0666);
45   if (fd < 0)
46     die("Unable to open log file %s: %m", name);
47   if (ls->idata < 0)
48     ls->idata = fd;
49   else
50     {
51       dup2(fd, ls->idata);
52       close(fd);
53     }
54   if (ls->name)
55     {
56       xfree(ls->name);
57       ls->name = NULL;          // We have to keep this consistent, die() below can invoke logging
58     }
59   ls->name = xstrdup(name);
60 }
61
62 static int
63 do_log_switch(struct log_stream *ls, struct tm *tm)
64 {
65   if (!(ls->udata & FF_FORMAT_NAME))
66     {
67       if (ls->idata >= 0)
68         return 1;
69       else
70         {
71           do_log_reopen(ls, ls->pdata);
72           return 1;
73         }
74     }
75
76   int buflen = strlen(ls->pdata) + MAX_EXPAND;
77   char name[buflen];
78   int switched = 0;
79
80   ucwlib_lock();
81   if (!log_switch_nest)         // Avoid infinite loops if we die when switching logs
82     {
83       log_switch_nest++;
84       int l = strftime(name, buflen, ls->pdata, tm);
85       if (l < 0 || l >= buflen)
86         die("Error formatting log file name: %m");
87       if (!ls->name || strcmp(name, ls->name))
88         {
89           do_log_reopen(ls, name);
90           switched = 1;
91         }
92       log_switch_nest--;
93     }
94   ucwlib_unlock();
95   return switched;
96 }
97
98 void
99 log_file(const char *name)
100 {
101   if (!name)
102     return;
103
104   // FIXME
105 }
106
107 int
108 log_switch(void)
109 {
110 #if 0 // FIXME
111   time_t tim = time(NULL);
112   return do_log_switch(localtime(&tim));
113 #else
114   return 0;
115 #endif
116 }
117
118 void
119 log_switch_disable(void)
120 {
121   log_switch_nest++;
122 }
123
124 void
125 log_switch_enable(void)
126 {
127   ASSERT(log_switch_nest);
128   log_switch_nest--;
129 }
130
131 /* destructor for standard files */
132 static void
133 file_close(struct log_stream *ls)
134 {
135   if ((ls->udata & FF_CLOSE_FD) && ls->idata >= 0)
136     close(ls->idata);
137   xfree(ls->name);
138 }
139
140 /* handler for standard files */
141 static int
142 file_handler(struct log_stream *ls, const char *m, uns cat UNUSED)
143 {
144   if (ls->udata & FF_FORMAT_NAME)
145     {
146       // FIXME: pass the time
147       time_t now = time(NULL);
148       struct tm *tm = localtime(&now);
149       do_log_switch(ls, tm);
150     }
151
152   int len = strlen(m);
153   int r = write(ls->idata, m, len);
154   /* FIXME: check for errors here? */
155   return 0;
156 }
157
158 /* assign log to a file descriptor */
159 /* initialize with the default formatting, does NOT close the descriptor */
160 struct log_stream *
161 log_new_fd(int fd)
162 {
163   struct log_stream *ls = log_new_stream();
164   ls->idata = fd;
165   ls->msgfmt = LSFMT_DEFAULT;
166   ls->handler = file_handler;
167   ls->close = file_close;
168   ls->name = xmalloc(16);
169   snprintf(ls->name, 16, "fd%d", fd);
170   return ls;
171 }
172
173 /* open() a file (append mode) */
174 /* initialize with the default formatting */
175 struct log_stream *
176 log_new_file(const char *path)
177 {
178   struct log_stream *ls = log_new_stream();
179   ls->idata = -1;
180   ls->pdata = (void *) path;
181   if (strchr(path, '%'))
182     ls->udata = FF_FORMAT_NAME;
183   ls->udata |= FF_CLOSE_FD;
184   ls->msgfmt = LSFMT_DEFAULT;
185   ls->handler = file_handler;
186   ls->close = file_close;
187
188   time_t now = time(NULL);
189   struct tm *tm = localtime(&now);
190   do_log_switch(ls, tm);                // die()'s on errors
191   return ls;
192 }
193
194 #ifdef TEST
195
196 int main(int argc, char **argv)
197 {
198   log_init(argv[0]);
199   // log_file("/proc/self/fd/1");
200   // struct log_stream *ls = log_new_fd(1);
201   struct log_stream *ls = log_new_file("/tmp/quork-%Y%m%d-%H%M%S");
202   for (int i=1; i<argc; i++)
203     msg(L_INFO | ls->regnum, argv[i]);
204   return 0;
205 }
206
207 #endif