1 /*
2  * libusual - Utility library for C
3  *
4  * Copyright (c) 2007-2009 Marko Kreen, Skype Technologies OÜ
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Wrappers around regular I/O functions (send/recv/read/write)
21  * that survive EINTR and also can log problems.
22  */
23 
24 #include <usual/safeio.h>
25 
26 #include <usual/socket.h>
27 #include <usual/logging.h>
28 #include <usual/string.h>
29 #include <usual/time.h>
30 
safe_read(int fd,void * buf,size_t len)31 ssize_t safe_read(int fd, void *buf, size_t len)
32 {
33 	ssize_t res;
34 loop:
35 	res = read(fd, buf, len);
36 	if (res < 0 && errno == EINTR)
37 		goto loop;
38 	return res;
39 }
40 
safe_write(int fd,const void * buf,size_t len)41 ssize_t safe_write(int fd, const void *buf, size_t len)
42 {
43 	ssize_t res;
44 loop:
45 	res = write(fd, buf, len);
46 	if (res < 0 && errno == EINTR)
47 		goto loop;
48 	return res;
49 }
50 
safe_recv(int fd,void * buf,size_t len,int flags)51 ssize_t safe_recv(int fd, void *buf, size_t len, int flags)
52 {
53 	ssize_t res;
54 	char ebuf[128];
55 loop:
56 	res = recv(fd, buf, len, flags);
57 	if (res < 0 && errno == EINTR)
58 		goto loop;
59 	if (res < 0)
60 		log_noise("safe_recv(%d, %zu) = %s", fd, len,
61 			  strerror_r(errno, ebuf, sizeof(ebuf)));
62 	else if (cf_verbose > 2)
63 		log_noise("safe_recv(%d, %zu) = %zd", fd, len, res);
64 	return res;
65 }
66 
safe_send(int fd,const void * buf,size_t len,int flags)67 ssize_t safe_send(int fd, const void *buf, size_t len, int flags)
68 {
69 	ssize_t res;
70 	char ebuf[128];
71 loop:
72 	res = send(fd, buf, len, flags);
73 	if (res < 0 && errno == EINTR)
74 		goto loop;
75 	if (res < 0)
76 		log_noise("safe_send(%d, %zu) = %s", fd, len,
77 			  strerror_r(errno, ebuf, sizeof(ebuf)));
78 	else if (cf_verbose > 2)
79 		log_noise("safe_send(%d, %zu) = %zd", fd, len, res);
80 	return res;
81 }
82 
safe_close(int fd)83 int safe_close(int fd)
84 {
85 	int res;
86 
87 #ifndef WIN32
88 	/*
89 	 * POSIX says close() can return EINTR but fd state is "undefined"
90 	 * later.  Seems Linux and BSDs close the fd anyway and EINTR is
91 	 * simply informative.  Thus retry is dangerous.
92 	 */
93 	res = close(fd);
94 #else
95 	/*
96 	 * Seems on windows it can returns proper EINTR but only when
97 	 * WSACancelBlockingCall() is called.  As we don't do it,
98 	 * ignore EINTR on win32 too.
99 	 */
100 	res = closesocket(fd);
101 #endif
102 	if (res < 0) {
103 		char ebuf[128];
104 		log_warning("safe_close(%d) = %s", fd,
105 			    strerror_r(errno, ebuf, sizeof(ebuf)));
106 	} else if (cf_verbose > 2) {
107 		log_noise("safe_close(%d) = %d", fd, res);
108 	}
109 
110 	/* ignore EINTR */
111 	if (res < 0 && errno == EINTR)
112 		return 0;
113 
114 	return res;
115 }
116 
safe_recvmsg(int fd,struct msghdr * msg,int flags)117 ssize_t safe_recvmsg(int fd, struct msghdr *msg, int flags)
118 {
119 	ssize_t res;
120 	char ebuf[128];
121 loop:
122 	res = recvmsg(fd, msg, flags);
123 	if (res < 0 && errno == EINTR)
124 		goto loop;
125 	if (res < 0)
126 		log_warning("safe_recvmsg(%d, msg, %d) = %s", fd, flags,
127 			    strerror_r(errno, ebuf, sizeof(ebuf)));
128 	else if (cf_verbose > 2)
129 		log_noise("safe_recvmsg(%d, msg, %d) = %zd", fd, flags, res);
130 	return res;
131 }
132 
safe_sendmsg(int fd,const struct msghdr * msg,int flags)133 ssize_t safe_sendmsg(int fd, const struct msghdr *msg, int flags)
134 {
135 	ssize_t res;
136 	int msgerr_count = 0;
137 	char ebuf[128];
138 loop:
139 	res = sendmsg(fd, msg, flags);
140 	if (res < 0 && errno == EINTR)
141 		goto loop;
142 
143 	if (res < 0) {
144 		log_warning("safe_sendmsg(%d, msg[%d,%d], %d) = %s", fd,
145 			    (int)msg->msg_iov[0].iov_len,
146 			    (int)msg->msg_controllen,
147 			    flags, strerror_r(errno, ebuf, sizeof(ebuf)));
148 
149 		/* with ancillary data on blocking socket OSX returns
150 		 * EMSGSIZE instead of blocking.  try to solve it by waiting */
151 		if (errno == EMSGSIZE && msgerr_count < 20) {
152 			struct timeval tv = {1, 0};
153 			log_warning("trying to sleep a bit");
154 			select(0, NULL, NULL, NULL, &tv);
155 			msgerr_count++;
156 			goto loop;
157 		}
158 	} else if (cf_verbose > 2)
159 		log_noise("safe_sendmsg(%d, msg, %d) = %zd", fd, flags, res);
160 	return res;
161 }
162 
safe_connect(int fd,const struct sockaddr * sa,socklen_t sa_len)163 int safe_connect(int fd, const struct sockaddr *sa, socklen_t sa_len)
164 {
165 	int res;
166 	char buf[128];
167 	char ebuf[128];
168 loop:
169 	res = connect(fd, sa, sa_len);
170 	if (res < 0 && errno == EINTR)
171 		goto loop;
172 	if (res < 0 && (errno != EINPROGRESS || cf_verbose > 2))
173 		log_noise("connect(%d, %s) = %s", fd,
174 			  sa2str(sa, buf, sizeof(buf)),
175 			  strerror_r(errno, ebuf, sizeof(ebuf)));
176 	else if (cf_verbose > 2)
177 		log_noise("connect(%d, %s) = %d", fd, sa2str(sa, buf, sizeof(buf)), res);
178 	return res;
179 }
180 
safe_accept(int fd,struct sockaddr * sa,socklen_t * sa_len_p)181 int safe_accept(int fd, struct sockaddr *sa, socklen_t *sa_len_p)
182 {
183 	int res;
184 	char buf[128];
185 	char ebuf[128];
186 loop:
187 	res = accept(fd, sa, sa_len_p);
188 	if (res < 0 && errno == EINTR)
189 		goto loop;
190 	if (res < 0)
191 		log_noise("safe_accept(%d) = %s", fd,
192 			  strerror_r(errno, ebuf, sizeof(ebuf)));
193 	else if (cf_verbose > 2) {
194 		if (sa->sa_family == AF_UNIX)
195 			/* sa2str() won't work here since accept() doesn't set sun_path */
196 			log_noise("safe_accept(%d) = %d (unix)", fd, res);
197 		else
198 			log_noise("safe_accept(%d) = %d (%s)", fd, res, sa2str(sa, buf, sizeof(buf)));
199 	}
200 	return res;
201 }
202