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