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