1
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) NGINX, Inc.
5 */
6
7 #include <nxt_main.h>
8
9
10 nxt_int_t
nxt_file_open(nxt_task_t * task,nxt_file_t * file,nxt_uint_t mode,nxt_uint_t create,nxt_file_access_t access)11 nxt_file_open(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode,
12 nxt_uint_t create, nxt_file_access_t access)
13 {
14 #ifdef __CYGWIN__
15 mode |= O_BINARY;
16 #endif
17
18 /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */
19 mode |= (O_NONBLOCK | create);
20
21 file->fd = open((char *) file->name, mode, access);
22
23 file->error = (file->fd == -1) ? nxt_errno : 0;
24
25 #if (NXT_DEBUG)
26 nxt_thread_time_update(task->thread);
27 #endif
28
29 nxt_debug(task, "open(\"%FN\", 0x%uXi, 0x%uXi): %FD err:%d",
30 file->name, mode, access, file->fd, file->error);
31
32 if (file->fd != -1) {
33 return NXT_OK;
34 }
35
36 if (file->log_level != 0) {
37 nxt_log(task, file->log_level, "open(\"%FN\") failed %E",
38 file->name, file->error);
39 }
40
41 return NXT_ERROR;
42 }
43
44
45 #if (NXT_HAVE_OPENAT2)
46
47 nxt_int_t
nxt_file_openat2(nxt_task_t * task,nxt_file_t * file,nxt_uint_t mode,nxt_uint_t create,nxt_file_access_t access,nxt_fd_t dfd,nxt_uint_t resolve)48 nxt_file_openat2(nxt_task_t *task, nxt_file_t *file, nxt_uint_t mode,
49 nxt_uint_t create, nxt_file_access_t access, nxt_fd_t dfd,
50 nxt_uint_t resolve)
51 {
52 struct open_how how;
53
54 nxt_memzero(&how, sizeof(how));
55
56 /* O_NONBLOCK is to prevent blocking on FIFOs, special devices, etc. */
57 mode |= (O_NONBLOCK | create);
58
59 how.flags = mode;
60 how.mode = access;
61 how.resolve = resolve;
62
63 file->fd = syscall(SYS_openat2, dfd, file->name, &how, sizeof(how));
64
65 file->error = (file->fd == -1) ? nxt_errno : 0;
66
67 #if (NXT_DEBUG)
68 nxt_thread_time_update(task->thread);
69 #endif
70
71 nxt_debug(task, "openat2(%FD, \"%FN\"): %FD err:%d", dfd, file->name,
72 file->fd, file->error);
73
74 if (file->fd != -1) {
75 return NXT_OK;
76 }
77
78 if (file->log_level != 0) {
79 nxt_log(task, file->log_level, "openat2(%FD, \"%FN\") failed %E", dfd,
80 file->name, file->error);
81 }
82
83 return NXT_ERROR;
84 }
85
86 #endif
87
88
89 void
nxt_file_close(nxt_task_t * task,nxt_file_t * file)90 nxt_file_close(nxt_task_t *task, nxt_file_t *file)
91 {
92 nxt_debug(task, "close(%FD)", file->fd);
93
94 if (close(file->fd) != 0) {
95 nxt_alert(task, "close(%FD, \"%FN\") failed %E",
96 file->fd, file->name, nxt_errno);
97 }
98 }
99
100
101 ssize_t
nxt_file_write(nxt_file_t * file,const u_char * buf,size_t size,nxt_off_t offset)102 nxt_file_write(nxt_file_t *file, const u_char *buf, size_t size,
103 nxt_off_t offset)
104 {
105 ssize_t n;
106
107 nxt_thread_debug(thr);
108
109 n = pwrite(file->fd, buf, size, offset);
110
111 file->error = (n < 0) ? nxt_errno : 0;
112
113 nxt_thread_time_debug_update(thr);
114
115 nxt_log_debug(thr->log, "pwrite(%FD, %p, %uz, %O): %z",
116 file->fd, buf, size, offset, n);
117
118 if (nxt_fast_path(n >= 0)) {
119 return n;
120 }
121
122 nxt_thread_log_alert("pwrite(%FD, \"%FN\", %p, %uz, %O) failed %E",
123 file->fd, file->name, buf, size,
124 offset, file->error);
125
126 return NXT_ERROR;
127 }
128
129
130 ssize_t
nxt_file_read(nxt_file_t * file,u_char * buf,size_t size,nxt_off_t offset)131 nxt_file_read(nxt_file_t *file, u_char *buf, size_t size, nxt_off_t offset)
132 {
133 ssize_t n;
134
135 nxt_thread_debug(thr);
136
137 n = pread(file->fd, buf, size, offset);
138
139 file->error = (n <= 0) ? nxt_errno : 0;
140
141 nxt_thread_time_debug_update(thr);
142
143 nxt_log_debug(thr->log, "pread(%FD, %p, %uz, %O): %z",
144 file->fd, buf, size, offset, n);
145
146 if (nxt_fast_path(n >= 0)) {
147 return n;
148 }
149
150 nxt_thread_log_alert("pread(%FD, \"%FN\", %p, %uz, %O) failed %E",
151 file->fd, file->name, buf, size,
152 offset, file->error);
153
154 return NXT_ERROR;
155 }
156
157
158 #if (NXT_HAVE_READAHEAD)
159
160 /* FreeBSD 8.0 fcntl(F_READAHEAD, size) enables read ahead up to the size. */
161
162 void
nxt_file_read_ahead(nxt_file_t * file,nxt_off_t offset,size_t size)163 nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
164 {
165 int ret;
166 u_char buf;
167
168 ret = fcntl(file->fd, F_READAHEAD, (int) size);
169
170 nxt_thread_log_debug("fcntl(%FD, F_READAHEAD, %uz): %d",
171 file->fd, size, ret);
172
173 if (nxt_fast_path(ret != -1)) {
174 (void) nxt_file_read(file, &buf, 1, offset);
175 return;
176 }
177
178 nxt_thread_log_alert("fcntl(%FD, \"%FN\", F_READAHEAD, %uz) failed %E",
179 file->fd, file->name, size, nxt_errno);
180 }
181
182 #elif (NXT_HAVE_POSIX_FADVISE)
183
184 /*
185 * POSIX_FADV_SEQUENTIAL
186 * Linux doubles the default readahead window size of a backing device
187 * which is usually 128K.
188 *
189 * FreeBSD does nothing.
190 *
191 * POSIX_FADV_WILLNEED
192 * Linux preloads synchronously up to 2M of specified file region in
193 * the kernel page cache. Linux-specific readahead(2) syscall does
194 * the same. Both operations are blocking despite posix_fadvise(2)
195 * claims the opposite.
196 *
197 * FreeBSD does nothing.
198 */
199
200 void
nxt_file_read_ahead(nxt_file_t * file,nxt_off_t offset,size_t size)201 nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
202 {
203 nxt_err_t err;
204
205 err = posix_fadvise(file->fd, offset, size, POSIX_FADV_WILLNEED);
206
207 nxt_thread_log_debug("posix_fadvise(%FD, \"%FN\", %O, %uz, %d): %d",
208 file->fd, file->name, offset, size,
209 POSIX_FADV_WILLNEED, err);
210
211 if (nxt_fast_path(err == 0)) {
212 return;
213 }
214
215 nxt_thread_log_alert("posix_fadvise(%FD, \"%FN\", %O, %uz, %d) failed %E",
216 file->fd, file->name, offset, size,
217 POSIX_FADV_WILLNEED, err);
218 }
219
220 #elif (NXT_HAVE_RDAHEAD)
221
222 /* MacOSX fcntl(F_RDAHEAD). */
223
224 void
nxt_file_read_ahead(nxt_file_t * file,nxt_off_t offset,size_t size)225 nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
226 {
227 int ret;
228 u_char buf;
229
230 ret = fcntl(file->fd, F_RDAHEAD, 1);
231
232 nxt_thread_log_debug("fcntl(%FD, F_RDAHEAD, 1): %d", file->fd, ret);
233
234 if (nxt_fast_path(ret != -1)) {
235 (void) nxt_file_read(file, &buf, 1, offset);
236 return;
237 }
238
239 nxt_thread_log_alert("fcntl(%FD, \"%FN\", F_RDAHEAD, 1) failed %E",
240 file->fd, file->name, nxt_errno);
241 }
242
243 #else
244
245 void
nxt_file_read_ahead(nxt_file_t * file,nxt_off_t offset,size_t size)246 nxt_file_read_ahead(nxt_file_t *file, nxt_off_t offset, size_t size)
247 {
248 u_char buf;
249
250 (void) nxt_file_read(file, &buf, 1, offset);
251 }
252
253 #endif
254
255
256 nxt_int_t
nxt_file_info(nxt_file_t * file,nxt_file_info_t * fi)257 nxt_file_info(nxt_file_t *file, nxt_file_info_t *fi)
258 {
259 int n;
260
261 if (file->fd == NXT_FILE_INVALID) {
262 n = stat((char *) file->name, fi);
263
264 file->error = (n != 0) ? nxt_errno : 0;
265
266 nxt_thread_log_debug("stat(\"%FN)\": %d", file->name, n);
267
268 if (n == 0) {
269 return NXT_OK;
270 }
271
272 if (file->log_level != 0) {
273 nxt_thread_log_error(file->log_level, "stat(\"%FN\") failed %E",
274 file->name, file->error);
275 }
276
277 return NXT_ERROR;
278
279 } else {
280 n = fstat(file->fd, fi);
281
282 file->error = (n != 0) ? nxt_errno : 0;
283
284 nxt_thread_log_debug("fstat(%FD): %d", file->fd, n);
285
286 if (n == 0) {
287 return NXT_OK;
288 }
289
290 /* Use NXT_LOG_ALERT because fstat() error on open file is strange. */
291
292 nxt_thread_log_alert("fstat(%FD, \"%FN\") failed %E",
293 file->fd, file->name, file->error);
294
295 return NXT_ERROR;
296 }
297 }
298
299
300 nxt_int_t
nxt_file_delete(nxt_file_name_t * name)301 nxt_file_delete(nxt_file_name_t *name)
302 {
303 nxt_thread_log_debug("unlink(\"%FN\")", name);
304
305 if (nxt_fast_path(unlink((char *) name) == 0)) {
306 return NXT_OK;
307 }
308
309 nxt_thread_log_alert("unlink(\"%FN\") failed %E", name, nxt_errno);
310
311 return NXT_ERROR;
312 }
313
314
315 nxt_int_t
nxt_file_set_access(nxt_file_name_t * name,nxt_file_access_t access)316 nxt_file_set_access(nxt_file_name_t *name, nxt_file_access_t access)
317 {
318 if (nxt_fast_path(chmod((char *) name, access) == 0)) {
319 return NXT_OK;
320 }
321
322 nxt_thread_log_alert("chmod(\"%FN\") failed %E", name, nxt_errno);
323
324 return NXT_ERROR;
325 }
326
327
328 nxt_int_t
nxt_file_rename(nxt_file_name_t * old_name,nxt_file_name_t * new_name)329 nxt_file_rename(nxt_file_name_t *old_name, nxt_file_name_t *new_name)
330 {
331 int ret;
332
333 nxt_thread_log_debug("rename(\"%FN\", \"%FN\")", old_name, new_name);
334
335 ret = rename((char *) old_name, (char *) new_name);
336 if (nxt_fast_path(ret == 0)) {
337 return NXT_OK;
338 }
339
340 nxt_thread_log_alert("rename(\"%FN\", \"%FN\") failed %E",
341 old_name, new_name, nxt_errno);
342
343 return NXT_ERROR;
344 }
345
346
347 /*
348 * ioctl(FIONBIO) sets a non-blocking mode using one syscall,
349 * thereas fcntl(F_SETFL, O_NONBLOCK) needs to learn the current state
350 * using fcntl(F_GETFL).
351 *
352 * ioctl() and fcntl() are syscalls at least in Linux 2.2, FreeBSD 2.x,
353 * and Solaris 7.
354 *
355 * Linux 2.4 uses BKL for ioctl() and fcntl(F_SETFL).
356 * Linux 2.6 does not use BKL.
357 */
358
359 #if (NXT_HAVE_FIONBIO)
360
361 nxt_int_t
nxt_fd_nonblocking(nxt_task_t * task,nxt_fd_t fd)362 nxt_fd_nonblocking(nxt_task_t *task, nxt_fd_t fd)
363 {
364 int nb;
365
366 nb = 1;
367
368 if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) {
369 return NXT_OK;
370 }
371
372 nxt_alert(task, "ioctl(%d, FIONBIO) failed %E", fd, nxt_errno);
373
374 return NXT_ERROR;
375
376 }
377
378
379 nxt_int_t
nxt_fd_blocking(nxt_task_t * task,nxt_fd_t fd)380 nxt_fd_blocking(nxt_task_t *task, nxt_fd_t fd)
381 {
382 int nb;
383
384 nb = 0;
385
386 if (nxt_fast_path(ioctl(fd, FIONBIO, &nb) != -1)) {
387 return NXT_OK;
388 }
389
390 nxt_alert(task, "ioctl(%d, !FIONBIO) failed %E", fd, nxt_errno);
391
392 return NXT_ERROR;
393 }
394
395 #else /* !(NXT_HAVE_FIONBIO) */
396
397 nxt_int_t
nxt_fd_nonblocking(nxt_task_t * task,nxt_fd_t fd)398 nxt_fd_nonblocking(nxt_task_t *task, nxt_fd_t fd)
399 {
400 int flags;
401
402 flags = fcntl(fd, F_GETFL);
403
404 if (nxt_slow_path(flags == -1)) {
405 nxt_alert(task, "fcntl(%d, F_GETFL) failed %E", fd, nxt_errno);
406 return NXT_ERROR;
407 }
408
409 flags |= O_NONBLOCK;
410
411 if (nxt_slow_path(fcntl(fd, F_SETFL, flags) == -1)) {
412 nxt_alert(task, "fcntl(%d, F_SETFL, O_NONBLOCK) failed %E",
413 fd, nxt_errno);
414 return NXT_ERROR;
415 }
416
417 return NXT_OK;
418 }
419
420
421 nxt_int_t
nxt_fd_blocking(nxt_task_t * task,nxt_fd_t fd)422 nxt_fd_blocking(nxt_task_t *task, nxt_fd_t fd)
423 {
424 int flags;
425
426 flags = fcntl(fd, F_GETFL);
427
428 if (nxt_slow_path(flags == -1)) {
429 nxt_alert(task, "fcntl(%d, F_GETFL) failed %E", fd, nxt_errno);
430 return NXT_ERROR;
431 }
432
433 flags &= O_NONBLOCK;
434
435 if (nxt_slow_path(fcntl(fd, F_SETFL, flags) == -1)) {
436 nxt_alert(task, "fcntl(%d, F_SETFL, !O_NONBLOCK) failed %E",
437 fd, nxt_errno);
438 return NXT_ERROR;
439 }
440
441 return NXT_OK;
442 }
443
444 #endif /* NXT_HAVE_FIONBIO */
445
446
447 ssize_t
nxt_fd_write(nxt_fd_t fd,u_char * buf,size_t size)448 nxt_fd_write(nxt_fd_t fd, u_char *buf, size_t size)
449 {
450 ssize_t n;
451 nxt_err_t err;
452
453 n = write(fd, buf, size);
454
455 err = (n == -1) ? nxt_errno : 0;
456
457 nxt_thread_log_debug("write(%FD, %p, %uz): %z", fd, buf, size, n);
458
459 if (nxt_slow_path(n <= 0)) {
460 nxt_thread_log_alert("write(%FD) failed %E", fd, err);
461 }
462
463 return n;
464 }
465
466
467 ssize_t
nxt_fd_read(nxt_fd_t fd,u_char * buf,size_t size)468 nxt_fd_read(nxt_fd_t fd, u_char *buf, size_t size)
469 {
470 ssize_t n;
471 nxt_err_t err;
472
473 n = read(fd, buf, size);
474
475 err = (n == -1) ? nxt_errno : 0;
476
477 nxt_thread_log_debug("read(%FD, %p, %uz): %z", fd, buf, size, n);
478
479 if (nxt_slow_path(n <= 0)) {
480
481 if (err == NXT_EAGAIN) {
482 return 0;
483 }
484
485 nxt_thread_log_alert("read(%FD) failed %E", fd, err);
486 }
487
488 return n;
489 }
490
491
492 void
nxt_fd_close(nxt_fd_t fd)493 nxt_fd_close(nxt_fd_t fd)
494 {
495 nxt_thread_log_debug("close(%FD)", fd);
496
497 if (nxt_slow_path(close(fd) != 0)) {
498 nxt_thread_log_alert("close(%FD) failed %E", fd, nxt_errno);
499 }
500 }
501
502
503 FILE *
nxt_file_fopen(nxt_task_t * task,const char * pathname,const char * mode)504 nxt_file_fopen(nxt_task_t *task, const char *pathname, const char *mode)
505 {
506 int err;
507 FILE *fp;
508
509 #if (NXT_DEBUG)
510 nxt_thread_time_update(task->thread);
511 #endif
512
513 fp = fopen(pathname, mode);
514 err = (fp == NULL) ? nxt_errno : 0;
515
516 nxt_debug(task, "fopen(\"%s\", \"%s\"): fp:%p err:%d", pathname, mode, fp,
517 err);
518
519 if (nxt_fast_path(fp != NULL)) {
520 return fp;
521 }
522
523 nxt_alert(task, "fopen(\"%s\") failed %E", pathname, err);
524
525 return NULL;
526 }
527
528
529 void
nxt_file_fclose(nxt_task_t * task,FILE * fp)530 nxt_file_fclose(nxt_task_t *task, FILE *fp)
531 {
532 nxt_debug(task, "fclose(%p)", fp);
533
534 if (nxt_slow_path(fclose(fp) == -1)) {
535 nxt_alert(task, "fclose() failed %E", nxt_errno);
536 }
537 }
538
539
540 /*
541 * nxt_file_redirect() redirects the file to the fd descriptor.
542 * Then the fd descriptor is closed.
543 */
544
545 nxt_int_t
nxt_file_redirect(nxt_file_t * file,nxt_fd_t fd)546 nxt_file_redirect(nxt_file_t *file, nxt_fd_t fd)
547 {
548 nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")", fd, file->fd, file->name);
549
550 if (dup2(fd, file->fd) == -1) {
551 nxt_thread_log_alert("dup2(%FD, %FD, \"%FN\") failed %E",
552 fd, file->fd, file->name, nxt_errno);
553 return NXT_ERROR;
554 }
555
556 if (close(fd) != 0) {
557 nxt_thread_log_alert("close(%FD, \"%FN\") failed %E",
558 fd, file->name, nxt_errno);
559 return NXT_ERROR;
560 }
561
562 return NXT_OK;
563 }
564
565
566 /* nxt_file_stdout() redirects the stdout descriptor to the file. */
567
568 nxt_int_t
nxt_file_stdout(nxt_file_t * file)569 nxt_file_stdout(nxt_file_t *file)
570 {
571 nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")",
572 file->fd, STDOUT_FILENO, file->name);
573
574 if (dup2(file->fd, STDOUT_FILENO) != -1) {
575 return NXT_OK;
576 }
577
578 nxt_thread_log_alert("dup2(%FD, %FD, \"%FN\") failed %E",
579 file->fd, STDOUT_FILENO, file->name, nxt_errno);
580
581 return NXT_ERROR;
582 }
583
584
585 /* nxt_file_stderr() redirects the stderr descriptor to the file. */
586
587 nxt_int_t
nxt_file_stderr(nxt_file_t * file)588 nxt_file_stderr(nxt_file_t *file)
589 {
590 nxt_thread_log_debug("dup2(%FD, %FD, \"%FN\")",
591 file->fd, STDERR_FILENO, file->name);
592
593 if (dup2(file->fd, STDERR_FILENO) != -1) {
594 return NXT_OK;
595 }
596
597 nxt_thread_log_alert("dup2(%FD, %FD, \"%FN\") failed %E",
598 file->fd, STDERR_FILENO, file->name, nxt_errno);
599
600 return NXT_ERROR;
601 }
602
603
604 nxt_int_t
nxt_stderr_start(void)605 nxt_stderr_start(void)
606 {
607 int flags, fd;
608
609 flags = fcntl(nxt_stderr, F_GETFL);
610
611 if (flags != -1) {
612 /*
613 * If the stderr output of a multithreaded application is
614 * redirected to a file:
615 * Linux, Solaris and MacOSX do not write atomically to the output;
616 * MacOSX besides adds zeroes to the output.
617 * O_APPEND fixes this.
618 */
619 (void) fcntl(nxt_stderr, F_SETFL, flags | O_APPEND);
620
621 } else {
622 /*
623 * The stderr descriptor is closed before application start.
624 * Reserve the stderr descriptor for future use. Errors are
625 * ignored because anyway they could be written nowhere.
626 */
627 fd = open("/dev/null", O_WRONLY | O_APPEND);
628
629 if (fd != -1) {
630 (void) dup2(fd, nxt_stderr);
631
632 if (fd != nxt_stderr) {
633 (void) close(fd);
634 }
635 }
636 }
637
638 return flags;
639 }
640
641
642 nxt_int_t
nxt_pipe_create(nxt_task_t * task,nxt_fd_t * pp,nxt_bool_t nbread,nxt_bool_t nbwrite)643 nxt_pipe_create(nxt_task_t *task, nxt_fd_t *pp, nxt_bool_t nbread,
644 nxt_bool_t nbwrite)
645 {
646 if (pipe(pp) != 0) {
647 nxt_alert(task, "pipe() failed %E", nxt_errno);
648
649 return NXT_ERROR;
650 }
651
652 nxt_debug(task, "pipe(): %FD:%FD", pp[0], pp[1]);
653
654 if (nbread) {
655 if (nxt_fd_nonblocking(task, pp[0]) != NXT_OK) {
656 return NXT_ERROR;
657 }
658 }
659
660 if (nbwrite) {
661 if (nxt_fd_nonblocking(task, pp[1]) != NXT_OK) {
662 return NXT_ERROR;
663 }
664 }
665
666 return NXT_OK;
667 }
668
669
670 void
nxt_pipe_close(nxt_task_t * task,nxt_fd_t * pp)671 nxt_pipe_close(nxt_task_t *task, nxt_fd_t *pp)
672 {
673 nxt_debug(task, "pipe close(%FD:%FD)", pp[0], pp[1]);
674
675 if (close(pp[0]) != 0) {
676 nxt_alert(task, "pipe close(%FD) failed %E", pp[0], nxt_errno);
677 }
678
679 if (close(pp[1]) != 0) {
680 nxt_alert(task, "pipe close(%FD) failed %E", pp[1], nxt_errno);
681 }
682 }
683
684
685 size_t
nxt_dir_current(char * buf,size_t len)686 nxt_dir_current(char *buf, size_t len)
687 {
688 if (nxt_fast_path(getcwd(buf, len) != NULL)) {
689 return nxt_strlen(buf);
690 }
691
692 nxt_thread_log_alert("getcwd(%uz) failed %E", len, nxt_errno);
693
694 return 0;
695 }
696