├── README.md ├── config └── ngx_http_pipelog_module.c /README.md: -------------------------------------------------------------------------------- 1 | # ngx_http_pipelog_module 2 | 3 | This module allows to send HTTP access log to an external program via pipe. 4 | 5 | ## Installation 6 | 7 | ***Clone the code*** 8 | 9 | git clone git://github.com/pandax381/ngx_http_pipelog_module.git 10 | 11 | ***Build nginx with ngx_http_pipelog_module*** 12 | 13 | ./configure --add-module=/path/to/ngx_http_pipelog_module 14 | make && sudo make install 15 | 16 | Note: This module has been tested with nginx 1.23.3 and 1.22.1 and 1.14.2 17 | 18 | ## Directives 19 | 20 | ***pipelog_format*** 21 | 22 | pipelog_format name [escape=default|json|none] string ... 23 | 24 | * syntax is same as log_format of HttpLogModule. 25 | * default value is *combined*. 26 | 27 | ***pipelog*** 28 | 29 | pipelog command [format [nonblocking] [if=condition]]; 30 | 31 | pipelog off; 32 | 33 | * default value is *off*. 34 | * command does not need the pipe symbol `|` prefix. 35 | 36 | ## Example 37 | 38 | pipelog_format main '$remote_addr - $remote_user [$time_local] "$request" ' 39 | '$status $body_bytes_sent "$http_referer" ' 40 | '"$http_user_agent" "$http_x_forwarded_for"'; 41 | 42 | pipelog "logger -t nginx" main; 43 | 44 | ## License 45 | 46 | ### Original Code (ngx_http_log_module) 47 | 48 | ``` 49 | Copyright (C) 2002-2009 Igor Sysoev 50 | Copyright (C) 2009-2013 Sergey A. Osokin 51 | 52 | Redistribution and use in source and binary forms, with or without 53 | modification, are permitted provided that the following conditions 54 | are met: 55 | 1. Redistributions of source code must retain the above copyright 56 | notice, this list of conditions and the following disclaimer. 57 | 2. Redistributions in binary form must reproduce the above copyright 58 | notice, this list of conditions and the following disclaimer in the 59 | documentation and/or other materials provided with the distribution. 60 | 61 | THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND 62 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 63 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 64 | ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE 65 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 66 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 67 | OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 68 | HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 69 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 70 | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 71 | SUCH DAMAGE. 72 | ``` 73 | 74 | ### Additional Code 75 | 76 | ``` 77 | Copyright (c) 2013-2022 YAMAMOTO Masaya 78 | Copyright (c) 2013-2022 KLab Inc. 79 | 80 | Permission is hereby granted, free of charge, to any person obtaining a copy 81 | of this software and associated documentation files (the "Software"), to deal 82 | in the Software without restriction, including without limitation the rights 83 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 84 | copies of the Software, and to permit persons to whom the Software is 85 | furnished to do so, subject to the following conditions: 86 | 87 | The above copyright notice and this permission notice shall be included in all 88 | copies or substantial portions of the Software. 89 | 90 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 91 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 92 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 93 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 94 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 95 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 96 | SOFTWARE. 97 | ``` 98 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_pipelog_module 2 | if test -n "$ngx_module_link"; then 3 | ngx_module_type=HTTP 4 | ngx_module_name=ngx_http_pipelog_module 5 | ngx_module_srcs="$ngx_addon_dir/ngx_http_pipelog_module.c" 6 | 7 | . auto/module 8 | else 9 | HTTP_MODULES="$HTTP_MODULES ngx_http_pipelog_module" 10 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_pipelog_module.c" 11 | fi 12 | -------------------------------------------------------------------------------- /ngx_http_pipelog_module.c: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) Igor Sysoev 4 | * Copyright (C) Nginx, Inc. 5 | */ 6 | 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #if (NGX_ZLIB) 13 | #include 14 | #endif 15 | #include 16 | 17 | typedef struct ngx_http_log_op_s ngx_http_log_op_t; 18 | 19 | typedef u_char *(*ngx_http_log_op_run_pt) (ngx_http_request_t *r, u_char *buf, 20 | ngx_http_log_op_t *op); 21 | 22 | typedef size_t (*ngx_http_log_op_getlen_pt) (ngx_http_request_t *r, 23 | uintptr_t data); 24 | 25 | 26 | struct ngx_http_log_op_s { 27 | size_t len; 28 | ngx_http_log_op_getlen_pt getlen; 29 | ngx_http_log_op_run_pt run; 30 | uintptr_t data; 31 | }; 32 | 33 | 34 | typedef struct { 35 | ngx_str_t name; 36 | ngx_array_t *flushes; 37 | ngx_array_t *ops; /* array of ngx_http_log_op_t */ 38 | } ngx_http_log_fmt_t; 39 | 40 | 41 | typedef struct { 42 | ngx_array_t formats; /* array of ngx_http_log_fmt_t */ 43 | ngx_uint_t combined_used; /* unsigned combined_used:1 */ 44 | } ngx_http_log_main_conf_t; 45 | 46 | 47 | typedef struct { 48 | u_char *start; 49 | u_char *pos; 50 | u_char *last; 51 | 52 | ngx_event_t *event; 53 | ngx_msec_t flush; 54 | ngx_int_t gzip; 55 | } ngx_http_log_buf_t; 56 | 57 | 58 | typedef struct { 59 | ngx_array_t *lengths; 60 | ngx_array_t *values; 61 | } ngx_http_log_script_t; 62 | 63 | 64 | typedef struct { 65 | ngx_open_file_t *file; 66 | ngx_http_log_script_t *script; 67 | time_t disk_full_time; 68 | time_t error_log_time; 69 | ngx_http_log_fmt_t *format; 70 | } ngx_http_log_t; 71 | 72 | 73 | typedef struct { 74 | ngx_array_t *logs; /* array of ngx_http_log_t */ 75 | 76 | ngx_open_file_cache_t *open_file_cache; 77 | time_t open_file_cache_valid; 78 | ngx_uint_t open_file_cache_min_uses; 79 | 80 | ngx_uint_t off; /* unsigned off:1 */ 81 | } ngx_http_log_loc_conf_t; 82 | 83 | 84 | typedef struct { 85 | ngx_str_t name; 86 | size_t len; 87 | ngx_http_log_op_run_pt run; 88 | } ngx_http_log_var_t; 89 | 90 | 91 | #define NGX_HTTP_LOG_ESCAPE_DEFAULT 0 92 | #define NGX_HTTP_LOG_ESCAPE_JSON 1 93 | #define NGX_HTTP_LOG_ESCAPE_NONE 2 94 | 95 | 96 | static void ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, 97 | u_char *buf, size_t len); 98 | static ssize_t ngx_http_log_script_write(ngx_http_request_t *r, 99 | ngx_http_log_script_t *script, u_char **name, u_char *buf, size_t len); 100 | 101 | #if (NGX_ZLIB) 102 | static ssize_t ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, 103 | ngx_int_t level, ngx_log_t *log); 104 | 105 | static void *ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size); 106 | static void ngx_http_log_gzip_free(void *opaque, void *address); 107 | #endif 108 | 109 | static void ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log); 110 | static void ngx_http_log_flush_handler(ngx_event_t *ev); 111 | 112 | static u_char *ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, 113 | ngx_http_log_op_t *op); 114 | static u_char *ngx_http_log_time(ngx_http_request_t *r, u_char *buf, 115 | ngx_http_log_op_t *op); 116 | static u_char *ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf, 117 | ngx_http_log_op_t *op); 118 | static u_char *ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, 119 | ngx_http_log_op_t *op); 120 | static u_char *ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf, 121 | ngx_http_log_op_t *op); 122 | static u_char *ngx_http_log_status(ngx_http_request_t *r, u_char *buf, 123 | ngx_http_log_op_t *op); 124 | static u_char *ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf, 125 | ngx_http_log_op_t *op); 126 | static u_char *ngx_http_log_body_bytes_sent(ngx_http_request_t *r, 127 | u_char *buf, ngx_http_log_op_t *op); 128 | static u_char *ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf, 129 | ngx_http_log_op_t *op); 130 | 131 | static ngx_int_t ngx_http_log_variable_compile(ngx_conf_t *cf, 132 | ngx_http_log_op_t *op, ngx_str_t *value, ngx_uint_t escape); 133 | static size_t ngx_http_log_variable_getlen(ngx_http_request_t *r, 134 | uintptr_t data); 135 | static u_char *ngx_http_log_variable(ngx_http_request_t *r, u_char *buf, 136 | ngx_http_log_op_t *op); 137 | static uintptr_t ngx_http_log_escape(u_char *dst, u_char *src, size_t size); 138 | static size_t ngx_http_log_json_variable_getlen(ngx_http_request_t *r, 139 | uintptr_t data); 140 | static u_char *ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf, 141 | ngx_http_log_op_t *op); 142 | static size_t ngx_http_log_unescaped_variable_getlen(ngx_http_request_t *r, 143 | uintptr_t data); 144 | static u_char *ngx_http_log_unescaped_variable(ngx_http_request_t *r, 145 | u_char *buf, ngx_http_log_op_t *op); 146 | 147 | 148 | static void *ngx_http_log_create_main_conf(ngx_conf_t *cf); 149 | static void *ngx_http_log_create_loc_conf(ngx_conf_t *cf); 150 | static char *ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, 151 | void *child); 152 | static char *ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, 153 | void *conf); 154 | static char *ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, 155 | void *conf); 156 | static char *ngx_http_log_compile_format(ngx_conf_t *cf, 157 | ngx_array_t *flushes, ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s); 158 | static char *ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, 159 | void *conf); 160 | static ngx_int_t ngx_http_log_init(ngx_conf_t *cf); 161 | static void close_pipes(ngx_cycle_t *cycle); 162 | 163 | static ngx_command_t ngx_http_log_commands[] = { 164 | 165 | { ngx_string("log_format"), 166 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, 167 | ngx_http_log_set_format, 168 | NGX_HTTP_MAIN_CONF_OFFSET, 169 | 0, 170 | NULL }, 171 | 172 | { ngx_string("access_log"), 173 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF 174 | |NGX_HTTP_LMT_CONF|NGX_CONF_1MORE, 175 | ngx_http_log_set_log, 176 | NGX_HTTP_LOC_CONF_OFFSET, 177 | 0, 178 | NULL }, 179 | 180 | { ngx_string("open_log_file_cache"), 181 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, 182 | ngx_http_log_open_file_cache, 183 | NGX_HTTP_LOC_CONF_OFFSET, 184 | 0, 185 | NULL }, 186 | 187 | ngx_null_command 188 | }; 189 | 190 | 191 | static ngx_http_module_t ngx_http_log_module_ctx = { 192 | NULL, /* preconfiguration */ 193 | ngx_http_log_init, /* postconfiguration */ 194 | 195 | ngx_http_log_create_main_conf, /* create main configuration */ 196 | NULL, /* init main configuration */ 197 | 198 | NULL, /* create server configuration */ 199 | NULL, /* merge server configuration */ 200 | 201 | ngx_http_log_create_loc_conf, /* create location configuration */ 202 | ngx_http_log_merge_loc_conf /* merge location configuration */ 203 | }; 204 | 205 | /* 206 | ngx_module_t ngx_http_log_module = { 207 | */ 208 | static ngx_module_t ngx_http_log_module = { 209 | NGX_MODULE_V1, 210 | &ngx_http_log_module_ctx, /* module context */ 211 | ngx_http_log_commands, /* module directives */ 212 | NGX_HTTP_MODULE, /* module type */ 213 | NULL, /* init master */ 214 | NULL, /* init module */ 215 | NULL, /* init process */ 216 | NULL, /* init thread */ 217 | NULL, /* exit thread */ 218 | NULL, /* exit process */ 219 | NULL, /* exit master */ 220 | NGX_MODULE_V1_PADDING 221 | }; 222 | 223 | 224 | static ngx_str_t ngx_http_access_log = ngx_string(NGX_HTTP_LOG_PATH); 225 | 226 | 227 | static ngx_str_t ngx_http_combined_fmt = 228 | ngx_string("$remote_addr - $remote_user [$time_local] " 229 | "\"$request\" $status $body_bytes_sent " 230 | "\"$http_referer\" \"$http_user_agent\""); 231 | 232 | 233 | static ngx_http_log_var_t ngx_http_log_vars[] = { 234 | { ngx_string("pipe"), 1, ngx_http_log_pipe }, 235 | { ngx_string("time_local"), sizeof("28/Sep/1970:12:00:00 +0600") - 1, 236 | ngx_http_log_time }, 237 | { ngx_string("time_iso8601"), sizeof("1970-09-28T12:00:00+06:00") - 1, 238 | ngx_http_log_iso8601 }, 239 | { ngx_string("msec"), NGX_TIME_T_LEN + 4, ngx_http_log_msec }, 240 | { ngx_string("request_time"), NGX_TIME_T_LEN + 4, 241 | ngx_http_log_request_time }, 242 | { ngx_string("status"), NGX_INT_T_LEN, ngx_http_log_status }, 243 | { ngx_string("bytes_sent"), NGX_OFF_T_LEN, ngx_http_log_bytes_sent }, 244 | { ngx_string("body_bytes_sent"), NGX_OFF_T_LEN, 245 | ngx_http_log_body_bytes_sent }, 246 | { ngx_string("request_length"), NGX_SIZE_T_LEN, 247 | ngx_http_log_request_length }, 248 | 249 | { ngx_null_string, 0, NULL } 250 | }; 251 | 252 | 253 | static ngx_int_t 254 | ngx_http_log_handler(ngx_http_request_t *r) 255 | { 256 | u_char *line, *p; 257 | size_t len; 258 | ngx_uint_t i, l; 259 | ngx_http_log_t *log; 260 | ngx_http_log_op_t *op; 261 | ngx_http_log_buf_t *buffer; 262 | ngx_http_log_loc_conf_t *lcf; 263 | 264 | ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 265 | "http log handler"); 266 | 267 | lcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module); 268 | 269 | if (lcf->off) { 270 | return NGX_OK; 271 | } 272 | 273 | log = lcf->logs->elts; 274 | for (l = 0; l < lcf->logs->nelts; l++) { 275 | 276 | if (ngx_time() == log[l].disk_full_time) { 277 | 278 | /* 279 | * on FreeBSD writing to a full filesystem with enabled softupdates 280 | * may block process for much longer time than writing to non-full 281 | * filesystem, so we skip writing to a log for one second 282 | */ 283 | 284 | continue; 285 | } 286 | 287 | ngx_http_script_flush_no_cacheable_variables(r, log[l].format->flushes); 288 | 289 | len = 0; 290 | op = log[l].format->ops->elts; 291 | for (i = 0; i < log[l].format->ops->nelts; i++) { 292 | if (op[i].len == 0) { 293 | len += op[i].getlen(r, op[i].data); 294 | 295 | } else { 296 | len += op[i].len; 297 | } 298 | } 299 | 300 | len += NGX_LINEFEED_SIZE; 301 | 302 | buffer = log[l].file ? log[l].file->data : NULL; 303 | 304 | if (buffer) { 305 | 306 | if (len > (size_t) (buffer->last - buffer->pos)) { 307 | 308 | ngx_http_log_write(r, &log[l], buffer->start, 309 | buffer->pos - buffer->start); 310 | 311 | buffer->pos = buffer->start; 312 | } 313 | 314 | if (len <= (size_t) (buffer->last - buffer->pos)) { 315 | 316 | p = buffer->pos; 317 | 318 | if (buffer->event && p == buffer->start) { 319 | ngx_add_timer(buffer->event, buffer->flush); 320 | } 321 | 322 | for (i = 0; i < log[l].format->ops->nelts; i++) { 323 | p = op[i].run(r, p, &op[i]); 324 | } 325 | 326 | ngx_linefeed(p); 327 | 328 | buffer->pos = p; 329 | 330 | continue; 331 | } 332 | 333 | if (buffer->event && buffer->event->timer_set) { 334 | ngx_del_timer(buffer->event); 335 | } 336 | } 337 | 338 | line = ngx_pnalloc(r->pool, len); 339 | if (line == NULL) { 340 | return NGX_ERROR; 341 | } 342 | 343 | p = line; 344 | 345 | for (i = 0; i < log[l].format->ops->nelts; i++) { 346 | p = op[i].run(r, p, &op[i]); 347 | } 348 | 349 | ngx_linefeed(p); 350 | 351 | ngx_http_log_write(r, &log[l], line, p - line); 352 | } 353 | 354 | return NGX_OK; 355 | } 356 | 357 | 358 | static void 359 | ngx_http_log_write(ngx_http_request_t *r, ngx_http_log_t *log, u_char *buf, 360 | size_t len) 361 | { 362 | u_char *name; 363 | time_t now; 364 | ssize_t n; 365 | ngx_err_t err; 366 | #if (NGX_ZLIB) 367 | ngx_http_log_buf_t *buffer; 368 | #endif 369 | 370 | if (log->script == NULL) { 371 | name = log->file->name.data; 372 | 373 | #if (NGX_ZLIB) 374 | buffer = log->file->data; 375 | 376 | if (buffer && buffer->gzip) { 377 | n = ngx_http_log_gzip(log->file->fd, buf, len, buffer->gzip, 378 | r->connection->log); 379 | } else { 380 | n = ngx_write_fd(log->file->fd, buf, len); 381 | } 382 | #else 383 | n = ngx_write_fd(log->file->fd, buf, len); 384 | #endif 385 | 386 | } else { 387 | name = NULL; 388 | n = ngx_http_log_script_write(r, log->script, &name, buf, len); 389 | } 390 | 391 | if (n == (ssize_t) len) { 392 | return; 393 | } 394 | 395 | now = ngx_time(); 396 | 397 | if (n == -1) { 398 | err = ngx_errno; 399 | 400 | if (err == NGX_ENOSPC) { 401 | log->disk_full_time = now; 402 | } 403 | 404 | if (now - log->error_log_time > 59) { 405 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, err, 406 | ngx_write_fd_n " to \"%s\" failed", name); 407 | 408 | log->error_log_time = now; 409 | } 410 | 411 | return; 412 | } 413 | 414 | if (now - log->error_log_time > 59) { 415 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, 416 | ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", 417 | name, n, len); 418 | 419 | log->error_log_time = now; 420 | } 421 | } 422 | 423 | 424 | static ssize_t 425 | ngx_http_log_script_write(ngx_http_request_t *r, ngx_http_log_script_t *script, 426 | u_char **name, u_char *buf, size_t len) 427 | { 428 | size_t root; 429 | ssize_t n; 430 | ngx_str_t log, path; 431 | ngx_open_file_info_t of; 432 | ngx_http_log_loc_conf_t *llcf; 433 | ngx_http_core_loc_conf_t *clcf; 434 | 435 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); 436 | 437 | if (!r->root_tested) { 438 | 439 | /* test root directory existence */ 440 | 441 | if (ngx_http_map_uri_to_path(r, &path, &root, 0) == NULL) { 442 | /* simulate successful logging */ 443 | return len; 444 | } 445 | 446 | path.data[root] = '\0'; 447 | 448 | ngx_memzero(&of, sizeof(ngx_open_file_info_t)); 449 | 450 | of.valid = clcf->open_file_cache_valid; 451 | of.min_uses = clcf->open_file_cache_min_uses; 452 | of.test_dir = 1; 453 | of.test_only = 1; 454 | of.errors = clcf->open_file_cache_errors; 455 | of.events = clcf->open_file_cache_events; 456 | 457 | if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) { 458 | /* simulate successful logging */ 459 | return len; 460 | } 461 | 462 | if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) 463 | != NGX_OK) 464 | { 465 | if (of.err == 0) { 466 | /* simulate successful logging */ 467 | return len; 468 | } 469 | 470 | ngx_log_error(NGX_LOG_ERR, r->connection->log, of.err, 471 | "testing \"%s\" existence failed", path.data); 472 | 473 | /* simulate successful logging */ 474 | return len; 475 | } 476 | 477 | if (!of.is_dir) { 478 | ngx_log_error(NGX_LOG_ERR, r->connection->log, NGX_ENOTDIR, 479 | "testing \"%s\" existence failed", path.data); 480 | 481 | /* simulate successful logging */ 482 | return len; 483 | } 484 | } 485 | 486 | if (ngx_http_script_run(r, &log, script->lengths->elts, 1, 487 | script->values->elts) 488 | == NULL) 489 | { 490 | /* simulate successful logging */ 491 | return len; 492 | } 493 | 494 | log.data[log.len - 1] = '\0'; 495 | *name = log.data; 496 | 497 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 498 | "http log \"%s\"", log.data); 499 | 500 | llcf = ngx_http_get_module_loc_conf(r, ngx_http_log_module); 501 | 502 | ngx_memzero(&of, sizeof(ngx_open_file_info_t)); 503 | 504 | of.log = 1; 505 | of.valid = llcf->open_file_cache_valid; 506 | of.min_uses = llcf->open_file_cache_min_uses; 507 | of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; 508 | 509 | if (ngx_http_set_disable_symlinks(r, clcf, &log, &of) != NGX_OK) { 510 | /* simulate successful logging */ 511 | return len; 512 | } 513 | 514 | if (ngx_open_cached_file(llcf->open_file_cache, &log, &of, r->pool) 515 | != NGX_OK) 516 | { 517 | ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, 518 | "%s \"%s\" failed", of.failed, log.data); 519 | /* simulate successful logging */ 520 | return len; 521 | } 522 | 523 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, 524 | "http log #%d", of.fd); 525 | 526 | n = ngx_write_fd(of.fd, buf, len); 527 | 528 | return n; 529 | } 530 | 531 | 532 | #if (NGX_ZLIB) 533 | 534 | static ssize_t 535 | ngx_http_log_gzip(ngx_fd_t fd, u_char *buf, size_t len, ngx_int_t level, 536 | ngx_log_t *log) 537 | { 538 | int rc, wbits, memlevel; 539 | u_char *out; 540 | size_t size; 541 | ssize_t n; 542 | z_stream zstream; 543 | ngx_err_t err; 544 | ngx_pool_t *pool; 545 | 546 | wbits = MAX_WBITS; 547 | memlevel = MAX_MEM_LEVEL - 1; 548 | 549 | while ((ssize_t) len < ((1 << (wbits - 1)) - 262)) { 550 | wbits--; 551 | memlevel--; 552 | } 553 | 554 | /* 555 | * This is a formula from deflateBound() for conservative upper bound of 556 | * compressed data plus 18 bytes of gzip wrapper. 557 | */ 558 | 559 | size = len + ((len + 7) >> 3) + ((len + 63) >> 6) + 5 + 18; 560 | 561 | ngx_memzero(&zstream, sizeof(z_stream)); 562 | 563 | pool = ngx_create_pool(256, log); 564 | if (pool == NULL) { 565 | /* simulate successful logging */ 566 | return len; 567 | } 568 | 569 | pool->log = log; 570 | 571 | zstream.zalloc = ngx_http_log_gzip_alloc; 572 | zstream.zfree = ngx_http_log_gzip_free; 573 | zstream.opaque = pool; 574 | 575 | out = ngx_pnalloc(pool, size); 576 | if (out == NULL) { 577 | goto done; 578 | } 579 | 580 | zstream.next_in = buf; 581 | zstream.avail_in = len; 582 | zstream.next_out = out; 583 | zstream.avail_out = size; 584 | 585 | rc = deflateInit2(&zstream, (int) level, Z_DEFLATED, wbits + 16, memlevel, 586 | Z_DEFAULT_STRATEGY); 587 | 588 | if (rc != Z_OK) { 589 | ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateInit2() failed: %d", rc); 590 | goto done; 591 | } 592 | 593 | ngx_log_debug4(NGX_LOG_DEBUG_HTTP, log, 0, 594 | "deflate in: ni:%p no:%p ai:%ud ao:%ud", 595 | zstream.next_in, zstream.next_out, 596 | zstream.avail_in, zstream.avail_out); 597 | 598 | rc = deflate(&zstream, Z_FINISH); 599 | 600 | if (rc != Z_STREAM_END) { 601 | ngx_log_error(NGX_LOG_ALERT, log, 0, 602 | "deflate(Z_FINISH) failed: %d", rc); 603 | goto done; 604 | } 605 | 606 | ngx_log_debug5(NGX_LOG_DEBUG_HTTP, log, 0, 607 | "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", 608 | zstream.next_in, zstream.next_out, 609 | zstream.avail_in, zstream.avail_out, 610 | rc); 611 | 612 | size -= zstream.avail_out; 613 | 614 | rc = deflateEnd(&zstream); 615 | 616 | if (rc != Z_OK) { 617 | ngx_log_error(NGX_LOG_ALERT, log, 0, "deflateEnd() failed: %d", rc); 618 | goto done; 619 | } 620 | 621 | n = ngx_write_fd(fd, out, size); 622 | 623 | if (n != (ssize_t) size) { 624 | err = (n == -1) ? ngx_errno : 0; 625 | 626 | ngx_destroy_pool(pool); 627 | 628 | ngx_set_errno(err); 629 | return -1; 630 | } 631 | 632 | done: 633 | 634 | ngx_destroy_pool(pool); 635 | 636 | /* simulate successful logging */ 637 | return len; 638 | } 639 | 640 | 641 | static void * 642 | ngx_http_log_gzip_alloc(void *opaque, u_int items, u_int size) 643 | { 644 | ngx_pool_t *pool = opaque; 645 | 646 | ngx_log_debug2(NGX_LOG_DEBUG_HTTP, pool->log, 0, 647 | "gzip alloc: n:%ud s:%ud", items, size); 648 | 649 | return ngx_palloc(pool, items * size); 650 | } 651 | 652 | 653 | static void 654 | ngx_http_log_gzip_free(void *opaque, void *address) 655 | { 656 | #if 0 657 | ngx_pool_t *pool = opaque; 658 | 659 | ngx_log_debug1(NGX_LOG_DEBUG_HTTP, pool->log, 0, "gzip free: %p", address); 660 | #endif 661 | } 662 | 663 | #endif 664 | 665 | 666 | static void 667 | ngx_http_log_flush(ngx_open_file_t *file, ngx_log_t *log) 668 | { 669 | size_t len; 670 | ssize_t n; 671 | ngx_http_log_buf_t *buffer; 672 | 673 | buffer = file->data; 674 | 675 | len = buffer->pos - buffer->start; 676 | 677 | if (len == 0) { 678 | return; 679 | } 680 | 681 | #if (NGX_ZLIB) 682 | if (buffer->gzip) { 683 | n = ngx_http_log_gzip(file->fd, buffer->start, len, buffer->gzip, log); 684 | } else { 685 | n = ngx_write_fd(file->fd, buffer->start, len); 686 | } 687 | #else 688 | n = ngx_write_fd(file->fd, buffer->start, len); 689 | #endif 690 | 691 | if (n == -1) { 692 | ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, 693 | ngx_write_fd_n " to \"%s\" failed", 694 | file->name.data); 695 | 696 | } else if ((size_t) n != len) { 697 | ngx_log_error(NGX_LOG_ALERT, log, 0, 698 | ngx_write_fd_n " to \"%s\" was incomplete: %z of %uz", 699 | file->name.data, n, len); 700 | } 701 | 702 | buffer->pos = buffer->start; 703 | 704 | if (buffer->event && buffer->event->timer_set) { 705 | ngx_del_timer(buffer->event); 706 | } 707 | } 708 | 709 | 710 | static void 711 | ngx_http_log_flush_handler(ngx_event_t *ev) 712 | { 713 | ngx_log_debug0(NGX_LOG_DEBUG_EVENT, ev->log, 0, 714 | "http log buffer flush handler"); 715 | 716 | ngx_http_log_flush(ev->data, ev->log); 717 | } 718 | 719 | 720 | static u_char * 721 | ngx_http_log_copy_short(ngx_http_request_t *r, u_char *buf, 722 | ngx_http_log_op_t *op) 723 | { 724 | size_t len; 725 | uintptr_t data; 726 | 727 | len = op->len; 728 | data = op->data; 729 | 730 | while (len--) { 731 | *buf++ = (u_char) (data & 0xff); 732 | data >>= 8; 733 | } 734 | 735 | return buf; 736 | } 737 | 738 | 739 | static u_char * 740 | ngx_http_log_copy_long(ngx_http_request_t *r, u_char *buf, 741 | ngx_http_log_op_t *op) 742 | { 743 | return ngx_cpymem(buf, (u_char *) op->data, op->len); 744 | } 745 | 746 | 747 | static u_char * 748 | ngx_http_log_pipe(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) 749 | { 750 | if (r->pipeline) { 751 | *buf = 'p'; 752 | } else { 753 | *buf = '.'; 754 | } 755 | 756 | return buf + 1; 757 | } 758 | 759 | 760 | static u_char * 761 | ngx_http_log_time(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) 762 | { 763 | return ngx_cpymem(buf, ngx_cached_http_log_time.data, 764 | ngx_cached_http_log_time.len); 765 | } 766 | 767 | static u_char * 768 | ngx_http_log_iso8601(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) 769 | { 770 | return ngx_cpymem(buf, ngx_cached_http_log_iso8601.data, 771 | ngx_cached_http_log_iso8601.len); 772 | } 773 | 774 | static u_char * 775 | ngx_http_log_msec(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) 776 | { 777 | ngx_time_t *tp; 778 | 779 | tp = ngx_timeofday(); 780 | 781 | return ngx_sprintf(buf, "%T.%03M", tp->sec, tp->msec); 782 | } 783 | 784 | 785 | static u_char * 786 | ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf, 787 | ngx_http_log_op_t *op) 788 | { 789 | ngx_time_t *tp; 790 | ngx_msec_int_t ms; 791 | 792 | tp = ngx_timeofday(); 793 | 794 | ms = (ngx_msec_int_t) 795 | ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec)); 796 | ms = ngx_max(ms, 0); 797 | 798 | return ngx_sprintf(buf, "%T.%03M", (time_t) ms / 1000, ms % 1000); 799 | } 800 | 801 | 802 | static u_char * 803 | ngx_http_log_status(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) 804 | { 805 | ngx_uint_t status; 806 | 807 | if (r->err_status) { 808 | status = r->err_status; 809 | 810 | } else if (r->headers_out.status) { 811 | status = r->headers_out.status; 812 | 813 | } else if (r->http_version == NGX_HTTP_VERSION_9) { 814 | status = 9; 815 | 816 | } else { 817 | status = 0; 818 | } 819 | 820 | return ngx_sprintf(buf, "%03ui", status); 821 | } 822 | 823 | 824 | static u_char * 825 | ngx_http_log_bytes_sent(ngx_http_request_t *r, u_char *buf, 826 | ngx_http_log_op_t *op) 827 | { 828 | return ngx_sprintf(buf, "%O", r->connection->sent); 829 | } 830 | 831 | 832 | /* 833 | * although there is a real $body_bytes_sent variable, 834 | * this log operation code function is more optimized for logging 835 | */ 836 | 837 | static u_char * 838 | ngx_http_log_body_bytes_sent(ngx_http_request_t *r, u_char *buf, 839 | ngx_http_log_op_t *op) 840 | { 841 | off_t length; 842 | 843 | length = r->connection->sent - r->header_size; 844 | 845 | if (length > 0) { 846 | return ngx_sprintf(buf, "%O", length); 847 | } 848 | 849 | *buf = '0'; 850 | 851 | return buf + 1; 852 | } 853 | 854 | 855 | static u_char * 856 | ngx_http_log_request_length(ngx_http_request_t *r, u_char *buf, 857 | ngx_http_log_op_t *op) 858 | { 859 | return ngx_sprintf(buf, "%O", r->request_length); 860 | } 861 | 862 | 863 | static ngx_int_t 864 | ngx_http_log_variable_compile(ngx_conf_t *cf, ngx_http_log_op_t *op, 865 | ngx_str_t *value, ngx_uint_t escape) 866 | { 867 | ngx_int_t index; 868 | 869 | index = ngx_http_get_variable_index(cf, value); 870 | if (index == NGX_ERROR) { 871 | return NGX_ERROR; 872 | } 873 | 874 | op->len = 0; 875 | 876 | switch (escape) { 877 | case NGX_HTTP_LOG_ESCAPE_JSON: 878 | op->getlen = ngx_http_log_json_variable_getlen; 879 | op->run = ngx_http_log_json_variable; 880 | break; 881 | 882 | case NGX_HTTP_LOG_ESCAPE_NONE: 883 | op->getlen = ngx_http_log_unescaped_variable_getlen; 884 | op->run = ngx_http_log_unescaped_variable; 885 | break; 886 | 887 | default: /* NGX_HTTP_LOG_ESCAPE_DEFAULT */ 888 | op->getlen = ngx_http_log_variable_getlen; 889 | op->run = ngx_http_log_variable; 890 | } 891 | 892 | op->data = index; 893 | 894 | return NGX_OK; 895 | } 896 | 897 | 898 | static size_t 899 | ngx_http_log_variable_getlen(ngx_http_request_t *r, uintptr_t data) 900 | { 901 | uintptr_t len; 902 | ngx_http_variable_value_t *value; 903 | 904 | value = ngx_http_get_indexed_variable(r, data); 905 | 906 | if (value == NULL || value->not_found) { 907 | return 1; 908 | } 909 | 910 | len = ngx_http_log_escape(NULL, value->data, value->len); 911 | 912 | value->escape = len ? 1 : 0; 913 | 914 | return value->len + len * 3; 915 | } 916 | 917 | 918 | static u_char * 919 | ngx_http_log_variable(ngx_http_request_t *r, u_char *buf, ngx_http_log_op_t *op) 920 | { 921 | ngx_http_variable_value_t *value; 922 | 923 | value = ngx_http_get_indexed_variable(r, op->data); 924 | 925 | if (value == NULL || value->not_found) { 926 | *buf = '-'; 927 | return buf + 1; 928 | } 929 | 930 | if (value->escape == 0) { 931 | return ngx_cpymem(buf, value->data, value->len); 932 | 933 | } else { 934 | return (u_char *) ngx_http_log_escape(buf, value->data, value->len); 935 | } 936 | } 937 | 938 | 939 | static uintptr_t 940 | ngx_http_log_escape(u_char *dst, u_char *src, size_t size) 941 | { 942 | ngx_uint_t n; 943 | static u_char hex[] = "0123456789ABCDEF"; 944 | 945 | static uint32_t escape[] = { 946 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 947 | 948 | /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ 949 | 0x00000004, /* 0000 0000 0000 0000 0000 0000 0000 0100 */ 950 | 951 | /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ 952 | 0x10000000, /* 0001 0000 0000 0000 0000 0000 0000 0000 */ 953 | 954 | /* ~}| {zyx wvut srqp onml kjih gfed cba` */ 955 | 0x80000000, /* 1000 0000 0000 0000 0000 0000 0000 0000 */ 956 | 957 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 958 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 959 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 960 | 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ 961 | }; 962 | 963 | 964 | if (dst == NULL) { 965 | 966 | /* find the number of the characters to be escaped */ 967 | 968 | n = 0; 969 | 970 | while (size) { 971 | if (escape[*src >> 5] & (1 << (*src & 0x1f))) { 972 | n++; 973 | } 974 | src++; 975 | size--; 976 | } 977 | 978 | return (uintptr_t) n; 979 | } 980 | 981 | while (size) { 982 | if (escape[*src >> 5] & (1 << (*src & 0x1f))) { 983 | *dst++ = '\\'; 984 | *dst++ = 'x'; 985 | *dst++ = hex[*src >> 4]; 986 | *dst++ = hex[*src & 0xf]; 987 | src++; 988 | 989 | } else { 990 | *dst++ = *src++; 991 | } 992 | size--; 993 | } 994 | 995 | return (uintptr_t) dst; 996 | } 997 | 998 | 999 | static size_t 1000 | ngx_http_log_json_variable_getlen(ngx_http_request_t *r, uintptr_t data) 1001 | { 1002 | uintptr_t len; 1003 | ngx_http_variable_value_t *value; 1004 | 1005 | value = ngx_http_get_indexed_variable(r, data); 1006 | 1007 | if (value == NULL || value->not_found) { 1008 | return 0; 1009 | } 1010 | 1011 | len = ngx_escape_json(NULL, value->data, value->len); 1012 | 1013 | value->escape = len ? 1 : 0; 1014 | 1015 | return value->len + len; 1016 | } 1017 | 1018 | 1019 | static u_char * 1020 | ngx_http_log_json_variable(ngx_http_request_t *r, u_char *buf, 1021 | ngx_http_log_op_t *op) 1022 | { 1023 | ngx_http_variable_value_t *value; 1024 | 1025 | value = ngx_http_get_indexed_variable(r, op->data); 1026 | 1027 | if (value == NULL || value->not_found) { 1028 | return buf; 1029 | } 1030 | 1031 | if (value->escape == 0) { 1032 | return ngx_cpymem(buf, value->data, value->len); 1033 | 1034 | } else { 1035 | return (u_char *) ngx_escape_json(buf, value->data, value->len); 1036 | } 1037 | } 1038 | 1039 | 1040 | static size_t 1041 | ngx_http_log_unescaped_variable_getlen(ngx_http_request_t *r, uintptr_t data) 1042 | { 1043 | ngx_http_variable_value_t *value; 1044 | 1045 | value = ngx_http_get_indexed_variable(r, data); 1046 | 1047 | if (value == NULL || value->not_found) { 1048 | return 0; 1049 | } 1050 | 1051 | value->escape = 0; 1052 | 1053 | return value->len; 1054 | } 1055 | 1056 | 1057 | static u_char * 1058 | ngx_http_log_unescaped_variable(ngx_http_request_t *r, u_char *buf, 1059 | ngx_http_log_op_t *op) 1060 | { 1061 | ngx_http_variable_value_t *value; 1062 | 1063 | value = ngx_http_get_indexed_variable(r, op->data); 1064 | 1065 | if (value == NULL || value->not_found) { 1066 | return buf; 1067 | } 1068 | 1069 | return ngx_cpymem(buf, value->data, value->len); 1070 | } 1071 | 1072 | 1073 | static void * 1074 | ngx_http_log_create_main_conf(ngx_conf_t *cf) 1075 | { 1076 | ngx_http_log_main_conf_t *conf; 1077 | 1078 | ngx_http_log_fmt_t *fmt; 1079 | 1080 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_main_conf_t)); 1081 | if (conf == NULL) { 1082 | return NULL; 1083 | } 1084 | 1085 | if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t)) 1086 | != NGX_OK) 1087 | { 1088 | return NULL; 1089 | } 1090 | 1091 | fmt = ngx_array_push(&conf->formats); 1092 | if (fmt == NULL) { 1093 | return NULL; 1094 | } 1095 | 1096 | ngx_str_set(&fmt->name, "combined"); 1097 | 1098 | fmt->flushes = NULL; 1099 | 1100 | fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t)); 1101 | if (fmt->ops == NULL) { 1102 | return NULL; 1103 | } 1104 | 1105 | return conf; 1106 | } 1107 | 1108 | 1109 | static void * 1110 | ngx_http_log_create_loc_conf(ngx_conf_t *cf) 1111 | { 1112 | ngx_http_log_loc_conf_t *conf; 1113 | 1114 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_loc_conf_t)); 1115 | if (conf == NULL) { 1116 | return NULL; 1117 | } 1118 | 1119 | conf->open_file_cache = NGX_CONF_UNSET_PTR; 1120 | 1121 | return conf; 1122 | } 1123 | 1124 | 1125 | static char * 1126 | ngx_http_log_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 1127 | { 1128 | ngx_http_log_loc_conf_t *prev = parent; 1129 | ngx_http_log_loc_conf_t *conf = child; 1130 | 1131 | ngx_http_log_t *log; 1132 | ngx_http_log_fmt_t *fmt; 1133 | ngx_http_log_main_conf_t *lmcf; 1134 | 1135 | if (conf->open_file_cache == NGX_CONF_UNSET_PTR) { 1136 | 1137 | conf->open_file_cache = prev->open_file_cache; 1138 | conf->open_file_cache_valid = prev->open_file_cache_valid; 1139 | conf->open_file_cache_min_uses = prev->open_file_cache_min_uses; 1140 | 1141 | if (conf->open_file_cache == NGX_CONF_UNSET_PTR) { 1142 | conf->open_file_cache = NULL; 1143 | } 1144 | } 1145 | 1146 | if (conf->logs || conf->off) { 1147 | return NGX_CONF_OK; 1148 | } 1149 | 1150 | conf->logs = prev->logs; 1151 | conf->off = prev->off; 1152 | 1153 | if (conf->logs || conf->off) { 1154 | return NGX_CONF_OK; 1155 | } 1156 | 1157 | conf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t)); 1158 | if (conf->logs == NULL) { 1159 | return NGX_CONF_ERROR; 1160 | } 1161 | 1162 | log = ngx_array_push(conf->logs); 1163 | if (log == NULL) { 1164 | return NGX_CONF_ERROR; 1165 | } 1166 | 1167 | log->file = ngx_conf_open_file(cf->cycle, &ngx_http_access_log); 1168 | if (log->file == NULL) { 1169 | return NGX_CONF_ERROR; 1170 | } 1171 | 1172 | log->script = NULL; 1173 | log->disk_full_time = 0; 1174 | log->error_log_time = 0; 1175 | 1176 | lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module); 1177 | fmt = lmcf->formats.elts; 1178 | 1179 | /* the default "combined" format */ 1180 | log->format = &fmt[0]; 1181 | lmcf->combined_used = 1; 1182 | 1183 | return NGX_CONF_OK; 1184 | } 1185 | 1186 | 1187 | static char * 1188 | ngx_http_log_set_log(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 1189 | { 1190 | ngx_http_log_loc_conf_t *llcf = conf; 1191 | 1192 | ssize_t size; 1193 | ngx_int_t gzip; 1194 | ngx_uint_t i, n; 1195 | ngx_msec_t flush; 1196 | ngx_str_t *value, name, s; 1197 | ngx_http_log_t *log; 1198 | ngx_http_log_buf_t *buffer; 1199 | ngx_http_log_fmt_t *fmt; 1200 | ngx_http_log_main_conf_t *lmcf; 1201 | ngx_http_script_compile_t sc; 1202 | 1203 | value = cf->args->elts; 1204 | 1205 | if (ngx_strcmp(value[1].data, "off") == 0) { 1206 | llcf->off = 1; 1207 | if (cf->args->nelts == 2) { 1208 | return NGX_CONF_OK; 1209 | } 1210 | 1211 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1212 | "invalid parameter \"%V\"", &value[2]); 1213 | return NGX_CONF_ERROR; 1214 | } 1215 | 1216 | if (llcf->logs == NULL) { 1217 | llcf->logs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_log_t)); 1218 | if (llcf->logs == NULL) { 1219 | return NGX_CONF_ERROR; 1220 | } 1221 | } 1222 | 1223 | lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module); 1224 | 1225 | log = ngx_array_push(llcf->logs); 1226 | if (log == NULL) { 1227 | return NGX_CONF_ERROR; 1228 | } 1229 | 1230 | ngx_memzero(log, sizeof(ngx_http_log_t)); 1231 | 1232 | n = ngx_http_script_variables_count(&value[1]); 1233 | 1234 | if (n == 0) { 1235 | log->file = ngx_conf_open_file(cf->cycle, &value[1]); 1236 | if (log->file == NULL) { 1237 | return NGX_CONF_ERROR; 1238 | } 1239 | 1240 | } else { 1241 | if (ngx_conf_full_name(cf->cycle, &value[1], 0) != NGX_OK) { 1242 | return NGX_CONF_ERROR; 1243 | } 1244 | 1245 | log->script = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_script_t)); 1246 | if (log->script == NULL) { 1247 | return NGX_CONF_ERROR; 1248 | } 1249 | 1250 | ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); 1251 | 1252 | sc.cf = cf; 1253 | sc.source = &value[1]; 1254 | sc.lengths = &log->script->lengths; 1255 | sc.values = &log->script->values; 1256 | sc.variables = n; 1257 | sc.complete_lengths = 1; 1258 | sc.complete_values = 1; 1259 | 1260 | if (ngx_http_script_compile(&sc) != NGX_OK) { 1261 | return NGX_CONF_ERROR; 1262 | } 1263 | } 1264 | 1265 | if (cf->args->nelts >= 3) { 1266 | name = value[2]; 1267 | 1268 | if (ngx_strcmp(name.data, "combined") == 0) { 1269 | lmcf->combined_used = 1; 1270 | } 1271 | 1272 | } else { 1273 | ngx_str_set(&name, "combined"); 1274 | lmcf->combined_used = 1; 1275 | } 1276 | 1277 | fmt = lmcf->formats.elts; 1278 | for (i = 0; i < lmcf->formats.nelts; i++) { 1279 | if (fmt[i].name.len == name.len 1280 | && ngx_strcasecmp(fmt[i].name.data, name.data) == 0) 1281 | { 1282 | log->format = &fmt[i]; 1283 | break; 1284 | } 1285 | } 1286 | 1287 | if (log->format == NULL) { 1288 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1289 | "unknown log format \"%V\"", &name); 1290 | return NGX_CONF_ERROR; 1291 | } 1292 | 1293 | size = 0; 1294 | flush = 0; 1295 | gzip = 0; 1296 | 1297 | for (i = 3; i < cf->args->nelts; i++) { 1298 | 1299 | if (ngx_strncmp(value[i].data, "buffer=", 7) == 0) { 1300 | s.len = value[i].len - 7; 1301 | s.data = value[i].data + 7; 1302 | 1303 | size = ngx_parse_size(&s); 1304 | 1305 | if (size == NGX_ERROR || size == 0) { 1306 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1307 | "invalid buffer size \"%V\"", &s); 1308 | return NGX_CONF_ERROR; 1309 | } 1310 | 1311 | continue; 1312 | } 1313 | 1314 | if (ngx_strncmp(value[i].data, "flush=", 6) == 0) { 1315 | s.len = value[i].len - 6; 1316 | s.data = value[i].data + 6; 1317 | 1318 | flush = ngx_parse_time(&s, 0); 1319 | 1320 | if (flush == (ngx_msec_t) NGX_ERROR || flush == 0) { 1321 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1322 | "invalid flush time \"%V\"", &s); 1323 | return NGX_CONF_ERROR; 1324 | } 1325 | 1326 | continue; 1327 | } 1328 | 1329 | if (ngx_strncmp(value[i].data, "gzip", 4) == 0 1330 | && (value[i].len == 4 || value[i].data[4] == '=')) 1331 | { 1332 | #if (NGX_ZLIB) 1333 | if (size == 0) { 1334 | size = 64 * 1024; 1335 | } 1336 | 1337 | if (value[i].len == 4) { 1338 | gzip = Z_BEST_SPEED; 1339 | continue; 1340 | } 1341 | 1342 | s.len = value[i].len - 5; 1343 | s.data = value[i].data + 5; 1344 | 1345 | gzip = ngx_atoi(s.data, s.len); 1346 | 1347 | if (gzip < 1 || gzip > 9) { 1348 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1349 | "invalid compression level \"%V\"", &s); 1350 | return NGX_CONF_ERROR; 1351 | } 1352 | 1353 | continue; 1354 | 1355 | #else 1356 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1357 | "nginx was built without zlib support"); 1358 | return NGX_CONF_ERROR; 1359 | #endif 1360 | } 1361 | 1362 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1363 | "invalid parameter \"%V\"", &value[i]); 1364 | return NGX_CONF_ERROR; 1365 | } 1366 | 1367 | if (flush && size == 0) { 1368 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1369 | "no buffer is defined for access_log \"%V\"", 1370 | &value[1]); 1371 | return NGX_CONF_ERROR; 1372 | } 1373 | 1374 | if (size) { 1375 | 1376 | if (log->script) { 1377 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1378 | "buffered logs cannot have variables in name"); 1379 | return NGX_CONF_ERROR; 1380 | } 1381 | 1382 | if (log->file->data) { 1383 | buffer = log->file->data; 1384 | 1385 | if (buffer->last - buffer->start != size 1386 | || buffer->flush != flush 1387 | || buffer->gzip != gzip) 1388 | { 1389 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1390 | "access_log \"%V\" already defined " 1391 | "with conflicting parameters", 1392 | &value[1]); 1393 | return NGX_CONF_ERROR; 1394 | } 1395 | 1396 | return NGX_CONF_OK; 1397 | } 1398 | 1399 | buffer = ngx_pcalloc(cf->pool, sizeof(ngx_http_log_buf_t)); 1400 | if (buffer == NULL) { 1401 | return NGX_CONF_ERROR; 1402 | } 1403 | 1404 | buffer->start = ngx_pnalloc(cf->pool, size); 1405 | if (buffer->start == NULL) { 1406 | return NGX_CONF_ERROR; 1407 | } 1408 | 1409 | buffer->pos = buffer->start; 1410 | buffer->last = buffer->start + size; 1411 | 1412 | if (flush) { 1413 | buffer->event = ngx_pcalloc(cf->pool, sizeof(ngx_event_t)); 1414 | if (buffer->event == NULL) { 1415 | return NGX_CONF_ERROR; 1416 | } 1417 | 1418 | buffer->event->data = log->file; 1419 | buffer->event->handler = ngx_http_log_flush_handler; 1420 | buffer->event->log = &cf->cycle->new_log; 1421 | 1422 | buffer->flush = flush; 1423 | } 1424 | 1425 | buffer->gzip = gzip; 1426 | 1427 | log->file->flush = ngx_http_log_flush; 1428 | log->file->data = buffer; 1429 | } 1430 | 1431 | return NGX_CONF_OK; 1432 | } 1433 | 1434 | 1435 | static char * 1436 | ngx_http_log_set_format(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 1437 | { 1438 | ngx_http_log_main_conf_t *lmcf = conf; 1439 | 1440 | ngx_str_t *value; 1441 | ngx_uint_t i; 1442 | ngx_http_log_fmt_t *fmt; 1443 | 1444 | if (cf->cmd_type != NGX_HTTP_MAIN_CONF) { 1445 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 1446 | "the \"log_format\" directive may be used " 1447 | "only on \"http\" level"); 1448 | } 1449 | 1450 | value = cf->args->elts; 1451 | 1452 | fmt = lmcf->formats.elts; 1453 | for (i = 0; i < lmcf->formats.nelts; i++) { 1454 | if (fmt[i].name.len == value[1].len 1455 | && ngx_strcmp(fmt[i].name.data, value[1].data) == 0) 1456 | { 1457 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1458 | "duplicate \"log_format\" name \"%V\"", 1459 | &value[1]); 1460 | return NGX_CONF_ERROR; 1461 | } 1462 | } 1463 | 1464 | fmt = ngx_array_push(&lmcf->formats); 1465 | if (fmt == NULL) { 1466 | return NGX_CONF_ERROR; 1467 | } 1468 | 1469 | fmt->name = value[1]; 1470 | 1471 | fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t)); 1472 | if (fmt->flushes == NULL) { 1473 | return NGX_CONF_ERROR; 1474 | } 1475 | 1476 | fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t)); 1477 | if (fmt->ops == NULL) { 1478 | return NGX_CONF_ERROR; 1479 | } 1480 | 1481 | return ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2); 1482 | } 1483 | 1484 | 1485 | static char * 1486 | ngx_http_log_compile_format(ngx_conf_t *cf, ngx_array_t *flushes, 1487 | ngx_array_t *ops, ngx_array_t *args, ngx_uint_t s) 1488 | { 1489 | u_char *data, *p, ch; 1490 | size_t i, len; 1491 | ngx_str_t *value, var; 1492 | ngx_int_t *flush; 1493 | ngx_uint_t bracket, escape; 1494 | ngx_http_log_op_t *op; 1495 | ngx_http_log_var_t *v; 1496 | 1497 | escape = NGX_HTTP_LOG_ESCAPE_DEFAULT; 1498 | value = args->elts; 1499 | 1500 | if (s < args->nelts && ngx_strncmp(value[s].data, "escape=", 7) == 0) { 1501 | data = value[s].data + 7; 1502 | 1503 | if (ngx_strcmp(data, "json") == 0) { 1504 | escape = NGX_HTTP_LOG_ESCAPE_JSON; 1505 | 1506 | } else if (ngx_strcmp(data, "none") == 0) { 1507 | escape = NGX_HTTP_LOG_ESCAPE_NONE; 1508 | 1509 | } else if (ngx_strcmp(data, "default") != 0) { 1510 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1511 | "unknown log format escaping \"%s\"", data); 1512 | return NGX_CONF_ERROR; 1513 | } 1514 | 1515 | s++; 1516 | } 1517 | 1518 | for ( /* void */ ; s < args->nelts; s++) { 1519 | 1520 | i = 0; 1521 | 1522 | while (i < value[s].len) { 1523 | 1524 | op = ngx_array_push(ops); 1525 | if (op == NULL) { 1526 | return NGX_CONF_ERROR; 1527 | } 1528 | 1529 | data = &value[s].data[i]; 1530 | 1531 | if (value[s].data[i] == '$') { 1532 | 1533 | if (++i == value[s].len) { 1534 | goto invalid; 1535 | } 1536 | 1537 | if (value[s].data[i] == '{') { 1538 | bracket = 1; 1539 | 1540 | if (++i == value[s].len) { 1541 | goto invalid; 1542 | } 1543 | 1544 | var.data = &value[s].data[i]; 1545 | 1546 | } else { 1547 | bracket = 0; 1548 | var.data = &value[s].data[i]; 1549 | } 1550 | 1551 | for (var.len = 0; i < value[s].len; i++, var.len++) { 1552 | ch = value[s].data[i]; 1553 | 1554 | if (ch == '}' && bracket) { 1555 | i++; 1556 | bracket = 0; 1557 | break; 1558 | } 1559 | 1560 | if ((ch >= 'A' && ch <= 'Z') 1561 | || (ch >= 'a' && ch <= 'z') 1562 | || (ch >= '0' && ch <= '9') 1563 | || ch == '_') 1564 | { 1565 | continue; 1566 | } 1567 | 1568 | break; 1569 | } 1570 | 1571 | if (bracket) { 1572 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1573 | "the closing bracket in \"%V\" " 1574 | "variable is missing", &var); 1575 | return NGX_CONF_ERROR; 1576 | } 1577 | 1578 | if (var.len == 0) { 1579 | goto invalid; 1580 | } 1581 | 1582 | for (v = ngx_http_log_vars; v->name.len; v++) { 1583 | 1584 | if (v->name.len == var.len 1585 | && ngx_strncmp(v->name.data, var.data, var.len) == 0) 1586 | { 1587 | op->len = v->len; 1588 | op->getlen = NULL; 1589 | op->run = v->run; 1590 | op->data = 0; 1591 | 1592 | goto found; 1593 | } 1594 | } 1595 | 1596 | if (ngx_http_log_variable_compile(cf, op, &var, escape) 1597 | != NGX_OK) 1598 | { 1599 | return NGX_CONF_ERROR; 1600 | } 1601 | 1602 | if (flushes) { 1603 | 1604 | flush = ngx_array_push(flushes); 1605 | if (flush == NULL) { 1606 | return NGX_CONF_ERROR; 1607 | } 1608 | 1609 | *flush = op->data; /* variable index */ 1610 | } 1611 | 1612 | found: 1613 | 1614 | continue; 1615 | } 1616 | 1617 | i++; 1618 | 1619 | while (i < value[s].len && value[s].data[i] != '$') { 1620 | i++; 1621 | } 1622 | 1623 | len = &value[s].data[i] - data; 1624 | 1625 | if (len) { 1626 | 1627 | op->len = len; 1628 | op->getlen = NULL; 1629 | 1630 | if (len <= sizeof(uintptr_t)) { 1631 | op->run = ngx_http_log_copy_short; 1632 | op->data = 0; 1633 | 1634 | while (len--) { 1635 | op->data <<= 8; 1636 | op->data |= data[len]; 1637 | } 1638 | 1639 | } else { 1640 | op->run = ngx_http_log_copy_long; 1641 | 1642 | p = ngx_pnalloc(cf->pool, len); 1643 | if (p == NULL) { 1644 | return NGX_CONF_ERROR; 1645 | } 1646 | 1647 | ngx_memcpy(p, data, len); 1648 | op->data = (uintptr_t) p; 1649 | } 1650 | } 1651 | } 1652 | } 1653 | 1654 | return NGX_CONF_OK; 1655 | 1656 | invalid: 1657 | 1658 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%s\"", data); 1659 | 1660 | return NGX_CONF_ERROR; 1661 | } 1662 | 1663 | 1664 | static char * 1665 | ngx_http_log_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 1666 | { 1667 | ngx_http_log_loc_conf_t *llcf = conf; 1668 | 1669 | time_t inactive, valid; 1670 | ngx_str_t *value, s; 1671 | ngx_int_t max, min_uses; 1672 | ngx_uint_t i; 1673 | 1674 | if (llcf->open_file_cache != NGX_CONF_UNSET_PTR) { 1675 | return "is duplicate"; 1676 | } 1677 | 1678 | value = cf->args->elts; 1679 | 1680 | max = 0; 1681 | inactive = 10; 1682 | valid = 60; 1683 | min_uses = 1; 1684 | 1685 | for (i = 1; i < cf->args->nelts; i++) { 1686 | 1687 | if (ngx_strncmp(value[i].data, "max=", 4) == 0) { 1688 | 1689 | max = ngx_atoi(value[i].data + 4, value[i].len - 4); 1690 | if (max == NGX_ERROR) { 1691 | goto failed; 1692 | } 1693 | 1694 | continue; 1695 | } 1696 | 1697 | if (ngx_strncmp(value[i].data, "inactive=", 9) == 0) { 1698 | 1699 | s.len = value[i].len - 9; 1700 | s.data = value[i].data + 9; 1701 | 1702 | inactive = ngx_parse_time(&s, 1); 1703 | if (inactive == (time_t) NGX_ERROR) { 1704 | goto failed; 1705 | } 1706 | 1707 | continue; 1708 | } 1709 | 1710 | if (ngx_strncmp(value[i].data, "min_uses=", 9) == 0) { 1711 | 1712 | min_uses = ngx_atoi(value[i].data + 9, value[i].len - 9); 1713 | if (min_uses == NGX_ERROR) { 1714 | goto failed; 1715 | } 1716 | 1717 | continue; 1718 | } 1719 | 1720 | if (ngx_strncmp(value[i].data, "valid=", 6) == 0) { 1721 | 1722 | s.len = value[i].len - 6; 1723 | s.data = value[i].data + 6; 1724 | 1725 | valid = ngx_parse_time(&s, 1); 1726 | if (valid == (time_t) NGX_ERROR) { 1727 | goto failed; 1728 | } 1729 | 1730 | continue; 1731 | } 1732 | 1733 | if (ngx_strcmp(value[i].data, "off") == 0) { 1734 | 1735 | llcf->open_file_cache = NULL; 1736 | 1737 | continue; 1738 | } 1739 | 1740 | failed: 1741 | 1742 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1743 | "invalid \"open_log_file_cache\" parameter \"%V\"", 1744 | &value[i]); 1745 | return NGX_CONF_ERROR; 1746 | } 1747 | 1748 | if (llcf->open_file_cache == NULL) { 1749 | return NGX_CONF_OK; 1750 | } 1751 | 1752 | if (max == 0) { 1753 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 1754 | "\"open_log_file_cache\" must have \"max\" parameter"); 1755 | return NGX_CONF_ERROR; 1756 | } 1757 | 1758 | llcf->open_file_cache = ngx_open_file_cache_init(cf->pool, max, inactive); 1759 | 1760 | if (llcf->open_file_cache) { 1761 | 1762 | llcf->open_file_cache_valid = valid; 1763 | llcf->open_file_cache_min_uses = min_uses; 1764 | 1765 | return NGX_CONF_OK; 1766 | } 1767 | 1768 | return NGX_CONF_ERROR; 1769 | } 1770 | 1771 | 1772 | static ngx_int_t 1773 | ngx_http_log_init(ngx_conf_t *cf) 1774 | { 1775 | ngx_str_t *value; 1776 | ngx_array_t a; 1777 | ngx_http_handler_pt *h; 1778 | ngx_http_log_fmt_t *fmt; 1779 | ngx_http_log_main_conf_t *lmcf; 1780 | ngx_http_core_main_conf_t *cmcf; 1781 | 1782 | lmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_log_module); 1783 | 1784 | if (lmcf->combined_used) { 1785 | if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) { 1786 | return NGX_ERROR; 1787 | } 1788 | 1789 | value = ngx_array_push(&a); 1790 | if (value == NULL) { 1791 | return NGX_ERROR; 1792 | } 1793 | 1794 | *value = ngx_http_combined_fmt; 1795 | fmt = lmcf->formats.elts; 1796 | 1797 | if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0) 1798 | != NGX_CONF_OK) 1799 | { 1800 | return NGX_ERROR; 1801 | } 1802 | } 1803 | 1804 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 1805 | 1806 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); 1807 | if (h == NULL) { 1808 | return NGX_ERROR; 1809 | } 1810 | 1811 | *h = ngx_http_log_handler; 1812 | 1813 | return NGX_OK; 1814 | } 1815 | 1816 | /* 1817 | * Copyright (C) Masaya YAMAMOTO 1818 | * Copyright (C) KLab Inc. 1819 | */ 1820 | 1821 | #include 1822 | #include 1823 | #include 1824 | 1825 | #define MODULE_NAME "ngx_http_pipelog_module" 1826 | #define LOGGER_PROC_NAME "logger process" 1827 | 1828 | typedef struct { 1829 | ngx_array_t pims; 1830 | ngx_array_t formats; 1831 | ngx_uint_t combined_used; 1832 | ngx_pid_t pid; 1833 | } ngx_http_pipelog_main_conf_t; 1834 | 1835 | typedef struct { 1836 | ngx_array_t *pipelogs; 1837 | ngx_uint_t off; 1838 | } ngx_http_pipelog_loc_conf_t; 1839 | 1840 | typedef struct { 1841 | ngx_fd_t fd[2]; 1842 | ngx_uint_t nonblocking; 1843 | wordexp_t w; 1844 | pid_t pid; 1845 | struct timeval timestamp; 1846 | } ngx_http_pipelog_pim_t; 1847 | 1848 | typedef struct { 1849 | ngx_http_pipelog_pim_t *pim; 1850 | ngx_http_log_fmt_t *format; 1851 | ngx_http_complex_value_t *filter; 1852 | } ngx_http_pipelog_t; 1853 | 1854 | static void * 1855 | ngx_http_pipelog_create_main_conf (ngx_conf_t *cf); 1856 | static void * 1857 | ngx_http_pipelog_create_loc_conf (ngx_conf_t *cf); 1858 | static char * 1859 | ngx_http_pipelog_merge_loc_conf (ngx_conf_t *cf, void *parent, void *child); 1860 | static char * 1861 | ngx_http_pipelog_set_format (ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 1862 | static char * 1863 | ngx_http_pipelog_set_log (ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 1864 | static ngx_int_t 1865 | ngx_http_pipelog_init (ngx_conf_t *cf); 1866 | static ngx_int_t 1867 | init_module (ngx_cycle_t *cycle); 1868 | static void 1869 | exit_master (ngx_cycle_t *cycle); 1870 | static void 1871 | exit_process (ngx_cycle_t *cycle); 1872 | 1873 | static ngx_command_t ngx_http_pipelog_commands[] = { 1874 | { ngx_string("pipelog_format"), 1875 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_2MORE, 1876 | ngx_http_pipelog_set_format, 1877 | NGX_HTTP_MAIN_CONF_OFFSET, 1878 | 0, 1879 | NULL }, 1880 | { ngx_string("pipelog"), 1881 | NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LIF_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_1MORE, 1882 | ngx_http_pipelog_set_log, 1883 | NGX_HTTP_LOC_CONF_OFFSET, 1884 | 0, 1885 | NULL }, 1886 | ngx_null_command 1887 | }; 1888 | 1889 | static ngx_http_module_t ngx_http_pipelog_module_ctx = { 1890 | NULL, /* preconfiguration */ 1891 | ngx_http_pipelog_init, /* postconfiguration */ 1892 | ngx_http_pipelog_create_main_conf, /* create main configuration */ 1893 | NULL, /* init main configuration */ 1894 | NULL, /* create server configuration */ 1895 | NULL, /* merge server configuration */ 1896 | ngx_http_pipelog_create_loc_conf, /* create location configuration */ 1897 | ngx_http_pipelog_merge_loc_conf, /* merge location configuration */ 1898 | }; 1899 | 1900 | ngx_module_t ngx_http_pipelog_module = { 1901 | NGX_MODULE_V1, 1902 | &ngx_http_pipelog_module_ctx, /* module context */ 1903 | ngx_http_pipelog_commands, /* module directives */ 1904 | NGX_HTTP_MODULE, /* module type */ 1905 | NULL, /* init master */ 1906 | init_module, /* init module */ 1907 | NULL, /* init process */ 1908 | NULL, /* init thread */ 1909 | NULL, /* exit thread */ 1910 | exit_process, /* exit process */ 1911 | exit_master, /* exit master */ 1912 | NGX_MODULE_V1_PADDING 1913 | }; 1914 | 1915 | struct timeval * 1916 | tvsub(struct timeval *a, struct timeval *b, struct timeval *res) { 1917 | res->tv_sec = a->tv_sec - b->tv_sec; 1918 | res->tv_usec = a->tv_usec - b->tv_usec; 1919 | if(res->tv_usec < 0) { 1920 | res->tv_sec -= 1; 1921 | res->tv_usec += 1000000; 1922 | } 1923 | return res; 1924 | } 1925 | 1926 | static void * 1927 | ngx_http_pipelog_create_main_conf (ngx_conf_t *cf) { 1928 | ngx_http_pipelog_main_conf_t *conf; 1929 | ngx_http_log_fmt_t *fmt; 1930 | 1931 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_pipelog_main_conf_t)); 1932 | if (conf == NULL) { 1933 | return NULL; 1934 | } 1935 | if (ngx_array_init(&conf->formats, cf->pool, 4, sizeof(ngx_http_log_fmt_t)) != NGX_OK) { 1936 | return NULL; 1937 | } 1938 | fmt = ngx_array_push(&conf->formats); 1939 | if (fmt == NULL) { 1940 | return NULL; 1941 | } 1942 | ngx_str_set(&fmt->name, "combined"); 1943 | fmt->flushes = NULL; 1944 | fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t)); 1945 | if (fmt->ops == NULL) { 1946 | return NULL; 1947 | } 1948 | if (ngx_array_init(&conf->pims, cf->pool, 4, sizeof(ngx_http_pipelog_pim_t)) != NGX_OK) { 1949 | return NULL; 1950 | } 1951 | return conf; 1952 | } 1953 | 1954 | static void * 1955 | ngx_http_pipelog_create_loc_conf (ngx_conf_t *cf) { 1956 | ngx_http_pipelog_loc_conf_t *conf; 1957 | 1958 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_pipelog_loc_conf_t)); 1959 | if (conf == NULL) { 1960 | return NULL; 1961 | } 1962 | return conf; 1963 | } 1964 | 1965 | static char * 1966 | ngx_http_pipelog_merge_loc_conf (ngx_conf_t *cf, void *parent, void *child) { 1967 | ngx_http_pipelog_loc_conf_t *prev = parent; 1968 | ngx_http_pipelog_loc_conf_t *conf = child; 1969 | 1970 | if (conf->pipelogs || conf->off) { 1971 | return NGX_CONF_OK; 1972 | } 1973 | conf->pipelogs = prev->pipelogs; 1974 | conf->off = prev->off; 1975 | return NGX_CONF_OK; 1976 | } 1977 | 1978 | static char * 1979 | ngx_http_pipelog_set_log (ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { 1980 | ngx_http_pipelog_loc_conf_t *plcf; 1981 | ngx_str_t *value, name, filter; 1982 | ngx_http_pipelog_t *pipelog; 1983 | ngx_http_pipelog_main_conf_t *pmcf; 1984 | ngx_uint_t idx; 1985 | ngx_http_log_fmt_t *fmt; 1986 | ngx_uint_t i; 1987 | ngx_http_compile_complex_value_t ccv; 1988 | ngx_uint_t nonblocking = 0; 1989 | 1990 | plcf = conf; 1991 | value = cf->args->elts; 1992 | if (ngx_strcmp(value[1].data, "off") == 0) { 1993 | plcf->off = 1; 1994 | if (cf->args->nelts == 2) { 1995 | return NGX_CONF_OK; 1996 | } 1997 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "invalid parameter \"%V\"", &value[2]); 1998 | return NGX_CONF_OK; 1999 | } 2000 | if (plcf->pipelogs == NULL) { 2001 | plcf->pipelogs = ngx_array_create(cf->pool, 2, sizeof(ngx_http_pipelog_t)); 2002 | if (plcf->pipelogs == NULL) { 2003 | return NGX_CONF_ERROR; 2004 | } 2005 | } 2006 | pipelog = ngx_array_push(plcf->pipelogs); 2007 | if (pipelog == NULL) { 2008 | return NGX_CONF_ERROR; 2009 | } 2010 | ngx_memzero(pipelog, sizeof(ngx_http_pipelog_t)); 2011 | if (ngx_http_script_variables_count(&value[1]) != 0) { 2012 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pipelog cannot have variables in command"); 2013 | return NGX_CONF_ERROR; 2014 | } 2015 | pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_pipelog_module); 2016 | if (cf->args->nelts >= 3) { 2017 | name = value[2]; 2018 | if (ngx_strcmp(name.data, "combined") == 0) { 2019 | pmcf->combined_used = 1; 2020 | } 2021 | if (cf->args->nelts >= 4 && ngx_strcmp(value[3].data, "nonblocking") == 0) { 2022 | nonblocking = 1; 2023 | } 2024 | } else { 2025 | ngx_str_set(&name, "combined"); 2026 | pmcf->combined_used = 1; 2027 | } 2028 | fmt = pmcf->formats.elts; 2029 | for (idx = 0; idx < pmcf->formats.nelts; idx++) { 2030 | if (fmt[idx].name.len == name.len && ngx_strcasecmp(fmt[idx].name.data, name.data) == 0) { 2031 | pipelog->format = &fmt[idx]; 2032 | break; 2033 | } 2034 | } 2035 | if (pipelog->format == NULL) { 2036 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "unknown log format \"%V\"", &name); 2037 | return NGX_CONF_ERROR; 2038 | } 2039 | filter.len = 0; 2040 | for (i = 3; i < cf->args->nelts; i++) { 2041 | if (ngx_strncmp(value[i].data, "if=", 3) == 0) { 2042 | filter.len = value[i].len - 3; 2043 | filter.data = value[i].data + 3; 2044 | break; 2045 | } 2046 | } 2047 | if (filter.len) { 2048 | pipelog->filter = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t)); 2049 | if (pipelog->filter == NULL) { 2050 | return NGX_CONF_ERROR; 2051 | } 2052 | ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t)); 2053 | ccv.cf = cf; 2054 | ccv.value = &filter; 2055 | ccv.complex_value = pipelog->filter; 2056 | if (ngx_http_compile_complex_value(&ccv) != NGX_OK) { 2057 | return NGX_CONF_ERROR; 2058 | } 2059 | } 2060 | pipelog->pim = ngx_array_push(&pmcf->pims); 2061 | if (pipelog->pim == NULL) { 2062 | return NGX_CONF_ERROR; 2063 | } 2064 | ngx_memzero(pipelog->pim, sizeof(ngx_http_pipelog_pim_t)); 2065 | if (pipe(pipelog->pim->fd) < 0) { 2066 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "pipe(): error"); 2067 | return NGX_CONF_ERROR; 2068 | } 2069 | pipelog->pim->nonblocking = nonblocking; 2070 | if (pipelog->pim->nonblocking) { 2071 | ngx_nonblocking(pipelog->pim->fd[1]); 2072 | } 2073 | 2074 | int result = wordexp((const char*)value[1].data, &pipelog->pim->w, 0); 2075 | if (result != 0) { 2076 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "wrong command syntax(%i): %s", result, value[1].data); 2077 | return NGX_CONF_ERROR; 2078 | } 2079 | int word_count = pipelog->pim->w.we_wordc; 2080 | 2081 | if (word_count < 1) { 2082 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "no command: %s", value[1].data); 2083 | return NGX_CONF_ERROR; 2084 | } 2085 | 2086 | return NGX_CONF_OK; 2087 | } 2088 | 2089 | static char * 2090 | ngx_http_pipelog_set_format (ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 2091 | { 2092 | ngx_http_pipelog_main_conf_t *pmcf = conf; 2093 | 2094 | ngx_str_t *value; 2095 | ngx_uint_t i; 2096 | ngx_http_log_fmt_t *fmt; 2097 | 2098 | if (cf->cmd_type != NGX_HTTP_MAIN_CONF) { 2099 | ngx_conf_log_error(NGX_LOG_WARN, cf, 0, 2100 | "the \"pipelog_format\" directive may be used " 2101 | "only on \"http\" level"); 2102 | } 2103 | 2104 | value = cf->args->elts; 2105 | 2106 | fmt = pmcf->formats.elts; 2107 | for (i = 0; i < pmcf->formats.nelts; i++) { 2108 | if (fmt[i].name.len == value[1].len 2109 | && ngx_strcmp(fmt[i].name.data, value[1].data) == 0) 2110 | { 2111 | ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, 2112 | "duplicate \"pipelog_format\" name \"%V\"", 2113 | &value[1]); 2114 | return NGX_CONF_ERROR; 2115 | } 2116 | } 2117 | 2118 | fmt = ngx_array_push(&pmcf->formats); 2119 | if (fmt == NULL) { 2120 | return NGX_CONF_ERROR; 2121 | } 2122 | 2123 | fmt->name = value[1]; 2124 | 2125 | fmt->flushes = ngx_array_create(cf->pool, 4, sizeof(ngx_int_t)); 2126 | if (fmt->flushes == NULL) { 2127 | return NGX_CONF_ERROR; 2128 | } 2129 | 2130 | fmt->ops = ngx_array_create(cf->pool, 16, sizeof(ngx_http_log_op_t)); 2131 | if (fmt->ops == NULL) { 2132 | return NGX_CONF_ERROR; 2133 | } 2134 | 2135 | return ngx_http_log_compile_format(cf, fmt->flushes, fmt->ops, cf->args, 2); 2136 | } 2137 | 2138 | static void 2139 | ngx_http_pipelog_write(ngx_http_request_t *r, ngx_http_pipelog_t *pipelog, u_char *buf, size_t len) { 2140 | ssize_t n; 2141 | 2142 | retry: 2143 | n = ngx_write_fd(pipelog->pim->fd[1], buf, len); 2144 | if (n == (ssize_t) len) { 2145 | return; 2146 | } 2147 | if (n < 0) { 2148 | if (!pipelog->pim->nonblocking && errno == EINTR) { 2149 | goto retry; 2150 | } 2151 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "ngx_http_pipelog_write(): failed: %s", strerror(errno)); 2152 | return; 2153 | } 2154 | ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "ngx_http_pipelog_write(): incomplete: %z of %uz", n, len); 2155 | } 2156 | 2157 | static ngx_int_t 2158 | ngx_http_pipelog_handler (ngx_http_request_t *r) { 2159 | ngx_http_pipelog_loc_conf_t *plcf; 2160 | ngx_http_pipelog_t *pipelog; 2161 | ngx_uint_t i, l; 2162 | ngx_str_t val; 2163 | size_t len; 2164 | u_char *line, *p; 2165 | ngx_http_log_op_t *op; 2166 | 2167 | plcf = ngx_http_get_module_loc_conf(r, ngx_http_pipelog_module); 2168 | if (plcf->off) { 2169 | return NGX_OK; 2170 | } 2171 | if (!plcf->pipelogs) { 2172 | return NGX_OK; 2173 | } 2174 | pipelog = plcf->pipelogs->elts; 2175 | for (l = 0; l < plcf->pipelogs->nelts; l++) { 2176 | if (pipelog[l].filter) { 2177 | if (ngx_http_complex_value(r, pipelog[l].filter, &val) != NGX_OK) { 2178 | return NGX_ERROR; 2179 | } 2180 | if (val.len == 0 || (val.len == 1 && val.data[0] == '0')) { 2181 | continue; 2182 | } 2183 | } 2184 | ngx_http_script_flush_no_cacheable_variables(r, pipelog[l].format->flushes); 2185 | len = 0; 2186 | op = pipelog[l].format->ops->elts; 2187 | for (i = 0; i < pipelog[l].format->ops->nelts; i++) { 2188 | if (op[i].len == 0) { 2189 | len += op[i].getlen(r, op[i].data); 2190 | } else { 2191 | len += op[i].len; 2192 | } 2193 | } 2194 | len += NGX_LINEFEED_SIZE; 2195 | line = ngx_pnalloc(r->pool, len); 2196 | if (line == NULL) { 2197 | return NGX_ERROR; 2198 | } 2199 | p = line; 2200 | for (i = 0; i < pipelog[l].format->ops->nelts; i++) { 2201 | p = op[i].run(r, p, &op[i]); 2202 | } 2203 | ngx_linefeed(p); 2204 | ngx_http_pipelog_write(r, &pipelog[l], line, p - line); 2205 | } 2206 | return NGX_OK; 2207 | } 2208 | 2209 | static ngx_int_t 2210 | ngx_http_pipelog_init (ngx_conf_t *cf) { 2211 | ngx_http_core_main_conf_t *cmcf; 2212 | ngx_http_pipelog_main_conf_t *pmcf; 2213 | ngx_http_log_fmt_t *fmt; 2214 | ngx_array_t a; 2215 | ngx_str_t *value; 2216 | ngx_http_handler_pt *handler; 2217 | 2218 | pmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_pipelog_module); 2219 | if (pmcf->combined_used) { 2220 | if (ngx_array_init(&a, cf->pool, 1, sizeof(ngx_str_t)) != NGX_OK) { 2221 | return NGX_ERROR; 2222 | } 2223 | value = ngx_array_push(&a); 2224 | if (value == NULL) { 2225 | return NGX_ERROR; 2226 | } 2227 | *value = ngx_http_combined_fmt; 2228 | fmt = pmcf->formats.elts; 2229 | if (ngx_http_log_compile_format(cf, NULL, fmt->ops, &a, 0) != NGX_CONF_OK) { 2230 | return NGX_ERROR; 2231 | } 2232 | } 2233 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 2234 | handler = ngx_array_push(&cmcf->phases[NGX_HTTP_LOG_PHASE].handlers); 2235 | if (handler == NULL) { 2236 | return NGX_ERROR; 2237 | } 2238 | *handler = ngx_http_pipelog_handler; 2239 | return NGX_OK; 2240 | } 2241 | 2242 | static ngx_pid_t 2243 | ngx_http_pipelog_command_exec (wordexp_t *w, ngx_fd_t rfd, ngx_cycle_t *cycle) { 2244 | ngx_pid_t pid; 2245 | ngx_fd_t fd; 2246 | sigset_t mask; 2247 | 2248 | if (!w) { 2249 | return -1; 2250 | } 2251 | pid = fork(); 2252 | switch (pid){ 2253 | case -1: 2254 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: ngx_http_pipelog_command_exec fork(): error", MODULE_NAME); 2255 | return NGX_ERROR; 2256 | case 0: 2257 | dup2(rfd, STDIN_FILENO); 2258 | for (fd = 0; fd < NOFILE; fd++) { 2259 | if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) { 2260 | close(fd); 2261 | } 2262 | } 2263 | sigfillset(&mask); 2264 | sigprocmask(SIG_UNBLOCK, &mask, NULL); 2265 | execvp(w->we_wordv[0], w->we_wordv); 2266 | exit(1); 2267 | default: 2268 | break; 2269 | } 2270 | return pid; 2271 | } 2272 | 2273 | static ngx_uint_t 2274 | ngx_http_pipelog_reap_child (ngx_cycle_t *cycle) { 2275 | struct timeval now, diff; 2276 | ngx_http_pipelog_main_conf_t *pmcf; 2277 | ngx_uint_t num, idx; 2278 | ngx_pid_t pid; 2279 | ngx_http_pipelog_pim_t *pim; 2280 | 2281 | gettimeofday(&now, NULL); 2282 | pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_pipelog_module); 2283 | for (num = 0; ;num++) { 2284 | pid = waitpid(-1, NULL, WNOHANG); 2285 | if (pid == -1) { 2286 | break; 2287 | } 2288 | pim = pmcf->pims.elts; 2289 | for (idx = 0; idx < pmcf->pims.nelts; idx++) { 2290 | if (pim[idx].pid == pid) { 2291 | break; 2292 | } 2293 | } 2294 | if (idx == pmcf->pims.nelts) { 2295 | break; 2296 | } 2297 | tvsub(&now, &pim[idx].timestamp, &diff); 2298 | if (!diff.tv_sec) { 2299 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: reap child process (pid='%d'), respawn child process after 1 sec", MODULE_NAME, pid); 2300 | pim[idx].pid = -1; 2301 | continue; 2302 | } 2303 | pim[idx].timestamp = now; 2304 | pim[idx].pid = ngx_http_pipelog_command_exec(&pim[idx].w, pim[idx].fd[0], cycle); 2305 | if (pim[idx].pid == -1) { 2306 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: reap child process (pid='%d'), respawn child process failed", MODULE_NAME, pid); 2307 | continue; 2308 | } 2309 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: reap child process (pid='%d'), respawn child process (pid='%d')", MODULE_NAME, pid, pim[idx].pid); 2310 | } 2311 | return num; 2312 | } 2313 | 2314 | void 2315 | ngx_http_pipelog_logger_process_main (ngx_cycle_t *cycle) { 2316 | struct sigaction sa; 2317 | sigset_t set; 2318 | ngx_core_conf_t *ccf; 2319 | struct timeval now, diff; 2320 | ngx_http_pipelog_main_conf_t *pmcf; 2321 | ngx_http_pipelog_pim_t *pim; 2322 | ngx_uint_t idx; 2323 | struct timespec timeout; 2324 | int sig; 2325 | 2326 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "p[%d]: %s: new logger process started", getpid(), MODULE_NAME); 2327 | 2328 | memset(&sa, 0, sizeof(sa)); 2329 | 2330 | sa.sa_handler = SIG_IGN; 2331 | sigaction(SIGPIPE, &sa, NULL); 2332 | sigemptyset(&set); 2333 | sigaddset(&set, SIGTERM); 2334 | sigaddset(&set, SIGCHLD); 2335 | sigprocmask(SIG_BLOCK, &set, NULL); 2336 | 2337 | ccf = (ngx_core_conf_t *)ngx_get_conf(cycle->conf_ctx, ngx_core_module); 2338 | if (geteuid() == 0) { 2339 | if (setresuid(-1, -1, ccf->user) == -1) { 2340 | ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno, "%s: setresuid(%d, %d, %d) failed", MODULE_NAME, -1, -1, ccf->user); 2341 | /* fatal */ 2342 | exit(2); 2343 | } 2344 | } 2345 | gettimeofday(&now, NULL); 2346 | pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_pipelog_module); 2347 | pim = pmcf->pims.elts; 2348 | for (idx = 0; idx < pmcf->pims.nelts; idx++) { 2349 | pim[idx].pid = ngx_http_pipelog_command_exec(&pim[idx].w, pim[idx].fd[0], cycle); 2350 | if (pim[idx].pid == -1) { 2351 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: ngx_http_pipelog_command_exec(): error", MODULE_NAME); 2352 | } 2353 | pim[idx].timestamp = now; 2354 | } 2355 | timeout.tv_sec = 1; 2356 | timeout.tv_nsec = 0; 2357 | while (1) { 2358 | sig = sigtimedwait(&set, NULL, &timeout); 2359 | ngx_time_update(); 2360 | 2361 | if(sig == SIGTERM) { 2362 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: ngx_http_pipelog_command_exec(): SIGTERM detected, gracefully shutting down...", MODULE_NAME); 2363 | close_pipes(cycle); 2364 | if(killpg(0, SIGTERM) == -1) { 2365 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: ngx_http_pipelog_command_exec: killpg(%d, SIGTERM) failed ", MODULE_NAME, 0); 2366 | } 2367 | _exit(0); 2368 | } 2369 | 2370 | if (sig != -1) { 2371 | ngx_http_pipelog_reap_child(cycle); 2372 | } else { 2373 | gettimeofday(&now, NULL); 2374 | pim = pmcf->pims.elts; 2375 | for (idx = 0; idx < pmcf->pims.nelts; idx++) { 2376 | if (pim[idx].pid != -1) { 2377 | continue; 2378 | } 2379 | tvsub(&now, &pim[idx].timestamp, &diff); 2380 | if (!diff.tv_sec) { 2381 | continue; 2382 | } 2383 | pim[idx].timestamp = now; 2384 | pim[idx].pid = ngx_http_pipelog_command_exec(&pim[idx].w, pim[idx].fd[0], cycle); 2385 | if (pim[idx].pid == -1) { 2386 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: respawn child process failed", MODULE_NAME); 2387 | continue; 2388 | } 2389 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: respawn child process (pid='%d')", MODULE_NAME, pim[idx].pid); 2390 | } 2391 | } 2392 | } 2393 | } 2394 | 2395 | static void close_pipes(ngx_cycle_t *cycle) { 2396 | ngx_http_pipelog_main_conf_t *pmcf; 2397 | ngx_http_pipelog_pim_t *pim; 2398 | ngx_uint_t idx; 2399 | 2400 | if (cycle && cycle->modules) { 2401 | pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_pipelog_module); 2402 | pim = pmcf->pims.elts; 2403 | for (idx = 0; idx < pmcf->pims.nelts; idx++) { 2404 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "p[%d]: %s: closing pipes: rd: %i, wr: %i", getpid(), MODULE_NAME, pim[idx].fd[0], pim[idx].fd[1]); 2405 | close(pim[idx].fd[0]); 2406 | close(pim[idx].fd[1]); 2407 | } 2408 | } 2409 | } 2410 | 2411 | static ngx_int_t 2412 | init_module (ngx_cycle_t *cycle) { 2413 | ngx_http_pipelog_main_conf_t *pmcf; 2414 | ngx_fd_t fd; 2415 | 2416 | if (!ngx_test_config) { 2417 | pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_pipelog_module); 2418 | pmcf->pid = fork(); 2419 | switch (pmcf->pid) { 2420 | case -1: 2421 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: init_module fork(): error", MODULE_NAME); 2422 | return NGX_ERROR; 2423 | case 0: 2424 | if(setpgid(0, 0) == -1) { 2425 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: init_module: setpgid() failed", MODULE_NAME); 2426 | exit(1); 2427 | } 2428 | fd = open("/dev/null", O_RDWR); 2429 | if (fd != -1) { 2430 | dup2(fd, STDIN_FILENO); 2431 | dup2(fd, STDOUT_FILENO); 2432 | dup2(fd, STDERR_FILENO); 2433 | close(fd); 2434 | } 2435 | close_pipes(cycle->old_cycle); 2436 | ngx_setproctitle(LOGGER_PROC_NAME); 2437 | ngx_http_pipelog_logger_process_main(cycle); 2438 | exit(1); 2439 | default: 2440 | close_pipes(cycle->old_cycle); 2441 | break; 2442 | } 2443 | } 2444 | return NGX_OK; 2445 | } 2446 | 2447 | static void 2448 | exit_process (ngx_cycle_t *cycle) { 2449 | ngx_http_pipelog_main_conf_t *pmcf; 2450 | pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_pipelog_module); 2451 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "%s: exit_process called", MODULE_NAME); 2452 | close_pipes(cycle); 2453 | 2454 | if(killpg(pmcf->pid, SIGTERM) == -1) { 2455 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: exit_process: killpg(%d, SIGTERM) failed ", MODULE_NAME, pmcf->pid); 2456 | } 2457 | } 2458 | 2459 | static void 2460 | exit_master (ngx_cycle_t *cycle) { 2461 | ngx_http_pipelog_main_conf_t *pmcf; 2462 | pmcf = ngx_http_cycle_get_module_main_conf(cycle, ngx_http_pipelog_module); 2463 | ngx_log_error(NGX_LOG_DEBUG, cycle->log, 0, "%s: exit_master called", MODULE_NAME); 2464 | close_pipes(cycle); 2465 | 2466 | if(!ngx_terminate) { 2467 | if(kill(pmcf->pid, SIGKILL) == -1) { 2468 | ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, "%s: exit_master: kill(%d, SIGTERM) failed ", MODULE_NAME, pmcf->pid); 2469 | } 2470 | } 2471 | } 2472 | --------------------------------------------------------------------------------