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