/src/dovecot/src/lib/fd-util.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (c) 1999-2018 Dovecot authors, see the included COPYING file */ |
2 | | |
3 | | #include "lib.h" |
4 | | #include "net.h" |
5 | | #include "path-util.h" |
6 | | |
7 | | #include <unistd.h> |
8 | | #include <fcntl.h> |
9 | | #include <sys/stat.h> |
10 | | #include <sys/un.h> |
11 | | |
12 | | void fd_close_on_exec(int fd, bool set) |
13 | 1 | { |
14 | 1 | int flags; |
15 | | |
16 | 1 | flags = fcntl(fd, F_GETFD, 0); |
17 | 1 | if (flags < 0) |
18 | 0 | i_fatal("fcntl(F_GETFD, %d) failed: %m", fd); |
19 | | |
20 | 1 | flags = set ? (flags | FD_CLOEXEC) : (flags & ~FD_CLOEXEC); |
21 | 1 | if (fcntl(fd, F_SETFD, flags) < 0) |
22 | 0 | i_fatal("fcntl(F_SETFD, %d) failed: %m", fd); |
23 | 1 | } |
24 | | |
25 | | void fd_debug_verify_leaks(int first_fd, int last_fd) |
26 | 0 | { |
27 | 0 | struct ip_addr addr, raddr; |
28 | 0 | in_port_t port, rport; |
29 | 0 | struct stat st; |
30 | 0 | int old_errno; |
31 | 0 | bool leaks = FALSE; |
32 | |
|
33 | 0 | for (int fd = first_fd; fd <= last_fd; ++fd) { |
34 | 0 | if (fcntl(fd, F_GETFD, 0) == -1 && errno == EBADF) |
35 | 0 | continue; |
36 | | |
37 | 0 | old_errno = errno; |
38 | |
|
39 | 0 | if (net_getsockname(fd, &addr, &port) == 0) { |
40 | 0 | if (addr.family == AF_UNIX) { |
41 | 0 | struct sockaddr_un sa; |
42 | |
|
43 | 0 | socklen_t socklen = sizeof(sa); |
44 | |
|
45 | 0 | if (getsockname(fd, (void *)&sa, |
46 | 0 | &socklen) < 0) |
47 | 0 | sa.sun_path[0] = '\0'; |
48 | |
|
49 | 0 | i_error("Leaked UNIX socket fd %d: %s", |
50 | 0 | fd, sa.sun_path); |
51 | 0 | leaks = TRUE; |
52 | 0 | continue; |
53 | 0 | } |
54 | | |
55 | 0 | if (net_getpeername(fd, &raddr, &rport) < 0) { |
56 | 0 | i_zero(&raddr); |
57 | 0 | rport = 0; |
58 | 0 | } |
59 | 0 | i_error("Leaked socket fd %d: %s:%u -> %s:%u", |
60 | 0 | fd, net_ip2addr(&addr), port, |
61 | 0 | net_ip2addr(&raddr), rport); |
62 | 0 | leaks = TRUE; |
63 | 0 | continue; |
64 | 0 | } |
65 | | |
66 | 0 | if (fstat(fd, &st) == 0) { |
67 | 0 | const char *error; |
68 | 0 | const char *fname; |
69 | 0 | if (t_readlink(t_strdup_printf("/proc/self/fd/%d", fd), |
70 | 0 | &fname, &error) < 0) |
71 | 0 | fname = t_strdup_printf("<error: %s>", error); |
72 | |
|
73 | | #ifdef __APPLE__ |
74 | | /* OSX workaround: gettimeofday() calls shm_open() |
75 | | internally and the fd won't get closed on exec. |
76 | | We'll just skip all ino/dev=0 files and hope they |
77 | | weren't anything else. */ |
78 | | if (st.st_ino == 0 && st.st_dev == 0) |
79 | | continue; |
80 | | #endif |
81 | 0 | #ifdef HAVE_SYS_SYSMACROS_H |
82 | 0 | i_error("Leaked file %s: fd %d dev %s.%s inode %s", |
83 | 0 | fname, fd, dec2str(major(st.st_dev)), |
84 | 0 | dec2str(minor(st.st_dev)), dec2str(st.st_ino)); |
85 | 0 | leaks = TRUE; |
86 | 0 | continue; |
87 | | #else |
88 | | i_error("Leaked file %s: fd %d dev %s inode %s", |
89 | | fname, fd, dec2str(st.st_dev), |
90 | | dec2str(st.st_ino)); |
91 | | leaks = TRUE; |
92 | | continue; |
93 | | #endif |
94 | 0 | } |
95 | | |
96 | 0 | i_error("Leaked unknown fd %d (errno = %s)", |
97 | 0 | fd, strerror(old_errno)); |
98 | 0 | leaks = TRUE; |
99 | 0 | continue; |
100 | 0 | } |
101 | 0 | if (leaks) |
102 | 0 | i_fatal("fd leak found"); |
103 | 0 | } |
104 | | |
105 | | void fd_set_nonblock(int fd, bool nonblock) |
106 | 0 | { |
107 | 0 | int flags; |
108 | |
|
109 | 0 | i_assert(fd > -1); |
110 | | |
111 | 0 | flags = fcntl(fd, F_GETFL, 0); |
112 | 0 | if (flags < 0) |
113 | 0 | i_fatal("fcntl(%d, F_GETFL) failed: %m", fd); |
114 | | |
115 | 0 | if (nonblock) |
116 | 0 | flags |= O_NONBLOCK; |
117 | 0 | else |
118 | 0 | flags &= ENUM_NEGATE(O_NONBLOCK); |
119 | |
|
120 | 0 | if (fcntl(fd, F_SETFL, flags) < 0) |
121 | 0 | i_fatal("fcntl(%d, F_SETFL) failed: %m", fd); |
122 | 0 | } |
123 | | |
124 | | void fd_close_maybe_stdio(int *fd_in, int *fd_out) |
125 | 0 | { |
126 | 0 | int *fdp[2] = { fd_in, fd_out }; |
127 | |
|
128 | 0 | if (*fd_in == *fd_out) |
129 | 0 | *fd_in = -1; |
130 | |
|
131 | 0 | for (unsigned int i = 0; i < N_ELEMENTS(fdp); i++) { |
132 | 0 | if (*fdp[i] == -1) |
133 | 0 | ; |
134 | 0 | else if (*fdp[i] > 1) |
135 | 0 | i_close_fd(fdp[i]); |
136 | 0 | else if (dup2(dev_null_fd, *fdp[i]) == *fdp[i]) |
137 | 0 | *fdp[i] = -1; |
138 | 0 | else |
139 | 0 | i_fatal("dup2(/dev/null, %d) failed: %m", *fdp[i]); |
140 | 0 | } |
141 | 0 | } |
142 | | |
143 | | #undef i_close_fd_path |
144 | | void i_close_fd_path(int *fd, const char *path, const char *arg, |
145 | | const char *func, const char *file, int line) |
146 | 0 | { |
147 | 0 | int saved_errno; |
148 | |
|
149 | 0 | if (*fd == -1) |
150 | 0 | return; |
151 | | |
152 | 0 | if (unlikely(*fd <= 0)) { |
153 | 0 | i_panic("%s: close(%s%s%s) @ %s:%d attempted with fd=%d", |
154 | 0 | func, arg, |
155 | 0 | (path == NULL) ? "" : " = ", |
156 | 0 | (path == NULL) ? "" : path, |
157 | 0 | file, line, *fd); |
158 | 0 | } |
159 | | |
160 | 0 | saved_errno = errno; |
161 | | /* Ignore ECONNRESET because we don't really care about it here, |
162 | | as we are closing the socket down in any case. There might be |
163 | | unsent data but nothing we can do about that. */ |
164 | 0 | if (unlikely(close(*fd) < 0 && errno != ECONNRESET)) |
165 | 0 | i_error("%s: close(%s%s%s) @ %s:%d failed (fd=%d): %m", |
166 | 0 | func, arg, |
167 | 0 | (path == NULL) ? "" : " = ", |
168 | 0 | (path == NULL) ? "" : path, |
169 | 0 | file, line, *fd); |
170 | 0 | errno = saved_errno; |
171 | |
|
172 | 0 | *fd = -1; |
173 | 0 | } |