1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020 Netflix, Inc. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/types.h> 29 #include <sys/socket.h> 30 #include <sys/sysctl.h> 31 #include <sys/uio.h> 32 33 #include <netinet/in.h> 34 35 #include <err.h> 36 #include <errno.h> 37 #include <fcntl.h> 38 #include <pthread.h> 39 #include <stdbool.h> 40 #include <stdio.h> 41 #include <stdlib.h> 42 #include <string.h> 43 #include <unistd.h> 44 45 static char buf[1024*1024]; 46 ssize_t readlen; 47 static volatile bool read_done = false; 48 49 static int 50 tcp_socketpair(int *sv) 51 { 52 struct sockaddr_in sin = { 53 .sin_len = sizeof(struct sockaddr_in), 54 .sin_family = AF_INET, 55 .sin_addr.s_addr = htonl(INADDR_LOOPBACK), 56 }; 57 int flags; 58 int ls; 59 60 ls = socket(PF_INET, SOCK_STREAM, 0); 61 if (ls < 0) 62 err(1, "socket ls"); 63 64 if (setsockopt(ls, SOL_SOCKET, SO_REUSEADDR, &(socklen_t){1}, 65 sizeof(int)) < 0) 66 err(1, "SO_REUSEADDR"); 67 68 if (bind(ls, (struct sockaddr *)&sin, sizeof(sin)) < 0) 69 err(1, "bind ls"); 70 71 if (getsockname(ls, (struct sockaddr *)&sin, 72 &(socklen_t){ sizeof(sin) }) < 0) 73 err(1, "getsockname"); 74 75 if (listen(ls, 5) < 0) 76 err(1, "listen ls"); 77 78 sv[0] = socket(PF_INET, SOCK_STREAM, 0); 79 if (sv[0] < 0) 80 err(1, "socket cs"); 81 82 flags = fcntl(sv[0], F_GETFL); 83 flags |= O_NONBLOCK; 84 if (fcntl(sv[0], F_SETFL, flags) == -1) 85 err(1, "fcntl +O_NONBLOCK"); 86 87 if (connect(sv[0], (void *)&sin, sizeof(sin)) != -1 || 88 errno != EINPROGRESS) 89 err(1, "connect cs"); 90 91 sv[1] = accept(ls, NULL, 0); 92 if (sv[1] < 0) 93 err(1, "accept ls"); 94 95 flags &= ~O_NONBLOCK; 96 if (fcntl(sv[0], F_SETFL, flags) == -1) 97 err(1, "fcntl -O_NONBLOCK"); 98 99 close(ls); 100 101 return (0); 102 } 103 104 static void * 105 receiver(void *arg) 106 { 107 int s = *(int *)arg; 108 ssize_t rv; 109 110 do { 111 rv = read(s, buf, sizeof(buf)); 112 if (rv == -1) 113 err(2, "read receiver"); 114 if (rv == 0) 115 break; 116 readlen -= rv; 117 } while (readlen != 0); 118 119 read_done = true; 120 121 return NULL; 122 } 123 124 static void 125 usage(void) 126 { 127 errx(1, "usage: %s [-u] <file> <start> <len> <flags>", getprogname()); 128 } 129 130 int 131 main(int argc, char **argv) 132 { 133 pthread_t pt; 134 off_t start; 135 int ch, fd, ss[2], flags, error; 136 bool pf_unix = false; 137 138 while ((ch = getopt(argc, argv, "u")) != -1) 139 switch (ch) { 140 case 'u': 141 pf_unix = true; 142 break; 143 default: 144 usage(); 145 } 146 argc -= optind; 147 argv += optind; 148 149 if (argc != 4) 150 usage(); 151 152 start = strtoull(argv[1], NULL, 0); 153 readlen = strtoull(argv[2], NULL, 0); 154 flags = strtoul(argv[3], NULL, 0); 155 156 fd = open(argv[0], O_RDONLY); 157 if (fd < 0) 158 err(1, "open"); 159 160 if (pf_unix) { 161 if (socketpair(PF_LOCAL, SOCK_STREAM, 0, ss) != 0) 162 err(1, "socketpair"); 163 } else 164 tcp_socketpair(ss); 165 166 error = pthread_create(&pt, NULL, receiver, &ss[1]); 167 if (error) 168 errc(1, error, "pthread_create"); 169 170 if (sendfile(fd, ss[0], start, readlen, NULL, NULL, flags) < 0) 171 err(3, "sendfile"); 172 173 while (!read_done) 174 usleep(1000); 175 176 exit(0); 177 } 178