pndnotifyd: fix some crashes
[pandora-libraries.git] / lib / pnd_logger.c
1
2 #include <stdarg.h> // va-args
3 #include <stdlib.h> // malloc/free
4 #include <string.h> // strdup
5 #include <time.h>
6 #include "pnd_logger.h"
7
8 static char *log_pretext = NULL;
9 static unsigned char log_filterlevel = 0;
10 static unsigned char log_flushafter = 0;
11 static time_t log_first = 0;
12
13 typedef enum {
14   pndl_nil = 0,
15   pndl_stream,
16   pndl_syslog,
17   pndl_callback
18 } pnd_logger_e;
19
20 typedef struct {
21   pnd_logger_e type;
22   union {
23     FILE *stream;
24     pnd_log_callback_f callback;
25   };
26 } pnd_log_target_t;
27
28 #define PND_LOG_MAX 5
29 static pnd_log_target_t log_targets [ PND_LOG_MAX ]; // implicitly nil
30
31 static int pnd_log_empty_slot ( void ) {
32   unsigned char i;
33
34   for ( i = 0; i < PND_LOG_MAX; i++ ) {
35     if ( log_targets [ i ].type == pndl_nil ) {
36       return ( i );
37     }
38   }
39
40   return ( -1 );
41 }
42
43 unsigned char pnd_log_set_filter ( unsigned char newlevel ) {
44   unsigned char foo = log_filterlevel;
45   log_filterlevel = newlevel;
46   return ( foo );
47 }
48
49 unsigned char pnd_log_get_filter ( void ) {
50   return ( log_filterlevel );
51 }
52
53 void pnd_log_set_flush ( unsigned char x ) {
54   log_flushafter = x;
55   return;
56 }
57
58 void pnd_log_set_pretext ( char *pre ) {
59
60   if ( log_pretext ) {
61     free ( log_pretext );
62     log_pretext = NULL;
63   }
64
65   if ( pre ) {
66     log_pretext = strdup ( pre );
67   }
68
69   return;
70 }
71
72 void pnd_log_to_nil ( void ) {
73   memset ( log_targets, '\0', sizeof(pnd_log_target_t) * PND_LOG_MAX );
74   return;
75 }
76
77 unsigned char pnd_log_to_stream ( FILE *f ) {
78   int i = pnd_log_empty_slot();
79
80   if ( i < 0 ) {
81     return ( 0 ); // fail!
82   }
83
84   log_targets [ i ].type = pndl_stream;
85   log_targets [ i ].stream = f;
86
87   return ( 1 );
88 }
89
90 unsigned char pnd_log_to_syslog ( char *facility ) {
91   return ( 0 ); // NYI
92 }
93
94 unsigned char pnd_log_to_callback ( pnd_log_callback_f f, void *userdata ) {
95   return ( 0 ); // NYI
96 }
97
98 unsigned char pnd_log_to_stdout ( void ) {
99   return ( pnd_log_to_stream ( stdout ) );
100 }
101
102 unsigned char pnd_log_to_stderr ( void ) {
103   return ( pnd_log_to_stream ( stderr ) );
104 }
105
106 unsigned char pnd_log_max_targets ( void ) {
107   return ( PND_LOG_MAX );
108 }
109
110 static void pnd_log_emit ( unsigned char level, char *message ) {
111   unsigned char i;
112
113   // iterate across targets and attempt to emit
114   for ( i = 0; i < PND_LOG_MAX; i++ ) {
115
116     switch ( log_targets [ i ].type ) {
117
118     case pndl_nil:
119       // nop
120       break;
121
122     case pndl_stream:
123       // pretext
124       if ( log_pretext ) {
125         fprintf ( log_targets [ i ].stream, "%s ", log_pretext );
126       }
127       // log level
128       fprintf ( log_targets [ i ].stream, "%u %u\t", level, (unsigned int) (time ( NULL ) - log_first) );
129       // message
130       if ( message ) {
131         fprintf ( log_targets [ i ].stream, "%s", message );
132         if ( strchr ( message, '\n' ) == NULL ) {
133           fprintf ( log_targets [ i ].stream, "\n" );
134         }
135         if ( log_flushafter ) {
136           fflush ( log_targets [ i ].stream );
137         }
138       }
139       break;
140
141     case pndl_syslog:
142       // NYI
143       break;
144
145     case pndl_callback:
146       // NYI
147       break;
148
149     } // switch
150
151   } // for
152
153   return;
154 }
155
156 unsigned char pnd_log ( unsigned char level, char *fmt, ... ) {
157
158   if ( log_first == 0 ) {
159     log_first = time ( NULL );
160   }
161
162   if ( level == PND_LOG_FORCE ) {
163     // always proceed
164   } else if ( level < log_filterlevel ) {
165     return ( 0 ); // too low level
166   }
167
168   // format the actual log string
169   int n, size = 100;
170   char *p, *np;
171   va_list ap;
172
173   if ( ( p = malloc ( size ) ) == NULL ) {
174     return ( 0 ); // fail!
175   }
176
177   while  ( 1 ) {
178
179     /* Try to print in the allocated space. */
180     va_start ( ap, fmt );
181     n = vsnprintf ( p, size, fmt, ap );
182     va_end ( ap );
183
184     /* If that worked, return the string. */
185     if ( n > -1 && n < size ) {
186       pnd_log_emit ( level, p );
187       break;
188     }
189
190     /* Else try again with more space. */
191     if ( n > -1 )   /* glibc 2.1 */
192       size = n + 1; /* precisely what is needed */
193     else           /* glibc 2.0 */
194       size *= 2;  /* twice the old size */
195     if ( ( np = realloc ( p, size ) ) == NULL ) {
196       free(p);
197       return ( 0 ); // fail!
198     } else {
199       p = np;
200     }
201
202   } // while
203
204   if ( p ) {
205     free ( p );
206   }
207
208   return ( 1 );
209 }
210
211
212 static unsigned char _do_buried_logging = 0;
213 void pnd_log_set_buried_logging ( unsigned char yesno ) {
214   _do_buried_logging = yesno;
215   return;
216 }
217
218 unsigned char pnd_log_do_buried_logging ( void ) {
219   if ( _do_buried_logging == 1 ) {
220     return ( 1 );
221   }
222   return ( 0 );
223 }