rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <string.h>
|
nuclear@0
|
4 #include <errno.h>
|
nuclear@0
|
5 #include <stdarg.h>
|
nuclear@0
|
6 #include "logger.h"
|
nuclear@0
|
7
|
nuclear@0
|
8 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
|
nuclear@0
|
9 #include <unistd.h>
|
nuclear@0
|
10 #include <fcntl.h>
|
nuclear@0
|
11 #include <pthread.h>
|
nuclear@0
|
12 #include <sys/select.h>
|
nuclear@0
|
13 #endif
|
nuclear@0
|
14
|
nuclear@0
|
15 enum targ_type { TARG_STREAM, TARG_FILE, TARG_FUNC };
|
nuclear@0
|
16
|
nuclear@0
|
17 struct log_target {
|
nuclear@0
|
18 unsigned int msg_type;
|
nuclear@0
|
19 enum targ_type targ_type;
|
nuclear@0
|
20 FILE *fp;
|
nuclear@0
|
21 void (*func)(const char*, void*);
|
nuclear@0
|
22 void *func_cls;
|
nuclear@0
|
23
|
nuclear@0
|
24 struct log_target *next;
|
nuclear@0
|
25 };
|
nuclear@0
|
26
|
nuclear@0
|
27 static struct log_target *targ_list = (void*)1; /* uninitialized */
|
nuclear@0
|
28
|
nuclear@0
|
29 static void init_once(void)
|
nuclear@0
|
30 {
|
nuclear@0
|
31 if(targ_list == (void*)1) {
|
nuclear@0
|
32 /* perform one-time init */
|
nuclear@0
|
33 targ_list = 0;
|
nuclear@0
|
34 log_add_stream(LOG_INFO | LOG_DEBUG, stdout);
|
nuclear@0
|
35 log_add_stream(LOG_WARNING | LOG_ERROR, stderr);
|
nuclear@0
|
36 }
|
nuclear@0
|
37 }
|
nuclear@0
|
38
|
nuclear@0
|
39 static void log_string(unsigned int type, const char *str);
|
nuclear@0
|
40 static int typecolor(int type);
|
nuclear@0
|
41
|
nuclear@0
|
42 void log_clear_targets(void)
|
nuclear@0
|
43 {
|
nuclear@0
|
44 init_once();
|
nuclear@0
|
45
|
nuclear@0
|
46 while(targ_list) {
|
nuclear@0
|
47 struct log_target *tmp = targ_list;
|
nuclear@0
|
48 targ_list = targ_list->next;
|
nuclear@0
|
49
|
nuclear@0
|
50 if(tmp->targ_type == TARG_FILE) {
|
nuclear@0
|
51 fclose(tmp->fp);
|
nuclear@0
|
52 }
|
nuclear@0
|
53 free(tmp);
|
nuclear@0
|
54 }
|
nuclear@0
|
55 targ_list = 0;
|
nuclear@0
|
56 }
|
nuclear@0
|
57
|
nuclear@0
|
58 int log_add_stream(unsigned int type_mask, FILE *fp)
|
nuclear@0
|
59 {
|
nuclear@0
|
60 struct log_target *targ;
|
nuclear@0
|
61
|
nuclear@0
|
62 init_once();
|
nuclear@0
|
63
|
nuclear@0
|
64 if(!(targ = malloc(sizeof *targ))) {
|
nuclear@0
|
65 perror("failed to allocate memory for log target");
|
nuclear@0
|
66 return -1;
|
nuclear@0
|
67 }
|
nuclear@0
|
68 targ->msg_type = type_mask;
|
nuclear@0
|
69 targ->targ_type = TARG_STREAM;
|
nuclear@0
|
70 targ->fp = fp;
|
nuclear@0
|
71 targ->next = targ_list;
|
nuclear@0
|
72 targ_list = targ;
|
nuclear@0
|
73 return 0;
|
nuclear@0
|
74 }
|
nuclear@0
|
75
|
nuclear@0
|
76 int log_add_file(unsigned int type_mask, const char *fname)
|
nuclear@0
|
77 {
|
nuclear@0
|
78 FILE *fp;
|
nuclear@0
|
79 struct log_target *targ;
|
nuclear@0
|
80
|
nuclear@0
|
81 init_once();
|
nuclear@0
|
82
|
nuclear@0
|
83 if(!(fp = fopen(fname, "w"))) {
|
nuclear@0
|
84 fprintf(stderr, "failed to open logfile: %s: %s\n", fname, strerror(errno));
|
nuclear@0
|
85 return -1;
|
nuclear@0
|
86 }
|
nuclear@0
|
87 if(type_mask & (LOG_ERROR | LOG_DEBUG)) {
|
nuclear@0
|
88 /* make error and debug logs unbuffered */
|
nuclear@0
|
89 setvbuf(fp, 0, _IONBF, 0);
|
nuclear@0
|
90 }
|
nuclear@0
|
91 if(!(targ = malloc(sizeof *targ))) {
|
nuclear@0
|
92 perror("failed to allocate memory for log target");
|
nuclear@0
|
93 fclose(fp);
|
nuclear@0
|
94 return -1;
|
nuclear@0
|
95 }
|
nuclear@0
|
96 targ->msg_type = type_mask;
|
nuclear@0
|
97 targ->targ_type = TARG_FILE;
|
nuclear@0
|
98 targ->fp = fp;
|
nuclear@0
|
99 targ->next = targ_list;
|
nuclear@0
|
100 targ_list = targ;
|
nuclear@0
|
101 return 0;
|
nuclear@0
|
102 }
|
nuclear@0
|
103
|
nuclear@0
|
104 int log_add_func(unsigned int type_mask, void (*func)(const char*, void*), void *cls)
|
nuclear@0
|
105 {
|
nuclear@0
|
106 struct log_target *targ;
|
nuclear@0
|
107
|
nuclear@0
|
108 init_once();
|
nuclear@0
|
109
|
nuclear@0
|
110 if(!(targ = malloc(sizeof *targ))) {
|
nuclear@0
|
111 perror("failed to allocate memory for log target");
|
nuclear@0
|
112 return -1;
|
nuclear@0
|
113 }
|
nuclear@0
|
114 targ->msg_type = type_mask;
|
nuclear@0
|
115 targ->targ_type = TARG_FUNC;
|
nuclear@0
|
116 targ->func = func;
|
nuclear@0
|
117 targ->func_cls = cls;
|
nuclear@0
|
118 targ->next = targ_list;
|
nuclear@0
|
119 targ_list = targ;
|
nuclear@0
|
120 return 0;
|
nuclear@0
|
121 }
|
nuclear@0
|
122
|
nuclear@0
|
123
|
nuclear@0
|
124 void log_va_msg(unsigned int type, const char *fmt, va_list ap)
|
nuclear@0
|
125 {
|
nuclear@0
|
126 char fixedbuf[256];
|
nuclear@0
|
127 char *buf = fixedbuf;
|
nuclear@0
|
128 int len, sz = sizeof fixedbuf;
|
nuclear@0
|
129
|
nuclear@0
|
130 init_once();
|
nuclear@0
|
131
|
nuclear@0
|
132 if(!targ_list || !*fmt) return; /* don't waste our time */
|
nuclear@0
|
133
|
nuclear@0
|
134 /* try with the fixed size buffer first which should be sufficient most of the
|
nuclear@0
|
135 * time. if this fails, allocate a buffer of the correct size.
|
nuclear@0
|
136 */
|
nuclear@0
|
137 while((len = vsnprintf(buf, sz, fmt, ap)) >= sz || len < 0) {
|
nuclear@0
|
138 sz = len >= 0 ? len + 1 : sz * 2;
|
nuclear@0
|
139 if(buf != fixedbuf)
|
nuclear@0
|
140 free(buf);
|
nuclear@0
|
141 if(!(buf = malloc(sz))) {
|
nuclear@0
|
142 return;
|
nuclear@0
|
143 }
|
nuclear@0
|
144 }
|
nuclear@0
|
145
|
nuclear@0
|
146 if(buf != fixedbuf)
|
nuclear@0
|
147 free(buf);
|
nuclear@0
|
148
|
nuclear@0
|
149 log_string(type, buf);
|
nuclear@0
|
150 }
|
nuclear@0
|
151
|
nuclear@0
|
152 /* helpers */
|
nuclear@0
|
153 #define LOG_VA_MSG(t) \
|
nuclear@0
|
154 do { \
|
nuclear@0
|
155 va_list ap; \
|
nuclear@0
|
156 va_start(ap, fmt); \
|
nuclear@0
|
157 log_va_msg(t, fmt, ap); \
|
nuclear@0
|
158 va_end(ap); \
|
nuclear@0
|
159 } while(0)
|
nuclear@0
|
160
|
nuclear@0
|
161 void log_msg(unsigned int type, const char *fmt, ...)
|
nuclear@0
|
162 {
|
nuclear@0
|
163 LOG_VA_MSG(type);
|
nuclear@0
|
164 }
|
nuclear@0
|
165
|
nuclear@0
|
166 void log_info(const char *fmt, ...)
|
nuclear@0
|
167 {
|
nuclear@0
|
168 LOG_VA_MSG(LOG_INFO);
|
nuclear@0
|
169 }
|
nuclear@0
|
170
|
nuclear@0
|
171 void log_warning(const char *fmt, ...)
|
nuclear@0
|
172 {
|
nuclear@0
|
173 LOG_VA_MSG(LOG_WARNING);
|
nuclear@0
|
174 }
|
nuclear@0
|
175
|
nuclear@0
|
176 void log_error(const char *fmt, ...)
|
nuclear@0
|
177 {
|
nuclear@0
|
178 LOG_VA_MSG(LOG_ERROR);
|
nuclear@0
|
179 }
|
nuclear@0
|
180
|
nuclear@0
|
181 void log_debug(const char *fmt, ...)
|
nuclear@0
|
182 {
|
nuclear@0
|
183 LOG_VA_MSG(LOG_DEBUG);
|
nuclear@0
|
184 }
|
nuclear@0
|
185
|
nuclear@0
|
186 void log_va_info(const char *fmt, va_list ap)
|
nuclear@0
|
187 {
|
nuclear@0
|
188 log_va_msg(LOG_INFO, fmt, ap);
|
nuclear@0
|
189 }
|
nuclear@0
|
190
|
nuclear@0
|
191 void log_va_warning(const char *fmt, va_list ap)
|
nuclear@0
|
192 {
|
nuclear@0
|
193 log_va_msg(LOG_WARNING, fmt, ap);
|
nuclear@0
|
194 }
|
nuclear@0
|
195
|
nuclear@0
|
196 void log_va_error(const char *fmt, va_list ap)
|
nuclear@0
|
197 {
|
nuclear@0
|
198 log_va_msg(LOG_ERROR, fmt, ap);
|
nuclear@0
|
199 }
|
nuclear@0
|
200
|
nuclear@0
|
201 void log_va_debug(const char *fmt, va_list ap)
|
nuclear@0
|
202 {
|
nuclear@0
|
203 log_va_msg(LOG_DEBUG, fmt, ap);
|
nuclear@0
|
204 }
|
nuclear@0
|
205
|
nuclear@0
|
206 static void log_string(unsigned int type, const char *str)
|
nuclear@0
|
207 {
|
nuclear@0
|
208 struct log_target *targ;
|
nuclear@0
|
209
|
nuclear@0
|
210 targ = targ_list;
|
nuclear@0
|
211 while(targ) {
|
nuclear@0
|
212 if(targ->msg_type & type) {
|
nuclear@0
|
213 if(targ->targ_type == TARG_STREAM || targ->targ_type == TARG_FILE) {
|
nuclear@0
|
214 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
|
nuclear@0
|
215 if(isatty(fileno(targ->fp)) && type != LOG_INFO) {
|
nuclear@0
|
216 int c = typecolor(type);
|
nuclear@0
|
217 fprintf(targ->fp, "\033[%dm%s\033[0m", c, str);
|
nuclear@0
|
218 } else
|
nuclear@0
|
219 #endif
|
nuclear@0
|
220 {
|
nuclear@0
|
221 fputs(str, targ->fp);
|
nuclear@0
|
222 }
|
nuclear@0
|
223
|
nuclear@0
|
224 } else if(targ->targ_type == TARG_FUNC) {
|
nuclear@0
|
225 targ->func(str, targ->func_cls);
|
nuclear@0
|
226 }
|
nuclear@0
|
227 }
|
nuclear@0
|
228 targ = targ->next;
|
nuclear@0
|
229 }
|
nuclear@0
|
230 }
|
nuclear@0
|
231
|
nuclear@0
|
232 enum {
|
nuclear@0
|
233 BLACK = 0,
|
nuclear@0
|
234 RED,
|
nuclear@0
|
235 GREEN,
|
nuclear@0
|
236 YELLOW,
|
nuclear@0
|
237 BLUE,
|
nuclear@0
|
238 MAGENTA,
|
nuclear@0
|
239 CYAN,
|
nuclear@0
|
240 WHITE
|
nuclear@0
|
241 };
|
nuclear@0
|
242
|
nuclear@0
|
243 #define ANSI_FGCOLOR(x) (30 + (x))
|
nuclear@0
|
244 #define ANSI_BGCOLOR(x) (40 + (x))
|
nuclear@0
|
245
|
nuclear@0
|
246 static int typecolor(int type)
|
nuclear@0
|
247 {
|
nuclear@0
|
248 switch(type) {
|
nuclear@0
|
249 case LOG_ERROR:
|
nuclear@0
|
250 return ANSI_FGCOLOR(RED);
|
nuclear@0
|
251 case LOG_WARNING:
|
nuclear@0
|
252 return ANSI_FGCOLOR(YELLOW);
|
nuclear@0
|
253 case LOG_DEBUG:
|
nuclear@0
|
254 return ANSI_FGCOLOR(MAGENTA);
|
nuclear@0
|
255 default:
|
nuclear@0
|
256 break;
|
nuclear@0
|
257 }
|
nuclear@0
|
258 return 37;
|
nuclear@0
|
259 }
|
nuclear@0
|
260
|
nuclear@0
|
261 #if defined(unix) || defined(__unix__) || defined(__APPLE__)
|
nuclear@0
|
262 static int out_pipe[2], err_pipe[2];
|
nuclear@0
|
263 static int thr_running;
|
nuclear@0
|
264
|
nuclear@0
|
265 static void *thr_func(void *arg);
|
nuclear@0
|
266
|
nuclear@0
|
267 static int std_intercept(void)
|
nuclear@0
|
268 {
|
nuclear@0
|
269 pthread_t thr;
|
nuclear@0
|
270
|
nuclear@0
|
271 if(thr_running) return 0;
|
nuclear@0
|
272
|
nuclear@0
|
273 pipe(out_pipe);
|
nuclear@0
|
274 pipe(err_pipe);
|
nuclear@0
|
275
|
nuclear@0
|
276 fcntl(out_pipe[0], F_SETFL, fcntl(out_pipe[0], F_GETFL) | O_NONBLOCK);
|
nuclear@0
|
277 fcntl(err_pipe[0], F_SETFL, fcntl(err_pipe[0], F_GETFL) | O_NONBLOCK);
|
nuclear@0
|
278
|
nuclear@0
|
279 if(pthread_create(&thr, 0, thr_func, 0) == -1) {
|
nuclear@0
|
280 log_error("failed to start std intercept thread\n");
|
nuclear@0
|
281 return -1;
|
nuclear@0
|
282 }
|
nuclear@0
|
283 thr_running = 1;
|
nuclear@0
|
284 pthread_detach(thr);
|
nuclear@0
|
285 return 0;
|
nuclear@0
|
286 }
|
nuclear@0
|
287
|
nuclear@0
|
288 static void *thr_func(void *arg)
|
nuclear@0
|
289 {
|
nuclear@0
|
290 static char buf[512];
|
nuclear@0
|
291 static int bufsz = sizeof buf;
|
nuclear@0
|
292 int max_fd = out_pipe[0] > err_pipe[0] ? out_pipe[0] : err_pipe[0];
|
nuclear@0
|
293
|
nuclear@0
|
294 for(;;) {
|
nuclear@0
|
295 int sz, res;
|
nuclear@0
|
296 fd_set rdset;
|
nuclear@0
|
297
|
nuclear@0
|
298 FD_ZERO(&rdset);
|
nuclear@0
|
299 FD_SET(out_pipe[0], &rdset);
|
nuclear@0
|
300 FD_SET(err_pipe[0], &rdset);
|
nuclear@0
|
301
|
nuclear@0
|
302 while((res = select(max_fd + 1, &rdset, 0, 0, 0)) == -1 && errno == EINTR);
|
nuclear@0
|
303 if(res == -1) {
|
nuclear@0
|
304 break; /* assume EPIPE or non-recoverable error */
|
nuclear@0
|
305 }
|
nuclear@0
|
306 if(res == 0) continue;
|
nuclear@0
|
307
|
nuclear@0
|
308 if(FD_ISSET(out_pipe[0], &rdset)) {
|
nuclear@0
|
309 while((sz = read(out_pipe[0], buf, bufsz - 1)) > 0) {
|
nuclear@0
|
310 buf[bufsz - 1] = 0;
|
nuclear@0
|
311 log_info("%s", buf);
|
nuclear@0
|
312 }
|
nuclear@0
|
313 if(sz == -1 && errno == EPIPE) break;
|
nuclear@0
|
314 }
|
nuclear@0
|
315 if(FD_ISSET(err_pipe[0], &rdset)) {
|
nuclear@0
|
316 while((sz = read(err_pipe[0], buf, bufsz - 1)) > 0) {
|
nuclear@0
|
317 buf[bufsz - 1] = 0;
|
nuclear@0
|
318 log_error("%s", buf);
|
nuclear@0
|
319 }
|
nuclear@0
|
320 if(sz == -1 && errno == EPIPE) break;
|
nuclear@0
|
321 }
|
nuclear@0
|
322 }
|
nuclear@0
|
323 return 0;
|
nuclear@0
|
324 }
|
nuclear@0
|
325
|
nuclear@0
|
326 void log_grab_stdout(void)
|
nuclear@0
|
327 {
|
nuclear@0
|
328 if(std_intercept() == -1) {
|
nuclear@0
|
329 return;
|
nuclear@0
|
330 }
|
nuclear@0
|
331 dup2(out_pipe[1], 1);
|
nuclear@0
|
332 }
|
nuclear@0
|
333
|
nuclear@0
|
334 void log_grab_stderr(void)
|
nuclear@0
|
335 {
|
nuclear@0
|
336 if(std_intercept() == -1) {
|
nuclear@0
|
337 return;
|
nuclear@0
|
338 }
|
nuclear@0
|
339 dup2(err_pipe[1], 2);
|
nuclear@0
|
340 }
|
nuclear@0
|
341 #else
|
nuclear@0
|
342 void log_grab_stdout(void)
|
nuclear@0
|
343 {
|
nuclear@0
|
344 log_error("log_grab_stdout only works on UNIX\n");
|
nuclear@0
|
345 }
|
nuclear@0
|
346
|
nuclear@0
|
347 void log_grab_stderr(void)
|
nuclear@0
|
348 {
|
nuclear@0
|
349 log_error("log_grab_stderr only works on UNIX\n");
|
nuclear@0
|
350 }
|
nuclear@0
|
351 #endif
|