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