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#include <ngx_stream.h>
11
12#if (NGX_ZLIB)
13#include <zlib.h>
14#endif
15
16
17typedef struct ngx_stream_log_op_s  ngx_stream_log_op_t;
18
19typedef u_char *(*ngx_stream_log_op_run_pt) (ngx_stream_session_t *s,
20    u_char *buf, ngx_stream_log_op_t *op);
21
22typedef size_t (*ngx_stream_log_op_getlen_pt) (ngx_stream_session_t *s,
23    uintptr_t data);
24
25
26struct ngx_stream_log_op_s {
27    size_t                       len;
28    ngx_stream_log_op_getlen_pt  getlen;
29    ngx_stream_log_op_run_pt     run;
30    uintptr_t                    data;
31};
32
33
34typedef struct {
35    ngx_str_t                    name;
36    ngx_array_t                 *flushes;
37    ngx_array_t                 *ops;        /* array of ngx_stream_log_op_t */
38} ngx_stream_log_fmt_t;
39
40
41typedef struct {
42    ngx_array_t                  formats;    /* array of ngx_stream_log_fmt_t */
43} ngx_stream_log_main_conf_t;
44
45
46typedef struct {
47    u_char                      *start;
48    u_char                      *pos;
49    u_char                      *last;
50
51    ngx_event_t                 *event;
52    ngx_msec_t                   flush;
53    ngx_int_t                    gzip;
54} ngx_stream_log_buf_t;
55
56
57typedef struct {
58    ngx_array_t                 *lengths;
59    ngx_array_t                 *values;
60} ngx_stream_log_script_t;
61
62
63typedef struct {
64    ngx_open_file_t             *file;
65    ngx_stream_log_script_t     *script;
66    time_t                       disk_full_time;
67    time_t                       error_log_time;
68    ngx_syslog_peer_t           *syslog_peer;
69    ngx_stream_log_fmt_t        *format;
70    ngx_stream_complex_value_t  *filter;
71} ngx_stream_log_t;
72
73
74typedef struct {
75    ngx_array_t                 *logs;       /* array of ngx_stream_log_t */
76
77    ngx_open_file_cache_t       *open_file_cache;
78    time_t                       open_file_cache_valid;
79    ngx_uint_t                   open_file_cache_min_uses;
80
81    ngx_uint_t                   off;        /* unsigned  off:1 */
82} ngx_stream_log_srv_conf_t;
83
84
85typedef struct {
86    ngx_str_t                    name;
87    size_t                       len;
88    ngx_stream_log_op_run_pt     run;
89} ngx_stream_log_var_t;
90
91
92static void ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log,
93    u_char *buf, size_t len);
94static ssize_t ngx_stream_log_script_write(ngx_stream_session_t *s,
95    ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len);
96
97#if (NGX_ZLIB)
98static ssize_t ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len,
99    ngx_int_t level, ngx_log_t *log);
100
101static void *ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size);
102static void ngx_stream_log_gzip_free(void *opaque, void *address);
103#endif
104
105static void ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log);
106static void ngx_stream_log_flush_handler(ngx_event_t *ev);
107
108static ngx_int_t ngx_stream_log_variable_compile(ngx_conf_t *cf,
109    ngx_stream_log_op_t *op, ngx_str_t *value, ngx_uint_t json);
110static size_t ngx_stream_log_variable_getlen(ngx_stream_session_t *s,
111    uintptr_t data);
112static u_char *ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,
113    ngx_stream_log_op_t *op);
114static uintptr_t ngx_stream_log_escape(u_char *dst, u_char *src, size_t size);
115static size_t ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s,
116    uintptr_t data);
117static u_char *ngx_stream_log_json_variable(ngx_stream_session_t *s,
118    u_char *buf, ngx_stream_log_op_t *op);
119
120
121static void *ngx_stream_log_create_main_conf(ngx_conf_t *cf);
122static void *ngx_stream_log_create_srv_conf(ngx_conf_t *cf);
123static char *ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent,
124    void *child);
125static char *ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd,
126    void *conf);
127static char *ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd,
128    void *conf);
129static char *ngx_stream_log_compile_format(ngx_conf_t *cf,
130    ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s);
131static char *ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd,
132    void *conf);
133static ngx_int_t ngx_stream_log_init(ngx_conf_t *cf);
134
135
136static ngx_command_t  ngx_stream_log_commands[] = {
137
138    { ngx_string("log_format"),
139      NGX_STREAM_MAIN_CONF|NGX_CONF_2MORE,
140      ngx_stream_log_set_format,
141      NGX_STREAM_MAIN_CONF_OFFSET,
142      0,
143      NULL },
144
145    { ngx_string("access_log"),
146      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_1MORE,
147      ngx_stream_log_set_log,
148      NGX_STREAM_SRV_CONF_OFFSET,
149      0,
150      NULL },
151
152    { ngx_string("open_log_file_cache"),
153      NGX_STREAM_MAIN_CONF|NGX_STREAM_SRV_CONF|NGX_CONF_TAKE1234,
154      ngx_stream_log_open_file_cache,
155      NGX_STREAM_SRV_CONF_OFFSET,
156      0,
157      NULL },
158
159      ngx_null_command
160};
161
162
163static ngx_stream_module_t  ngx_stream_log_module_ctx = {
164    NULL,                                  /* preconfiguration */
165    ngx_stream_log_init,                   /* postconfiguration */
166
167    ngx_stream_log_create_main_conf,       /* create main configuration */
168    NULL,                                  /* init main configuration */
169
170    ngx_stream_log_create_srv_conf,        /* create server configuration */
171    ngx_stream_log_merge_srv_conf          /* merge server configuration */
172};
173
174
175ngx_module_t  ngx_stream_log_module = {
176    NGX_MODULE_V1,
177    &ngx_stream_log_module_ctx,            /* module context */
178    ngx_stream_log_commands,               /* module directives */
179    NGX_STREAM_MODULE,                     /* module type */
180    NULL,                                  /* init master */
181    NULL,                                  /* init module */
182    NULL,                                  /* init process */
183    NULL,                                  /* init thread */
184    NULL,                                  /* exit thread */
185    NULL,                                  /* exit process */
186    NULL,                                  /* exit master */
187    NGX_MODULE_V1_PADDING
188};
189
190
191static ngx_int_t
192ngx_stream_log_handler(ngx_stream_session_t *s)
193{
194    u_char                     *line, *p;
195    size_t                      len, size;
196    ssize_t                     n;
197    ngx_str_t                   val;
198    ngx_uint_t                  i, l;
199    ngx_stream_log_t           *log;
200    ngx_stream_log_op_t        *op;
201    ngx_stream_log_buf_t       *buffer;
202    ngx_stream_log_srv_conf_t  *lscf;
203
204    ngx_log_debug0(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
205                   "stream log handler");
206
207    lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);
208
209    if (lscf->off || lscf->logs == NULL) {
210        return NGX_OK;
211    }
212
213    log = lscf->logs->elts;
214    for (l = 0; l < lscf->logs->nelts; l++) {
215
216        if (log[l].filter) {
217            if (ngx_stream_complex_value(s, log[l].filter, &val) != NGX_OK) {
218                return NGX_ERROR;
219            }
220
221            if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) {
222                continue;
223            }
224        }
225
226        if (ngx_time() == log[l].disk_full_time) {
227
228            /*
229             * on FreeBSD writing to a full filesystem with enabled softupdates
230             * may block process for much longer time than writing to non-full
231             * filesystem, so we skip writing to a log for one second
232             */
233
234            continue;
235        }
236
237        ngx_stream_script_flush_no_cacheable_variables(s,
238                                                       log[l].format->flushes);
239
240        len = 0;
241        op = log[l].format->ops->elts;
242        for (i = 0; i < log[l].format->ops->nelts; i++) {
243            if (op[i].len == 0) {
244                len += op[i].getlen(s, op[i].data);
245
246            } else {
247                len += op[i].len;
248            }
249        }
250
251        if (log[l].syslog_peer) {
252
253            /* length of syslog's PRI and HEADER message parts */
254            len += sizeof("<255>Jan 01 00:00:00 ") - 1
255                   + ngx_cycle->hostname.len + 1
256                   + log[l].syslog_peer->tag.len + 2;
257
258            goto alloc_line;
259        }
260
261        len += NGX_LINEFEED_SIZE;
262
263        buffer = log[l].file ? log[l].file->data : NULL;
264
265        if (buffer) {
266
267            if (len > (size_t) (buffer->last - buffer->pos)) {
268
269                ngx_stream_log_write(s, &log[l], buffer->start,
270                                     buffer->pos - buffer->start);
271
272                buffer->pos = buffer->start;
273            }
274
275            if (len <= (size_t) (buffer->last - buffer->pos)) {
276
277                p = buffer->pos;
278
279                if (buffer->event && p == buffer->start) {
280                    ngx_add_timer(buffer->event, buffer->flush);
281                }
282
283                for (i = 0; i < log[l].format->ops->nelts; i++) {
284                    p = op[i].run(s, p, &op[i]);
285                }
286
287                ngx_linefeed(p);
288
289                buffer->pos = p;
290
291                continue;
292            }
293
294            if (buffer->event && buffer->event->timer_set) {
295                ngx_del_timer(buffer->event);
296            }
297        }
298
299    alloc_line:
300
301        line = ngx_pnalloc(s->connection->pool, len);
302        if (line == NULL) {
303            return NGX_ERROR;
304        }
305
306        p = line;
307
308        if (log[l].syslog_peer) {
309            p = ngx_syslog_add_header(log[l].syslog_peer, line);
310        }
311
312        for (i = 0; i < log[l].format->ops->nelts; i++) {
313            p = op[i].run(s, p, &op[i]);
314        }
315
316        if (log[l].syslog_peer) {
317
318            size = p - line;
319
320            n = ngx_syslog_send(log[l].syslog_peer, line, size);
321
322            if (n < 0) {
323                ngx_log_error(NGX_LOG_WARN, s->connection->log, 0,
324                              "send() to syslog failed");
325
326            } else if ((size_t) n != size) {
327                ngx_log_error(NGX_LOG_WARN, s->connection->log, 0,
328                              "send() to syslog has written only %z of %uz",
329                              n, size);
330            }
331
332            continue;
333        }
334
335        ngx_linefeed(p);
336
337        ngx_stream_log_write(s, &log[l], line, p - line);
338    }
339
340    return NGX_OK;
341}
342
343
344static void
345ngx_stream_log_write(ngx_stream_session_t *s, ngx_stream_log_t *log,
346    u_char *buf, size_t len)
347{
348    u_char                *name;
349    time_t                 now;
350    ssize_t                n;
351    ngx_err_t              err;
352#if (NGX_ZLIB)
353    ngx_stream_log_buf_t  *buffer;
354#endif
355
356    if (log->script == NULL) {
357        name = log->file->name.data;
358
359#if (NGX_ZLIB)
360        buffer = log->file->data;
361
362        if (buffer && buffer->gzip) {
363            n = ngx_stream_log_gzip(log->file->fd, buf, len, buffer->gzip,
364                                    s->connection->log);
365        } else {
366            n = ngx_write_fd(log->file->fd, buf, len);
367        }
368#else
369        n = ngx_write_fd(log->file->fd, buf, len);
370#endif
371
372    } else {
373        name = NULL;
374        n = ngx_stream_log_script_write(s, log->script, &name, buf, len);
375    }
376
377    if (n == (ssize_t) len) {
378        return;
379    }
380
381    now = ngx_time();
382
383    if (n == -1) {
384        err = ngx_errno;
385
386        if (err == NGX_ENOSPC) {
387            log->disk_full_time = now;
388        }
389
390        if (now - log->error_log_time > 59) {
391            ngx_log_error(NGX_LOG_ALERT, s->connection->log, err,
392                          ngx_write_fd_n " to \"%s\" failed", name);
393
394            log->error_log_time = now;
395        }
396
397        return;
398    }
399
400    if (now - log->error_log_time > 59) {
401        ngx_log_error(NGX_LOG_ALERT, s->connection->log, 0,
402                      ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
403                      name, n, len);
404
405        log->error_log_time = now;
406    }
407}
408
409
410static ssize_t
411ngx_stream_log_script_write(ngx_stream_session_t *s,
412    ngx_stream_log_script_t *script, u_char **name, u_char *buf, size_t len)
413{
414    ssize_t                     n;
415    ngx_str_t                   log;
416    ngx_open_file_info_t        of;
417    ngx_stream_log_srv_conf_t  *lscf;
418
419    if (ngx_stream_script_run(s, &log, script->lengths->elts, 1,
420                              script->values->elts)
421        == NULL)
422    {
423        /* simulate successful logging */
424        return len;
425    }
426
427    log.data[log.len - 1] = '\0';
428    *name = log.data;
429
430    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
431                   "stream log \"%s\"", log.data);
432
433    lscf = ngx_stream_get_module_srv_conf(s, ngx_stream_log_module);
434
435    ngx_memzero(&of, sizeof(ngx_open_file_info_t));
436
437    of.log = 1;
438    of.valid = lscf->open_file_cache_valid;
439    of.min_uses = lscf->open_file_cache_min_uses;
440    of.directio = NGX_OPEN_FILE_DIRECTIO_OFF;
441
442    if (ngx_open_cached_file(lscf->open_file_cache, &log, &of,
443                             s->connection->pool)
444        != NGX_OK)
445    {
446        if (of.err == 0) {
447            /* simulate successful logging */
448            return len;
449        }
450
451        ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno,
452                      "%s \"%s\" failed", of.failed, log.data);
453        /* simulate successful logging */
454        return len;
455    }
456
457    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, s->connection->log, 0,
458                   "stream log #%d", of.fd);
459
460    n = ngx_write_fd(of.fd, buf, len);
461
462    return n;
463}
464
465
466#if (NGX_ZLIB)
467
468static ssize_t
469ngx_stream_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level,
470    ngx_log_t *log)
471{
472    int          rc, wbits, memlevel;
473    u_char      *out;
474    size_t       size;
475    ssize_t      n;
476    z_stream     zstream;
477    ngx_err_t    err;
478    ngx_pool_t  *pool;
479
480    wbits = MAX_WBITS;
481    memlevel = MAX_MEM_LEVEL - 1;
482
483    while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) {
484        wbits--;
485        memlevel--;
486    }
487
488    /*
489     * This is a formula from deflateBound() for conservative upper bound of
490     * compressed data plus 18 bytes of gzip wrapper.
491     */
492
493    size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18;
494
495    ngx_memzero(&zstream, sizeof(z_stream));
496
497    pool = ngx_create_pool(256, log);
498    if (pool == NULL) {
499        /* simulate successful logging */
500        return len;
501    }
502
503    pool->log = log;
504
505    zstream.zalloc = ngx_stream_log_gzip_alloc;
506    zstream.zfree = ngx_stream_log_gzip_free;
507    zstream.opaque = pool;
508
509    out = ngx_pnalloc(pool, size);
510    if (out == NULL) {
511        goto done;
512    }
513
514    zstream.next_in = buf;
515    zstream.avail_in = len;
516    zstream.next_out = out;
517    zstream.avail_out = size;
518
519    rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel,
520                      Z_DEFAULT_STRATEGY);
521
522    if (rc != Z_OK) {
523        ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc);
524        goto done;
525    }
526
527    ngx_log_debug4(NGX_LOG_DEBUG_STREAM, log, 0,
528                   "deflate in: ni:%p no:%p ai:%ud ao:%ud",
529                   zstream.next_in, zstream.next_out,
530                   zstream.avail_in, zstream.avail_out);
531
532    rc = deflate(&zstream, Z_FINISH);
533
534    if (rc != Z_STREAM_END) {
535        ngx_log_error(NGX_LOG_ALERT, log, 0,
536                      "deflate(Z_FINISH) failed: %d", rc);
537        goto done;
538    }
539
540    ngx_log_debug5(NGX_LOG_DEBUG_STREAM, log, 0,
541                   "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d",
542                   zstream.next_in, zstream.next_out,
543                   zstream.avail_in, zstream.avail_out,
544                   rc);
545
546    size -= zstream.avail_out;
547
548    rc = deflateEnd(&zstream);
549
550    if (rc != Z_OK) {
551        ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc);
552        goto done;
553    }
554
555    n = ngx_write_fd(fd, out, size);
556
557    if (n != (ssize_t) size) {
558        err = (n == -1) ? ngx_errno : 0;
559
560        ngx_destroy_pool(pool);
561
562        ngx_set_errno(err);
563        return -1;
564    }
565
566done:
567
568    ngx_destroy_pool(pool);
569
570    /* simulate successful logging */
571    return len;
572}
573
574
575static void *
576ngx_stream_log_gzip_alloc(void *opaque, u_int items, u_int size)
577{
578    ngx_pool_t *pool = opaque;
579
580    ngx_log_debug2(NGX_LOG_DEBUG_STREAM, pool->log, 0,
581                   "gzip alloc: n:%ud s:%ud", items, size);
582
583    return ngx_palloc(pool, items * size);
584}
585
586
587static void
588ngx_stream_log_gzip_free(void *opaque, void *address)
589{
590#if 0
591    ngx_pool_t *pool = opaque;
592
593    ngx_log_debug1(NGX_LOG_DEBUG_STREAM, pool->log, 0,
594                   "gzip free: %p", address);
595#endif
596}
597
598#endif
599
600
601static void
602ngx_stream_log_flush(ngx_open_file_t *file, ngx_log_t *log)
603{
604    size_t                 len;
605    ssize_t                n;
606    ngx_stream_log_buf_t  *buffer;
607
608    buffer = file->data;
609
610    len = buffer->pos - buffer->start;
611
612    if (len == 0) {
613        return;
614    }
615
616#if (NGX_ZLIB)
617    if (buffer->gzip) {
618        n = ngx_stream_log_gzip(file->fd, buffer->start, len, buffer->gzip,
619                                log);
620    } else {
621        n = ngx_write_fd(file->fd, buffer->start, len);
622    }
623#else
624    n = ngx_write_fd(file->fd, buffer->start, len);
625#endif
626
627    if (n == -1) {
628        ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
629                      ngx_write_fd_n " to \"%s\" failed",
630                      file->name.data);
631
632    } else if ((size_t) n != len) {
633        ngx_log_error(NGX_LOG_ALERT, log, 0,
634                      ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz",
635                      file->name.data, n, len);
636    }
637
638    buffer->pos = buffer->start;
639
640    if (buffer->event && buffer->event->timer_set) {
641        ngx_del_timer(buffer->event);
642    }
643}
644
645
646static void
647ngx_stream_log_flush_handler(ngx_event_t *ev)
648{
649    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0,
650                   "stream log buffer flush handler");
651
652    ngx_stream_log_flush(ev->data, ev->log);
653}
654
655
656static u_char *
657ngx_stream_log_copy_short(ngx_stream_session_t *s, u_char *buf,
658    ngx_stream_log_op_t *op)
659{
660    size_t     len;
661    uintptr_t  data;
662
663    len = op->len;
664    data = op->data;
665
666    while (len--) {
667        *buf++ = (u_char) (data & 0xff);
668        data >>= 8;
669    }
670
671    return buf;
672}
673
674
675static u_char *
676ngx_stream_log_copy_long(ngx_stream_session_t *s, u_char *buf,
677    ngx_stream_log_op_t *op)
678{
679    return ngx_cpymem(buf, (u_char *) op->data, op->len);
680}
681
682
683static ngx_int_t
684ngx_stream_log_variable_compile(ngx_conf_t *cf, ngx_stream_log_op_t *op,
685    ngx_str_t *value, ngx_uint_t json)
686{
687    ngx_int_t  index;
688
689    index = ngx_stream_get_variable_index(cf, value);
690    if (index == NGX_ERROR) {
691        return NGX_ERROR;
692    }
693
694    op->len = 0;
695
696    if (json) {
697        op->getlen = ngx_stream_log_json_variable_getlen;
698        op->run = ngx_stream_log_json_variable;
699
700    } else {
701        op->getlen = ngx_stream_log_variable_getlen;
702        op->run = ngx_stream_log_variable;
703    }
704
705    op->data = index;
706
707    return NGX_OK;
708}
709
710
711static size_t
712ngx_stream_log_variable_getlen(ngx_stream_session_t *s, uintptr_t data)
713{
714    uintptr_t                     len;
715    ngx_stream_variable_value_t  *value;
716
717    value = ngx_stream_get_indexed_variable(s, data);
718
719    if (value == NULL || value->not_found) {
720        return 1;
721    }
722
723    len = ngx_stream_log_escape(NULL, value->data, value->len);
724
725    value->escape = len ? 1 : 0;
726
727    return value->len + len * 3;
728}
729
730
731static u_char *
732ngx_stream_log_variable(ngx_stream_session_t *s, u_char *buf,
733    ngx_stream_log_op_t *op)
734{
735    ngx_stream_variable_value_t  *value;
736
737    value = ngx_stream_get_indexed_variable(s, op->data);
738
739    if (value == NULL || value->not_found) {
740        *buf = '-';
741        return buf + 1;
742    }
743
744    if (value->escape == 0) {
745        return ngx_cpymem(buf, value->data, value->len);
746
747    } else {
748        return (u_char *) ngx_stream_log_escape(buf, value->data, value->len);
749    }
750}
751
752
753static uintptr_t
754ngx_stream_log_escape(u_char *dst, u_char *src, size_t size)
755{
756    ngx_uint_t      n;
757    static u_char   hex[] = "0123456789ABCDEF";
758
759    static uint32_t   escape[] = {
760        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
761
762                    /* ?>=< ;:98 7654 3210  /.-, +*)( '&%$ #"!  */
763        0x00000004, /* 0000 0000 0000 0000  0000 0000 0000 0100 */
764
765                    /* _^]\ [ZYX WVUT SRQP  ONML KJIH GFED CBA@ */
766        0x10000000, /* 0001 0000 0000 0000  0000 0000 0000 0000 */
767
768                    /*  ~}| {zyx wvut srqp  onml kjih gfed cba` */
769        0x80000000, /* 1000 0000 0000 0000  0000 0000 0000 0000 */
770
771        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
772        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
773        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
774        0xffffffff, /* 1111 1111 1111 1111  1111 1111 1111 1111 */
775    };
776
777
778    if (dst == NULL) {
779
780        /* find the number of the characters to be escaped */
781
782        n = 0;
783
784        while (size) {
785            if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
786                n++;
787            }
788            src++;
789            size--;
790        }
791
792        return (uintptr_t) n;
793    }
794
795    while (size) {
796        if (escape[*src >> 5] & (1U << (*src & 0x1f))) {
797            *dst++ = '\\';
798            *dst++ = 'x';
799            *dst++ = hex[*src >> 4];
800            *dst++ = hex[*src & 0xf];
801            src++;
802
803        } else {
804            *dst++ = *src++;
805        }
806        size--;
807    }
808
809    return (uintptr_t) dst;
810}
811
812
813static size_t
814ngx_stream_log_json_variable_getlen(ngx_stream_session_t *s, uintptr_t data)
815{
816    uintptr_t                     len;
817    ngx_stream_variable_value_t  *value;
818
819    value = ngx_stream_get_indexed_variable(s, data);
820
821    if (value == NULL || value->not_found) {
822        return 0;
823    }
824
825    len = ngx_escape_json(NULL, value->data, value->len);
826
827    value->escape = len ? 1 : 0;
828
829    return value->len + len;
830}
831
832
833static u_char *
834ngx_stream_log_json_variable(ngx_stream_session_t *s, u_char *buf,
835    ngx_stream_log_op_t *op)
836{
837    ngx_stream_variable_value_t  *value;
838
839    value = ngx_stream_get_indexed_variable(s, op->data);
840
841    if (value == NULL || value->not_found) {
842        return buf;
843    }
844
845    if (value->escape == 0) {
846        return ngx_cpymem(buf, value->data, value->len);
847
848    } else {
849        return (u_char *) ngx_escape_json(buf, value->data, value->len);
850    }
851}
852
853
854static void *
855ngx_stream_log_create_main_conf(ngx_conf_t *cf)
856{
857    ngx_stream_log_main_conf_t  *conf;
858
859    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_main_conf_t));
860    if (conf == NULL) {
861        return NULL;
862    }
863
864    if (ngx_array_init(&conf->formats, cf->pool, 4,
865                       sizeof(ngx_stream_log_fmt_t))
866        != NGX_OK)
867    {
868        return NULL;
869    }
870
871    return conf;
872}
873
874
875static void *
876ngx_stream_log_create_srv_conf(ngx_conf_t *cf)
877{
878    ngx_stream_log_srv_conf_t  *conf;
879
880    conf = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_srv_conf_t));
881    if (conf == NULL) {
882        return NULL;
883    }
884
885    conf->open_file_cache = NGX_CONF_UNSET_PTR;
886
887    return conf;
888}
889
890
891static char *
892ngx_stream_log_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
893{
894    ngx_stream_log_srv_conf_t *prev = parent;
895    ngx_stream_log_srv_conf_t *conf = child;
896
897    if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
898
899        conf->open_file_cache = prev->open_file_cache;
900        conf->open_file_cache_valid = prev->open_file_cache_valid;
901        conf->open_file_cache_min_uses = prev->open_file_cache_min_uses;
902
903        if (conf->open_file_cache == NGX_CONF_UNSET_PTR) {
904            conf->open_file_cache = NULL;
905        }
906    }
907
908    if (conf->logs || conf->off) {
909        return NGX_CONF_OK;
910    }
911
912    conf->logs = prev->logs;
913    conf->off = prev->off;
914
915    return NGX_CONF_OK;
916}
917
918
919static char *
920ngx_stream_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
921{
922    ngx_stream_log_srv_conf_t *lscf = conf;
923
924    ssize_t                              size;
925    ngx_int_t                            gzip;
926    ngx_uint_t                           i, n;
927    ngx_msec_t                           flush;
928    ngx_str_t                           *value, name, s;
929    ngx_stream_log_t                    *log;
930    ngx_syslog_peer_t                   *peer;
931    ngx_stream_log_buf_t                *buffer;
932    ngx_stream_log_fmt_t                *fmt;
933    ngx_stream_script_compile_t          sc;
934    ngx_stream_log_main_conf_t          *lmcf;
935    ngx_stream_compile_complex_value_t   ccv;
936
937    value = cf->args->elts;
938
939    if (ngx_strcmp(value[1].data, "off") == 0) {
940        lscf->off = 1;
941        if (cf->args->nelts == 2) {
942            return NGX_CONF_OK;
943        }
944
945        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
946                           "invalid parameter \"%V\"", &value[2]);
947        return NGX_CONF_ERROR;
948    }
949
950    if (lscf->logs == NULL) {
951        lscf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_stream_log_t));
952        if (lscf->logs == NULL) {
953            return NGX_CONF_ERROR;
954        }
955    }
956
957    lmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_log_module);
958
959    log = ngx_array_push(lscf->logs);
960    if (log == NULL) {
961        return NGX_CONF_ERROR;
962    }
963
964    ngx_memzero(log, sizeof(ngx_stream_log_t));
965
966
967    if (ngx_strncmp(value[1].data, "syslog:", 7) == 0) {
968
969        peer = ngx_pcalloc(cf->pool, sizeof(ngx_syslog_peer_t));
970        if (peer == NULL) {
971            return NGX_CONF_ERROR;
972        }
973
974        if (ngx_syslog_process_conf(cf, peer) != NGX_CONF_OK) {
975            return NGX_CONF_ERROR;
976        }
977
978        log->syslog_peer = peer;
979
980        goto process_formats;
981    }
982
983    n = ngx_stream_script_variables_count(&value[1]);
984
985    if (n == 0) {
986        log->file = ngx_conf_open_file(cf->cycle, &value[1]);
987        if (log->file == NULL) {
988            return NGX_CONF_ERROR;
989        }
990
991    } else {
992        if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) {
993            return NGX_CONF_ERROR;
994        }
995
996        log->script = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_script_t));
997        if (log->script == NULL) {
998            return NGX_CONF_ERROR;
999        }
1000
1001        ngx_memzero(&sc, sizeof(ngx_stream_script_compile_t));
1002
1003        sc.cf = cf;
1004        sc.source = &value[1];
1005        sc.lengths = &log->script->lengths;
1006        sc.values = &log->script->values;
1007        sc.variables = n;
1008        sc.complete_lengths = 1;
1009        sc.complete_values = 1;
1010
1011        if (ngx_stream_script_compile(&sc) != NGX_OK) {
1012            return NGX_CONF_ERROR;
1013        }
1014    }
1015
1016process_formats:
1017
1018    if (cf->args->nelts >= 3) {
1019        name = value[2];
1020
1021    } else {
1022        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1023                           "log format is not specified");
1024        return NGX_CONF_ERROR;
1025    }
1026
1027    fmt = lmcf->formats.elts;
1028    for (i = 0; i < lmcf->formats.nelts; i++) {
1029        if (fmt[i].name.len == name.len
1030            && ngx_strcasecmp(fmt[i].name.data, name.data) == 0)
1031        {
1032            log->format = &fmt[i];
1033            break;
1034        }
1035    }
1036
1037    if (log->format == NULL) {
1038        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1039                           "unknown log format \"%V\"", &name);
1040        return NGX_CONF_ERROR;
1041    }
1042
1043    size = 0;
1044    flush = 0;
1045    gzip = 0;
1046
1047    for (i = 3; i < cf->args->nelts; i++) {
1048
1049        if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) {
1050            s.len = value[i].len - 7;
1051            s.data = value[i].data + 7;
1052
1053            size = ngx_parse_size(&s);
1054
1055            if (size == NGX_ERROR || size == 0) {
1056                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1057                                   "invalid buffer size \"%V\"", &s);
1058                return NGX_CONF_ERROR;
1059            }
1060
1061            continue;
1062        }
1063
1064        if (ngx_strncmp(value[i].data, "flush=", 6) == 0) {
1065            s.len = value[i].len - 6;
1066            s.data = value[i].data + 6;
1067
1068            flush = ngx_parse_time(&s, 0);
1069
1070            if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) {
1071                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1072                                   "invalid flush time \"%V\"", &s);
1073                return NGX_CONF_ERROR;
1074            }
1075
1076            continue;
1077        }
1078
1079        if (ngx_strncmp(value[i].data, "gzip", 4) == 0
1080            && (value[i].len == 4 || value[i].data[4] == '='))
1081        {
1082#if (NGX_ZLIB)
1083            if (size == 0) {
1084                size = 64 * 1024;
1085            }
1086
1087            if (value[i].len == 4) {
1088                gzip = Z_BEST_SPEED;
1089                continue;
1090            }
1091
1092            s.len = value[i].len - 5;
1093            s.data = value[i].data + 5;
1094
1095            gzip = ngx_atoi(s.data, s.len);
1096
1097            if (gzip < 1 || gzip > 9) {
1098                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1099                                   "invalid compression level \"%V\"", &s);
1100                return NGX_CONF_ERROR;
1101            }
1102
1103            continue;
1104
1105#else
1106            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1107                               "nginx was built without zlib support");
1108            return NGX_CONF_ERROR;
1109#endif
1110        }
1111
1112        if (ngx_strncmp(value[i].data, "if=", 3) == 0) {
1113            s.len = value[i].len - 3;
1114            s.data = value[i].data + 3;
1115
1116            ngx_memzero(&ccv, sizeof(ngx_stream_compile_complex_value_t));
1117
1118            ccv.cf = cf;
1119            ccv.value = &s;
1120            ccv.complex_value = ngx_palloc(cf->pool,
1121                                           sizeof(ngx_stream_complex_value_t));
1122            if (ccv.complex_value == NULL) {
1123                return NGX_CONF_ERROR;
1124            }
1125
1126            if (ngx_stream_compile_complex_value(&ccv) != NGX_OK) {
1127                return NGX_CONF_ERROR;
1128            }
1129
1130            log->filter = ccv.complex_value;
1131
1132            continue;
1133        }
1134
1135        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1136                           "invalid parameter \"%V\"", &value[i]);
1137        return NGX_CONF_ERROR;
1138    }
1139
1140    if (flush && size == 0) {
1141        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1142                           "no buffer is defined for access_log \"%V\"",
1143                           &value[1]);
1144        return NGX_CONF_ERROR;
1145    }
1146
1147    if (size) {
1148
1149        if (log->script) {
1150            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1151                               "buffered logs cannot have variables in name");
1152            return NGX_CONF_ERROR;
1153        }
1154
1155        if (log->syslog_peer) {
1156            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1157                               "logs to syslog cannot be buffered");
1158            return NGX_CONF_ERROR;
1159        }
1160
1161        if (log->file->data) {
1162            buffer = log->file->data;
1163
1164            if (buffer->last - buffer->start != size
1165                || buffer->flush != flush
1166                || buffer->gzip != gzip)
1167            {
1168                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1169                                   "access_log \"%V\" already defined "
1170                                   "with conflicting parameters",
1171                                   &value[1]);
1172                return NGX_CONF_ERROR;
1173            }
1174
1175            return NGX_CONF_OK;
1176        }
1177
1178        buffer = ngx_pcalloc(cf->pool, sizeof(ngx_stream_log_buf_t));
1179        if (buffer == NULL) {
1180            return NGX_CONF_ERROR;
1181        }
1182
1183        buffer->start = ngx_pnalloc(cf->pool, size);
1184        if (buffer->start == NULL) {
1185            return NGX_CONF_ERROR;
1186        }
1187
1188        buffer->pos = buffer->start;
1189        buffer->last = buffer->start + size;
1190
1191        if (flush) {
1192            buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t));
1193            if (buffer->event == NULL) {
1194                return NGX_CONF_ERROR;
1195            }
1196
1197            buffer->event->data = log->file;
1198            buffer->event->handler = ngx_stream_log_flush_handler;
1199            buffer->event->log = &cf->cycle->new_log;
1200            buffer->event->cancelable = 1;
1201
1202            buffer->flush = flush;
1203        }
1204
1205        buffer->gzip = gzip;
1206
1207        log->file->flush = ngx_stream_log_flush;
1208        log->file->data = buffer;
1209    }
1210
1211    return NGX_CONF_OK;
1212}
1213
1214
1215static char *
1216ngx_stream_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1217{
1218    ngx_stream_log_main_conf_t *lmcf = conf;
1219
1220    ngx_str_t             *value;
1221    ngx_uint_t             i;
1222    ngx_stream_log_fmt_t  *fmt;
1223
1224    value = cf->args->elts;
1225
1226    fmt = lmcf->formats.elts;
1227    for (i = 0; i < lmcf->formats.nelts; i++) {
1228        if (fmt[i].name.len == value[1].len
1229            && ngx_strcmp(fmt[i].name.data, value[1].data) == 0)
1230        {
1231            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1232                               "duplicate \"log_format\" name \"%V\"",
1233                               &value[1]);
1234            return NGX_CONF_ERROR;
1235        }
1236    }
1237
1238    fmt = ngx_array_push(&lmcf->formats);
1239    if (fmt == NULL) {
1240        return NGX_CONF_ERROR;
1241    }
1242
1243    fmt->name = value[1];
1244
1245    fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t));
1246    if (fmt->flushes == NULL) {
1247        return NGX_CONF_ERROR;
1248    }
1249
1250    fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_stream_log_op_t));
1251    if (fmt->ops == NULL) {
1252        return NGX_CONF_ERROR;
1253    }
1254
1255    return ngx_stream_log_compile_format(cf, fmt->flushes, fmt->ops,
1256                                         cf->args, 2);
1257}
1258
1259
1260static char *
1261ngx_stream_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes,
1262    ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s)
1263{
1264    u_char                *data, *p, ch;
1265    size_t                 i, len;
1266    ngx_str_t             *value, var;
1267    ngx_int_t             *flush;
1268    ngx_uint_t             bracket, json;
1269    ngx_stream_log_op_t   *op;
1270
1271    json = 0;
1272    value = args->elts;
1273
1274    if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) {
1275        data = value[s].data + 7;
1276
1277        if (ngx_strcmp(data, "json") == 0) {
1278            json = 1;
1279
1280        } else if (ngx_strcmp(data, "default") != 0) {
1281            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1282                               "unknown log format escaping \"%s\"", data);
1283            return NGX_CONF_ERROR;
1284        }
1285
1286        s++;
1287    }
1288
1289    for ( /* void */ ; s < args->nelts; s++) {
1290
1291        i = 0;
1292
1293        while (i < value[s].len) {
1294
1295            op = ngx_array_push(ops);
1296            if (op == NULL) {
1297                return NGX_CONF_ERROR;
1298            }
1299
1300            data = &value[s].data[i];
1301
1302            if (value[s].data[i] == '$') {
1303
1304                if (++i == value[s].len) {
1305                    goto invalid;
1306                }
1307
1308                if (value[s].data[i] == '{') {
1309                    bracket = 1;
1310
1311                    if (++i == value[s].len) {
1312                        goto invalid;
1313                    }
1314
1315                    var.data = &value[s].data[i];
1316
1317                } else {
1318                    bracket = 0;
1319                    var.data = &value[s].data[i];
1320                }
1321
1322                for (var.len = 0; i < value[s].len; i++, var.len++) {
1323                    ch = value[s].data[i];
1324
1325                    if (ch == '}' && bracket) {
1326                        i++;
1327                        bracket = 0;
1328                        break;
1329                    }
1330
1331                    if ((ch >= 'A' && ch <= 'Z')
1332                        || (ch >= 'a' && ch <= 'z')
1333                        || (ch >= '0' && ch <= '9')
1334                        || ch == '_')
1335                    {
1336                        continue;
1337                    }
1338
1339                    break;
1340                }
1341
1342                if (bracket) {
1343                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1344                                       "the closing bracket in \"%V\" "
1345                                       "variable is missing", &var);
1346                    return NGX_CONF_ERROR;
1347                }
1348
1349                if (var.len == 0) {
1350                    goto invalid;
1351                }
1352
1353                if (ngx_stream_log_variable_compile(cf, op, &var, json)
1354                    != NGX_OK)
1355                {
1356                    return NGX_CONF_ERROR;
1357                }
1358
1359                if (flushes) {
1360
1361                    flush = ngx_array_push(flushes);
1362                    if (flush == NULL) {
1363                        return NGX_CONF_ERROR;
1364                    }
1365
1366                    *flush = op->data; /* variable index */
1367                }
1368
1369                continue;
1370            }
1371
1372            i++;
1373
1374            while (i < value[s].len && value[s].data[i] != '$') {
1375                i++;
1376            }
1377
1378            len = &value[s].data[i] - data;
1379
1380            if (len) {
1381
1382                op->len = len;
1383                op->getlen = NULL;
1384
1385                if (len <= sizeof(uintptr_t)) {
1386                    op->run = ngx_stream_log_copy_short;
1387                    op->data = 0;
1388
1389                    while (len--) {
1390                        op->data <<= 8;
1391                        op->data |= data[len];
1392                    }
1393
1394                } else {
1395                    op->run = ngx_stream_log_copy_long;
1396
1397                    p = ngx_pnalloc(cf->pool, len);
1398                    if (p == NULL) {
1399                        return NGX_CONF_ERROR;
1400                    }
1401
1402                    ngx_memcpy(p, data, len);
1403                    op->data = (uintptr_t) p;
1404                }
1405            }
1406        }
1407    }
1408
1409    return NGX_CONF_OK;
1410
1411invalid:
1412
1413    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data);
1414
1415    return NGX_CONF_ERROR;
1416}
1417
1418
1419static char *
1420ngx_stream_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
1421{
1422    ngx_stream_log_srv_conf_t *lscf = conf;
1423
1424    time_t       inactive, valid;
1425    ngx_str_t   *value, s;
1426    ngx_int_t    max, min_uses;
1427    ngx_uint_t   i;
1428
1429    if (lscf->open_file_cache != NGX_CONF_UNSET_PTR) {
1430        return "is duplicate";
1431    }
1432
1433    value = cf->args->elts;
1434
1435    max = 0;
1436    inactive = 10;
1437    valid = 60;
1438    min_uses = 1;
1439
1440    for (i = 1; i < cf->args->nelts; i++) {
1441
1442        if (ngx_strncmp(value[i].data, "max=", 4) == 0) {
1443
1444            max = ngx_atoi(value[i].data + 4, value[i].len - 4);
1445            if (max == NGX_ERROR) {
1446                goto failed;
1447            }
1448
1449            continue;
1450        }
1451
1452        if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) {
1453
1454            s.len = value[i].len - 9;
1455            s.data = value[i].data + 9;
1456
1457            inactive = ngx_parse_time(&s, 1);
1458            if (inactive == (time_t) NGX_ERROR) {
1459                goto failed;
1460            }
1461
1462            continue;
1463        }
1464
1465        if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) {
1466
1467            min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9);
1468            if (min_uses == NGX_ERROR) {
1469                goto failed;
1470            }
1471
1472            continue;
1473        }
1474
1475        if (ngx_strncmp(value[i].data, "valid=", 6) == 0) {
1476
1477            s.len = value[i].len - 6;
1478            s.data = value[i].data + 6;
1479
1480            valid = ngx_parse_time(&s, 1);
1481            if (valid == (time_t) NGX_ERROR) {
1482                goto failed;
1483            }
1484
1485            continue;
1486        }
1487
1488        if (ngx_strcmp(value[i].data, "off") == 0) {
1489
1490            lscf->open_file_cache = NULL;
1491
1492            continue;
1493        }
1494
1495    failed:
1496
1497        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1498                           "invalid \"open_log_file_cache\" parameter \"%V\"",
1499                           &value[i]);
1500        return NGX_CONF_ERROR;
1501    }
1502
1503    if (lscf->open_file_cache == NULL) {
1504        return NGX_CONF_OK;
1505    }
1506
1507    if (max == 0) {
1508        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
1509                        "\"open_log_file_cache\" must have \"max\" parameter");
1510        return NGX_CONF_ERROR;
1511    }
1512
1513    lscf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive);
1514
1515    if (lscf->open_file_cache) {
1516
1517        lscf->open_file_cache_valid = valid;
1518        lscf->open_file_cache_min_uses = min_uses;
1519
1520        return NGX_CONF_OK;
1521    }
1522
1523    return NGX_CONF_ERROR;
1524}
1525
1526
1527static ngx_int_t
1528ngx_stream_log_init(ngx_conf_t *cf)
1529{
1530    ngx_stream_handler_pt        *h;
1531    ngx_stream_core_main_conf_t  *cmcf;
1532
1533    cmcf = ngx_stream_conf_get_module_main_conf(cf, ngx_stream_core_module);
1534
1535    h = ngx_array_push(&cmcf->phases[NGX_STREAM_LOG_PHASE].handlers);
1536    if (h == NULL) {
1537        return NGX_ERROR;
1538    }
1539
1540    *h = ngx_stream_log_handler;
1541
1542    return NGX_OK;
1543}
1544