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