aboutsummaryrefslogtreecommitdiff
path: root/src/core/ngx_syslog.c
diff options
context:
space:
mode:
authorVladimir Homutov <vl@nginx.com>2014-05-12 16:34:15 +0400
committerVladimir Homutov <vl@nginx.com>2014-05-12 16:34:15 +0400
commit493b898ae94ae7dd390d77c42f5500974be48393 (patch)
treeb85c242bcdba82459aa209c76efbe0b4f826f103 /src/core/ngx_syslog.c
parent1736c180f4683d57d175b98d5596b779bab950d4 (diff)
downloadnginx-493b898ae94ae7dd390d77c42f5500974be48393.tar.gz
nginx-493b898ae94ae7dd390d77c42f5500974be48393.zip
Added syslog support for error_log and access_log directives.
Diffstat (limited to 'src/core/ngx_syslog.c')
-rw-r--r--src/core/ngx_syslog.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/src/core/ngx_syslog.c b/src/core/ngx_syslog.c
new file mode 100644
index 000000000..f341a25e5
--- /dev/null
+++ b/src/core/ngx_syslog.c
@@ -0,0 +1,332 @@
+
+/*
+ * Copyright (C) Nginx, Inc.
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+
+
+#define NGX_SYSLOG_MAX_STR \
+ NGX_MAX_ERROR_STR + sizeof("<255>Jan 01 00:00:00 ") - 1 \
+ + (NGX_MAXHOSTNAMELEN - 1) + 1 /* space */ \
+ + 32 /* tag */ + 2 /* colon, space */
+
+
+static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer);
+static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer);
+static void ngx_syslog_cleanup(void *data);
+
+
+static char *facilities[] = {
+ "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp",
+ "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0",
+ "local1", "local2", "local3", "local4", "local5", "local6", "local7",
+ NULL
+};
+
+/* note 'error/warn' like in nginx.conf, not 'err/warning' */
+static char *severities[] = {
+ "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", NULL
+};
+
+static ngx_log_t ngx_syslog_dummy_log;
+static ngx_event_t ngx_syslog_dummy_event;
+
+
+char *
+ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
+{
+ peer->pool = cf->pool;
+ peer->facility = NGX_CONF_UNSET_UINT;
+ peer->severity = NGX_CONF_UNSET_UINT;
+
+ if (ngx_syslog_parse_args(cf, peer) != NGX_CONF_OK) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (peer->server.sockaddr == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "no syslog server specified");
+ return NGX_CONF_ERROR;
+ }
+
+ if (peer->facility == NGX_CONF_UNSET_UINT) {
+ peer->facility = 23; /* local7 */
+ }
+
+ if (peer->severity == NGX_CONF_UNSET_UINT) {
+ peer->severity = 6; /* info */
+ }
+
+ if (peer->tag.data == NULL) {
+ ngx_str_set(&peer->tag, "nginx");
+ }
+
+ peer->conn.fd = (ngx_socket_t) -1;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
+{
+ u_char *p, *comma, c;
+ size_t len;
+ ngx_str_t *value;
+ ngx_url_t u;
+ ngx_uint_t i;
+
+ value = cf->args->elts;
+
+ p = value[1].data + sizeof("syslog:") - 1;
+
+ for ( ;; ) {
+ comma = (u_char *) ngx_strchr(p, ',');
+
+ if (comma != NULL) {
+ len = comma - p;
+ *comma = '\0';
+
+ } else {
+ len = value[1].data + value[1].len - p;
+ }
+
+ if (ngx_strncmp(p, "server=", 7) == 0) {
+
+ if (peer->server.sockaddr != NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate syslog \"server\"");
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_memzero(&u, sizeof(ngx_url_t));
+
+ u.url.data = p + 7;
+ u.url.len = len - 7;
+ u.default_port = 514;
+
+ if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
+ if (u.err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in syslog server \"%V\"",
+ u.err, &u.url);
+ }
+
+ return NGX_CONF_ERROR;
+ }
+
+ peer->server = u.addrs[0];
+
+ } else if (ngx_strncmp(p, "facility=", 9) == 0) {
+
+ if (peer->facility != NGX_CONF_UNSET_UINT) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate syslog \"facility\"");
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; facilities[i] != NULL; i++) {
+
+ if (ngx_strcmp(p + 9, facilities[i]) == 0) {
+ peer->facility = i;
+ goto next;
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown syslog facility \"%s\"", p + 9);
+ return NGX_CONF_ERROR;
+
+ } else if (ngx_strncmp(p, "severity=", 9) == 0) {
+
+ if (peer->severity != NGX_CONF_UNSET_UINT) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate syslog \"severity\"");
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; severities[i] != NULL; i++) {
+
+ if (ngx_strcmp(p + 9, severities[i]) == 0) {
+ peer->severity = i;
+ goto next;
+ }
+ }
+
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown syslog severity \"%s\"", p + 9);
+ return NGX_CONF_ERROR;
+
+ } else if (ngx_strncmp(p, "tag=", 4) == 0) {
+
+ if (peer->tag.data != NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "duplicate syslog \"tag\"");
+ return NGX_CONF_ERROR;
+ }
+
+ /*
+ * RFC 3164: the TAG is a string of ABNF alphanumeric characters
+ * that MUST NOT exceed 32 characters.
+ */
+ if (len - 4 > 32) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "syslog tag length exceeds 32");
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 4; i < len; i++) {
+ c = ngx_tolower(p[i]);
+
+ if (c < '0' || (c > '9' && c < 'a') || c > 'z') {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "syslog \"tag\" only allows "
+ "alphanumeric characters");
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ peer->tag.data = p + 4;
+ peer->tag.len = len - 4;
+
+ } else {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "unknown syslog parameter \"%s\"", p);
+ return NGX_CONF_ERROR;
+ }
+
+ next:
+
+ if (comma == NULL) {
+ break;
+ }
+
+ p = comma + 1;
+ }
+
+ return NGX_CONF_OK;
+}
+
+
+u_char *
+ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf)
+{
+ ngx_uint_t pri;
+
+ pri = peer->facility * 8 + peer->severity;
+
+ return ngx_sprintf(buf, "<%ui>%V %V %V: ", pri, &ngx_cached_syslog_time,
+ &ngx_cycle->hostname, &peer->tag);
+}
+
+
+void
+ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,
+ size_t len)
+{
+ u_char *p, msg[NGX_SYSLOG_MAX_STR];
+ ngx_uint_t head_len;
+ ngx_syslog_peer_t *peer;
+
+ peer = log->wdata;
+
+ if (peer->processing) {
+ return;
+ }
+
+ peer->processing = 1;
+ peer->severity = level - 1;
+
+ p = ngx_syslog_add_header(peer, msg);
+ head_len = p - msg;
+
+ len -= NGX_LINEFEED_SIZE;
+
+ if (len > NGX_SYSLOG_MAX_STR - head_len) {
+ len = NGX_SYSLOG_MAX_STR - head_len;
+ }
+
+ p = ngx_snprintf(p, len, "%s", buf);
+
+ (void) ngx_syslog_send(peer, msg, p - msg);
+
+ peer->processing = 0;
+}
+
+
+ssize_t
+ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len)
+{
+ if (peer->conn.fd == (ngx_socket_t) -1) {
+ if (ngx_syslog_init_peer(peer) != NGX_OK) {
+ return NGX_ERROR;
+ }
+ }
+
+ if (ngx_send) {
+ return ngx_send(&peer->conn, buf, len);
+
+ } else {
+ /* event module has not yet set ngx_io */
+ return ngx_os_io.send(&peer->conn, buf, len);
+ }
+}
+
+
+static ngx_int_t
+ngx_syslog_init_peer(ngx_syslog_peer_t *peer)
+{
+ ngx_socket_t fd;
+ ngx_pool_cleanup_t *cln;
+
+ peer->conn.read = &ngx_syslog_dummy_event;
+ peer->conn.write = &ngx_syslog_dummy_event;
+ peer->conn.log = &ngx_syslog_dummy_log;
+
+ ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;
+
+ cln = ngx_pool_cleanup_add(peer->pool, 0);
+ if (cln == NULL) {
+ return NGX_ERROR;
+ }
+
+ cln->data = peer;
+ cln->handler = ngx_syslog_cleanup;
+
+ fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0);
+ if (fd == (ngx_socket_t) -1) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
+ ngx_socket_n " failed");
+ return NGX_ERROR;
+ }
+
+ if (ngx_nonblocking(fd) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
+ ngx_nonblocking_n " failed");
+ return NGX_ERROR;
+ }
+
+ if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) {
+ ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno,
+ "connect() failed");
+ return NGX_ERROR;
+ }
+
+ peer->conn.fd = fd;
+ return NGX_OK;
+}
+
+
+static void
+ngx_syslog_cleanup(void *data)
+{
+ ngx_syslog_peer_t *peer = data;
+
+ if (peer->conn.fd != (ngx_socket_t) -1) {
+ (void) ngx_close_socket(peer->conn.fd);
+ }
+}