1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 
11 
12 static char *ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
13 static char *ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log);
14 static void ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log);
15 
16 
17 #if (NGX_DEBUG)
18 
19 static void ngx_log_memory_writer(ngx_log_t *log, ngx_uint_t level,
20     u_char *buf, size_t len);
21 static void ngx_log_memory_cleanup(void *data);
22 
23 
24 typedef struct {
25     u_char        *start;
26     u_char        *end;
27     u_char        *pos;
28     ngx_atomic_t   written;
29 } ngx_log_memory_buf_t;
30 
31 #endif
32 
33 
34 static ngx_command_t  ngx_errlog_commands[] = {
35 
36     { ngx_string("error_log"),
37       NGX_MAIN_CONF|NGX_CONF_1MORE,
38       ngx_error_log,
39       0,
40       0,
41       NULL },
42 
43       ngx_null_command
44 };
45 
46 
47 static ngx_core_module_t  ngx_errlog_module_ctx = {
48     ngx_string("errlog"),
49     NULL,
50     NULL
51 };
52 
53 
54 ngx_module_t  ngx_errlog_module = {
55     NGX_MODULE_V1,
56     &ngx_errlog_module_ctx,                /* module context */
57     ngx_errlog_commands,                   /* module directives */
58     NGX_CORE_MODULE,                       /* module type */
59     NULL,                                  /* init master */
60     NULL,                                  /* init module */
61     NULL,                                  /* init process */
62     NULL,                                  /* init thread */
63     NULL,                                  /* exit thread */
64     NULL,                                  /* exit process */
65     NULL,                                  /* exit master */
66     NGX_MODULE_V1_PADDING
67 };
68 
69 
70 static ngx_log_t        ngx_log;
71 static ngx_open_file_t  ngx_log_file;
72 ngx_uint_t              ngx_use_stderr = 1;
73 
74 
75 static ngx_str_t err_levels[] = {
76     ngx_null_string,
77     ngx_string("emerg"),
78     ngx_string("alert"),
79     ngx_string("crit"),
80     ngx_string("error"),
81     ngx_string("warn"),
82     ngx_string("notice"),
83     ngx_string("info"),
84     ngx_string("debug")
85 };
86 
87 static const char *debug_levels[] = {
88     "debug_core", "debug_alloc", "debug_mutex", "debug_event",
89     "debug_http", "debug_mail", "debug_stream"
90 };
91 
92 
93 #if (NGX_HAVE_VARIADIC_MACROS)
94 
95 void
ngx_log_error_core(ngx_uint_t level,ngx_log_t * log,ngx_err_t err,const char * fmt,...)96 ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
97     const char *fmt, ...)
98 
99 #else
100 
101 void
102 ngx_log_error_core(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
103     const char *fmt, va_list args)
104 
105 #endif
106 {
107 #if (NGX_HAVE_VARIADIC_MACROS)
108     va_list      args;
109 #endif
110     u_char      *p, *last, *msg;
111     ssize_t      n;
112     ngx_uint_t   wrote_stderr, debug_connection;
113     u_char       errstr[NGX_MAX_ERROR_STR];
114 
115     last = errstr + NGX_MAX_ERROR_STR;
116 
117     p = ngx_cpymem(errstr, ngx_cached_err_log_time.data,
118                    ngx_cached_err_log_time.len);
119 
120     p = ngx_slprintf(p, last, " [%V] ", &err_levels[level]);
121 
122     /* pid#tid */
123     p = ngx_slprintf(p, last, "%P#" NGX_TID_T_FMT ": ",
124                     ngx_log_pid, ngx_log_tid);
125 
126     if (log->connection) {
127         p = ngx_slprintf(p, last, "*%uA ", log->connection);
128     }
129 
130     msg = p;
131 
132 #if (NGX_HAVE_VARIADIC_MACROS)
133 
134     va_start(args, fmt);
135     p = ngx_vslprintf(p, last, fmt, args);
136     va_end(args);
137 
138 #else
139 
140     p = ngx_vslprintf(p, last, fmt, args);
141 
142 #endif
143 
144     if (err) {
145         p = ngx_log_errno(p, last, err);
146     }
147 
148     if (level != NGX_LOG_DEBUG && log->handler) {
149         p = log->handler(log, p, last - p);
150     }
151 
152     if (p > last - NGX_LINEFEED_SIZE) {
153         p = last - NGX_LINEFEED_SIZE;
154     }
155 
156     ngx_linefeed(p);
157 
158     wrote_stderr = 0;
159     debug_connection = (log->log_level & NGX_LOG_DEBUG_CONNECTION) != 0;
160 
161     while (log) {
162 
163         if (log->log_level < level && !debug_connection) {
164             break;
165         }
166 
167         if (log->writer) {
168             log->writer(log, level, errstr, p - errstr);
169             goto next;
170         }
171 
172         if (ngx_time() == log->disk_full_time) {
173 
174             /*
175              * on FreeBSD writing to a full filesystem with enabled softupdates
176              * may block process for much longer time than writing to non-full
177              * filesystem, so we skip writing to a log for one second
178              */
179 
180             goto next;
181         }
182 
183         n = ngx_write_fd(log->file->fd, errstr, p - errstr);
184 
185         if (n == -1 && ngx_errno == NGX_ENOSPC) {
186             log->disk_full_time = ngx_time();
187         }
188 
189         if (log->file->fd == ngx_stderr) {
190             wrote_stderr = 1;
191         }
192 
193     next:
194 
195         log = log->next;
196     }
197 
198     if (!ngx_use_stderr
199         || level > NGX_LOG_WARN
200         || wrote_stderr)
201     {
202         return;
203     }
204 
205     msg -= (7 + err_levels[level].len + 3);
206 
207     (void) ngx_sprintf(msg, "nginx: [%V] ", &err_levels[level]);
208 
209     (void) ngx_write_console(ngx_stderr, msg, p - msg);
210 }
211 
212 
213 #if !(NGX_HAVE_VARIADIC_MACROS)
214 
215 void ngx_cdecl
ngx_log_error(ngx_uint_t level,ngx_log_t * log,ngx_err_t err,const char * fmt,...)216 ngx_log_error(ngx_uint_t level, ngx_log_t *log, ngx_err_t err,
217     const char *fmt, ...)
218 {
219     va_list  args;
220 
221     if (log->log_level >= level) {
222         va_start(args, fmt);
223         ngx_log_error_core(level, log, err, fmt, args);
224         va_end(args);
225     }
226 }
227 
228 
229 void ngx_cdecl
ngx_log_debug_core(ngx_log_t * log,ngx_err_t err,const char * fmt,...)230 ngx_log_debug_core(ngx_log_t *log, ngx_err_t err, const char *fmt, ...)
231 {
232     va_list  args;
233 
234     va_start(args, fmt);
235     ngx_log_error_core(NGX_LOG_DEBUG, log, err, fmt, args);
236     va_end(args);
237 }
238 
239 #endif
240 
241 
242 void ngx_cdecl
ngx_log_abort(ngx_err_t err,const char * fmt,...)243 ngx_log_abort(ngx_err_t err, const char *fmt, ...)
244 {
245     u_char   *p;
246     va_list   args;
247     u_char    errstr[NGX_MAX_CONF_ERRSTR];
248 
249     va_start(args, fmt);
250     p = ngx_vsnprintf(errstr, sizeof(errstr) - 1, fmt, args);
251     va_end(args);
252 
253     ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err,
254                   "%*s", p - errstr, errstr);
255 }
256 
257 
258 void ngx_cdecl
ngx_log_stderr(ngx_err_t err,const char * fmt,...)259 ngx_log_stderr(ngx_err_t err, const char *fmt, ...)
260 {
261     u_char   *p, *last;
262     va_list   args;
263     u_char    errstr[NGX_MAX_ERROR_STR];
264 
265     last = errstr + NGX_MAX_ERROR_STR;
266 
267     p = ngx_cpymem(errstr, "nginx: ", 7);
268 
269     va_start(args, fmt);
270     p = ngx_vslprintf(p, last, fmt, args);
271     va_end(args);
272 
273     if (err) {
274         p = ngx_log_errno(p, last, err);
275     }
276 
277     if (p > last - NGX_LINEFEED_SIZE) {
278         p = last - NGX_LINEFEED_SIZE;
279     }
280 
281     ngx_linefeed(p);
282 
283     (void) ngx_write_console(ngx_stderr, errstr, p - errstr);
284 }
285 
286 
287 u_char *
ngx_log_errno(u_char * buf,u_char * last,ngx_err_t err)288 ngx_log_errno(u_char *buf, u_char *last, ngx_err_t err)
289 {
290     if (buf > last - 50) {
291 
292         /* leave a space for an error code */
293 
294         buf = last - 50;
295         *buf++ = '.';
296         *buf++ = '.';
297         *buf++ = '.';
298     }
299 
300 #if (NGX_WIN32)
301     buf = ngx_slprintf(buf, last, ((unsigned) err < 0x80000000)
302                                        ? " (%d: " : " (%Xd: ", err);
303 #else
304     buf = ngx_slprintf(buf, last, " (%d: ", err);
305 #endif
306 
307     buf = ngx_strerror(err, buf, last - buf);
308 
309     if (buf < last) {
310         *buf++ = ')';
311     }
312 
313     return buf;
314 }
315 
316 
317 ngx_log_t *
ngx_log_init(u_char * prefix,u_char * error_log)318 ngx_log_init(u_char *prefix, u_char *error_log)
319 {
320     u_char  *p, *name;
321     size_t   nlen, plen;
322 
323     ngx_log.file = &ngx_log_file;
324     ngx_log.log_level = NGX_LOG_NOTICE;
325 
326     if (error_log == NULL) {
327         error_log = (u_char *) NGX_ERROR_LOG_PATH;
328     }
329 
330     name = error_log;
331     nlen = ngx_strlen(name);
332 
333     if (nlen == 0) {
334         ngx_log_file.fd = ngx_stderr;
335         return &ngx_log;
336     }
337 
338     p = NULL;
339 
340 #if (NGX_WIN32)
341     if (name[1] != ':') {
342 #else
343     if (name[0] != '/') {
344 #endif
345 
346         if (prefix) {
347             plen = ngx_strlen(prefix);
348 
349         } else {
350 #ifdef NGX_PREFIX
351             prefix = (u_char *) NGX_PREFIX;
352             plen = ngx_strlen(prefix);
353 #else
354             plen = 0;
355 #endif
356         }
357 
358         if (plen) {
359             name = malloc(plen + nlen + 2);
360             if (name == NULL) {
361                 return NULL;
362             }
363 
364             p = ngx_cpymem(name, prefix, plen);
365 
366             if (!ngx_path_separator(*(p - 1))) {
367                 *p++ = '/';
368             }
369 
370             ngx_cpystrn(p, error_log, nlen + 1);
371 
372             p = name;
373         }
374     }
375 
376     ngx_log_file.fd = ngx_open_file(name, NGX_FILE_APPEND,
377                                     NGX_FILE_CREATE_OR_OPEN,
378                                     NGX_FILE_DEFAULT_ACCESS);
379 
380     if (ngx_log_file.fd == NGX_INVALID_FILE) {
381         ngx_log_stderr(ngx_errno,
382                        "[alert] could not open error log file: "
383                        ngx_open_file_n " \"%s\" failed", name);
384 #if (NGX_WIN32)
385         ngx_event_log(ngx_errno,
386                        "could not open error log file: "
387                        ngx_open_file_n " \"%s\" failed", name);
388 #endif
389 
390         ngx_log_file.fd = ngx_stderr;
391     }
392 
393     if (p) {
394         ngx_free(p);
395     }
396 
397     return &ngx_log;
398 }
399 
400 
401 ngx_int_t
402 ngx_log_open_default(ngx_cycle_t *cycle)
403 {
404     ngx_log_t  *log;
405 
406     if (ngx_log_get_file_log(&cycle->new_log) != NULL) {
407         return NGX_OK;
408     }
409 
410     if (cycle->new_log.log_level != 0) {
411         /* there are some error logs, but no files */
412 
413         log = ngx_pcalloc(cycle->pool, sizeof(ngx_log_t));
414         if (log == NULL) {
415             return NGX_ERROR;
416         }
417 
418     } else {
419         /* no error logs at all */
420         log = &cycle->new_log;
421     }
422 
423     log->log_level = NGX_LOG_ERR;
424 
425     log->file = ngx_conf_open_file(cycle, &cycle->error_log);
426     if (log->file == NULL) {
427         return NGX_ERROR;
428     }
429 
430     if (log != &cycle->new_log) {
431         ngx_log_insert(&cycle->new_log, log);
432     }
433 
434     return NGX_OK;
435 }
436 
437 
438 ngx_int_t
439 ngx_log_redirect_stderr(ngx_cycle_t *cycle)
440 {
441     ngx_fd_t  fd;
442 
443     if (cycle->log_use_stderr) {
444         return NGX_OK;
445     }
446 
447     /* file log always exists when we are called */
448     fd = ngx_log_get_file_log(cycle->log)->file->fd;
449 
450     if (fd != ngx_stderr) {
451         if (ngx_set_stderr(fd) == NGX_FILE_ERROR) {
452             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
453                           ngx_set_stderr_n " failed");
454 
455             return NGX_ERROR;
456         }
457     }
458 
459     return NGX_OK;
460 }
461 
462 
463 ngx_log_t *
464 ngx_log_get_file_log(ngx_log_t *head)
465 {
466     ngx_log_t  *log;
467 
468     for (log = head; log; log = log->next) {
469         if (log->file != NULL) {
470             return log;
471         }
472     }
473 
474     return NULL;
475 }
476 
477 
478 static char *
479 ngx_log_set_levels(ngx_conf_t *cf, ngx_log_t *log)
480 {
481     ngx_uint_t   i, n, d, found;
482     ngx_str_t   *value;
483 
484     if (cf->args->nelts == 2) {
485         log->log_level = NGX_LOG_ERR;
486         return NGX_CONF_OK;
487     }
488 
489     value = cf->args->elts;
490 
491     for (i = 2; i < cf->args->nelts; i++) {
492         found = 0;
493 
494         for (n = 1; n <= NGX_LOG_DEBUG; n++) {
495             if (ngx_strcmp(value[i].data, err_levels[n].data) == 0) {
496 
497                 if (log->log_level != 0) {
498                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
499                                        "duplicate log level \"%V\"",
500                                        &value[i]);
501                     return NGX_CONF_ERROR;
502                 }
503 
504                 log->log_level = n;
505                 found = 1;
506                 break;
507             }
508         }
509 
510         for (n = 0, d = NGX_LOG_DEBUG_FIRST; d <= NGX_LOG_DEBUG_LAST; d <<= 1) {
511             if (ngx_strcmp(value[i].data, debug_levels[n++]) == 0) {
512                 if (log->log_level & ~NGX_LOG_DEBUG_ALL) {
513                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
514                                        "invalid log level \"%V\"",
515                                        &value[i]);
516                     return NGX_CONF_ERROR;
517                 }
518 
519                 log->log_level |= d;
520                 found = 1;
521                 break;
522             }
523         }
524 
525 
526         if (!found) {
527             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
528                                "invalid log level \"%V\"", &value[i]);
529             return NGX_CONF_ERROR;
530         }
531     }
532 
533     if (log->log_level == NGX_LOG_DEBUG) {
534         log->log_level = NGX_LOG_DEBUG_ALL;
535     }
536 
537     return NGX_CONF_OK;
538 }
539 
540 
541 static char *
542 ngx_error_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
543 {
544     ngx_log_t  *dummy;
545 
546     dummy = &cf->cycle->new_log;
547 
548     return ngx_log_set_log(cf, &dummy);
549 }
550 
551 
552 char *
553 ngx_log_set_log(ngx_conf_t *cf, ngx_log_t **head)
554 {
555     ngx_log_t          *new_log;
556     ngx_str_t          *value, name;
557     ngx_syslog_peer_t  *peer;
558 
559     if (*head != NULL && (*head)->log_level == 0) {
560         new_log = *head;
561 
562     } else {
563 
564         new_log = ngx_pcalloc(cf->pool, sizeof(ngx_log_t));
565         if (new_log == NULL) {
566             return NGX_CONF_ERROR;
567         }
568 
569         if (*head == NULL) {
570             *head = new_log;
571         }
572     }
573 
574     value = cf->args->elts;
575 
576     if (ngx_strcmp(value[1].data, "stderr") == 0) {
577         ngx_str_null(&name);
578         cf->cycle->log_use_stderr = 1;
579 
580         new_log->file = ngx_conf_open_file(cf->cycle, &name);
581         if (new_log->file == NULL) {
582             return NGX_CONF_ERROR;
583         }
584 
585     } else if (ngx_strncmp(value[1].data, "memory:", 7) == 0) {
586 
587 #if (NGX_DEBUG)
588         size_t                 size, needed;
589         ngx_pool_cleanup_t    *cln;
590         ngx_log_memory_buf_t  *buf;
591 
592         value[1].len -= 7;
593         value[1].data += 7;
594 
595         needed = sizeof("MEMLOG  :" NGX_LINEFEED)
596                  + cf->conf_file->file.name.len
597                  + NGX_SIZE_T_LEN
598                  + NGX_INT_T_LEN
599                  + NGX_MAX_ERROR_STR;
600 
601         size = ngx_parse_size(&value[1]);
602 
603         if (size == (size_t) NGX_ERROR || size < needed) {
604             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
605                                "invalid buffer size \"%V\"", &value[1]);
606             return NGX_CONF_ERROR;
607         }
608 
609         buf = ngx_pcalloc(cf->pool, sizeof(ngx_log_memory_buf_t));
610         if (buf == NULL) {
611             return NGX_CONF_ERROR;
612         }
613 
614         buf->start = ngx_pnalloc(cf->pool, size);
615         if (buf->start == NULL) {
616             return NGX_CONF_ERROR;
617         }
618 
619         buf->end = buf->start + size;
620 
621         buf->pos = ngx_slprintf(buf->start, buf->end, "MEMLOG %uz %V:%ui%N",
622                                 size, &cf->conf_file->file.name,
623                                 cf->conf_file->line);
624 
625         ngx_memset(buf->pos, ' ', buf->end - buf->pos);
626 
627         cln = ngx_pool_cleanup_add(cf->pool, 0);
628         if (cln == NULL) {
629             return NGX_CONF_ERROR;
630         }
631 
632         cln->data = new_log;
633         cln->handler = ngx_log_memory_cleanup;
634 
635         new_log->writer = ngx_log_memory_writer;
636         new_log->wdata = buf;
637 
638 #else
639         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
640                            "nginx was built without debug support");
641         return NGX_CONF_ERROR;
642 #endif
643 
644     } else if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
645         peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
646         if (peer == NULL) {
647             return NGX_CONF_ERROR;
648         }
649 
650         if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
651             return NGX_CONF_ERROR;
652         }
653 
654         new_log->writer = ngx_syslog_writer;
655         new_log->wdata = peer;
656 
657     } else {
658         new_log->file = ngx_conf_open_file(cf->cycle, &value[1]);
659         if (new_log->file == NULL) {
660             return NGX_CONF_ERROR;
661         }
662     }
663 
664     if (ngx_log_set_levels(cf, new_log) != NGX_CONF_OK) {
665         return NGX_CONF_ERROR;
666     }
667 
668     if (*head != new_log) {
669         ngx_log_insert(*head, new_log);
670     }
671 
672     return NGX_CONF_OK;
673 }
674 
675 
676 static void
677 ngx_log_insert(ngx_log_t *log, ngx_log_t *new_log)
678 {
679     ngx_log_t  tmp;
680 
681     if (new_log->log_level > log->log_level) {
682 
683         /*
684          * list head address is permanent, insert new log after
685          * head and swap its contents with head
686          */
687 
688         tmp = *log;
689         *log = *new_log;
690         *new_log = tmp;
691 
692         log->next = new_log;
693         return;
694     }
695 
696     while (log->next) {
697         if (new_log->log_level > log->next->log_level) {
698             new_log->next = log->next;
699             log->next = new_log;
700             return;
701         }
702 
703         log = log->next;
704     }
705 
706     log->next = new_log;
707 }
708 
709 
710 #if (NGX_DEBUG)
711 
712 static void
713 ngx_log_memory_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,
714     size_t len)
715 {
716     u_char                *p;
717     size_t                 avail, written;
718     ngx_log_memory_buf_t  *mem;
719 
720     mem = log->wdata;
721 
722     if (mem == NULL) {
723         return;
724     }
725 
726     written = ngx_atomic_fetch_add(&mem->written, len);
727 
728     p = mem->pos + written % (mem->end - mem->pos);
729 
730     avail = mem->end - p;
731 
732     if (avail >= len) {
733         ngx_memcpy(p, buf, len);
734 
735     } else {
736         ngx_memcpy(p, buf, avail);
737         ngx_memcpy(mem->pos, buf + avail, len - avail);
738     }
739 }
740 
741 
742 static void
743 ngx_log_memory_cleanup(void *data)
744 {
745     ngx_log_t *log = data;
746 
747     ngx_log_debug0(NGX_LOG_DEBUG_CORE, log, 0, "destroy memory log buffer");
748 
749     log->wdata = NULL;
750 }
751 
752 #endif
753