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_tstr_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_tstr_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_tstr_compile(rtcf->tstr_state, &str, NXT_TSTR_LOGGING);
129 if (nxt_slow_path(format == NULL)) {
130 return NXT_ERROR;
131 }
132
133 rtcf->access_log = access_log;
134 rtcf->log_format = format;
135
136 return NXT_OK;
137 }
138
139
140 static void
nxt_router_access_log_writer(nxt_task_t * task,nxt_http_request_t * r,nxt_router_access_log_t * access_log,nxt_tstr_t * format)141 nxt_router_access_log_writer(nxt_task_t *task, nxt_http_request_t *r,
142 nxt_router_access_log_t *access_log, nxt_tstr_t *format)
143 {
144 nxt_int_t ret;
145 nxt_router_conf_t *rtcf;
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_tstr_is_const(format)) {
156 nxt_tstr_str(format, &ctx->text);
157
158 nxt_router_access_log_write_ready(task, r, ctx);
159
160 } else {
161 rtcf = r->conf->socket_conf->router_conf;
162
163 ret = nxt_tstr_query_init(&r->tstr_query, rtcf->tstr_state,
164 &r->tstr_cache, r, r->mem_pool);
165 if (nxt_slow_path(ret != NXT_OK)) {
166 return;
167 }
168
169 nxt_tstr_query(task, r->tstr_query, format, &ctx->text);
170 nxt_tstr_query_resolve(task, r->tstr_query, ctx,
171 nxt_router_access_log_write_ready,
172 nxt_router_access_log_write_error);
173 }
174 }
175
176
177 static void
nxt_router_access_log_write_ready(nxt_task_t * task,void * obj,void * data)178 nxt_router_access_log_write_ready(nxt_task_t *task, void *obj, void *data)
179 {
180 nxt_http_request_t *r;
181 nxt_router_access_log_ctx_t *ctx;
182
183 r = obj;
184 ctx = data;
185
186 nxt_fd_write(ctx->access_log->fd, ctx->text.start, ctx->text.length);
187
188 nxt_http_request_close_handler(task, r, r->proto.any);
189 }
190
191
192 static void
nxt_router_access_log_write_error(nxt_task_t * task,void * obj,void * data)193 nxt_router_access_log_write_error(nxt_task_t *task, void *obj, void *data)
194 {
195
196 }
197
198
199 void
nxt_router_access_log_open(nxt_task_t * task,nxt_router_temp_conf_t * tmcf)200 nxt_router_access_log_open(nxt_task_t *task, nxt_router_temp_conf_t *tmcf)
201 {
202 uint32_t stream;
203 nxt_int_t ret;
204 nxt_buf_t *b;
205 nxt_port_t *main_port, *router_port;
206 nxt_runtime_t *rt;
207 nxt_router_access_log_t *access_log;
208
209 access_log = tmcf->router_conf->access_log;
210
211 b = nxt_buf_mem_alloc(tmcf->mem_pool, access_log->path.length + 1, 0);
212 if (nxt_slow_path(b == NULL)) {
213 goto fail;
214 }
215
216 b->completion_handler = nxt_buf_dummy_completion;
217
218 nxt_buf_cpystr(b, &access_log->path);
219 *b->mem.free++ = '\0';
220
221 rt = task->thread->runtime;
222 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
223 router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
224
225 stream = nxt_port_rpc_register_handler(task, router_port,
226 nxt_router_access_log_ready,
227 nxt_router_access_log_error,
228 -1, tmcf);
229 if (nxt_slow_path(stream == 0)) {
230 goto fail;
231 }
232
233 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_ACCESS_LOG, -1,
234 stream, router_port->id, b);
235
236 if (nxt_slow_path(ret != NXT_OK)) {
237 nxt_port_rpc_cancel(task, router_port, stream);
238 goto fail;
239 }
240
241 return;
242
243 fail:
244
245 nxt_router_conf_error(task, tmcf);
246 }
247
248
249 static void
nxt_router_access_log_ready(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)250 nxt_router_access_log_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
251 void *data)
252 {
253 nxt_router_temp_conf_t *tmcf;
254 nxt_router_access_log_t *access_log;
255
256 tmcf = data;
257
258 access_log = tmcf->router_conf->access_log;
259
260 access_log->fd = msg->fd[0];
261
262 nxt_work_queue_add(&task->thread->engine->fast_work_queue,
263 nxt_router_conf_apply, task, tmcf, NULL);
264 }
265
266
267 static void
nxt_router_access_log_error(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)268 nxt_router_access_log_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
269 void *data)
270 {
271 nxt_router_temp_conf_t *tmcf;
272
273 tmcf = data;
274
275 nxt_router_conf_error(task, tmcf);
276 }
277
278
279 void
nxt_router_access_log_use(nxt_thread_spinlock_t * lock,nxt_router_access_log_t * access_log)280 nxt_router_access_log_use(nxt_thread_spinlock_t *lock,
281 nxt_router_access_log_t *access_log)
282 {
283 if (access_log == NULL) {
284 return;
285 }
286
287 nxt_thread_spin_lock(lock);
288
289 access_log->count++;
290
291 nxt_thread_spin_unlock(lock);
292 }
293
294
295 void
nxt_router_access_log_release(nxt_task_t * task,nxt_thread_spinlock_t * lock,nxt_router_access_log_t * access_log)296 nxt_router_access_log_release(nxt_task_t *task, nxt_thread_spinlock_t *lock,
297 nxt_router_access_log_t *access_log)
298 {
299 if (access_log == NULL) {
300 return;
301 }
302
303 nxt_thread_spin_lock(lock);
304
305 if (--access_log->count != 0) {
306 access_log = NULL;
307 }
308
309 nxt_thread_spin_unlock(lock);
310
311 if (access_log != NULL) {
312
313 if (access_log->fd != -1) {
314 nxt_fd_close(access_log->fd);
315 }
316
317 nxt_free(access_log);
318 }
319 }
320
321
322 typedef struct {
323 nxt_mp_t *mem_pool;
324 nxt_router_access_log_t *access_log;
325 } nxt_router_access_log_reopen_t;
326
327
328 void
nxt_router_access_log_reopen_handler(nxt_task_t * task,nxt_port_recv_msg_t * msg)329 nxt_router_access_log_reopen_handler(nxt_task_t *task, nxt_port_recv_msg_t *msg)
330 {
331 nxt_mp_t *mp;
332 uint32_t stream;
333 nxt_int_t ret;
334 nxt_buf_t *b;
335 nxt_port_t *main_port, *router_port;
336 nxt_runtime_t *rt;
337 nxt_router_access_log_t *access_log;
338 nxt_router_access_log_reopen_t *reopen;
339
340 access_log = nxt_router->access_log;
341
342 if (access_log == NULL) {
343 return;
344 }
345
346 mp = nxt_mp_create(1024, 128, 256, 32);
347 if (nxt_slow_path(mp == NULL)) {
348 return;
349 }
350
351 reopen = nxt_mp_get(mp, sizeof(nxt_router_access_log_reopen_t));
352 if (nxt_slow_path(reopen == NULL)) {
353 goto fail;
354 }
355
356 reopen->mem_pool = mp;
357 reopen->access_log = access_log;
358
359 b = nxt_buf_mem_alloc(mp, access_log->path.length + 1, 0);
360 if (nxt_slow_path(b == NULL)) {
361 goto fail;
362 }
363
364 b->completion_handler = nxt_router_access_log_reopen_completion;
365
366 nxt_buf_cpystr(b, &access_log->path);
367 *b->mem.free++ = '\0';
368
369 rt = task->thread->runtime;
370 main_port = rt->port_by_type[NXT_PROCESS_MAIN];
371 router_port = rt->port_by_type[NXT_PROCESS_ROUTER];
372
373 stream = nxt_port_rpc_register_handler(task, router_port,
374 nxt_router_access_log_reopen_ready,
375 nxt_router_access_log_reopen_error,
376 -1, reopen);
377 if (nxt_slow_path(stream == 0)) {
378 goto fail;
379 }
380
381 ret = nxt_port_socket_write(task, main_port, NXT_PORT_MSG_ACCESS_LOG, -1,
382 stream, router_port->id, b);
383
384 if (nxt_slow_path(ret != NXT_OK)) {
385 nxt_port_rpc_cancel(task, router_port, stream);
386 goto fail;
387 }
388
389 nxt_mp_retain(mp);
390
391 return;
392
393 fail:
394
395 nxt_mp_destroy(mp);
396 }
397
398
399 static void
nxt_router_access_log_reopen_completion(nxt_task_t * task,void * obj,void * data)400 nxt_router_access_log_reopen_completion(nxt_task_t *task, void *obj, void *data)
401 {
402 nxt_mp_t *mp;
403 nxt_buf_t *b;
404
405 b = obj;
406 mp = b->data;
407
408 nxt_mp_release(mp);
409 }
410
411
412 static void
nxt_router_access_log_reopen_ready(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)413 nxt_router_access_log_reopen_ready(nxt_task_t *task, nxt_port_recv_msg_t *msg,
414 void *data)
415 {
416 nxt_router_access_log_t *access_log;
417 nxt_router_access_log_reopen_t *reopen;
418
419 reopen = data;
420
421 access_log = reopen->access_log;
422
423 if (access_log == nxt_router->access_log) {
424
425 if (nxt_slow_path(dup2(msg->fd[0], access_log->fd) == -1)) {
426 nxt_alert(task, "dup2(%FD, %FD) failed %E",
427 msg->fd[0], access_log->fd, nxt_errno);
428 }
429 }
430
431 nxt_fd_close(msg->fd[0]);
432 nxt_mp_release(reopen->mem_pool);
433 }
434
435
436 static void
nxt_router_access_log_reopen_error(nxt_task_t * task,nxt_port_recv_msg_t * msg,void * data)437 nxt_router_access_log_reopen_error(nxt_task_t *task, nxt_port_recv_msg_t *msg,
438 void *data)
439 {
440 nxt_router_access_log_reopen_t *reopen;
441
442 reopen = data;
443
444 nxt_mp_release(reopen->mem_pool);
445 }
446