proto_common.c revision 220273
159191Skris/*- 2280304Sjkim * Copyright (c) 2009-2010 The FreeBSD Foundation 3280304Sjkim * Copyright (c) 2011 Pawel Jakub Dawidek <pawel@dawidek.net> 4280304Sjkim * All rights reserved. 559191Skris * 659191Skris * This software was developed by Pawel Jakub Dawidek under sponsorship from 7127128Snectar * the FreeBSD Foundation. 859191Skris * 959191Skris * Redistribution and use in source and binary forms, with or without 1059191Skris * modification, are permitted provided that the following conditions 1159191Skris * are met: 1259191Skris * 1. Redistributions of source code must retain the above copyright 1359191Skris * notice, this list of conditions and the following disclaimer. 14280304Sjkim * 2. Redistributions in binary form must reproduce the above copyright 1559191Skris * notice, this list of conditions and the following disclaimer in the 1659191Skris * documentation and/or other materials provided with the distribution. 1759191Skris * 1859191Skris * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 1959191Skris * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2059191Skris * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2159191Skris * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 2259191Skris * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2359191Skris * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2459191Skris * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2559191Skris * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2659191Skris * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2759191Skris * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2859191Skris * SUCH DAMAGE. 2959191Skris */ 3059191Skris 3159191Skris#include <sys/cdefs.h> 3259191Skris__FBSDID("$FreeBSD: head/sbin/hastd/proto_common.c 220273 2011-04-02 09:31:02Z pjd $"); 3359191Skris 3459191Skris#include <sys/types.h> 3559191Skris#include <sys/socket.h> 3659191Skris 3759191Skris#include <errno.h> 3859191Skris#include <fcntl.h> 3959191Skris#include <stdbool.h> 4059191Skris#include <stdlib.h> 4159191Skris#include <strings.h> 4259191Skris#include <unistd.h> 4359191Skris 4459191Skris#include "pjdlog.h" 4559191Skris#include "proto_impl.h" 4659191Skris 4759191Skris/* Maximum size of packet we want to use when sending data. */ 4859191Skris#ifndef MAX_SEND_SIZE 4959191Skris#define MAX_SEND_SIZE 32768 5059191Skris#endif 5159191Skris 5259191Skrisstatic bool 5359191Skrisblocking_socket(int sock) 5459191Skris{ 5559191Skris int flags; 5659191Skris 5759191Skris flags = fcntl(sock, F_GETFL); 5859191Skris PJDLOG_ASSERT(flags >= 0); 5959191Skris return ((flags & O_NONBLOCK) == 0); 6059191Skris} 6159191Skris 6259191Skrisstatic int 6368651Skrisproto_descriptor_send(int sock, int fd) 6459191Skris{ 6559191Skris unsigned char ctrl[CMSG_SPACE(sizeof(fd))]; 6659191Skris struct msghdr msg; 6768651Skris struct cmsghdr *cmsg; 68280304Sjkim 69280304Sjkim PJDLOG_ASSERT(sock >= 0); 70280304Sjkim PJDLOG_ASSERT(fd >= 0); 71280304Sjkim 72280304Sjkim bzero(&msg, sizeof(msg)); 73280304Sjkim bzero(&ctrl, sizeof(ctrl)); 7468651Skris 75280304Sjkim msg.msg_iov = NULL; 76280304Sjkim msg.msg_iovlen = 0; 77280304Sjkim msg.msg_control = ctrl; 78280304Sjkim msg.msg_controllen = sizeof(ctrl); 79280304Sjkim 80280304Sjkim cmsg = CMSG_FIRSTHDR(&msg); 81280304Sjkim cmsg->cmsg_level = SOL_SOCKET; 82280304Sjkim cmsg->cmsg_type = SCM_RIGHTS; 8368651Skris cmsg->cmsg_len = CMSG_LEN(sizeof(fd)); 84109998Smarkm bcopy(&fd, CMSG_DATA(cmsg), sizeof(fd)); 8559191Skris 86280304Sjkim if (sendmsg(sock, &msg, 0) == -1) 8759191Skris return (errno); 8859191Skris 8959191Skris return (0); 90280304Sjkim} 91280304Sjkim 92280304Sjkimint 93280304Sjkimproto_common_send(int sock, const unsigned char *data, size_t size, int fd) 94280304Sjkim{ 95280304Sjkim ssize_t done; 96280304Sjkim size_t sendsize; 97280304Sjkim int errcount = 0; 98280304Sjkim 99280304Sjkim PJDLOG_ASSERT(sock >= 0); 100280304Sjkim 101280304Sjkim if (data == NULL) { 102280304Sjkim /* The caller is just trying to decide about direction. */ 103280304Sjkim 104280304Sjkim PJDLOG_ASSERT(size == 0); 105280304Sjkim 106280304Sjkim if (shutdown(sock, SHUT_RD) == -1) 107280304Sjkim return (errno); 108280304Sjkim return (0); 10959191Skris } 11059191Skris 11159191Skris PJDLOG_ASSERT(data != NULL); 11259191Skris PJDLOG_ASSERT(size > 0); 11359191Skris 11459191Skris do { 11559191Skris sendsize = size < MAX_SEND_SIZE ? size : MAX_SEND_SIZE; 11659191Skris done = send(sock, data, sendsize, MSG_NOSIGNAL); 117280304Sjkim if (done == 0) { 11859191Skris return (ENOTCONN); 119280304Sjkim } else if (done < 0) { 12059191Skris if (errno == EINTR) 12159191Skris continue; 122280304Sjkim if (errno == ENOBUFS) { 123280304Sjkim /* 124280304Sjkim * If there are no buffers we retry. 125280304Sjkim * After each try we increase delay before the 126280304Sjkim * next one and we give up after fifteen times. 12759191Skris * This gives 11s of total wait time. 12859191Skris */ 129280304Sjkim if (errcount == 15) { 130280304Sjkim pjdlog_warning("Getting ENOBUFS errors for 11s on send(), giving up."); 131280304Sjkim } else { 132280304Sjkim if (errcount == 0) 133280304Sjkim pjdlog_warning("Got ENOBUFS error on send(), retrying for a bit."); 134280304Sjkim errcount++; 135280304Sjkim usleep(100000 * errcount); 136280304Sjkim continue; 137280304Sjkim } 138280304Sjkim } 139280304Sjkim /* 140280304Sjkim * If this is blocking socket and we got EAGAIN, this 141280304Sjkim * means the request timed out. Translate errno to 142280304Sjkim * ETIMEDOUT, to give administrator a hint to 14359191Skris * eventually increase timeout. 14459191Skris */ 145109998Smarkm if (errno == EAGAIN && blocking_socket(sock)) 146109998Smarkm errno = ETIMEDOUT; 147280304Sjkim return (errno); 148280304Sjkim } 149280304Sjkim data += done; 150280304Sjkim size -= done; 151280304Sjkim } while (size > 0); 152280304Sjkim if (errcount > 0) { 153109998Smarkm pjdlog_info("Data sent successfully after %d ENOBUFS error%s.", 154109998Smarkm errcount, errcount == 1 ? "" : "s"); 15559191Skris } 15659191Skris 157280304Sjkim if (fd == -1) 158280304Sjkim return (0); 159280304Sjkim return (proto_descriptor_send(sock, fd)); 16059191Skris} 16159191Skris 162280304Sjkimstatic int 16359191Skrisproto_descriptor_recv(int sock, int *fdp) 164280304Sjkim{ 165280304Sjkim unsigned char ctrl[CMSG_SPACE(sizeof(*fdp))]; 166280304Sjkim struct msghdr msg; 167280304Sjkim struct cmsghdr *cmsg; 168280304Sjkim 16959191Skris PJDLOG_ASSERT(sock >= 0); 17059191Skris PJDLOG_ASSERT(fdp != NULL); 17159191Skris 17259191Skris bzero(&msg, sizeof(msg)); 173280304Sjkim bzero(&ctrl, sizeof(ctrl)); 174280304Sjkim 175280304Sjkim msg.msg_iov = NULL; 176280304Sjkim msg.msg_iovlen = 0; 177280304Sjkim msg.msg_control = ctrl; 178280304Sjkim msg.msg_controllen = sizeof(ctrl); 179280304Sjkim 180280304Sjkim if (recvmsg(sock, &msg, 0) == -1) 18159191Skris return (errno); 18259191Skris 18359191Skris cmsg = CMSG_FIRSTHDR(&msg); 18459191Skris if (cmsg->cmsg_level != SOL_SOCKET || 185280304Sjkim cmsg->cmsg_type != SCM_RIGHTS) { 186280304Sjkim return (EINVAL); 187280304Sjkim } 188280304Sjkim bcopy(CMSG_DATA(cmsg), fdp, sizeof(*fdp)); 189280304Sjkim 190280304Sjkim return (0); 191280304Sjkim} 192280304Sjkim 193280304Sjkimint 194280304Sjkimproto_common_recv(int sock, unsigned char *data, size_t size, int *fdp) 195280304Sjkim{ 19659191Skris ssize_t done; 19759191Skris 19859191Skris PJDLOG_ASSERT(sock >= 0); 199280304Sjkim 200280304Sjkim if (data == NULL) { 20159191Skris /* The caller is just trying to decide about direction. */ 202280304Sjkim 203280304Sjkim PJDLOG_ASSERT(size == 0); 204280304Sjkim 205280304Sjkim if (shutdown(sock, SHUT_WR) == -1) 206280304Sjkim return (errno); 207280304Sjkim return (0); 208280304Sjkim } 209280304Sjkim 210280304Sjkim PJDLOG_ASSERT(data != NULL); 211280304Sjkim PJDLOG_ASSERT(size > 0); 212280304Sjkim 213280304Sjkim do { 214280304Sjkim done = recv(sock, data, size, MSG_WAITALL); 215280304Sjkim } while (done == -1 && errno == EINTR); 216280304Sjkim if (done == 0) { 217280304Sjkim return (ENOTCONN); 218280304Sjkim } else if (done < 0) { 219280304Sjkim /* 220280304Sjkim * If this is blocking socket and we got EAGAIN, this 22159191Skris * means the request timed out. Translate errno to 222280304Sjkim * ETIMEDOUT, to give administrator a hint to 223280304Sjkim * eventually increase timeout. 224280304Sjkim */ 225280304Sjkim if (errno == EAGAIN && blocking_socket(sock)) 226280304Sjkim errno = ETIMEDOUT; 227280304Sjkim return (errno); 228280304Sjkim } 229280304Sjkim if (fdp == NULL) 230280304Sjkim return (0); 231280304Sjkim return (proto_descriptor_recv(sock, fdp)); 232280304Sjkim} 233280304Sjkim