ngx_syslog.c revision e18a033b
1
2/*
3 * Copyright (C) Nginx, Inc.
4 */
5
6
7#include <ngx_config.h>
8#include <ngx_core.h>
9#include <ngx_event.h>
10
11
12#define NGX_SYSLOG_MAX_STR                                                    \
13    NGX_MAX_ERROR_STR + sizeof("<255>Jan 01 00:00:00 ") - 1                   \
14    + (NGX_MAXHOSTNAMELEN - 1) + 1 /* space */                                \
15    + 32 /* tag */ + 2 /* colon, space */
16
17
18static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer);
19static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer);
20static void ngx_syslog_cleanup(void *data);
21
22
23static char  *facilities[] = {
24    "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp",
25    "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0",
26    "local1", "local2", "local3", "local4", "local5", "local6", "local7",
27    NULL
28};
29
30/* note 'error/warn' like in nginx.conf, not 'err/warning' */
31static char  *severities[] = {
32    "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", NULL
33};
34
35static ngx_log_t    ngx_syslog_dummy_log;
36static ngx_event_t  ngx_syslog_dummy_event;
37
38
39char *
40ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
41{
42    peer->pool = cf->pool;
43    peer->facility = NGX_CONF_UNSET_UINT;
44    peer->severity = NGX_CONF_UNSET_UINT;
45
46    if (ngx_syslog_parse_args(cf, peer) != NGX_CONF_OK) {
47        return NGX_CONF_ERROR;
48    }
49
50    if (peer->server.sockaddr == NULL) {
51        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
52                           "no syslog server specified");
53        return NGX_CONF_ERROR;
54    }
55
56    if (peer->facility == NGX_CONF_UNSET_UINT) {
57        peer->facility = 23; /* local7 */
58    }
59
60    if (peer->severity == NGX_CONF_UNSET_UINT) {
61        peer->severity = 6; /* info */
62    }
63
64    if (peer->tag.data == NULL) {
65        ngx_str_set(&peer->tag, "nginx");
66    }
67
68    peer->conn.fd = (ngx_socket_t) -1;
69
70    return NGX_CONF_OK;
71}
72
73
74static char *
75ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
76{
77    u_char      *p, *comma, c;
78    size_t       len;
79    ngx_str_t   *value;
80    ngx_url_t    u;
81    ngx_uint_t   i;
82
83    value = cf->args->elts;
84
85    p = value[1].data + sizeof("syslog:") - 1;
86
87    for ( ;; ) {
88        comma = (u_char *) ngx_strchr(p, ',');
89
90        if (comma != NULL) {
91            len = comma - p;
92            *comma = '\0';
93
94        } else {
95            len = value[1].data + value[1].len - p;
96        }
97
98        if (ngx_strncmp(p, "server=", 7) == 0) {
99
100            if (peer->server.sockaddr != NULL) {
101                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
102                                   "duplicate syslog \"server\"");
103                return NGX_CONF_ERROR;
104            }
105
106            ngx_memzero(&u, sizeof(ngx_url_t));
107
108            u.url.data = p + 7;
109            u.url.len = len - 7;
110            u.default_port = 514;
111
112            if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
113                if (u.err) {
114                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
115                                       "%s in syslog server \"%V\"",
116                                       u.err, &u.url);
117                }
118
119                return NGX_CONF_ERROR;
120            }
121
122            peer->server = u.addrs[0];
123
124        } else if (ngx_strncmp(p, "facility=", 9) == 0) {
125
126            if (peer->facility != NGX_CONF_UNSET_UINT) {
127                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
128                                   "duplicate syslog \"facility\"");
129                return NGX_CONF_ERROR;
130            }
131
132            for (i = 0; facilities[i] != NULL; i++) {
133
134                if (ngx_strcmp(p + 9, facilities[i]) == 0) {
135                    peer->facility = i;
136                    goto next;
137                }
138            }
139
140            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
141                               "unknown syslog facility \"%s\"", p + 9);
142            return NGX_CONF_ERROR;
143
144        } else if (ngx_strncmp(p, "severity=", 9) == 0) {
145
146            if (peer->severity != NGX_CONF_UNSET_UINT) {
147                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
148                                   "duplicate syslog \"severity\"");
149                return NGX_CONF_ERROR;
150            }
151
152            for (i = 0; severities[i] != NULL; i++) {
153
154                if (ngx_strcmp(p + 9, severities[i]) == 0) {
155                    peer->severity = i;
156                    goto next;
157                }
158            }
159
160            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
161                               "unknown syslog severity \"%s\"", p + 9);
162            return NGX_CONF_ERROR;
163
164        } else if (ngx_strncmp(p, "tag=", 4) == 0) {
165
166            if (peer->tag.data != NULL) {
167                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
168                                   "duplicate syslog \"tag\"");
169                return NGX_CONF_ERROR;
170            }
171
172            /*
173             * RFC 3164: the TAG is a string of ABNF alphanumeric characters
174             * that MUST NOT exceed 32 characters.
175             */
176            if (len - 4 > 32) {
177                ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
178                                   "syslog tag length exceeds 32");
179                return NGX_CONF_ERROR;
180            }
181
182            for (i = 4; i < len; i++) {
183                c = ngx_tolower(p[i]);
184
185                if (c < '0' || (c > '9' && c < 'a' && c != '_') || c > 'z') {
186                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
187                                       "syslog \"tag\" only allows "
188                                       "alphanumeric characters "
189                                       "and underscore");
190                    return NGX_CONF_ERROR;
191                }
192            }
193
194            peer->tag.data = p + 4;
195            peer->tag.len = len - 4;
196
197        } else if (len == 10 && ngx_strncmp(p, "nohostname", 10) == 0) {
198            peer->nohostname = 1;
199
200        } else {
201            ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
202                               "unknown syslog parameter \"%s\"", p);
203            return NGX_CONF_ERROR;
204        }
205
206    next:
207
208        if (comma == NULL) {
209            break;
210        }
211
212        p = comma + 1;
213    }
214
215    return NGX_CONF_OK;
216}
217
218
219u_char *
220ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf)
221{
222    ngx_uint_t  pri;
223
224    pri = peer->facility * 8 + peer->severity;
225
226    if (peer->nohostname) {
227        return ngx_sprintf(buf, "<%ui>%V %V: ", pri, &ngx_cached_syslog_time,
228                           &peer->tag);
229    }
230
231    return ngx_sprintf(buf, "<%ui>%V %V %V: ", pri, &ngx_cached_syslog_time,
232                       &ngx_cycle->hostname, &peer->tag);
233}
234
235
236void
237ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,
238    size_t len)
239{
240    u_char             *p, msg[NGX_SYSLOG_MAX_STR];
241    ngx_uint_t          head_len;
242    ngx_syslog_peer_t  *peer;
243
244    peer = log->wdata;
245
246    if (peer->busy) {
247        return;
248    }
249
250    peer->busy = 1;
251    peer->severity = level - 1;
252
253    p = ngx_syslog_add_header(peer, msg);
254    head_len = p - msg;
255
256    len -= NGX_LINEFEED_SIZE;
257
258    if (len > NGX_SYSLOG_MAX_STR - head_len) {
259        len = NGX_SYSLOG_MAX_STR - head_len;
260    }
261
262    p = ngx_snprintf(p, len, "%s", buf);
263
264    (void) ngx_syslog_send(peer, msg, p - msg);
265
266    peer->busy = 0;
267}
268
269
270ssize_t
271ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len)
272{
273    ssize_t  n;
274
275    if (peer->conn.fd == (ngx_socket_t) -1) {
276        if (ngx_syslog_init_peer(peer) != NGX_OK) {
277            return NGX_ERROR;
278        }
279    }
280
281    /* log syslog socket events with valid log */
282    peer->conn.log = ngx_cycle->log;
283
284    if (ngx_send) {
285        n = ngx_send(&peer->conn, buf, len);
286
287    } else {
288        /* event module has not yet set ngx_io */
289        n = ngx_os_io.send(&peer->conn, buf, len);
290    }
291
292#if (NGX_HAVE_UNIX_DOMAIN)
293
294    if (n == NGX_ERROR && peer->server.sockaddr->sa_family == AF_UNIX) {
295
296        if (ngx_close_socket(peer->conn.fd) == -1) {
297            ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
298                          ngx_close_socket_n " failed");
299        }
300
301        peer->conn.fd = (ngx_socket_t) -1;
302    }
303
304#endif
305
306    return n;
307}
308
309
310static ngx_int_t
311ngx_syslog_init_peer(ngx_syslog_peer_t *peer)
312{
313    ngx_socket_t         fd;
314    ngx_pool_cleanup_t  *cln;
315
316    peer->conn.read = &ngx_syslog_dummy_event;
317    peer->conn.write = &ngx_syslog_dummy_event;
318
319    ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;
320
321    fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0);
322    if (fd == (ngx_socket_t) -1) {
323        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
324                      ngx_socket_n " failed");
325        return NGX_ERROR;
326    }
327
328    if (ngx_nonblocking(fd) == -1) {
329        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
330                      ngx_nonblocking_n " failed");
331        goto failed;
332    }
333
334    if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) {
335        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
336                      "connect() failed");
337        goto failed;
338    }
339
340    cln = ngx_pool_cleanup_add(peer->pool, 0);
341    if (cln == NULL) {
342        goto failed;
343    }
344
345    cln->data = peer;
346    cln->handler = ngx_syslog_cleanup;
347
348    peer->conn.fd = fd;
349
350    /* UDP sockets are always ready to write */
351    peer->conn.write->ready = 1;
352
353    return NGX_OK;
354
355failed:
356
357    if (ngx_close_socket(fd) == -1) {
358        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
359                      ngx_close_socket_n " failed");
360    }
361
362    return NGX_ERROR;
363}
364
365
366static void
367ngx_syslog_cleanup(void *data)
368{
369    ngx_syslog_peer_t  *peer = data;
370
371    /* prevents further use of this peer */
372    peer->busy = 1;
373
374    if (peer->conn.fd == (ngx_socket_t) -1) {
375        return;
376    }
377
378    if (ngx_close_socket(peer->conn.fd) == -1) {
379        ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
380                      ngx_close_socket_n " failed");
381    }
382}
383