| |
| /* |
| * 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 != '_') || c > 'z') { |
| ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, |
| "syslog \"tag\" only allows " |
| "alphanumeric characters " |
| "and underscore"); |
| return NGX_CONF_ERROR; |
| } |
| } |
| |
| peer->tag.data = p + 4; |
| peer->tag.len = len - 4; |
| |
| } else if (len == 10 && ngx_strncmp(p, "nohostname", 10) == 0) { |
| peer->nohostname = 1; |
| |
| } 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; |
| |
| if (peer->nohostname) { |
| return ngx_sprintf(buf, "<%ui>%V %V: ", pri, &ngx_cached_syslog_time, |
| &peer->tag); |
| } |
| |
| 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->busy) { |
| return; |
| } |
| |
| peer->busy = 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->busy = 0; |
| } |
| |
| |
| ssize_t |
| ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len) |
| { |
| ssize_t n; |
| |
| if (peer->conn.fd == (ngx_socket_t) -1) { |
| if (ngx_syslog_init_peer(peer) != NGX_OK) { |
| return NGX_ERROR; |
| } |
| } |
| |
| /* log syslog socket events with valid log */ |
| peer->conn.log = ngx_cycle->log; |
| |
| if (ngx_send) { |
| n = ngx_send(&peer->conn, buf, len); |
| |
| } else { |
| /* event module has not yet set ngx_io */ |
| n = ngx_os_io.send(&peer->conn, buf, len); |
| } |
| |
| #if (NGX_HAVE_UNIX_DOMAIN) |
| |
| if (n == NGX_ERROR && peer->server.sockaddr->sa_family == AF_UNIX) { |
| |
| if (ngx_close_socket(peer->conn.fd) == -1) { |
| ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, |
| ngx_close_socket_n " failed"); |
| } |
| |
| peer->conn.fd = (ngx_socket_t) -1; |
| } |
| |
| #endif |
| |
| return n; |
| } |
| |
| |
| 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; |
| |
| ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log; |
| |
| 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"); |
| goto failed; |
| } |
| |
| if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) { |
| ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, |
| "connect() failed"); |
| goto failed; |
| } |
| |
| cln = ngx_pool_cleanup_add(peer->pool, 0); |
| if (cln == NULL) { |
| goto failed; |
| } |
| |
| cln->data = peer; |
| cln->handler = ngx_syslog_cleanup; |
| |
| peer->conn.fd = fd; |
| |
| /* UDP sockets are always ready to write */ |
| peer->conn.write->ready = 1; |
| |
| return NGX_OK; |
| |
| failed: |
| |
| if (ngx_close_socket(fd) == -1) { |
| ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, |
| ngx_close_socket_n " failed"); |
| } |
| |
| return NGX_ERROR; |
| } |
| |
| |
| static void |
| ngx_syslog_cleanup(void *data) |
| { |
| ngx_syslog_peer_t *peer = data; |
| |
| /* prevents further use of this peer */ |
| peer->busy = 1; |
| |
| if (peer->conn.fd == (ngx_socket_t) -1) { |
| return; |
| } |
| |
| if (ngx_close_socket(peer->conn.fd) == -1) { |
| ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, ngx_socket_errno, |
| ngx_close_socket_n " failed"); |
| } |
| } |