ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/vendor/ircservices-5.1.24/log.c
Revision: 3389
Committed: Fri Apr 25 14:12:15 2014 UTC (9 years, 10 months ago) by michael
Content type: text/x-csrc
File size: 11041 byte(s)
Log Message:
- Imported ircservices-5.1.24

File Contents

# Content
1 /* Logging routines.
2 *
3 * IRC Services is copyright (c) 1996-2009 Andrew Church.
4 * E-mail: <achurch@achurch.org>
5 * Parts written by Andrew Kempe and others.
6 * This program is free but copyrighted software; see the file GPL.txt for
7 * details.
8 */
9
10 #include "services.h"
11 #include <netdb.h> /* for hstrerror() */
12
13 /* Pattern for generating logfile names */
14 static char logfile_pattern[PATH_MAX+1];
15
16 /* Currently open log file */
17 static FILE *logfile;
18 static char current_filename[PATH_MAX+1];
19
20 /* Memory log buffer (see open_memory_log()) */
21 static char logmem[LOGMEMSIZE];
22 static char *logmemptr = NULL;
23
24 /* Are we in fatal() or fatal_perror()? (This is used to avoid infinite
25 * recursion if wallops() does a fatal().) */
26 static int in_fatal = 0;
27
28 /*************************************************************************/
29 /*************************************************************************/
30
31 /* Local routine to generate a filename from a filename pattern (possibly
32 * containing %y/%m/%d for year/month/day). Result is returned in a static
33 * buffer, and will not be longer than PATH_MAX characters.
34 */
35
36 static char *gen_log_filename(void)
37 {
38 static char result[PATH_MAX+1];
39 const char *s, *from;
40 char *to;
41
42 time_t now = time(NULL);
43 struct tm *tm = localtime(&now);
44 tm->tm_year += 1900;
45 tm->tm_mon++;
46
47 from = LogFilename;
48 if (!*from) {
49 *result = 0;
50 return result;
51 }
52 to = result;
53
54 while ((s = strchr(from, '%')) != NULL) {
55 to += snprintf(to, sizeof(result)-(to-result), "%.*s", s-from, from);
56 s++;
57 switch (*s) {
58 case 'y':
59 to += snprintf(to, sizeof(result)-(to-result), "%d", tm->tm_year);
60 break;
61 case 'm':
62 to += snprintf(to, sizeof(result)-(to-result), "%02d", tm->tm_mon);
63 break;
64 case 'd':
65 to += snprintf(to, sizeof(result)-(to-result), "%02d",tm->tm_mday);
66 break;
67 default:
68 if (to-result < sizeof(result)-1)
69 *to++ = *s;
70 break;
71 }
72 from = s+1;
73 }
74 to += snprintf(to, sizeof(result)-(to-result), "%s", from);
75
76 *to = 0;
77 return result;
78 }
79
80 /*************************************************************************/
81
82 /* Local routine to check whether the log file needs to be rotated, and
83 * rotate it if so. Assumes the log file is already open.
84 */
85
86 static void check_log_rotate(void)
87 {
88 char *newname = gen_log_filename();
89 if (strlen(newname) > sizeof(current_filename)-1)
90 newname[sizeof(current_filename)-1] = 0;
91 if (strcmp(current_filename, gen_log_filename()) != 0) {
92 if (!reopen_log())
93 log("Warning: Unable to rotate log file: %s", strerror(errno));
94 }
95 }
96
97 /*************************************************************************/
98 /*************************************************************************/
99
100 /* Local routines to write text to the log file and/or stderr as needed. */
101
102 static void vlogprintf(const char *fmt, va_list args)
103 {
104 if (nofork) {
105 va_list args_copy;
106 va_copy(args_copy, args);
107 vfprintf(stderr, fmt, args_copy);
108 va_end(args_copy);
109 }
110 if (logfile) {
111 vfprintf(logfile, fmt, args);
112 } else if (logmemptr) {
113 char tmpbuf[BUFSIZE];
114 int len = vsnprintf(tmpbuf, sizeof(tmpbuf), fmt, args);
115 if (len > LOGMEMSIZE - (logmemptr-logmem)) {
116 int oldlen = len;
117 len = LOGMEMSIZE - (logmemptr-logmem);
118 if (len > 0) {
119 if (tmpbuf[oldlen-1] == '\n') {
120 tmpbuf[len-1] = '\n'; /* always end with a newline */
121 } else {
122 len--;
123 }
124 }
125 }
126 if (len > 0) {
127 memcpy(logmemptr, tmpbuf, len);
128 logmemptr += len;
129 }
130 }
131 }
132
133 static void logprintf(const char *fmt, ...) FORMAT(printf,1,2);
134 static void logprintf(const char *fmt, ...)
135 {
136 va_list args;
137 va_start(args, fmt);
138 vlogprintf(fmt, args);
139 va_end(args);
140 }
141
142 static void logputs(const char *str)
143 {
144 logprintf("%s", str);
145 }
146
147 /*************************************************************************/
148
149 /* Local routine to write the time of day to the log. */
150
151 static void write_time(void)
152 {
153 time_t t;
154 struct tm tm;
155 char buf[256];
156
157 time(&t);
158 tm = *localtime(&t);
159 #if HAVE_GETTIMEOFDAY
160 if (debug) {
161 char *s;
162 struct timeval tv;
163 gettimeofday(&tv, NULL);
164 strftime(buf, sizeof(buf)-1, "[%b %d %H:%M:%S", &tm);
165 s = buf + strlen(buf);
166 s += snprintf(s, sizeof(buf)-(s-buf), ".%06d", (int)tv.tv_usec);
167 strftime(s, sizeof(buf)-(s-buf)-1, " %Y] ", &tm);
168 } else {
169 #endif
170 strftime(buf, sizeof(buf)-1, "[%b %d %H:%M:%S %Y] ", &tm);
171 #if HAVE_GETTIMEOFDAY
172 }
173 #endif
174 logputs(buf);
175 }
176
177 /*************************************************************************/
178 /*************************************************************************/
179
180 /* Set the filename pattern to use for generating the log filename. */
181
182 void set_logfile(const char *pattern)
183 {
184 if (pattern) { /* Just to be safe */
185 strscpy(logfile_pattern, pattern, sizeof(logfile_pattern));
186 }
187 }
188
189 /*************************************************************************/
190
191 /* Open the log file. Return zero if the log file could not be opened,
192 * else return nonzero (success).
193 */
194
195 int open_log(void)
196 {
197 if (logfile)
198 return 1;
199 strbcpy(current_filename, gen_log_filename());
200 logfile = fopen(current_filename, "a");
201 if (logfile) {
202 setbuf(logfile, NULL);
203 if (logmemptr) {
204 int res, errno_save;
205 if (logmemptr > logmem) {
206 res = fwrite(logmem, logmemptr-logmem, 1, logfile);
207 errno_save = errno;
208 } else {
209 res = 1; /* i.e. no error */
210 #if CLEAN_COMPILE
211 errno_save = 0; /* warning killer for dumb compilers */
212 #endif
213 }
214 logmemptr = NULL;
215 if (res != 1) {
216 /* make sure this is AFTER the memory log has been closed! */
217 errno = errno_save;
218 log_perror("open_log(): error writing memory log");
219 }
220 }
221 }
222 return logfile!=NULL ? 1 : 0;
223 }
224
225 /*************************************************************************/
226
227 /* Open a virtual log file in memory. The contents of the memory log file
228 * will be written to a physical log file when open_log() is called and
229 * executes successfully. If a physical log file is already open, does
230 * nothing. Always succeeds (and returns nonzero).
231 */
232
233 int open_memory_log(void)
234 {
235 if (logfile || logmemptr)
236 return 1;
237 logmemptr = logmem;
238 return 1;
239 }
240
241 /*************************************************************************/
242
243 /* Close the log file. */
244
245 void close_log(void)
246 {
247 if (logmemptr) {
248 logmemptr = NULL;
249 }
250 if (logfile) {
251 fclose(logfile);
252 logfile = NULL;
253 }
254 }
255
256 /*************************************************************************/
257
258 /* Reopen the log file with the current value of LogFilename (for use when
259 * rehashing configuration files or rotating the log file). Return value
260 * is like open_log().
261 */
262
263 int reopen_log(void)
264 {
265 char *newname;
266 FILE *f;
267
268 newname = gen_log_filename();
269 /* Make sure it will fit in current_filename later */
270 if (strlen(newname) > sizeof(current_filename)-1)
271 newname[sizeof(current_filename)-1] = 0;
272 f = fopen(newname, "a");
273 if (!f)
274 return 0;
275 setbuf(f, NULL);
276 if (logfile)
277 fclose(logfile);
278 logfile = f;
279 strcpy(current_filename, newname); /* safe b/c of length check above */
280 return 1;
281 }
282
283 /*************************************************************************/
284
285 /* Return nonzero if the log file is currently open, zero if closed. */
286
287 int log_is_open(void)
288 {
289 return logfile != NULL;
290 }
291
292 /*************************************************************************/
293 /*************************************************************************/
294
295 /* Log stuff to the log file with a datestamp when debug >= debuglevel.
296 * errno is preserved. If debuglevel is greater than zero, "debug: " is
297 * prefixed to the message. If do_perror is nonzero, the message is
298 * followed by a ": " and system error message, a la perror() (if errno<0,
299 * it is treated as an herror value from hostname resolution). If
300 * modulename is not NULL, it is used as a prefix to the error message.
301 *
302 * [module_]log[_perror][_debug]() are implemented as macros which call
303 * this function.
304 */
305
306 void do_log(int debuglevel, int do_perror, const char *modulename,
307 const char *fmt, ...)
308 {
309 if (debug >= debuglevel) {
310 va_list args;
311 int errno_save = errno;
312
313 va_start(args, fmt);
314 if (logfile)
315 check_log_rotate();
316 write_time();
317 if (debuglevel > 0)
318 logputs("debug: ");
319 if (modulename)
320 logprintf("(%s) ", modulename);
321 vlogprintf(fmt, args);
322 if (do_perror) {
323 logprintf(": %s",
324 (errno_save<0) ? hstrerror(-errno_save)
325 : strerror(errno_save));
326 }
327 logputs("\n");
328 va_end(args);
329 errno = errno_save;
330 }
331 }
332
333 /*************************************************************************/
334 /*************************************************************************/
335
336 /* We've hit something we can't recover from. Let people know what
337 * happened, then go down.
338 */
339
340 void fatal(const char *fmt, ...)
341 {
342 va_list args;
343 char buf[4096];
344
345 if (in_fatal)
346 exit(2);
347 in_fatal++;
348 va_start(args, fmt);
349 vsnprintf(buf, sizeof(buf), fmt, args);
350 va_end(args);
351 if (logfile)
352 check_log_rotate();
353 write_time();
354 logprintf("FATAL: %s\n", buf);
355 if (servsock && sock_isconn(servsock))
356 wallops(NULL, "FATAL ERROR! %s", buf);
357 exit(1);
358 }
359
360 /*************************************************************************/
361
362 /* Same thing, but do it like perror(). */
363
364 void fatal_perror(const char *fmt, ...)
365 {
366 va_list args;
367 char buf[4096];
368 const char *errstr;
369
370 if (in_fatal)
371 exit(2);
372 in_fatal++;
373 errstr = (errno<0) ? hstrerror(-errno) : strerror(errno);
374 va_start(args, fmt);
375 vsnprintf(buf, sizeof(buf), fmt, args);
376 va_end(args);
377 if (logfile)
378 check_log_rotate();
379 write_time();
380 logprintf("FATAL: %s: %s\n", buf, errstr);
381 if (servsock && sock_isconn(servsock))
382 wallops(NULL, "FATAL ERROR! %s: %s", buf, errstr);
383 exit(1);
384 }
385
386 /*************************************************************************/
387
388 /*
389 * Local variables:
390 * c-file-style: "stroustrup"
391 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
392 * indent-tabs-mode: nil
393 * End:
394 *
395 * vim: expandtab shiftwidth=4:
396 */