UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2013 Olaf Wintermann. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #ifdef __gnu_linux__ 30 #define _GNU_SOURCE 31 #endif 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <string.h> 36 #include <unistd.h> 37 #include <aio.h> 38 #include <time.h> 39 #include <pthread.h> 40 41 #include "log.h" 42 #include "../util/strbuf.h" 43 #include "../util/io.h" 44 #include "../util/atomic.h" 45 46 #include <cx/hash_map.h> 47 #include <cx/linked_list.h> 48 #include <cx/compare.h> 49 50 static int is_initialized = 0; 51 52 static int log_file_fd; 53 static int log_level = 0; 54 55 static uint32_t log_dup_count = 0; 56 static CxList *log_dup_list = NULL; 57 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 58 59 WSBool main_is_daemon(void); 60 61 /* 62 * if the log file is uninitialized, output is written to the ui_buffer 63 */ 64 static sbuf_t *ui_buffer = NULL; 65 66 /* 67 * access log file map 68 */ 69 static CxMap *access_log_files; // map of LogFile* 70 71 72 static char *log_date_month[] = { 73 "Jan", 74 "Feb", 75 "Mar", 76 "Apr", 77 "May", 78 "Jun", 79 "Jul", 80 "Aug", 81 "Sep", 82 "Oct", 83 "Nov", 84 "Dec" 85 }; 86 87 static const char *log_levels[] = { 88 "warning", 89 "config", 90 "security", 91 "failure", 92 "catastrophe", 93 "info", 94 "verbose", 95 "debug" 96 }; 97 98 static int can_log[] = { 99 1, // warning 100 1, // config 101 1, // security 102 1, // failure 103 1, // catastrophe 104 1, // info 105 0, // verbose 106 0 // debug 107 }; 108 109 int init_logging(void) { 110 log_dup_list = cxLinkedListCreate(cxDefaultAllocator, NULL, CX_STORE_POINTERS); 111 return log_dup_list == NULL; 112 } 113 114 int init_log_file(LogConfig *cfg) { 115 if(is_initialized) { 116 return 0; 117 } 118 119 /* open the log file */ 120 mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; 121 log_file_fd = open(cfg->file, O_WRONLY | O_CREAT | O_APPEND, mode); 122 if(log_file_fd == -1) { 123 log_ereport(LOG_FAILURE, "Cannot open log file %s: %s", cfg->file, strerror(errno)); 124 return -1; 125 } 126 127 if(!strcmp(cfg->level, "ERROR")) { 128 can_log[LOG_WARN] = 0; 129 can_log[LOG_INFORM] = 0; 130 } else if(!strcmp(cfg->level, "WARNING")) { 131 can_log[LOG_INFORM] = 0; 132 } else if(!strcmp(cfg->level, "VERBOSE")) { 133 can_log[LOG_VERBOSE] = 1; 134 } else if(!strcmp(cfg->level, "DEBUG")) { 135 can_log[LOG_VERBOSE] = 1; 136 can_log[LOG_DEBUG] = 1; 137 } 138 139 if(cfg->log_stdout) { 140 // TODO 141 } 142 if(cfg->log_stderr) { 143 // TODO 144 } 145 146 147 is_initialized = 1; 148 149 /* if ui_buffer is not NULL, write it to the log file */ 150 if(ui_buffer) { 151 size_t len = ui_buffer->length; 152 size_t r; 153 while(len > 0) { 154 r = write(log_file_fd, ui_buffer->ptr, ui_buffer->length); 155 len -= r; 156 } 157 158 sbuf_free(ui_buffer); 159 } 160 161 return 0; 162 } 163 164 void log_uninitialized_writeln(char *str, size_t len) { 165 if(ui_buffer == NULL) { 166 ui_buffer = sbuf_new(1024); 167 if(ui_buffer == NULL) { 168 return; /* TODO: critical error, exit */ 169 } 170 } 171 172 cxstring s; 173 s.ptr = str; 174 s.length = len; 175 176 sbuf_append(ui_buffer, s); 177 sbuf_put(ui_buffer, '\n'); 178 } 179 180 void log_file_writeln(char *str, size_t len) { 181 struct iovec io[] = { 182 { str, len }, 183 { "\n", 1} 184 }; 185 186 WSBool write_to_stdout = !main_is_daemon(); 187 if(is_initialized) { 188 writev(log_file_fd, io, 2); /* TODO: aio? */ 189 } else { 190 //write_to_stdout = TRUE; 191 log_uninitialized_writeln(str, len); 192 } 193 194 if(write_to_stdout) { 195 writev(STDOUT_FILENO, io, 2); 196 } 197 198 if(log_dup_count > 0) { 199 char *msg = malloc(len + 1); 200 memcpy(msg, str, len); 201 msg[len] = '\n'; 202 203 pthread_mutex_lock(&mutex); 204 CxIterator i = cxListIterator(log_dup_list); 205 cx_foreach(LogDup *, dup, i) { 206 dup->write(dup->cookie, msg, len + 1); 207 } 208 pthread_mutex_unlock(&mutex); 209 210 free(msg); 211 } 212 } 213 214 cxmutstr log_get_prefix_str(const char *level) { 215 time_t t = time(NULL); 216 217 cxmutstr d; 218 d.ptr = NULL; 219 d.length = 0; 220 221 struct tm date; 222 localtime_r(&t, &date); 223 224 char *buf = malloc(64); 225 int len = snprintf( 226 buf, 227 64, 228 "[%02d/%s/%d:%02d:%02d:%02d] %s : ", 229 date.tm_mday, 230 log_date_month[date.tm_mon], 231 1900 + date.tm_year, 232 date.tm_hour, 233 date.tm_min, 234 date.tm_sec, 235 level); 236 237 if(len > 0) { 238 d.ptr = buf; 239 d.length = len; 240 } 241 242 return d; 243 } 244 245 cxmutstr log_get_prefix(int level) { 246 return log_get_prefix_str(log_levels[level]); 247 } 248 249 void log_add_logdup(LogDup *dup) { 250 pthread_mutex_lock(&mutex); 251 cxListAdd(log_dup_list, dup); 252 ws_atomic_inc32(&log_dup_count); 253 pthread_mutex_unlock(&mutex); 254 } 255 256 void log_remove_logdup(LogDup *ldup) { 257 pthread_mutex_lock(&mutex); 258 CxMutIterator i = cxListMutIterator(log_dup_list); 259 WSBool finished = 0; 260 cx_foreach(LogDup *, dup, i) { 261 if(finished) break; 262 if(dup == ldup) { 263 cxIteratorFlagRemoval(i); 264 finished = 1; 265 ws_atomic_dec32(&log_dup_count); 266 } 267 } 268 pthread_mutex_unlock(&mutex); 269 } 270 271 272 /* 273 * log api functions 274 */ 275 276 int log_ereport(int degree, const char *format, ...) { 277 va_list args; 278 va_start(args, format); 279 int ret = log_ereport_v(degree, format, args); 280 va_end(args); 281 return ret; 282 } 283 284 int log_ereport_v(int degree, const char *format, va_list args) { 285 if(degree < 0 || degree > 7) { 286 return 0; 287 } 288 if(!can_log[degree]) { 289 return 0; 290 } 291 292 cxmutstr lmsg; 293 lmsg.ptr = NULL; 294 295 /* create log message prefix */ 296 cxmutstr lpre = log_get_prefix(degree); 297 298 /* format message */ 299 int len = vasprintf(&lmsg.ptr, format, args); 300 lmsg.length = len; 301 302 /* create message string */ 303 cxmutstr message = cx_strcat(2, lpre, lmsg); 304 305 /* write message to the log file */ 306 log_file_writeln(message.ptr, message.length); 307 308 /* cleanup */ 309 free(lmsg.ptr); 310 free(lpre.ptr); 311 free(message.ptr); 312 313 return 0; 314 } 315 316 int log_error(int degree, const char *func, Session *sn, Request *rq, 317 const char *format, ...) 318 { 319 va_list args; 320 va_start(args, format); 321 int ret = log_error_v(degree, func, sn, rq, format, args); 322 va_end(args); 323 return ret; 324 } 325 326 int log_error_v(int degree, const char *func, Session *sn, Request *rq, 327 const char *format, va_list args) 328 { 329 // TODO: implement 330 return log_ereport(degree, format, args); 331 } 332 333 int log_message(const char *degree, const char *format, ...) { 334 va_list args; 335 va_start(args, format); 336 int ret = log_message_v(degree, format, args); 337 va_end(args); 338 return ret; 339 } 340 341 int log_message_v(const char *degree, const char *format, va_list args) { 342 cxmutstr lmsg; 343 lmsg.ptr = NULL; 344 345 cxmutstr lpre = log_get_prefix_str(degree); 346 347 /* format message */ 348 int len = vasprintf(&lmsg.ptr, format, args); 349 lmsg.length = len; 350 351 /* create message string */ 352 cxmutstr message = cx_strcat(2, lpre, lmsg); 353 354 /* write message to the log file */ 355 log_file_writeln(message.ptr, message.length); 356 357 /* cleanup */ 358 free(lmsg.ptr); 359 free(lpre.ptr); 360 free(message.ptr); 361 362 return 0; 363 } 364 365 366 void ws_log_assert(const char *file, const char *func, int line) { 367 log_ereport( 368 LOG_CATASTROPHE, 369 "assertion failed: %s: %s:%d", 370 file, 371 func, 372 line); 373 exit(-1); 374 } 375 376 377 /* 378 * access log 379 * This source file only manages access log files. IO is performed directly 380 * by AddLog safs. 381 */ 382 LogFile* get_access_log_file(cxstring file) { 383 // TODO: this looks dubious 384 385 if(!access_log_files) { 386 access_log_files = cxHashMapCreate(cxDefaultAllocator, CX_STORE_POINTERS, 4); 387 } 388 389 if(file.ptr == NULL || file.length == 0) { 390 return NULL; 391 } 392 393 CxHashKey key = cx_hash_key_bytes((unsigned const char*)file.ptr, file.length); 394 LogFile *log = cxMapGet(access_log_files, key); 395 if(log != NULL) { 396 ws_atomic_inc32(&log->ref); 397 return log; 398 } 399 400 // the log file is not opened 401 // check first if we can open it 402 FILE *out = fopen(file.ptr, "a"); 403 if(out == NULL) { 404 return NULL; 405 } 406 407 // create LogFile object 408 log = calloc(1, sizeof(LogFile)); 409 log->file = out; 410 log->ref = 1; 411 412 // add access log to the map 413 cxMapPut(access_log_files, key, log); 414 415 return log; 416 } 417 418