1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Valentin V. Bartenev
5 * Copyright (C) NGINX, Inc.
6 */
7
8 #include <nxt_router.h>
9 #include <nxt_conf.h>
10 #include <nxt_http.h>
11
12
13 typedef struct {
14 nxt_str_t path;
15 nxt_str_t format;
16 } nxt_router_access_log_conf_t;
17
18
19 typedef struct {
20 nxt_str_t text;
21 nxt_router_access_log_t *access_log;
22 } nxt_router_access_log_ctx_t;
23
24
25 static void nxt_router_access_log_writer(nxt_task_t *task,
26 nxt_http_request_t *r, nxt_router_access_log_t *access_log,
27 nxt_var_t *format);
28 static void nxt_router_access_log_write_ready(nxt_task_t *task, void *obj,
29 void *data);
30 static void nxt_router_access_log_write_error(nxt_task_t *task, void *obj,
31 void *data);
32 static void nxt_router_access_log_ready(nxt_task_t *task,
33 nxt_port_recv_msg_t *msg, void *data);
34 static void nxt_router_access_log_error(nxt_task_t *task,
35 nxt_port_recv_msg_t *msg, void *data);
36 static void nxt_router_access_log_reopen_completion(nxt_task_t *task, void *obj,
37 void *data);
38 static void nxt_router_access_log_reopen_ready(nxt_task_t *task,
39 nxt_port_recv_msg_t *msg, void *data);
40 static void nxt_router_access_log_reopen_error(nxt_task_t *task,
41 nxt_port_recv_msg_t *msg, void *data);
42
43
44 static nxt_conf_map_t nxt_router_access_log_conf[] = {
45 {
46 nxt_string("path"),
47 NXT_CONF_MAP_STR,
48 offsetof(nxt_router_access_log_conf_t, path),
49 },
50
51 {
52 nxt_string("format"),
53 NXT_CONF_MAP_STR,
54 offsetof(nxt_router_access_log_conf_t, format),
55 },
56 };
57
58
59 nxt_int_t
nxt_router_access_log_create(nxt_task_t * task,nxt_router_conf_t * rtcf,nxt_conf_value_t * value)60 nxt_router_access_log_create(nxt_task_t *task, nxt_router_conf_t *rtcf,
61 nxt_conf_value_t *value)
62 {
63 u_char *p;
64 nxt_int_t ret;
65 nxt_str_t str;
66 nxt_var_t *format;
67 nxt_router_t *router;
68 nxt_router_access_log_t *access_log;
69 nxt_router_access_log_conf_t alcf;
70
71 static nxt_str_t log_format_str = nxt_string("$remote_addr - - "
72 "[$time_local] \"$request_line\" $status $body_bytes_sent "
73 "\"$header_referer\" \"$header_user_agent\"");
74
75 alcf.format = log_format_str;
76
77 if (nxt_conf_type(value) == NXT_CONF_STRING) {
78 nxt_conf_get_string(value, &alcf.path);
79
80 } else {
81 ret = nxt_conf_map_object(rtcf->mem_pool, value,
82 nxt_router_access_log_conf,
83 nxt_nitems(nxt_router_access_log_conf),
84 &alcf);
85 if (ret != NXT_OK) {
86 nxt_alert(task, "access log map error");
87 return NXT_ERROR;
88 }
89 }
90
91 router = nxt_router;
92
93 access_log = router->access_log;
94
95 if (access_log != NULL && nxt_strstr_eq(&alcf.path, &access_log->path)) {
96 nxt_router_access_log_use(&router->lock, access_log);
97
98 } else {
99 access_log = nxt_malloc(sizeof(nxt_router_access_log_t)
100 + alcf.path.length);
101 if (access_log == NULL) {
102 nxt_alert(task, "failed to allocate access log structure");
103 return NXT_ERROR;
104 }
105
106 access_log->fd = -1;
107 access_log->handler = &nxt_router_access_log_writer;
108 access_log->count = 1;
109
110 access_log->path.length = alcf.path.length;
111 access_log->path.start = (u_char *) access_log
112 + sizeof(nxt_router_access_log_t);
113
114 nxt_memcpy(access_log->path.start, alcf.path.start, alcf.path.length);
115 }
116
117 str.length = alcf.format.length + 1;
118
119 str.start = nxt_malloc(str.length);
120 if (str.start == NULL) {
121 nxt_alert(task, "failed to allocate log format structure");
122 return NXT_ERROR;
123 }
124
125 p = nxt_cpymem(str.start, alcf.format.start, alcf.format.length);
126 *p = '\n';
127
128 format = nxt_var_compile(&str, rtcf->mem_pool, rtcf->var_fields,
129 NXT_VAR_LOGGING);
130 if (nxt_slow_path(format == NULL)) {
131 return NXT_ERROR;
132 }
133
134 rtcf->access_log = access_log;
135 rtcf->log_format = format;
136
137 return NXT_OK;
138 }
139
140
141 static void
nxt_router_access_log_writer(nxt_task_t * task,nxt_http_request_t * r,nxt_router_access_log_t * access_log,nxt_var_t * format)142 nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r,
143 nxt_router_access_log_t *access_log, nxt_var_t *format)
144 {
145 nxt_int_t ret;
146 nxt_router_access_log_ctx_t *ctx;
147
148 ctx = nxt_mp_get(r->mem_pool, sizeof(nxt_router_access_log_ctx_t));
149 if (nxt_slow_path(ctx == NULL)) {
150 return;
151 }
152
153 ctx->access_log = access_log;
154
155 if (nxt_var_is_const(format)) {
156 nxt_var_raw(format, &ctx->text);
157
158 nxt_router_access_log_write_ready(task, r, ctx);
159
160 } else {
161 ret = nxt_var_query_init(&r->var_query, r, r->mem_pool);
162 if (nxt_slow_path(ret != NXT_OK)) {
163 return;
164 }
165
166 nxt_var_query(task, r->var_query, format, &ctx->text);
167 nxt_var_query_resolve(task, r->var_query, ctx,
168 nxt_router_access_log_write_ready,
169 nxt_router_access_log_write_error);
170 }
171 }
172
173
174 static void
nxt_router_access_log_write_ready(nxt_task_t * task,void * obj,void * data)175 nxt_router_access_log_write_ready(nxt_task_t *task, void *obj, void *data)
176 {
177 nxt_http_request_t *r;
178 nxt_router_access_log_ctx_t *ctx;
179
180 r = obj;
181 ctx = data;
182
183 nxt_fd_write(ctx->access_log->fd, ctx->text.start, ctx->text.length);
184
185 nxt_http_request_close_handler(task, r, r->proto.any);
186 }
187
188
189 static void
nxt_router_access_log_write_error(nxt_task_t * task,void * obj,void * data)190 nxt_router_access_log_write_error(nxt_task_t *task, void *obj, void *data)
191 {
192
193 }
194
195
196 void
nxt_router_access_log_open(nxt_task_t * task,nxt_router_temp_conf_t * tmcf)197 nxt_router_access_log_open(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
198 {
199 uint32_t stream;
200 nxt_int_t ret;
201 nxt_buf_t *b;
202 nxt_port_t *main_port, *router_port;
203 nxt_runtime_t *rt;
204 nxt_router_access_log_t *access_log;
205
206 access_log = tmcf->router_conf->access_log;
207
208 b = nxt_buf_mem_alloc(tmcf->mem_pool, access_log->path.length + 1, 0);
209 if (nxt_slow_path(b == NULL)) {
210 goto fail;
211 }
212
213 b->completion_handler = nxt_buf_dummy_completion;
214
215 nxt_buf_cpystr(b, &access_log->path);
216 *b->mem.free++ = '\0';
217
218 rt = task->thread->runtime;
219 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
220 router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
221
222 stream = nxt_port_rpc_register_handler(task, router_port,
223 nxt_router_access_log_ready,
224 nxt_router_access_log_error,
225 -1, tmcf);
226 if (nxt_slow_path(stream == 0)) {
227 goto fail;
228 }
229
230 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_ACCESS_LOG, -1,
231 stream, router_port->id, b);
232
233 if (nxt_slow_path(ret != NXT_OK)) {
234 nxt_port_rpc_cancel(task, router_port, stream);
235 goto fail;
236 }
237
238 return;
239
240 fail:
241
242 nxt_router_conf_error(task, tmcf);
243 }
244
245
246 static void
nxt_router_access_log_ready(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)247 nxt_router_access_log_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
248 void *data)
249 {
250 nxt_router_temp_conf_t *tmcf;
251 nxt_router_access_log_t *access_log;
252
253 tmcf = data;
254
255 access_log = tmcf->router_conf->access_log;
256
257 access_log->fd = msg->fd[0];
258
259 nxt_work_queue_add(&task->thread->engine->fast_work_queue,
260 nxt_router_conf_apply, task, tmcf, NULL);
261 }
262
263
264 static void
nxt_router_access_log_error(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)265 nxt_router_access_log_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
266 void *data)
267 {
268 nxt_router_temp_conf_t *tmcf;
269
270 tmcf = data;
271
272 nxt_router_conf_error(task, tmcf);
273 }
274
275
276 void
nxt_router_access_log_use(nxt_thread_spinlock_t * lock,nxt_router_access_log_t * access_log)277 nxt_router_access_log_use(nxt_thread_spinlock_t *lock,
278 nxt_router_access_log_t *access_log)
279 {
280 if (access_log == NULL) {
281 return;
282 }
283
284 nxt_thread_spin_lock(lock);
285
286 access_log->count++;
287
288 nxt_thread_spin_unlock(lock);
289 }
290
291
292 void
nxt_router_access_log_release(nxt_task_t * task,nxt_thread_spinlock_t * lock,nxt_router_access_log_t * access_log)293 nxt_router_access_log_release(nxt_task_t *task, nxt_thread_spinlock_t *lock,
294 nxt_router_access_log_t *access_log)
295 {
296 if (access_log == NULL) {
297 return;
298 }
299
300 nxt_thread_spin_lock(lock);
301
302 if (--access_log->count != 0) {
303 access_log = NULL;
304 }
305
306 nxt_thread_spin_unlock(lock);
307
308 if (access_log != NULL) {
309
310 if (access_log->fd != -1) {
311 nxt_fd_close(access_log->fd);
312 }
313
314 nxt_free(access_log);
315 }
316 }
317
318
319 typedef struct {
320 nxt_mp_t *mem_pool;
321 nxt_router_access_log_t *access_log;
322 } nxt_router_access_log_reopen_t;
323
324
325 void
nxt_router_access_log_reopen_handler(nxt_task_t * task,nxt_port_recv_msg_t * msg)326 nxt_router_access_log_reopen_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
327 {
328 nxt_mp_t *mp;
329 uint32_t stream;
330 nxt_int_t ret;
331 nxt_buf_t *b;
332 nxt_port_t *main_port, *router_port;
333 nxt_runtime_t *rt;
334 nxt_router_access_log_t *access_log;
335 nxt_router_access_log_reopen_t *reopen;
336
337 access_log = nxt_router->access_log;
338
339 if (access_log == NULL) {
340 return;
341 }
342
343 mp = nxt_mp_create(1024, 128, 256, 32);
344 if (nxt_slow_path(mp == NULL)) {
345 return;
346 }
347
348 reopen = nxt_mp_get(mp, sizeof(nxt_router_access_log_reopen_t));
349 if (nxt_slow_path(reopen == NULL)) {
350 goto fail;
351 }
352
353 reopen->mem_pool = mp;
354 reopen->access_log = access_log;
355
356 b = nxt_buf_mem_alloc(mp, access_log->path.length + 1, 0);
357 if (nxt_slow_path(b == NULL)) {
358 goto fail;
359 }
360
361 b->completion_handler = nxt_router_access_log_reopen_completion;
362
363 nxt_buf_cpystr(b, &access_log->path);
364 *b->mem.free++ = '\0';
365
366 rt = task->thread->runtime;
367 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
368 router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
369
370 stream = nxt_port_rpc_register_handler(task, router_port,
371 nxt_router_access_log_reopen_ready,
372 nxt_router_access_log_reopen_error,
373 -1, reopen);
374 if (nxt_slow_path(stream == 0)) {
375 goto fail;
376 }
377
378 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_ACCESS_LOG, -1,
379 stream, router_port->id, b);
380
381 if (nxt_slow_path(ret != NXT_OK)) {
382 nxt_port_rpc_cancel(task, router_port, stream);
383 goto fail;
384 }
385
386 nxt_mp_retain(mp);
387
388 return;
389
390 fail:
391
392 nxt_mp_destroy(mp);
393 }
394
395
396 static void
nxt_router_access_log_reopen_completion(nxt_task_t * task,void * obj,void * data)397 nxt_router_access_log_reopen_completion(nxt_task_t *task, void *obj, void *data)
398 {
399 nxt_mp_t *mp;
400 nxt_buf_t *b;
401
402 b = obj;
403 mp = b->data;
404
405 nxt_mp_release(mp);
406 }
407
408
409 static void
nxt_router_access_log_reopen_ready(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)410 nxt_router_access_log_reopen_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
411 void *data)
412 {
413 nxt_router_access_log_t *access_log;
414 nxt_router_access_log_reopen_t *reopen;
415
416 reopen = data;
417
418 access_log = reopen->access_log;
419
420 if (access_log == nxt_router->access_log) {
421
422 if (nxt_slow_path(dup2(msg->fd[0], access_log->fd) == -1)) {
423 nxt_alert(task, "dup2(%FD, %FD) failed %E",
424 msg->fd[0], access_log->fd, nxt_errno);
425 }
426 }
427
428 nxt_fd_close(msg->fd[0]);
429 nxt_mp_release(reopen->mem_pool);
430 }
431
432
433 static void
nxt_router_access_log_reopen_error(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)434 nxt_router_access_log_reopen_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
435 void *data)
436 {
437 nxt_router_access_log_reopen_t *reopen;
438
439 reopen = data;
440
441 nxt_mp_release(reopen->mem_pool);
442 }
443