Annotation of embedaddon/lighttpd/src/log.c, revision 1.1
1.1 ! misho 1: #include "base.h"
! 2: #include "log.h"
! 3: #include "array.h"
! 4:
! 5: #include <sys/types.h>
! 6:
! 7: #include <errno.h>
! 8: #include <fcntl.h>
! 9: #include <time.h>
! 10: #include <unistd.h>
! 11: #include <string.h>
! 12: #include <stdlib.h>
! 13:
! 14: #include <stdarg.h>
! 15: #include <stdio.h>
! 16:
! 17: #ifdef HAVE_SYSLOG_H
! 18: # include <syslog.h>
! 19: #endif
! 20:
! 21: #ifdef HAVE_VALGRIND_VALGRIND_H
! 22: # include <valgrind/valgrind.h>
! 23: #endif
! 24:
! 25: #ifndef O_LARGEFILE
! 26: # define O_LARGEFILE 0
! 27: #endif
! 28:
! 29: /* Close fd and _try_ to get a /dev/null for it instead.
! 30: * close() alone may trigger some bugs when a
! 31: * process opens another file and gets fd = STDOUT_FILENO or STDERR_FILENO
! 32: * and later tries to just print on stdout/stderr
! 33: *
! 34: * Returns 0 on success and -1 on failure (fd gets closed in all cases)
! 35: */
! 36: int openDevNull(int fd) {
! 37: int tmpfd;
! 38: close(fd);
! 39: #if defined(__WIN32)
! 40: /* Cygwin should work with /dev/null */
! 41: tmpfd = open("nul", O_RDWR);
! 42: #else
! 43: tmpfd = open("/dev/null", O_RDWR);
! 44: #endif
! 45: if (tmpfd != -1 && tmpfd != fd) {
! 46: dup2(tmpfd, fd);
! 47: close(tmpfd);
! 48: }
! 49: return (tmpfd != -1) ? 0 : -1;
! 50: }
! 51:
! 52: int open_logfile_or_pipe(server *srv, const char* logfile) {
! 53: int fd;
! 54:
! 55: if (logfile[0] == '|') {
! 56: #ifdef HAVE_FORK
! 57: /* create write pipe and spawn process */
! 58:
! 59: int to_log_fds[2];
! 60:
! 61: if (pipe(to_log_fds)) {
! 62: log_error_write(srv, __FILE__, __LINE__, "ss", "pipe failed: ", strerror(errno));
! 63: return -1;
! 64: }
! 65:
! 66: /* fork, execve */
! 67: switch (fork()) {
! 68: case 0:
! 69: /* child */
! 70: close(STDIN_FILENO);
! 71:
! 72: /* dup the filehandle to STDIN */
! 73: if (to_log_fds[0] != STDIN_FILENO) {
! 74: if (STDIN_FILENO != dup2(to_log_fds[0], STDIN_FILENO)) {
! 75: log_error_write(srv, __FILE__, __LINE__, "ss",
! 76: "dup2 failed: ", strerror(errno));
! 77: exit(-1);
! 78: }
! 79: close(to_log_fds[0]);
! 80: }
! 81: close(to_log_fds[1]);
! 82:
! 83: #ifndef FD_CLOEXEC
! 84: {
! 85: int i;
! 86: /* we don't need the client socket */
! 87: for (i = 3; i < 256; i++) {
! 88: close(i);
! 89: }
! 90: }
! 91: #endif
! 92:
! 93: /* close old stderr */
! 94: openDevNull(STDERR_FILENO);
! 95:
! 96: /* exec the log-process (skip the | ) */
! 97: execl("/bin/sh", "sh", "-c", logfile + 1, NULL);
! 98: log_error_write(srv, __FILE__, __LINE__, "sss",
! 99: "spawning log process failed: ", strerror(errno),
! 100: logfile + 1);
! 101:
! 102: exit(-1);
! 103: break;
! 104: case -1:
! 105: /* error */
! 106: log_error_write(srv, __FILE__, __LINE__, "ss", "fork failed: ", strerror(errno));
! 107: return -1;
! 108: default:
! 109: close(to_log_fds[0]);
! 110: fd = to_log_fds[1];
! 111: break;
! 112: }
! 113:
! 114: #else
! 115: return -1;
! 116: #endif
! 117: } else if (-1 == (fd = open(logfile, O_APPEND | O_WRONLY | O_CREAT | O_LARGEFILE, 0644))) {
! 118: log_error_write(srv, __FILE__, __LINE__, "SSSS",
! 119: "opening errorlog '", logfile,
! 120: "' failed: ", strerror(errno));
! 121:
! 122: return -1;
! 123: }
! 124:
! 125: #ifdef FD_CLOEXEC
! 126: fcntl(fd, F_SETFD, FD_CLOEXEC);
! 127: #endif
! 128:
! 129: return fd;
! 130: }
! 131:
! 132:
! 133: /**
! 134: * open the errorlog
! 135: *
! 136: * we have 4 possibilities:
! 137: * - stderr (default)
! 138: * - syslog
! 139: * - logfile
! 140: * - pipe
! 141: *
! 142: * if the open failed, report to the user and die
! 143: *
! 144: */
! 145:
! 146: int log_error_open(server *srv) {
! 147: #ifdef HAVE_SYSLOG_H
! 148: /* perhaps someone wants to use syslog() */
! 149: openlog("lighttpd", LOG_CONS | LOG_PID, LOG_DAEMON);
! 150: #endif
! 151:
! 152: srv->errorlog_mode = ERRORLOG_FD;
! 153: srv->errorlog_fd = STDERR_FILENO;
! 154:
! 155: if (srv->srvconf.errorlog_use_syslog) {
! 156: srv->errorlog_mode = ERRORLOG_SYSLOG;
! 157: } else if (!buffer_is_empty(srv->srvconf.errorlog_file)) {
! 158: const char *logfile = srv->srvconf.errorlog_file->ptr;
! 159:
! 160: if (-1 == (srv->errorlog_fd = open_logfile_or_pipe(srv, logfile))) {
! 161: return -1;
! 162: }
! 163: srv->errorlog_mode = (logfile[0] == '|') ? ERRORLOG_PIPE : ERRORLOG_FILE;
! 164: }
! 165:
! 166: log_error_write(srv, __FILE__, __LINE__, "s", "server started");
! 167:
! 168: if (srv->errorlog_mode == ERRORLOG_FD && !srv->srvconf.dont_daemonize) {
! 169: /* We can only log to stderr in dont-daemonize mode;
! 170: * if we do daemonize and no errorlog file is specified, we log into /dev/null
! 171: */
! 172: srv->errorlog_fd = -1;
! 173: }
! 174:
! 175: if (!buffer_is_empty(srv->srvconf.breakagelog_file)) {
! 176: int breakage_fd;
! 177: const char *logfile = srv->srvconf.breakagelog_file->ptr;
! 178:
! 179: if (srv->errorlog_mode == ERRORLOG_FD) {
! 180: srv->errorlog_fd = dup(STDERR_FILENO);
! 181: #ifdef FD_CLOEXEC
! 182: fcntl(srv->errorlog_fd, F_SETFD, FD_CLOEXEC);
! 183: #endif
! 184: }
! 185:
! 186: if (-1 == (breakage_fd = open_logfile_or_pipe(srv, logfile))) {
! 187: return -1;
! 188: }
! 189:
! 190: if (STDERR_FILENO != breakage_fd) {
! 191: dup2(breakage_fd, STDERR_FILENO);
! 192: close(breakage_fd);
! 193: }
! 194: } else if (!srv->srvconf.dont_daemonize) {
! 195: /* move stderr to /dev/null */
! 196: openDevNull(STDERR_FILENO);
! 197: }
! 198: return 0;
! 199: }
! 200:
! 201: /**
! 202: * open the errorlog
! 203: *
! 204: * if the open failed, report to the user and die
! 205: * if no filename is given, use syslog instead
! 206: *
! 207: */
! 208:
! 209: int log_error_cycle(server *srv) {
! 210: /* only cycle if the error log is a file */
! 211:
! 212: if (srv->errorlog_mode == ERRORLOG_FILE) {
! 213: const char *logfile = srv->srvconf.errorlog_file->ptr;
! 214: /* already check of opening time */
! 215:
! 216: int new_fd;
! 217:
! 218: if (-1 == (new_fd = open_logfile_or_pipe(srv, logfile))) {
! 219: /* write to old log */
! 220: log_error_write(srv, __FILE__, __LINE__, "SSSSS",
! 221: "cycling errorlog '", logfile,
! 222: "' failed: ", strerror(errno),
! 223: ", falling back to syslog()");
! 224:
! 225: close(srv->errorlog_fd);
! 226: srv->errorlog_fd = -1;
! 227: #ifdef HAVE_SYSLOG_H
! 228: srv->errorlog_mode = ERRORLOG_SYSLOG;
! 229: #endif
! 230: } else {
! 231: /* ok, new log is open, close the old one */
! 232: close(srv->errorlog_fd);
! 233: srv->errorlog_fd = new_fd;
! 234: #ifdef FD_CLOEXEC
! 235: /* close fd on exec (cgi) */
! 236: fcntl(srv->errorlog_fd, F_SETFD, FD_CLOEXEC);
! 237: #endif
! 238: }
! 239: }
! 240:
! 241: return 0;
! 242: }
! 243:
! 244: int log_error_close(server *srv) {
! 245: switch(srv->errorlog_mode) {
! 246: case ERRORLOG_PIPE:
! 247: case ERRORLOG_FILE:
! 248: case ERRORLOG_FD:
! 249: if (-1 != srv->errorlog_fd) {
! 250: /* don't close STDERR */
! 251: if (STDERR_FILENO != srv->errorlog_fd)
! 252: close(srv->errorlog_fd);
! 253: srv->errorlog_fd = -1;
! 254: }
! 255: break;
! 256: case ERRORLOG_SYSLOG:
! 257: #ifdef HAVE_SYSLOG_H
! 258: closelog();
! 259: #endif
! 260: break;
! 261: }
! 262:
! 263: return 0;
! 264: }
! 265:
! 266: /* lowercase: append space, uppercase: don't */
! 267: static void log_buffer_append_printf(buffer *out, const char *fmt, va_list ap) {
! 268: for(; *fmt; fmt++) {
! 269: int d;
! 270: char *s;
! 271: buffer *b;
! 272: off_t o;
! 273:
! 274: switch(*fmt) {
! 275: case 's': /* string */
! 276: s = va_arg(ap, char *);
! 277: buffer_append_string(out, s);
! 278: buffer_append_string_len(out, CONST_STR_LEN(" "));
! 279: break;
! 280: case 'b': /* buffer */
! 281: b = va_arg(ap, buffer *);
! 282: buffer_append_string_buffer(out, b);
! 283: buffer_append_string_len(out, CONST_STR_LEN(" "));
! 284: break;
! 285: case 'd': /* int */
! 286: d = va_arg(ap, int);
! 287: buffer_append_long(out, d);
! 288: buffer_append_string_len(out, CONST_STR_LEN(" "));
! 289: break;
! 290: case 'o': /* off_t */
! 291: o = va_arg(ap, off_t);
! 292: buffer_append_off_t(out, o);
! 293: buffer_append_string_len(out, CONST_STR_LEN(" "));
! 294: break;
! 295: case 'x': /* int (hex) */
! 296: d = va_arg(ap, int);
! 297: buffer_append_string_len(out, CONST_STR_LEN("0x"));
! 298: buffer_append_long_hex(out, d);
! 299: buffer_append_string_len(out, CONST_STR_LEN(" "));
! 300: break;
! 301: case 'S': /* string */
! 302: s = va_arg(ap, char *);
! 303: buffer_append_string(out, s);
! 304: break;
! 305: case 'B': /* buffer */
! 306: b = va_arg(ap, buffer *);
! 307: buffer_append_string_buffer(out, b);
! 308: break;
! 309: case 'D': /* int */
! 310: d = va_arg(ap, int);
! 311: buffer_append_long(out, d);
! 312: break;
! 313: case 'O': /* off_t */
! 314: o = va_arg(ap, off_t);
! 315: buffer_append_off_t(out, o);
! 316: break;
! 317: case 'X': /* int (hex) */
! 318: d = va_arg(ap, int);
! 319: buffer_append_string_len(out, CONST_STR_LEN("0x"));
! 320: buffer_append_long_hex(out, d);
! 321: break;
! 322: case '(':
! 323: case ')':
! 324: case '<':
! 325: case '>':
! 326: case ',':
! 327: case ' ':
! 328: buffer_append_string_len(out, fmt, 1);
! 329: break;
! 330: }
! 331: }
! 332: }
! 333:
! 334: static int log_buffer_prepare(buffer *b, server *srv, const char *filename, unsigned int line) {
! 335: switch(srv->errorlog_mode) {
! 336: case ERRORLOG_PIPE:
! 337: case ERRORLOG_FILE:
! 338: case ERRORLOG_FD:
! 339: if (-1 == srv->errorlog_fd) return -1;
! 340: /* cache the generated timestamp */
! 341: if (srv->cur_ts != srv->last_generated_debug_ts) {
! 342: buffer_prepare_copy(srv->ts_debug_str, 255);
! 343: strftime(srv->ts_debug_str->ptr, srv->ts_debug_str->size - 1, "%Y-%m-%d %H:%M:%S", localtime(&(srv->cur_ts)));
! 344: srv->ts_debug_str->used = strlen(srv->ts_debug_str->ptr) + 1;
! 345:
! 346: srv->last_generated_debug_ts = srv->cur_ts;
! 347: }
! 348:
! 349: buffer_copy_string_buffer(b, srv->ts_debug_str);
! 350: buffer_append_string_len(b, CONST_STR_LEN(": ("));
! 351: break;
! 352: case ERRORLOG_SYSLOG:
! 353: /* syslog is generating its own timestamps */
! 354: buffer_copy_string_len(b, CONST_STR_LEN("("));
! 355: break;
! 356: }
! 357:
! 358: buffer_append_string(b, filename);
! 359: buffer_append_string_len(b, CONST_STR_LEN("."));
! 360: buffer_append_long(b, line);
! 361: buffer_append_string_len(b, CONST_STR_LEN(") "));
! 362:
! 363: return 0;
! 364: }
! 365:
! 366: static void log_write(server *srv, buffer *b) {
! 367: switch(srv->errorlog_mode) {
! 368: case ERRORLOG_PIPE:
! 369: case ERRORLOG_FILE:
! 370: case ERRORLOG_FD:
! 371: buffer_append_string_len(b, CONST_STR_LEN("\n"));
! 372: write(srv->errorlog_fd, b->ptr, b->used - 1);
! 373: break;
! 374: case ERRORLOG_SYSLOG:
! 375: syslog(LOG_ERR, "%s", b->ptr);
! 376: break;
! 377: }
! 378: }
! 379:
! 380: int log_error_write(server *srv, const char *filename, unsigned int line, const char *fmt, ...) {
! 381: va_list ap;
! 382:
! 383: if (-1 == log_buffer_prepare(srv->errorlog_buf, srv, filename, line)) return 0;
! 384:
! 385: va_start(ap, fmt);
! 386: log_buffer_append_printf(srv->errorlog_buf, fmt, ap);
! 387: va_end(ap);
! 388:
! 389: log_write(srv, srv->errorlog_buf);
! 390:
! 391: return 0;
! 392: }
! 393:
! 394: int log_error_write_multiline_buffer(server *srv, const char *filename, unsigned int line, buffer *multiline, const char *fmt, ...) {
! 395: va_list ap;
! 396: size_t prefix_used;
! 397: buffer *b = srv->errorlog_buf;
! 398: char *pos, *end, *current_line;
! 399:
! 400: if (multiline->used < 2) return 0;
! 401:
! 402: if (-1 == log_buffer_prepare(b, srv, filename, line)) return 0;
! 403:
! 404: va_start(ap, fmt);
! 405: log_buffer_append_printf(b, fmt, ap);
! 406: va_end(ap);
! 407:
! 408: prefix_used = b->used;
! 409:
! 410: current_line = pos = multiline->ptr;
! 411: end = multiline->ptr + multiline->used;
! 412:
! 413: for ( ; pos < end ; ++pos) {
! 414: switch (*pos) {
! 415: case '\n':
! 416: case '\r':
! 417: case '\0': /* handles end of string */
! 418: if (current_line < pos) {
! 419: /* truncate to prefix */
! 420: b->used = prefix_used;
! 421: b->ptr[b->used - 1] = '\0';
! 422:
! 423: buffer_append_string_len(b, current_line, pos - current_line);
! 424: log_write(srv, b);
! 425: }
! 426: current_line = pos + 1;
! 427: break;
! 428: default:
! 429: break;
! 430: }
! 431: }
! 432:
! 433: return 0;
! 434: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>