napa-baselibs / dclog / dclog.c @ 5f3adef4
History | View | Annotate | Download (25.2 KB)
1 |
/* =========================================================================
|
---|---|
2 |
* File: dclog.h
|
3 |
*
|
4 |
* Copyright (c) 2003 and onwards, Twenty First Century Communications, Inc.
|
5 |
* <josh.glover@tfcci.com>
|
6 |
*
|
7 |
* LICENCE:
|
8 |
*
|
9 |
* This file is distributed under the terms of the BSD License, v2. See
|
10 |
* the COPYING file, which should have been distributed with this file,
|
11 |
* for details. If you did not receive the COPYING file, see:
|
12 |
*
|
13 |
* http://www.jmglov.net/open-source/licences/bsd.html
|
14 |
*
|
15 |
* DESCRIPTION:
|
16 |
*
|
17 |
* Implements the DCLog class.
|
18 |
*
|
19 |
* USAGE:
|
20 |
*
|
21 |
* #include <dclog.h>
|
22 |
*
|
23 |
* EXAMPLES:
|
24 |
*
|
25 |
* See dclog.h
|
26 |
*
|
27 |
* TODO:
|
28 |
*
|
29 |
* - Nothing, this code is perfect
|
30 |
*
|
31 |
* DEPENDENCIES:
|
32 |
*
|
33 |
* None
|
34 |
*
|
35 |
* MODIFICATIONS:
|
36 |
*
|
37 |
* Josh Glover <josh.glover@tfcci.com> (2003/09/03): Initial revision
|
38 |
* =========================================================================
|
39 |
*/
|
40 |
|
41 |
// Standard headers
|
42 |
#include <sys/time.h> |
43 |
#include <sys/types.h> |
44 |
#include <sys/stat.h> |
45 |
#include <errno.h> |
46 |
#include <fcntl.h> |
47 |
#include <netdb.h> |
48 |
#include <stdarg.h> |
49 |
#include <stdio.h> |
50 |
#include <stdlib.h> |
51 |
#include <string.h> |
52 |
#include <time.h> |
53 |
#include <unistd.h> |
54 |
#ifndef WIN32
|
55 |
#include <sys/socket.h> |
56 |
#include <netinet/in.h> |
57 |
#endif
|
58 |
|
59 |
// dclog headers
|
60 |
#include <dclog.h> |
61 |
|
62 |
// Features
|
63 |
#define DCLOG_FEAT_HEADER 1 << 0 //!< see DCLogSetHeader() |
64 |
#define DCLOG_FEAT_UNIQUE 1 << 1 //!< see DCLogSetUniqueByDay() |
65 |
#define DCLOG_FEAT_PLEVEL 1 << 2 //!< see DCLogSetPrintLevel() |
66 |
#define DCLOG_FEAT_NEWLINE 1 << 3 //!< see DCLogSetNewLine() |
67 |
|
68 |
//! String representations of log levels
|
69 |
static char *levels[] = { |
70 |
"ALARM", // 0 |
71 |
"ERROR", // 1 |
72 |
"WARNING", // 2 |
73 |
"INFO", // 3 |
74 |
"DEBUG", // 4 |
75 |
"PROFILE", // 5 |
76 |
NULL // 6 |
77 |
}; // levels[]
|
78 |
|
79 |
|
80 |
// Private method prototypes
|
81 |
// -------------------------------------------------------------------------
|
82 |
|
83 |
|
84 |
static UCHAR _truncate( DCLog *dclog );
|
85 |
|
86 |
|
87 |
// -------------------------------------------------------------------------
|
88 |
|
89 |
|
90 |
// Private methods
|
91 |
// -------------------------------------------------------------------------
|
92 |
|
93 |
|
94 |
/** Truncates the currently open logfile.
|
95 |
*
|
96 |
* @param *dclog pointer to a DCLog object
|
97 |
*
|
98 |
* @return 1 on success, 0 on failure
|
99 |
*/
|
100 |
|
101 |
static UCHAR _truncate( DCLog *dclog ) {
|
102 |
|
103 |
if (dclog == NULL || dclog->fn == NULL || dclog->fp == NULL) |
104 |
return 0; |
105 |
|
106 |
if ((dclog->fp = freopen( dclog->fn, "w", dclog->fp )) == NULL) |
107 |
return 0; |
108 |
|
109 |
return 1; |
110 |
|
111 |
} // _truncate()
|
112 |
|
113 |
|
114 |
// -------------------------------------------------------------------------
|
115 |
|
116 |
|
117 |
// Constructors and destructors
|
118 |
// -------------------------------------------------------------------------
|
119 |
|
120 |
|
121 |
/** Constructer; creates a new DCLog object.
|
122 |
*
|
123 |
* @return pointer to the newly created DCLog object
|
124 |
*/
|
125 |
|
126 |
DCLog *NewDCLog( void ) {
|
127 |
|
128 |
DCLog *dclog; |
129 |
int i;
|
130 |
|
131 |
if ((dclog = malloc( sizeof( DCLog ) )) == NULL) { |
132 |
|
133 |
fprintf( stderr, "NewDCLog() could not malloc() %d bytes, is the system "
|
134 |
"low on memory?\n", sizeof( DCLog ) ); |
135 |
|
136 |
return (DCLog *)NULL; |
137 |
|
138 |
} // if (malloc(3) failed)
|
139 |
|
140 |
// Zero out the memory
|
141 |
memset( dclog, 0, sizeof( DCLog ) ); |
142 |
|
143 |
// Allocate enough memory for the alarming stuff and set the defaults
|
144 |
dclog->mailhost = malloc( (strlen( DCLOG_MAILHOST ) + 1) * sizeof( char ) ); |
145 |
strcpy( dclog->mailhost, DCLOG_MAILHOST ); |
146 |
dclog->to = malloc( (strlen( DCLOG_TO ) + 1) * sizeof( char ) ); |
147 |
strcpy( dclog->to, DCLOG_TO ); |
148 |
strcpy( dclog->spool, DCLOG_SPOOL ); |
149 |
|
150 |
/*** Why are sizes hard-coded? We need a comment here! ***/
|
151 |
for (i = 0; i < 128; i++) { |
152 |
|
153 |
dclog->oldtime[i] = (time_t) 0;
|
154 |
dclog->count[i] = 1;
|
155 |
|
156 |
} // for (***)
|
157 |
|
158 |
// Turn size limiting off
|
159 |
dclog->limit_size = 0;
|
160 |
dclog->max_file_size = 0;
|
161 |
|
162 |
return dclog;
|
163 |
|
164 |
} // NewLog()
|
165 |
|
166 |
|
167 |
/** Destroys a log object that is no longer needed.
|
168 |
*
|
169 |
* @param *dclog pointer to a DCLog object
|
170 |
*
|
171 |
* @return 1 on success, 0 on failure
|
172 |
*/
|
173 |
|
174 |
UCHAR DestroyDCLog( DCLog *dclog ) { |
175 |
|
176 |
if (dclog == NULL) |
177 |
return 0; |
178 |
|
179 |
DCLogClose( dclog ); |
180 |
|
181 |
free( dclog->fn ); |
182 |
dclog->fn = NULL;
|
183 |
free( dclog->mailhost ); |
184 |
dclog->mailhost = NULL;
|
185 |
free( dclog->to ); |
186 |
dclog->to = NULL;
|
187 |
free( dclog->from ); |
188 |
dclog->from = NULL;
|
189 |
|
190 |
if (dclog->prev_date != NULL) |
191 |
free( dclog->prev_date ); |
192 |
|
193 |
free( dclog ); |
194 |
dclog = NULL;
|
195 |
|
196 |
return 1; |
197 |
|
198 |
} // DestroyDCLog()
|
199 |
|
200 |
|
201 |
// -------------------------------------------------------------------------
|
202 |
|
203 |
|
204 |
// Stringifying methods
|
205 |
// -------------------------------------------------------------------------
|
206 |
|
207 |
|
208 |
/** Returns the numeric log level for the specified string representation.
|
209 |
*
|
210 |
* @param *str string representation of a log level
|
211 |
*
|
212 |
* @return numeric log level on success, -1 on error
|
213 |
*/
|
214 |
|
215 |
char DCLogLevelToNum( const char *str ) { |
216 |
|
217 |
char i;
|
218 |
|
219 |
// Step through our array of names. If the string matches, return the
|
220 |
// array index.
|
221 |
for (i = 0; levels[(int)i] != NULL; i++) |
222 |
if (strncasecmp( str, levels[(int)i], 10 ) == 0) |
223 |
return i;
|
224 |
|
225 |
// Nope, found nothing
|
226 |
return -1; |
227 |
|
228 |
} // DCLogLevelToNum()
|
229 |
|
230 |
|
231 |
/** Returns the string representation of the specified numeric log level.
|
232 |
*
|
233 |
* @param lev numeric log level
|
234 |
*
|
235 |
* @return string representation of a log level on success, NULL on error
|
236 |
*/
|
237 |
|
238 |
const char *DCLogLevelToString( const char lev ) { |
239 |
|
240 |
char i;
|
241 |
|
242 |
// Loop through the array of strings, returning the string when the
|
243 |
// index matches lev (we do it this way to make sure lev is legal)
|
244 |
for (i = 0; levels[(int)i] != NULL; i++) |
245 |
if (i == lev)
|
246 |
return levels[(int)i]; |
247 |
|
248 |
// We did not find a match, return error
|
249 |
return (char *)NULL; |
250 |
|
251 |
} // DCLogLevelToString()
|
252 |
|
253 |
|
254 |
// -------------------------------------------------------------------------
|
255 |
|
256 |
|
257 |
// Setup methods
|
258 |
// -------------------------------------------------------------------------
|
259 |
|
260 |
|
261 |
/** Turns the log header on or off.
|
262 |
*
|
263 |
* If on, each log message will be appended to the timestamp and process
|
264 |
* ID (e.g. 15:27:13.05 [2727]: test for echo).
|
265 |
*
|
266 |
* @param *dclog pointer to a DCLog object
|
267 |
* @param val 1 for on, 0 for off
|
268 |
*
|
269 |
* @return 1 on success, 0 on failure
|
270 |
*/
|
271 |
|
272 |
UCHAR DCLogSetHeader( DCLog *dclog, const UCHAR val ) {
|
273 |
|
274 |
// Make sure that we have a valid DCLog object
|
275 |
if (dclog == NULL) |
276 |
return 0; |
277 |
|
278 |
if (val == 0) |
279 |
dclog->features ^= DCLOG_FEAT_HEADER; |
280 |
else if (val == 1) |
281 |
dclog->features |= DCLOG_FEAT_HEADER; |
282 |
else
|
283 |
return 0; |
284 |
|
285 |
return 1; |
286 |
|
287 |
} // DCLogSetHeader()
|
288 |
|
289 |
|
290 |
/** Sets the logging level.
|
291 |
*
|
292 |
* @param *dclog pointer to a DCLog object
|
293 |
* @param lev logging level (messages with a higher level will not be
|
294 |
* logged)
|
295 |
*
|
296 |
* @return 1 on success, 0 on failure
|
297 |
*/
|
298 |
|
299 |
UCHAR DCLogSetLevel( DCLog *dclog, const UCHAR lev ) {
|
300 |
|
301 |
// Make sure that we have a valid DCLog object
|
302 |
if (dclog == NULL) |
303 |
return 0; |
304 |
|
305 |
// Set the logging level
|
306 |
dclog->lev = lev; |
307 |
|
308 |
return 1; |
309 |
|
310 |
} // DCLogSetLevel()
|
311 |
|
312 |
|
313 |
/** Sets the hostname of the SMTP mail server.
|
314 |
*
|
315 |
* Alarm emails will be sent through this mail server.
|
316 |
*
|
317 |
* @param *dclog pointer to a DCLog object
|
318 |
* @param mailhost email address of the server
|
319 |
*
|
320 |
* @return 1 on success, 2 on failure.
|
321 |
*/
|
322 |
|
323 |
UCHAR DCLogSetMailServer( DCLog *dclog, const char *mailhost ) { |
324 |
|
325 |
// Make sure that we have a valid DCLog object and a string
|
326 |
if (dclog == NULL || mailhost == NULL) |
327 |
return 0; |
328 |
|
329 |
// If dclog->to is already set...
|
330 |
if (dclog->mailhost != NULL) { |
331 |
|
332 |
// ...and it is the same, return success
|
333 |
if (strcmp( mailhost, dclog->mailhost ) == 0) |
334 |
return 1; |
335 |
|
336 |
// ...and it is not the same, destroy it
|
337 |
free( dclog->mailhost ); |
338 |
dclog->mailhost = NULL;
|
339 |
|
340 |
} // if (dclog->to != NULL)
|
341 |
|
342 |
// Allocate enough memory for dclog->to and set it
|
343 |
if ((dclog->mailhost = malloc( (strlen( mailhost ) + 1) * sizeof( char ) )) |
344 |
== NULL)
|
345 |
return 0; |
346 |
if (strcpy( dclog->mailhost, mailhost ) == NULL) |
347 |
return 0; |
348 |
|
349 |
return 1; |
350 |
|
351 |
} // DCLogSetMailServer()
|
352 |
|
353 |
|
354 |
/** Sets maximum file size and turns on size limits.
|
355 |
*
|
356 |
* @param *dclog pointer to a DCLog object
|
357 |
* @param size maximum file size, in bytes
|
358 |
*
|
359 |
* @return 1 on success, 0 on error
|
360 |
*/
|
361 |
|
362 |
UCHAR DCLogSetMaxFileSize( DCLog *dclog, const int size ) { |
363 |
|
364 |
// Make sure that we have a valid DCLog object
|
365 |
if (dclog == NULL) |
366 |
return 0; |
367 |
|
368 |
dclog->limit_size = 1;
|
369 |
dclog->max_file_size = size; |
370 |
|
371 |
return 1; |
372 |
|
373 |
} // DCLogSetMaxFileSize()
|
374 |
|
375 |
|
376 |
/** Sets the page server's email address.
|
377 |
*
|
378 |
* Alarm emails will be sent to this address.
|
379 |
*
|
380 |
* @param *dclog pointer to a DCLog object
|
381 |
* @param server email address of the server
|
382 |
*
|
383 |
* @return 1 on success, 0 on failure.
|
384 |
*/
|
385 |
|
386 |
UCHAR DCLogSetPageServer( DCLog *dclog, const char *server ) { |
387 |
|
388 |
// Make sure that we have a valid DCLog object and a string
|
389 |
if (dclog == NULL || server == NULL) |
390 |
return 0; |
391 |
|
392 |
// If dclog->to is already set...
|
393 |
if (dclog->to != NULL) { |
394 |
|
395 |
// ...and it is the same, return success
|
396 |
if (strcmp( server, dclog->to ) == 0) |
397 |
return 1; |
398 |
|
399 |
// ...and it is not the same, destroy it
|
400 |
free( dclog->to ); |
401 |
dclog->to = NULL;
|
402 |
|
403 |
} // if (dclog->to != NULL)
|
404 |
|
405 |
// Allocate enough memory for dclog->to and set it
|
406 |
if ((dclog->to = malloc( (strlen( server ) + 1) * sizeof( char ) )) |
407 |
== NULL)
|
408 |
return 0; |
409 |
if (strcpy( dclog->to, server ) == NULL) |
410 |
return 0; |
411 |
|
412 |
return 1; |
413 |
|
414 |
} // DCLogSetPageServer()
|
415 |
|
416 |
|
417 |
/** Turns printing the message level on or off.
|
418 |
*
|
419 |
* If on, each log message will be appended to the mnemonic level at
|
420 |
* which it was logged (e.g. messages logged at DCLOG_DEBUG would look like:
|
421 |
* "DEBUG some message or other").
|
422 |
*
|
423 |
* @param *dclog pointer to a DCLog object
|
424 |
* @param val 1 for on, 0 for off
|
425 |
*
|
426 |
* @return 1 on success, 0 on failure
|
427 |
*/
|
428 |
|
429 |
UCHAR DCLogSetPrintLevel( DCLog *dclog, const UCHAR val ) {
|
430 |
|
431 |
// Make sure that we have a valid DCLog object
|
432 |
if (dclog == NULL) |
433 |
return 0; |
434 |
|
435 |
if (val == 0) |
436 |
dclog->features ^= DCLOG_FEAT_PLEVEL; |
437 |
else if (val == 1) |
438 |
dclog->features |= DCLOG_FEAT_PLEVEL; |
439 |
else
|
440 |
return 0; |
441 |
|
442 |
return 1; |
443 |
|
444 |
} // DCLogSetPrintLevel()
|
445 |
|
446 |
/** Turn automatic appending of a \n to log messages on or off.
|
447 |
*
|
448 |
* If on, a newline will be automatically appended to the message
|
449 |
*
|
450 |
* @param *dclog pointer to a DCLog object
|
451 |
* @param val 1 for on, 0 for off
|
452 |
*
|
453 |
* @return 1 on success, 0 on failure
|
454 |
*/
|
455 |
UCHAR DCLogSetNewLine( DCLog *dclog, const UCHAR val ) {
|
456 |
|
457 |
// Make sure that we have a valid DCLog object
|
458 |
if (dclog == NULL) |
459 |
return 0; |
460 |
|
461 |
if (val == 0) |
462 |
dclog->features ^= DCLOG_FEAT_NEWLINE; |
463 |
else if (val == 1) |
464 |
dclog->features |= DCLOG_FEAT_NEWLINE; |
465 |
else
|
466 |
return 0; |
467 |
|
468 |
return 1; |
469 |
|
470 |
} // DCLogSetNewLine
|
471 |
|
472 |
/** Sets the program name that will be used when alarming.
|
473 |
*
|
474 |
* The from address in the alarm email will be program_name@hostname,
|
475 |
* where program_name is set by this function, and hostname is the
|
476 |
* hostname of the machine on which your code is running.
|
477 |
*
|
478 |
* @param *dclog pointer to a DCLog object
|
479 |
* @param *prog program string
|
480 |
*
|
481 |
* @return 1 on success, 0 on failure
|
482 |
*/
|
483 |
|
484 |
UCHAR DCLogSetProgram( DCLog *dclog, const char *prog ) { |
485 |
|
486 |
char hostname[256]; |
487 |
char *from;
|
488 |
|
489 |
// Make sure that we have a valid DCLog object and a string
|
490 |
if (dclog == NULL || prog == NULL) |
491 |
return 0; |
492 |
|
493 |
// Grab the hostname or use a default
|
494 |
if (gethostname( hostname, 256 ) == -1) |
495 |
strcpy( hostname, DCLOG_FROM ); |
496 |
|
497 |
// The from line should be program@hostname
|
498 |
from = malloc( (strlen( prog ) + strlen( hostname ) + 2)
|
499 |
* sizeof( char ) ); |
500 |
sprintf( from, "%s@%s", prog, hostname );
|
501 |
|
502 |
// If dclog->from is already set...
|
503 |
if (dclog->from != NULL) { |
504 |
|
505 |
// ...and it is the same, return success
|
506 |
if (strcmp( from, dclog->from ) == 0) |
507 |
return 1; |
508 |
|
509 |
// ...and it is not the same, destroy it
|
510 |
free( dclog->from ); |
511 |
dclog->from = NULL;
|
512 |
|
513 |
} // if (dclog->from != NULL)
|
514 |
|
515 |
// Allocate enough memory for dclog->from and set it
|
516 |
dclog->from = from; |
517 |
|
518 |
return 1; |
519 |
|
520 |
} // DCLogSetProgram()
|
521 |
|
522 |
|
523 |
/** Sets the alarm spool directory.
|
524 |
*
|
525 |
* @param *dclog pointer to a DCLog object
|
526 |
* @param *spool spool directory
|
527 |
*
|
528 |
* @return 1 on success, 0 on failure
|
529 |
*/
|
530 |
|
531 |
UCHAR DCLogSetSpool( DCLog *dclog, const char *spool ) { |
532 |
|
533 |
// Make sure that we have a valid DCLog object and a string
|
534 |
if (dclog == NULL || spool == NULL) |
535 |
return 0; |
536 |
|
537 |
if (strncpy( dclog->spool, spool, 256 ) == NULL) |
538 |
return 0; |
539 |
|
540 |
return 1; |
541 |
|
542 |
} // DCLogSetSpool()
|
543 |
|
544 |
|
545 |
/** Turns unique logfiles on or off.
|
546 |
*
|
547 |
* If on, each logfile will get the day of the month appended to it
|
548 |
* (e.g. my_logfile.27 versus my_logfile). Setting this after the logfile
|
549 |
* is opened does not make a whole lot of sense.
|
550 |
*
|
551 |
* @param *dclog pointer to a DCLog object
|
552 |
* @param val 1 for on, 0 for off
|
553 |
*
|
554 |
* @return 1 on success, 0 on failure
|
555 |
*/
|
556 |
|
557 |
UCHAR DCLogSetUniqueByDay( DCLog *dclog, const UCHAR val ) {
|
558 |
|
559 |
// Make sure that we have a valid DCLog object
|
560 |
if (dclog == NULL) |
561 |
return 0; |
562 |
|
563 |
// Update the features bitmask
|
564 |
if (val == 0) |
565 |
dclog->features ^= DCLOG_FEAT_UNIQUE; |
566 |
else if (val == 1) |
567 |
dclog->features |= DCLOG_FEAT_UNIQUE; |
568 |
else
|
569 |
return 0; |
570 |
|
571 |
// If ts_fmt is not set, apply the default
|
572 |
if (dclog->ts_fmt[0] == '\0' && |
573 |
strncpy( dclog->ts_fmt, DCLOG_TS_FMT_DEFAULT, DCLOG_TS_FMT_LEN ) |
574 |
== NULL)
|
575 |
return 0; |
576 |
|
577 |
return 1; |
578 |
|
579 |
} // DCLogSetUniqueByDay()
|
580 |
|
581 |
|
582 |
/** Sets the timestamp format string and turns the unique feature on
|
583 |
*
|
584 |
* @param *dclog pointer to a DCLog object
|
585 |
* @param ts_fmt strftime(3) time format string
|
586 |
*
|
587 |
* @return 1 on success, 0 on failure
|
588 |
*/
|
589 |
|
590 |
UCHAR DCLogSetTimestampFormat( DCLog *dclog, char *const ts_fmt ) { |
591 |
|
592 |
// Validate args
|
593 |
if (dclog == NULL || ts_fmt == NULL) |
594 |
return 0; |
595 |
|
596 |
if (strncpy( dclog->ts_fmt, ts_fmt, DCLOG_TS_FMT_LEN ) == NULL) |
597 |
return 0; |
598 |
|
599 |
return DCLogSetUniqueByDay( dclog, 1 ); |
600 |
|
601 |
} // DCLogSetTimestampFormat()
|
602 |
|
603 |
|
604 |
// -------------------------------------------------------------------------
|
605 |
|
606 |
|
607 |
// Logging methods
|
608 |
// -------------------------------------------------------------------------
|
609 |
|
610 |
|
611 |
/** Opens a logfile.
|
612 |
*
|
613 |
* If the unique_by_day member of the DCLog object is set to true, the day
|
614 |
* of the month will be appended to the filename of the logfile (see
|
615 |
* DCLogSetUniqueByDay, above).
|
616 |
*
|
617 |
* @param *dclog pointer to a DCLog object
|
618 |
* @param fn fully qualified filename
|
619 |
* @param mode mode for opening file (w truncates, a appends)
|
620 |
*
|
621 |
* @return 1 on success, 0 on failure
|
622 |
*/
|
623 |
|
624 |
UCHAR DCLogOpen( DCLog *dclog, const char *fn, const char *mode ) { |
625 |
|
626 |
// Make sure that we have a valid DCLog object
|
627 |
if (dclog == NULL) |
628 |
return 0; |
629 |
|
630 |
// Allocate enough memory to store the filename string, unless we already
|
631 |
// have a filename, in which case we will assume it is correct.
|
632 |
if (dclog->fn == NULL && |
633 |
(dclog->fn = malloc( sizeof( char ) * DCLOG_FN_LEN )) == NULL) |
634 |
return 0; |
635 |
|
636 |
// Copy the filename string
|
637 |
strncpy( dclog->fn, fn, DCLOG_FN_LEN ); |
638 |
|
639 |
// If the filename string is "stderr", log to stderr
|
640 |
if (strcasecmp( fn, "stderr" ) == 0) { |
641 |
|
642 |
dclog->fp = stderr; |
643 |
return 1; |
644 |
|
645 |
} // if (strcasecmp( fn, "stderr" ) == 0)
|
646 |
|
647 |
// If the filename string is "stdout", log to stdout
|
648 |
else if (strcasecmp( fn, "stdout" ) == 0) { |
649 |
|
650 |
dclog->fp = stdout; |
651 |
return 1; |
652 |
|
653 |
} // else if (strcasecmp( fn, "stdout" ) == 0)
|
654 |
|
655 |
// If control reaches here, we are logging to a file
|
656 |
|
657 |
// If unique feature is on, append the timestamp string
|
658 |
if (dclog->features & DCLOG_FEAT_UNIQUE) {
|
659 |
|
660 |
time_t tv = time( NULL );
|
661 |
struct tm *tm = localtime( &tv );
|
662 |
|
663 |
strftime( &dclog->fn[strlen( dclog->fn )], |
664 |
DCLOG_FN_LEN - 1 - strlen( dclog->fn ),
|
665 |
dclog->ts_fmt, tm ); |
666 |
|
667 |
} // if (dclog->features & DCLOG_FEAT_UNIQUE)
|
668 |
|
669 |
// Open the file
|
670 |
if ((dclog->fp = fopen( dclog->fn, mode )) == NULL) |
671 |
return 0; |
672 |
|
673 |
// Set the stream to unbuffered
|
674 |
if (setvbuf( dclog->fp, (char *)NULL, _IONBF, 0 ) != 0) |
675 |
fprintf( stderr, |
676 |
"WARNING: DCLogOpen() could not set log file '%s' unbuffered!\n",
|
677 |
dclog->fn ); |
678 |
|
679 |
// Return success.
|
680 |
return 1; |
681 |
|
682 |
} // DCLogOpen()
|
683 |
|
684 |
|
685 |
/** Closes an open logfile.
|
686 |
*
|
687 |
* @param *dclog pointer to a DCLog object
|
688 |
*
|
689 |
* @return 1 on success, 0 on failure
|
690 |
*/
|
691 |
|
692 |
UCHAR DCLogClose( DCLog *dclog ) { |
693 |
|
694 |
int retval;
|
695 |
|
696 |
// Make sure that we have a valid DCLog object
|
697 |
if (dclog == NULL) |
698 |
return 0; |
699 |
|
700 |
// If the file is not open, return failure
|
701 |
if (dclog->fp == NULL) |
702 |
return 0; |
703 |
|
704 |
// Attempt to close the file (unless we are logging to stderr or
|
705 |
// stdout). If the close fails, we will return an error later.
|
706 |
if (strcasecmp( dclog->fn, "stderr" ) == 0 || |
707 |
strcasecmp( dclog->fn, "stdout" ) == 0) |
708 |
retval = 0;
|
709 |
else
|
710 |
retval = fclose( dclog->fp ); |
711 |
|
712 |
// Set the file pointer to NULL (so that this DCLog object cannot be
|
713 |
// written to again)
|
714 |
dclog->fp = NULL;
|
715 |
|
716 |
// If fclose() returned EOF, we need to return failure (success
|
717 |
// otherwise)
|
718 |
return retval == EOF ? 0 : 1; |
719 |
|
720 |
} // DCLogClose()
|
721 |
|
722 |
|
723 |
/** Writes a message to an open logfile at the specified level.
|
724 |
*
|
725 |
* If the global logging level for this object is lower than the specified
|
726 |
* level, the mesage will not be logged, but success will be returned. If
|
727 |
* the ts_header member of the DCLog object is set to true, a timestamp /
|
728 |
* PID header will be written before every log message (see DCLogSetHeader(),
|
729 |
* above). No newline is appended to your message, so if you want one, put
|
730 |
* it in your format string. If the level is zero, "ERROR: " will be
|
731 |
* written before the message.
|
732 |
*
|
733 |
* @param *dclog pointer to a DCLog object
|
734 |
* @param lev level of this log message
|
735 |
* @param fmt printf-style format string
|
736 |
* @param ... arguments to the format string
|
737 |
*
|
738 |
* @return 1 on success, 0 on failure
|
739 |
*/
|
740 |
|
741 |
inline UCHAR DCLogWrite( DCLog *dclog, const UCHAR lev, |
742 |
const char *fmt, ... ) { |
743 |
|
744 |
va_list str_args; |
745 |
|
746 |
// Make sure that we have a valid DCLog object
|
747 |
if (dclog == NULL) |
748 |
return 0; |
749 |
|
750 |
// If the FILE object does not exist, bail
|
751 |
if (dclog->fp == NULL) |
752 |
return 0; |
753 |
|
754 |
// If the user-specified level is higher than the logging level for
|
755 |
// this DCLog object, return success
|
756 |
if (lev > dclog->lev)
|
757 |
return 1; |
758 |
|
759 |
// If we are using the unique filename by day of the month feature,
|
760 |
// DCLOG_FEAT_UNIQUE, see if the day of the month has changed
|
761 |
if (dclog->features & DCLOG_FEAT_UNIQUE) {
|
762 |
|
763 |
time_t current_time; |
764 |
struct tm now;
|
765 |
|
766 |
// What time is it?
|
767 |
current_time = time( NULL );
|
768 |
|
769 |
// Check for an unset prev_date and set it to the current time
|
770 |
if (!dclog->prev_date) {
|
771 |
|
772 |
dclog->prev_date = (struct tm *)malloc( sizeof ( struct tm ) ); |
773 |
localtime_r( ¤t_time, dclog->prev_date ); |
774 |
|
775 |
} // if (!prev_date)
|
776 |
|
777 |
// Set the current time
|
778 |
localtime_r( ¤t_time, &now ); |
779 |
|
780 |
// Rotate logfile if the current day of the month is not the same as the
|
781 |
// previous one
|
782 |
if (dclog->prev_date->tm_mday != now.tm_mday) {
|
783 |
|
784 |
char new_fn[256], old_fn[256]; |
785 |
int i;
|
786 |
|
787 |
fprintf( dclog->fp, |
788 |
"\n\n*******************************************\n\n"
|
789 |
"Warning: rotating log files due the day of the month changing "
|
790 |
"from %02d to %02d"
|
791 |
"\n\n*******************************************\n\n",
|
792 |
dclog->prev_date->tm_mday, now.tm_mday ); |
793 |
|
794 |
if (strncpy( new_fn, dclog->fn, 256 ) == NULL || |
795 |
strncpy( old_fn, dclog->fn, 256 ) == NULL) { |
796 |
|
797 |
fprintf( stderr, "Failed to copy filename\n" );
|
798 |
return 0; |
799 |
|
800 |
} // if (couldn't copy filename)
|
801 |
|
802 |
// Snip off the rightmost '.' and everything to the right of it
|
803 |
// in the filename
|
804 |
for (i = strlen( new_fn ); i > 0; i--) |
805 |
if (new_fn[i] == '.') { |
806 |
|
807 |
new_fn[i] = '\0';
|
808 |
break;
|
809 |
|
810 |
} // if (found rightmost '.')
|
811 |
|
812 |
if (DCLogClose( dclog ) == 0) { |
813 |
|
814 |
fprintf( stderr, "Failed to close file: %s\n", dclog->fn );
|
815 |
return 0; |
816 |
|
817 |
} // if (couldn't close old logfile)
|
818 |
|
819 |
if (DCLogOpen( dclog, new_fn, "w" ) == 0) { |
820 |
|
821 |
fprintf( stderr,"Failed to open file: %s\n", new_fn );
|
822 |
return 0; |
823 |
|
824 |
} // if (couldn't open new logfile)
|
825 |
|
826 |
fprintf( dclog->fp, |
827 |
"\n\n*******************************************\n\n"
|
828 |
"Warning: rotated logfiles from '%s' to '%s' due the day of the "
|
829 |
"month changing from %02d to %02d"
|
830 |
"\n\n*******************************************\n\n",
|
831 |
old_fn, dclog->fn, dclog->prev_date->tm_mday, now.tm_mday ); |
832 |
|
833 |
*dclog->prev_date = now; |
834 |
|
835 |
} // if (rotating logfile)
|
836 |
|
837 |
} // if (checking date)
|
838 |
|
839 |
// Do we also need to check the current filesize?
|
840 |
if (dclog->limit_size) {
|
841 |
|
842 |
int size;
|
843 |
struct stat statbuf;
|
844 |
|
845 |
// If over max file size, rotate the logfile
|
846 |
if (stat( dclog->fn, &statbuf ) >= 0 && |
847 |
(size = (int)statbuf.st_size) >= dclog->max_file_size) {
|
848 |
|
849 |
time_t current_time = time( NULL );
|
850 |
struct tm now;
|
851 |
char old_fn[256]; |
852 |
|
853 |
fprintf( dclog->fp, "Warning: rotating log files due to the size of the "
|
854 |
"current logfile, %d bytes, being larger than the maxiumum "
|
855 |
"allowable size, %d bytes\n", size, dclog->max_file_size );
|
856 |
|
857 |
localtime_r( ¤t_time, &now ); |
858 |
if (snprintf( old_fn, 256, "%s_%.2d%.2d%.2d", |
859 |
dclog->fn, now.tm_hour, now.tm_min, now.tm_sec ) < 0) {
|
860 |
|
861 |
fprintf( stderr, "Failed to write string for old filename\n" );
|
862 |
return 0; |
863 |
|
864 |
} // if (failed to write string)
|
865 |
|
866 |
rename( dclog->fn, old_fn ); |
867 |
|
868 |
if (!_truncate( dclog )) {
|
869 |
|
870 |
fprintf( stderr, "Failed to truncate logfile: %s\n", dclog->fn );
|
871 |
return 0; |
872 |
|
873 |
} // if (truncating file failed)
|
874 |
|
875 |
fprintf( dclog->fp, "Warning: rotated log files from '%s' to '%s' due to "
|
876 |
"the size of the current logfile, %d bytes, being larger than the "
|
877 |
"maxiumum allowable size, %d bytes\n",
|
878 |
old_fn, dclog->fn, size, dclog->max_file_size ); |
879 |
|
880 |
} // if (rotating logfiles)
|
881 |
|
882 |
} // if (checking size)
|
883 |
|
884 |
// If header is on, generate a message header and print it
|
885 |
if (dclog->features & DCLOG_FEAT_HEADER) {
|
886 |
|
887 |
struct timeval tv;
|
888 |
struct tm *tm;
|
889 |
|
890 |
// If gettimeofday() fails, don't write the header, but try to complain
|
891 |
if (gettimeofday( &tv, NULL ) == -1) |
892 |
fprintf( dclog->fp, "gettimeofday() call failed!\n" );
|
893 |
|
894 |
else {
|
895 |
|
896 |
tm = localtime( &(tv.tv_sec) ); |
897 |
|
898 |
fprintf( dclog->fp, "%02d:%02d:%02d.%02ld: ", tm->tm_hour,
|
899 |
tm->tm_min, tm->tm_sec, tv.tv_usec ); |
900 |
|
901 |
} // else (gettimeofday(3) succeeded)
|
902 |
|
903 |
} // if (dclog->features & DCLOG_FEAT_HEADER)
|
904 |
|
905 |
// If the DCLOG_FEAT_PLEVEL feature is turned on, we need to insert the level's
|
906 |
// mnemonic
|
907 |
if (dclog->features & DCLOG_FEAT_PLEVEL)
|
908 |
fprintf( dclog->fp, "%s ", levels[lev] );
|
909 |
|
910 |
#if 0
|
911 |
const char *f = fmt;
|
912 |
static char nlfmt[4096];
|
913 |
if (dclog->features & DCLOG_FEAT_NEWLINE) {
|
914 |
strncpy(nlfmt, fmt, 4095);
|
915 |
int len = strlen(nlfmt);
|
916 |
nlfmt[len] = '\n';
|
917 |
nlfmt[len+1] = 0;
|
918 |
f = nlfmt;
|
919 |
}
|
920 |
#endif
|
921 |
|
922 |
// Initialise the variable str_args list
|
923 |
va_start( str_args, fmt ); |
924 |
|
925 |
// Print the log message
|
926 |
if (vfprintf( dclog->fp, fmt, str_args ) < 0) |
927 |
return 0; |
928 |
|
929 |
// Finalise the variable str_args list
|
930 |
va_end( str_args ); |
931 |
|
932 |
return 1; |
933 |
|
934 |
} // DCLogWrite()
|
935 |
|
936 |
|
937 |
/** Sends an alarm.
|
938 |
*
|
939 |
* Forks a child process, calls the function specified by the fp function
|
940 |
* pointer argument (if fp is NULL, no function is called), connects to
|
941 |
* the SMTP mail server (which should have been set by DCLogSetMailServer()),
|
942 |
* and sends an alarm email of the following format:
|
943 |
*
|
944 |
* From: [program, set by DCLogSetProgram()]@[hostname].
|
945 |
* To: [page server, set by DCLogSetPageServer()].
|
946 |
* Subject: [code] : [syn].
|
947 |
* Body: [controlled by format string fmt].
|
948 |
*
|
949 |
* @param *dclog pointer to a DCLog object
|
950 |
* @param code alarm code
|
951 |
* @param syn alarm synopsis
|
952 |
* @param fmt detailed description (printf-style format string)
|
953 |
* @param ... arguments to the format string
|
954 |
*
|
955 |
* @return 1 on success, 0 on failure
|
956 |
*/
|
957 |
|
958 |
UCHAR DCLogAlarm( DCLog *dclog, const char *code, const char *syn, |
959 |
const char *fmt, ... ) { |
960 |
|
961 |
char msg[DCLOG_MAX_ALARM_SIZE];
|
962 |
char fn[256]; |
963 |
|
964 |
va_list str_args; |
965 |
FILE *fp = NULL;
|
966 |
|
967 |
// Make sure that we have valid arguments
|
968 |
if (dclog == NULL || code == NULL || syn == NULL || fmt == NULL) |
969 |
return 0; |
970 |
|
971 |
// Make sure we know the program code
|
972 |
if (dclog->from == NULL) |
973 |
DCLogSetProgram( dclog, DCLOG_PROG ); |
974 |
|
975 |
// Write the user's message
|
976 |
va_start( str_args, fmt ); |
977 |
vsnprintf( msg, 480, fmt, str_args );
|
978 |
va_end( str_args ); |
979 |
|
980 |
if (snprintf( fn, 256, "%s/%s.%s.%ld", dclog->spool, dclog->from, code, |
981 |
random() ) < 0)
|
982 |
return 0; |
983 |
|
984 |
if ((fp = fopen( fn, "w" )) == NULL) |
985 |
return 0; |
986 |
|
987 |
fprintf( fp, |
988 |
"To: %s\r\n"
|
989 |
"From: %s\r\n"
|
990 |
"Subject: %s: %s\r\n"
|
991 |
"\r\n"
|
992 |
"%s"
|
993 |
"\r\n",
|
994 |
dclog->to, dclog->from, code, syn, msg ); |
995 |
|
996 |
fclose( fp ); |
997 |
|
998 |
return 1; |
999 |
|
1000 |
} // DCLogAlarm()
|
1001 |
|
1002 |
|
1003 |
// -------------------------------------------------------------------------
|
1004 |
|
1005 |
|
1006 |
// Miscellaneous methods
|
1007 |
// -------------------------------------------------------------------------
|
1008 |
|
1009 |
|
1010 |
/** Makes sure the object is able to log.
|
1011 |
*
|
1012 |
* @param *dclog pointer to a DCLog object
|
1013 |
*
|
1014 |
* @returns 1 on success, 0 on failure
|
1015 |
*/
|
1016 |
|
1017 |
UCHAR DCLogCanLog( const DCLog *dclog ) {
|
1018 |
|
1019 |
if (dclog == NULL || dclog->fp == NULL || dclog->fn == NULL) |
1020 |
return 0; |
1021 |
else
|
1022 |
return 1; |
1023 |
|
1024 |
} // DCLogCanLog()
|
1025 |
|
1026 |
|
1027 |
// -------------------------------------------------------------------------
|