proto_common.c revision 223143
1210284Sjmallett/*-
2232812Sjmallett * Copyright (c) 2009-2010 The FreeBSD Foundation
3215990Sjmallett * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4210284Sjmallett * All rights reserved.
5210284Sjmallett *
6215990Sjmallett * This software was developed by Pawel Jakub Dawidek under sponsorship from
7215990Sjmallett * the FreeBSD Foundation.
8215990Sjmallett *
9210284Sjmallett * Redistribution and use in source and binary forms, with or without
10215990Sjmallett * modification, are permitted provided that the following conditions
11215990Sjmallett * are met:
12210284Sjmallett * 1. Redistributions of source code must retain the above copyright
13215990Sjmallett *    notice, this list of conditions and the following disclaimer.
14215990Sjmallett * 2. Redistributions in binary form must reproduce the above copyright
15215990Sjmallett *    notice, this list of conditions and the following disclaimer in the
16215990Sjmallett *    documentation and/or other materials provided with the distribution.
17210284Sjmallett *
18232812Sjmallett * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19215990Sjmallett * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20215990Sjmallett * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21215990Sjmallett * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22210284Sjmallett * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23215990Sjmallett * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24215990Sjmallett * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25215990Sjmallett * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26215990Sjmallett * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27210284Sjmallett * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28215990Sjmallett * SUCH DAMAGE.
29232812Sjmallett */
30215990Sjmallett
31215990Sjmallett#include <sys/cdefs.h>
32215990Sjmallett__FBSDID("$FreeBSD: head/sbin/hastd/proto_common.c 223143 2011-06-16 08:31:06Z sobomax $");
33215990Sjmallett
34215990Sjmallett#include <sys/types.h>
35215990Sjmallett#include <sys/socket.h>
36215990Sjmallett
37215990Sjmallett#include <errno.h>
38215990Sjmallett#include <fcntl.h>
39210284Sjmallett#include <stdbool.h>
40210284Sjmallett#include <stdlib.h>
41210284Sjmallett#include <strings.h>
42210284Sjmallett#include <unistd.h>
43210284Sjmallett
44210284Sjmallett#include "pjdlog.h"
45210284Sjmallett#include "proto_impl.h"
46210284Sjmallett
47210284Sjmallett/* Maximum size of packet we want to use when sending data. */
48210284Sjmallett#ifndef MAX_SEND_SIZE
49210284Sjmallett#define	MAX_SEND_SIZE	32768
50210284Sjmallett#endif
51210284Sjmallett
52210284Sjmallettstatic bool
53210284Sjmallettblocking_socket(int sock)
54210284Sjmallett{
55210284Sjmallett	int flags;
56210284Sjmallett
57210284Sjmallett	flags = fcntl(sock, F_GETFL);
58210284Sjmallett	PJDLOG_ASSERT(flags >= 0);
59210284Sjmallett	return ((flags & O_NONBLOCK) == 0);
60210284Sjmallett}
61210284Sjmallett
62210284Sjmallettstatic int
63210284Sjmallettproto_descriptor_send(int sock, int fd)
64232812Sjmallett{
65210284Sjmallett	unsigned char ctrl[CMSG_SPACE(sizeof(fd))];
66210284Sjmallett	struct msghdr msg;
67210284Sjmallett	struct cmsghdr *cmsg;
68210284Sjmallett
69210284Sjmallett	PJDLOG_ASSERT(sock >= 0);
70210284Sjmallett	PJDLOG_ASSERT(fd >= 0);
71210284Sjmallett
72215990Sjmallett	bzero(&msg, sizeof(msg));
73232812Sjmallett	bzero(&ctrl, sizeof(ctrl));
74232812Sjmallett
75232812Sjmallett	msg.msg_iov = NULL;
76210284Sjmallett	msg.msg_iovlen = 0;
77215990Sjmallett	msg.msg_control = ctrl;
78210284Sjmallett	msg.msg_controllen = sizeof(ctrl);
79232812Sjmallett
80210284Sjmallett	cmsg = CMSG_FIRSTHDR(&msg);
81210284Sjmallett	cmsg->cmsg_level = SOL_SOCKET;
82210284Sjmallett	cmsg->cmsg_type = SCM_RIGHTS;
83243473Sjmallett	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
84243473Sjmallett	bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
85243473Sjmallett
86243473Sjmallett	if (sendmsg(sock, &msg, 0) == -1)
87243473Sjmallett		return (errno);
88243473Sjmallett
89243473Sjmallett	return (0);
90243473Sjmallett}
91243473Sjmallett
92243473Sjmallettint
93243473Sjmallettproto_common_send(int sock, const unsigned char *data, size_t size, int fd)
94243473Sjmallett{
95243473Sjmallett	ssize_t done;
96243473Sjmallett	size_t sendsize;
97243473Sjmallett	int errcount = 0;
98243473Sjmallett
99243473Sjmallett	PJDLOG_ASSERT(sock >= 0);
100210284Sjmallett
101210284Sjmallett	if (data == NULL) {
102210284Sjmallett		/* The caller is just trying to decide about direction. */
103210284Sjmallett
104210284Sjmallett		PJDLOG_ASSERT(size == 0);
105210284Sjmallett
106210284Sjmallett		if (shutdown(sock, SHUT_RD) == -1)
107210284Sjmallett			return (errno);
108210284Sjmallett		return (0);
109210284Sjmallett	}
110210284Sjmallett
111210284Sjmallett	PJDLOG_ASSERT(data != NULL);
112210284Sjmallett	PJDLOG_ASSERT(size > 0);
113210284Sjmallett
114210284Sjmallett	do {
115210284Sjmallett		sendsize = size < MAX_SEND_SIZE ? size : MAX_SEND_SIZE;
116210284Sjmallett		done = send(sock, data, sendsize, MSG_NOSIGNAL);
117210284Sjmallett		if (done == 0) {
118210284Sjmallett			return (ENOTCONN);
119210284Sjmallett		} else if (done < 0) {
120210284Sjmallett			if (errno == EINTR)
121210284Sjmallett				continue;
122210284Sjmallett			if (errno == ENOBUFS) {
123210284Sjmallett				/*
124210284Sjmallett				 * If there are no buffers we retry.
125210284Sjmallett				 * After each try we increase delay before the
126210284Sjmallett				 * next one and we give up after fifteen times.
127210284Sjmallett				 * This gives 11s of total wait time.
128210284Sjmallett				 */
129210284Sjmallett				if (errcount == 15) {
130210284Sjmallett					pjdlog_warning("Getting ENOBUFS errors for 11s on send(), giving up.");
131210284Sjmallett				} else {
132210284Sjmallett					if (errcount == 0)
133210284Sjmallett						pjdlog_warning("Got ENOBUFS error on send(), retrying for a bit.");
134210284Sjmallett					errcount++;
135210284Sjmallett					usleep(100000 * errcount);
136210284Sjmallett					continue;
137210284Sjmallett				}
138210284Sjmallett			}
139210284Sjmallett			/*
140210284Sjmallett			 * If this is blocking socket and we got EAGAIN, this
141210284Sjmallett			 * means the request timed out. Translate errno to
142210284Sjmallett			 * ETIMEDOUT, to give administrator a hint to
143210284Sjmallett			 * eventually increase timeout.
144210284Sjmallett			 */
145210284Sjmallett			if (errno == EAGAIN && blocking_socket(sock))
146210284Sjmallett				errno = ETIMEDOUT;
147210284Sjmallett			return (errno);
148210284Sjmallett		}
149210284Sjmallett		data += done;
150210284Sjmallett		size -= done;
151210284Sjmallett	} while (size > 0);
152210284Sjmallett	if (errcount > 0) {
153210284Sjmallett		pjdlog_info("Data sent successfully after %d ENOBUFS error%s.",
154210284Sjmallett		    errcount, errcount == 1 ? "" : "s");
155210284Sjmallett	}
156210284Sjmallett
157210284Sjmallett	if (fd == -1)
158210284Sjmallett		return (0);
159210284Sjmallett	return (proto_descriptor_send(sock, fd));
160210284Sjmallett}
161210284Sjmallett
162210284Sjmallettstatic int
163210284Sjmallettproto_descriptor_recv(int sock, int *fdp)
164232812Sjmallett{
165210284Sjmallett	unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))];
166210284Sjmallett	struct msghdr msg;
167210284Sjmallett	struct cmsghdr *cmsg;
168210284Sjmallett
169210284Sjmallett	PJDLOG_ASSERT(sock >= 0);
170210284Sjmallett	PJDLOG_ASSERT(fdp != NULL);
171210284Sjmallett
172210284Sjmallett	bzero(&msg, sizeof(msg));
173210284Sjmallett	bzero(&ctrl, sizeof(ctrl));
174210284Sjmallett
175210284Sjmallett	msg.msg_iov = NULL;
176210284Sjmallett	msg.msg_iovlen = 0;
177210284Sjmallett	msg.msg_control = ctrl;
178210284Sjmallett	msg.msg_controllen = sizeof(ctrl);
179210284Sjmallett
180210284Sjmallett	if (recvmsg(sock, &msg, 0) == -1)
181210284Sjmallett		return (errno);
182210284Sjmallett
183210284Sjmallett	cmsg = CMSG_FIRSTHDR(&msg);
184210284Sjmallett	if (cmsg->cmsg_level != SOL_SOCKET ||
185232812Sjmallett	    cmsg->cmsg_type != SCM_RIGHTS) {
186232812Sjmallett		return (EINVAL);
187232812Sjmallett	}
188232812Sjmallett	bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp));
189232812Sjmallett
190232812Sjmallett	return (0);
191232812Sjmallett}
192232812Sjmallett
193232812Sjmallettint
194232812Sjmallettproto_common_recv(int sock, unsigned char *data, size_t size, int *fdp)
195232812Sjmallett{
196232812Sjmallett	ssize_t done;
197232812Sjmallett
198232812Sjmallett	PJDLOG_ASSERT(sock >= 0);
199232812Sjmallett
200232812Sjmallett	if (data == NULL) {
201232812Sjmallett		/* The caller is just trying to decide about direction. */
202232812Sjmallett
203232812Sjmallett		PJDLOG_ASSERT(size == 0);
204232812Sjmallett
205232812Sjmallett		if (shutdown(sock, SHUT_WR) == -1)
206232812Sjmallett			return (errno);
207232812Sjmallett		return (0);
208232812Sjmallett	}
209232812Sjmallett
210232812Sjmallett	PJDLOG_ASSERT(data != NULL);
211232812Sjmallett	PJDLOG_ASSERT(size > 0);
212232812Sjmallett
213232812Sjmallett	do {
214232812Sjmallett		done = recv(sock, data, size, MSG_WAITALL);
215232812Sjmallett	} while (done == -1 && errno == EINTR);
216232812Sjmallett	if (done == 0) {
217232812Sjmallett		return (ENOTCONN);
218232812Sjmallett	} else if (done < 0) {
219232812Sjmallett		/*
220232812Sjmallett		 * If this is blocking socket and we got EAGAIN, this
221232812Sjmallett		 * means the request timed out. Translate errno to
222232812Sjmallett		 * ETIMEDOUT, to give administrator a hint to
223232812Sjmallett		 * eventually increase timeout.
224232812Sjmallett		 */
225232812Sjmallett		if (errno == EAGAIN && blocking_socket(sock))
226232812Sjmallett			errno = ETIMEDOUT;
227232812Sjmallett		return (errno);
228232812Sjmallett	}
229232812Sjmallett	if (fdp == NULL)
230232812Sjmallett		return (0);
231232812Sjmallett	return (proto_descriptor_recv(sock, fdp));
232232812Sjmallett}
233232812Sjmallett