proto_common.c revision 302408
1257648Sian/*-
2257648Sian * Copyright (c) 2009-2010 The FreeBSD Foundation
3257648Sian * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net>
4257648Sian * All rights reserved.
5257648Sian *
6257648Sian * This software was developed by Pawel Jakub Dawidek under sponsorship from
7257648Sian * the FreeBSD Foundation.
8257648Sian *
9257648Sian * Redistribution and use in source and binary forms, with or without
10257648Sian * modification, are permitted provided that the following conditions
11257648Sian * are met:
12257648Sian * 1. Redistributions of source code must retain the above copyright
13257648Sian *    notice, this list of conditions and the following disclaimer.
14257648Sian * 2. Redistributions in binary form must reproduce the above copyright
15257648Sian *    notice, this list of conditions and the following disclaimer in the
16257648Sian *    documentation and/or other materials provided with the distribution.
17257648Sian *
18257648Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
19257648Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20257648Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21257648Sian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
22257648Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23257648Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24257648Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25257648Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26257648Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27257648Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28257648Sian * SUCH DAMAGE.
29257648Sian */
30257648Sian
31257648Sian#include <sys/cdefs.h>
32257648Sian__FBSDID("$FreeBSD: stable/11/sbin/hastd/proto_common.c 237931 2012-07-01 16:26:07Z pjd $");
33257648Sian
34266086Sian#include <sys/types.h>
35266086Sian#include <sys/socket.h>
36257648Sian
37257648Sian#include <errno.h>
38257648Sian#include <fcntl.h>
39257648Sian#include <stdbool.h>
40257648Sian#include <stdlib.h>
41259365Sian#include <strings.h>
42257648Sian#include <unistd.h>
43257648Sian
44259364Sian#include "pjdlog.h"
45259365Sian#include "proto_impl.h"
46257648Sian
47257648Sian/* Maximum size of packet we want to use when sending data. */
48259365Sian#ifndef MAX_SEND_SIZE
49259365Sian#define	MAX_SEND_SIZE	32768
50259365Sian#endif
51259365Sian
52259365Sianstatic bool
53259365Sianblocking_socket(int sock)
54259365Sian{
55259365Sian	int flags;
56259365Sian
57259365Sian	flags = fcntl(sock, F_GETFL);
58259365Sian	PJDLOG_ASSERT(flags >= 0);
59266086Sian	return ((flags & O_NONBLOCK) == 0);
60266086Sian}
61266086Sian
62266086Sianstatic int
63266086Sianproto_descriptor_send(int sock, int fd)
64266086Sian{
65266086Sian	unsigned char ctrl[CMSG_SPACE(sizeof(fd))];
66266086Sian	struct msghdr msg;
67266086Sian	struct cmsghdr *cmsg;
68266086Sian
69266086Sian	PJDLOG_ASSERT(sock >= 0);
70266086Sian	PJDLOG_ASSERT(fd >= 0);
71266086Sian
72266086Sian	bzero(&msg, sizeof(msg));
73266086Sian	bzero(&ctrl, sizeof(ctrl));
74266086Sian
75266086Sian	msg.msg_iov = NULL;
76266086Sian	msg.msg_iovlen = 0;
77266086Sian	msg.msg_control = ctrl;
78266086Sian	msg.msg_controllen = sizeof(ctrl);
79266086Sian
80266086Sian	cmsg = CMSG_FIRSTHDR(&msg);
81266086Sian	cmsg->cmsg_level = SOL_SOCKET;
82266086Sian	cmsg->cmsg_type = SCM_RIGHTS;
83266086Sian	cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
84266086Sian	bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd));
85266086Sian
86266086Sian	if (sendmsg(sock, &msg, 0) == -1)
87266086Sian		return (errno);
88266086Sian
89259365Sian	return (0);
90259365Sian}
91259365Sian
92259365Sianint
93259365Sianproto_common_send(int sock, const unsigned char *data, size_t size, int fd)
94259365Sian{
95259365Sian	ssize_t done;
96259365Sian	size_t sendsize;
97259365Sian	int errcount = 0;
98259365Sian
99259365Sian	PJDLOG_ASSERT(sock >= 0);
100259365Sian
101259365Sian	if (data == NULL) {
102259365Sian		/* The caller is just trying to decide about direction. */
103266084Sian
104259365Sian		PJDLOG_ASSERT(size == 0);
105259365Sian
106259365Sian		if (shutdown(sock, SHUT_RD) == -1)
107259365Sian			return (errno);
108259365Sian		return (0);
109259365Sian	}
110259365Sian
111259365Sian	PJDLOG_ASSERT(data != NULL);
112259365Sian	PJDLOG_ASSERT(size > 0);
113259365Sian
114259365Sian	do {
115259365Sian		sendsize = size < MAX_SEND_SIZE ? size : MAX_SEND_SIZE;
116259365Sian		done = send(sock, data, sendsize, MSG_NOSIGNAL);
117259365Sian		if (done == 0) {
118259365Sian			return (ENOTCONN);
119259365Sian		} else if (done == -1) {
120259365Sian			if (errno == EINTR)
121259365Sian				continue;
122259365Sian			if (errno == ENOBUFS) {
123259365Sian				/*
124259365Sian				 * If there are no buffers we retry.
125259365Sian				 * After each try we increase delay before the
126259365Sian				 * next one and we give up after fifteen times.
127259365Sian				 * This gives 11s of total wait time.
128259365Sian				 */
129259365Sian				if (errcount == 15) {
130259365Sian					pjdlog_warning("Getting ENOBUFS errors for 11s on send(), giving up.");
131259365Sian				} else {
132259365Sian					if (errcount == 0)
133259365Sian						pjdlog_warning("Got ENOBUFS error on send(), retrying for a bit.");
134259365Sian					errcount++;
135259365Sian					usleep(100000 * errcount);
136259365Sian					continue;
137259365Sian				}
138259365Sian			}
139259365Sian			/*
140259365Sian			 * If this is blocking socket and we got EAGAIN, this
141259365Sian			 * means the request timed out. Translate errno to
142259365Sian			 * ETIMEDOUT, to give administrator a hint to
143259365Sian			 * eventually increase timeout.
144259365Sian			 */
145259365Sian			if (errno == EAGAIN && blocking_socket(sock))
146259365Sian				errno = ETIMEDOUT;
147259365Sian			return (errno);
148259365Sian		}
149259365Sian		data += done;
150259365Sian		size -= done;
151259365Sian	} while (size > 0);
152259365Sian	if (errcount > 0) {
153259364Sian		pjdlog_info("Data sent successfully after %d ENOBUFS error%s.",
154257648Sian		    errcount, errcount == 1 ? "" : "s");
155257648Sian	}
156259364Sian
157257648Sian	if (fd == -1)
158257648Sian		return (0);
159257648Sian	return (proto_descriptor_send(sock, fd));
160259364Sian}
161257648Sian
162259364Sianstatic int
163259364Sianproto_descriptor_recv(int sock, int *fdp)
164259364Sian{
165259364Sian	unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))];
166259364Sian	struct msghdr msg;
167259364Sian	struct cmsghdr *cmsg;
168259364Sian
169259364Sian	PJDLOG_ASSERT(sock >= 0);
170259364Sian	PJDLOG_ASSERT(fdp != NULL);
171259364Sian
172259364Sian	bzero(&msg, sizeof(msg));
173259364Sian	bzero(&ctrl, sizeof(ctrl));
174259364Sian
175259364Sian	msg.msg_iov = NULL;
176259364Sian	msg.msg_iovlen = 0;
177266084Sian	msg.msg_control = ctrl;
178266084Sian	msg.msg_controllen = sizeof(ctrl);
179259364Sian
180266084Sian	if (recvmsg(sock, &msg, 0) == -1)
181266084Sian		return (errno);
182259364Sian
183259364Sian	cmsg = CMSG_FIRSTHDR(&msg);
184259364Sian	if (cmsg == NULL || cmsg->cmsg_level != SOL_SOCKET ||
185259364Sian	    cmsg->cmsg_type != SCM_RIGHTS) {
186266084Sian		return (EINVAL);
187259364Sian	}
188257648Sian	bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp));
189257648Sian
190257648Sian	return (0);
191257648Sian}
192257648Sian
193257648Sianint
194257648Sianproto_common_recv(int sock, unsigned char *data, size_t size, int *fdp)
195257648Sian{
196257648Sian	ssize_t done;
197257648Sian
198257648Sian	PJDLOG_ASSERT(sock >= 0);
199257648Sian
200257648Sian	if (data == NULL) {
201259364Sian		/* The caller is just trying to decide about direction. */
202257648Sian
203257648Sian		PJDLOG_ASSERT(size == 0);
204257648Sian
205257648Sian		if (shutdown(sock, SHUT_WR) == -1)
206257648Sian			return (errno);
207257648Sian		return (0);
208257648Sian	}
209257648Sian
210257648Sian	PJDLOG_ASSERT(data != NULL);
211257648Sian	PJDLOG_ASSERT(size > 0);
212257648Sian
213257648Sian	do {
214257648Sian		done = recv(sock, data, size, MSG_WAITALL);
215257648Sian	} while (done == -1 && errno == EINTR);
216257648Sian	if (done == 0) {
217257648Sian		return (ENOTCONN);
218257648Sian	} else if (done == -1) {
219257648Sian		/*
220257648Sian		 * If this is blocking socket and we got EAGAIN, this
221259364Sian		 * means the request timed out. Translate errno to
222257648Sian		 * ETIMEDOUT, to give administrator a hint to
223257648Sian		 * eventually increase timeout.
224257648Sian		 */
225257648Sian		if (errno == EAGAIN && blocking_socket(sock))
226257648Sian			errno = ETIMEDOUT;
227257648Sian		return (errno);
228257648Sian	}
229257648Sian	if (fdp == NULL)
230257648Sian		return (0);
231257648Sian	return (proto_descriptor_recv(sock, fdp));
232257648Sian}
233257648Sian