xglcomp

annotate src/logger.c @ 0:d9b3fba68705

initial commit
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 21 Jan 2016 08:45:31 +0200
parents
children 876efea9424c
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