src/core/ngx_syslog.c - nginx source code

Global variables defined

Functions defined

Macros defined

Source code


  1. /*
  2. * Copyright (C) Nginx, Inc.
  3. */


  4. #include <ngx_config.h>
  5. #include <ngx_core.h>
  6. #include <ngx_event.h>


  7. #define NGX_SYSLOG_MAX_STR                                                    \
  8.     NGX_MAX_ERROR_STR + sizeof("<255>Jan 01 00:00:00 ") - 1                   \
  9.     + (NGX_MAXHOSTNAMELEN - 1) + 1 /* space */                                \
  10.     + 32 /* tag */ + 2 /* colon, space */


  11. static char *ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer);
  12. static ngx_int_t ngx_syslog_init_peer(ngx_syslog_peer_t *peer);
  13. static void ngx_syslog_cleanup(void *data);
  14. static u_char *ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len);


  15. static char  *facilities[] = {
  16.     "kern", "user", "mail", "daemon", "auth", "intern", "lpr", "news", "uucp",
  17.     "clock", "authpriv", "ftp", "ntp", "audit", "alert", "cron", "local0",
  18.     "local1", "local2", "local3", "local4", "local5", "local6", "local7",
  19.     NULL
  20. };

  21. /* note 'error/warn' like in nginx.conf, not 'err/warning' */
  22. static char  *severities[] = {
  23.     "emerg", "alert", "crit", "error", "warn", "notice", "info", "debug", NULL
  24. };

  25. static ngx_log_t    ngx_syslog_dummy_log;
  26. static ngx_event_t  ngx_syslog_dummy_event;


  27. char *
  28. ngx_syslog_process_conf(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
  29. {
  30.     ngx_pool_cleanup_t  *cln;

  31.     peer->facility = NGX_CONF_UNSET_UINT;
  32.     peer->severity = NGX_CONF_UNSET_UINT;

  33.     if (ngx_syslog_parse_args(cf, peer) != NGX_CONF_OK) {
  34.         return NGX_CONF_ERROR;
  35.     }

  36.     if (peer->server.sockaddr == NULL) {
  37.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  38.                            "no syslog server specified");
  39.         return NGX_CONF_ERROR;
  40.     }

  41.     if (peer->facility == NGX_CONF_UNSET_UINT) {
  42.         peer->facility = 23; /* local7 */
  43.     }

  44.     if (peer->severity == NGX_CONF_UNSET_UINT) {
  45.         peer->severity = 6; /* info */
  46.     }

  47.     if (peer->tag.data == NULL) {
  48.         ngx_str_set(&peer->tag, "nginx");
  49.     }

  50.     peer->hostname = &cf->cycle->hostname;
  51.     peer->logp = &cf->cycle->new_log;

  52.     peer->conn.fd = (ngx_socket_t) -1;

  53.     peer->conn.read = &ngx_syslog_dummy_event;
  54.     peer->conn.write = &ngx_syslog_dummy_event;

  55.     ngx_syslog_dummy_event.log = &ngx_syslog_dummy_log;

  56.     cln = ngx_pool_cleanup_add(cf->pool, 0);
  57.     if (cln == NULL) {
  58.         return NGX_CONF_ERROR;
  59.     }

  60.     cln->data = peer;
  61.     cln->handler = ngx_syslog_cleanup;

  62.     return NGX_CONF_OK;
  63. }


  64. static char *
  65. ngx_syslog_parse_args(ngx_conf_t *cf, ngx_syslog_peer_t *peer)
  66. {
  67.     u_char      *p, *comma, c;
  68.     size_t       len;
  69.     ngx_str_t   *value;
  70.     ngx_url_t    u;
  71.     ngx_uint_t   i;

  72.     value = cf->args->elts;

  73.     p = value[1].data + sizeof("syslog:") - 1;

  74.     for ( ;; ) {
  75.         comma = (u_char *) ngx_strchr(p, ',');

  76.         if (comma != NULL) {
  77.             len = comma - p;
  78.             *comma = '\0';

  79.         } else {
  80.             len = value[1].data + value[1].len - p;
  81.         }

  82.         if (ngx_strncmp(p, "server=", 7) == 0) {

  83.             if (peer->server.sockaddr != NULL) {
  84.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  85.                                    "duplicate syslog \"server\"");
  86.                 return NGX_CONF_ERROR;
  87.             }

  88.             ngx_memzero(&u, sizeof(ngx_url_t));

  89.             u.url.data = p + 7;
  90.             u.url.len = len - 7;
  91.             u.default_port = 514;

  92.             if (ngx_parse_url(cf->pool, &u) != NGX_OK) {
  93.                 if (u.err) {
  94.                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  95.                                        "%s in syslog server \"%V\"",
  96.                                        u.err, &u.url);
  97.                 }

  98.                 return NGX_CONF_ERROR;
  99.             }

  100.             peer->server = u.addrs[0];

  101.         } else if (ngx_strncmp(p, "facility=", 9) == 0) {

  102.             if (peer->facility != NGX_CONF_UNSET_UINT) {
  103.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  104.                                    "duplicate syslog \"facility\"");
  105.                 return NGX_CONF_ERROR;
  106.             }

  107.             for (i = 0; facilities[i] != NULL; i++) {

  108.                 if (ngx_strcmp(p + 9, facilities[i]) == 0) {
  109.                     peer->facility = i;
  110.                     goto next;
  111.                 }
  112.             }

  113.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  114.                                "unknown syslog facility \"%s\"", p + 9);
  115.             return NGX_CONF_ERROR;

  116.         } else if (ngx_strncmp(p, "severity=", 9) == 0) {

  117.             if (peer->severity != NGX_CONF_UNSET_UINT) {
  118.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  119.                                    "duplicate syslog \"severity\"");
  120.                 return NGX_CONF_ERROR;
  121.             }

  122.             for (i = 0; severities[i] != NULL; i++) {

  123.                 if (ngx_strcmp(p + 9, severities[i]) == 0) {
  124.                     peer->severity = i;
  125.                     goto next;
  126.                 }
  127.             }

  128.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  129.                                "unknown syslog severity \"%s\"", p + 9);
  130.             return NGX_CONF_ERROR;

  131.         } else if (ngx_strncmp(p, "tag=", 4) == 0) {

  132.             if (peer->tag.data != NULL) {
  133.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  134.                                    "duplicate syslog \"tag\"");
  135.                 return NGX_CONF_ERROR;
  136.             }

  137.             /*
  138.              * RFC 3164: the TAG is a string of ABNF alphanumeric characters
  139.              * that MUST NOT exceed 32 characters.
  140.              */
  141.             if (len - 4 > 32) {
  142.                 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  143.                                    "syslog tag length exceeds 32");
  144.                 return NGX_CONF_ERROR;
  145.             }

  146.             for (i = 4; i < len; i++) {
  147.                 c = ngx_tolower(p[i]);

  148.                 if (c < '0' || (c > '9' && c < 'a' && c != '_') || c > 'z') {
  149.                     ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  150.                                        "syslog \"tag\" only allows "
  151.                                        "alphanumeric characters "
  152.                                        "and underscore");
  153.                     return NGX_CONF_ERROR;
  154.                 }
  155.             }

  156.             peer->tag.data = p + 4;
  157.             peer->tag.len = len - 4;

  158.         } else if (len == 10 && ngx_strncmp(p, "nohostname", 10) == 0) {
  159.             peer->nohostname = 1;

  160.         } else {
  161.             ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
  162.                                "unknown syslog parameter \"%s\"", p);
  163.             return NGX_CONF_ERROR;
  164.         }

  165.     next:

  166.         if (comma == NULL) {
  167.             break;
  168.         }

  169.         p = comma + 1;
  170.     }

  171.     return NGX_CONF_OK;
  172. }


  173. u_char *
  174. ngx_syslog_add_header(ngx_syslog_peer_t *peer, u_char *buf)
  175. {
  176.     ngx_uint_t  pri;

  177.     pri = peer->facility * 8 + peer->severity;

  178.     if (peer->nohostname) {
  179.         return ngx_sprintf(buf, "<%ui>%V %V: ", pri, &ngx_cached_syslog_time,
  180.                            &peer->tag);
  181.     }

  182.     return ngx_sprintf(buf, "<%ui>%V %V %V: ", pri, &ngx_cached_syslog_time,
  183.                        peer->hostname, &peer->tag);
  184. }


  185. void
  186. ngx_syslog_writer(ngx_log_t *log, ngx_uint_t level, u_char *buf,
  187.     size_t len)
  188. {
  189.     u_char             *p, msg[NGX_SYSLOG_MAX_STR];
  190.     ngx_uint_t          head_len;
  191.     ngx_syslog_peer_t  *peer;

  192.     peer = log->wdata;

  193.     if (peer->busy) {
  194.         return;
  195.     }

  196.     peer->busy = 1;
  197.     peer->severity = level - 1;

  198.     p = ngx_syslog_add_header(peer, msg);
  199.     head_len = p - msg;

  200.     len -= NGX_LINEFEED_SIZE;

  201.     if (len > NGX_SYSLOG_MAX_STR - head_len) {
  202.         len = NGX_SYSLOG_MAX_STR - head_len;
  203.     }

  204.     p = ngx_snprintf(p, len, "%s", buf);

  205.     (void) ngx_syslog_send(peer, msg, p - msg);

  206.     peer->busy = 0;
  207. }


  208. ssize_t
  209. ngx_syslog_send(ngx_syslog_peer_t *peer, u_char *buf, size_t len)
  210. {
  211.     ssize_t  n;

  212.     if (peer->log.handler == NULL) {
  213.         peer->log = *peer->logp;
  214.         peer->log.handler = ngx_syslog_log_error;
  215.         peer->log.data = peer;
  216.         peer->log.action = "logging to syslog";
  217.     }

  218.     if (peer->conn.fd == (ngx_socket_t) -1) {
  219.         if (ngx_syslog_init_peer(peer) != NGX_OK) {
  220.             return NGX_ERROR;
  221.         }
  222.     }

  223.     if (ngx_send) {
  224.         n = ngx_send(&peer->conn, buf, len);

  225.     } else {
  226.         /* event module has not yet set ngx_io */
  227.         n = ngx_os_io.send(&peer->conn, buf, len);
  228.     }

  229.     if (n == NGX_ERROR) {

  230.         if (ngx_close_socket(peer->conn.fd) == -1) {
  231.             ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
  232.                           ngx_close_socket_n " failed");
  233.         }

  234.         peer->conn.fd = (ngx_socket_t) -1;
  235.     }

  236.     return n;
  237. }


  238. static ngx_int_t
  239. ngx_syslog_init_peer(ngx_syslog_peer_t *peer)
  240. {
  241.     ngx_socket_t  fd;

  242.     fd = ngx_socket(peer->server.sockaddr->sa_family, SOCK_DGRAM, 0);
  243.     if (fd == (ngx_socket_t) -1) {
  244.         ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
  245.                       ngx_socket_n " failed");
  246.         return NGX_ERROR;
  247.     }

  248.     if (ngx_nonblocking(fd) == -1) {
  249.         ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
  250.                       ngx_nonblocking_n " failed");
  251.         goto failed;
  252.     }

  253.     if (connect(fd, peer->server.sockaddr, peer->server.socklen) == -1) {
  254.         ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
  255.                       "connect() failed");
  256.         goto failed;
  257.     }

  258.     peer->conn.fd = fd;
  259.     peer->conn.log = &peer->log;

  260.     /* UDP sockets are always ready to write */
  261.     peer->conn.write->ready = 1;

  262.     return NGX_OK;

  263. failed:

  264.     if (ngx_close_socket(fd) == -1) {
  265.         ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
  266.                       ngx_close_socket_n " failed");
  267.     }

  268.     return NGX_ERROR;
  269. }


  270. static void
  271. ngx_syslog_cleanup(void *data)
  272. {
  273.     ngx_syslog_peer_t  *peer = data;

  274.     /* prevents further use of this peer */
  275.     peer->busy = 1;

  276.     if (peer->conn.fd == (ngx_socket_t) -1) {
  277.         return;
  278.     }

  279.     if (ngx_close_socket(peer->conn.fd) == -1) {
  280.         ngx_log_error(NGX_LOG_ALERT, &peer->log, ngx_socket_errno,
  281.                       ngx_close_socket_n " failed");
  282.     }
  283. }


  284. static u_char *
  285. ngx_syslog_log_error(ngx_log_t *log, u_char *buf, size_t len)
  286. {
  287.     u_char             *p;
  288.     ngx_syslog_peer_t  *peer;

  289.     p = buf;

  290.     if (log->action) {
  291.         p = ngx_snprintf(buf, len, " while %s", log->action);
  292.         len -= p - buf;
  293.     }

  294.     peer = log->data;

  295.     if (peer) {
  296.         p = ngx_snprintf(p, len, ", server: %V", &peer->server.name);
  297.     }

  298.     return p;
  299. }