Statistics
| Branch: | Revision:

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( &current_time, dclog->prev_date );
774
      
775
    } // if (!prev_date)
776

    
777
    // Set the current time
778
    localtime_r( &current_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( &current_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
// -------------------------------------------------------------------------