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