ggated.c revision 241720
1128766Spjd/*- 2128766Spjd * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3128766Spjd * All rights reserved. 4128766Spjd * 5128766Spjd * Redistribution and use in source and binary forms, with or without 6128766Spjd * modification, are permitted provided that the following conditions 7128766Spjd * are met: 8128766Spjd * 1. Redistributions of source code must retain the above copyright 9128766Spjd * notice, this list of conditions and the following disclaimer. 10128766Spjd * 2. Redistributions in binary form must reproduce the above copyright 11128766Spjd * notice, this list of conditions and the following disclaimer in the 12128766Spjd * documentation and/or other materials provided with the distribution. 13204075Spjd * 14128766Spjd * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15128766Spjd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16128766Spjd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17128766Spjd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18128766Spjd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19128766Spjd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20128766Spjd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21128766Spjd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22128766Spjd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23128766Spjd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24128766Spjd * SUCH DAMAGE. 25128766Spjd * 26128766Spjd * $FreeBSD: head/sbin/ggate/ggated/ggated.c 241720 2012-10-19 05:43:38Z ed $ 27128766Spjd */ 28128766Spjd 29128766Spjd#include <stdio.h> 30128766Spjd#include <stdlib.h> 31128766Spjd#include <stdint.h> 32128766Spjd#include <unistd.h> 33128766Spjd#include <fcntl.h> 34147844Spjd#include <pthread.h> 35128766Spjd#include <sys/param.h> 36128766Spjd#include <sys/queue.h> 37128766Spjd#include <sys/endian.h> 38128766Spjd#include <sys/socket.h> 39128766Spjd#include <sys/ioctl.h> 40128766Spjd#include <sys/stat.h> 41128912Sbde#include <sys/time.h> 42128766Spjd#include <sys/disk.h> 43128766Spjd#include <sys/bio.h> 44128766Spjd#include <netinet/in.h> 45128766Spjd#include <netinet/tcp.h> 46128766Spjd#include <arpa/inet.h> 47128766Spjd#include <signal.h> 48147844Spjd#include <assert.h> 49128766Spjd#include <err.h> 50128766Spjd#include <errno.h> 51128766Spjd#include <string.h> 52128766Spjd#include <libgen.h> 53128766Spjd#include <syslog.h> 54128766Spjd#include <stdarg.h> 55128766Spjd 56128766Spjd#include "ggate.h" 57128766Spjd 58128766Spjd 59147844Spjd#define GGATED_EXPORT_FILE "/etc/gg.exports" 60128766Spjd 61147844Spjdstruct ggd_connection { 62147844Spjd off_t c_mediasize; 63165327Spjd unsigned c_sectorsize; 64147844Spjd unsigned c_flags; /* flags (RO/RW) */ 65147844Spjd int c_diskfd; 66147844Spjd int c_sendfd; 67147844Spjd int c_recvfd; 68147844Spjd time_t c_birthtime; 69147844Spjd char *c_path; 70147844Spjd uint64_t c_token; 71147844Spjd in_addr_t c_srcip; 72147844Spjd LIST_ENTRY(ggd_connection) c_next; 73147844Spjd}; 74128766Spjd 75147844Spjdstruct ggd_request { 76147844Spjd struct g_gate_hdr r_hdr; 77147844Spjd char *r_data; 78147844Spjd TAILQ_ENTRY(ggd_request) r_next; 79147844Spjd}; 80147844Spjd#define r_cmd r_hdr.gh_cmd 81147844Spjd#define r_offset r_hdr.gh_offset 82147844Spjd#define r_length r_hdr.gh_length 83147844Spjd#define r_error r_hdr.gh_error 84147844Spjd 85147844Spjdstruct ggd_export { 86128766Spjd char *e_path; /* path to device/file */ 87128766Spjd in_addr_t e_ip; /* remote IP address */ 88128766Spjd in_addr_t e_mask; /* IP mask */ 89128766Spjd unsigned e_flags; /* flags (RO/RW) */ 90147844Spjd SLIST_ENTRY(ggd_export) e_next; 91128766Spjd}; 92128766Spjd 93147844Spjdstatic const char *exports_file = GGATED_EXPORT_FILE; 94147844Spjdstatic int got_sighup = 0; 95241720Sedstatic in_addr_t bindaddr; 96147844Spjd 97147844Spjdstatic TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue); 98147844Spjdstatic TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue); 99241720Sedstatic pthread_mutex_t inqueue_mtx, outqueue_mtx; 100241720Sedstatic pthread_cond_t inqueue_cond, outqueue_cond; 101147844Spjd 102201145Santoinestatic SLIST_HEAD(, ggd_export) exports = SLIST_HEAD_INITIALIZER(exports); 103201145Santoinestatic LIST_HEAD(, ggd_connection) connections = LIST_HEAD_INITIALIZER(connections); 104147844Spjd 105147844Spjdstatic void *recv_thread(void *arg); 106147844Spjdstatic void *disk_thread(void *arg); 107147844Spjdstatic void *send_thread(void *arg); 108147844Spjd 109128766Spjdstatic void 110128766Spjdusage(void) 111128766Spjd{ 112128766Spjd 113128766Spjd fprintf(stderr, "usage: %s [-nv] [-a address] [-p port] [-R rcvbuf] " 114128766Spjd "[-S sndbuf] [exports file]\n", getprogname()); 115128766Spjd exit(EXIT_FAILURE); 116128766Spjd} 117128766Spjd 118128766Spjdstatic char * 119128766Spjdip2str(in_addr_t ip) 120128766Spjd{ 121128766Spjd static char sip[16]; 122128766Spjd 123128766Spjd snprintf(sip, sizeof(sip), "%u.%u.%u.%u", 124128766Spjd ((ip >> 24) & 0xff), 125128766Spjd ((ip >> 16) & 0xff), 126128766Spjd ((ip >> 8) & 0xff), 127128766Spjd (ip & 0xff)); 128128766Spjd return (sip); 129128766Spjd} 130128766Spjd 131128766Spjdstatic in_addr_t 132128766Spjdcountmask(unsigned m) 133128766Spjd{ 134128766Spjd in_addr_t mask; 135128766Spjd 136128766Spjd if (m == 0) { 137128766Spjd mask = 0x0; 138128766Spjd } else { 139128766Spjd mask = 1 << (32 - m); 140128766Spjd mask--; 141128766Spjd mask = ~mask; 142128766Spjd } 143128766Spjd return (mask); 144128766Spjd} 145128766Spjd 146128766Spjdstatic void 147128766Spjdline_parse(char *line, unsigned lineno) 148128766Spjd{ 149147844Spjd struct ggd_export *ex; 150128766Spjd char *word, *path, *sflags; 151128766Spjd unsigned flags, i, vmask; 152128766Spjd in_addr_t ip, mask; 153128766Spjd 154128766Spjd ip = mask = flags = vmask = 0; 155128766Spjd path = NULL; 156128766Spjd sflags = NULL; 157128766Spjd 158128766Spjd for (i = 0, word = strtok(line, " \t"); word != NULL; 159128766Spjd i++, word = strtok(NULL, " \t")) { 160128766Spjd switch (i) { 161128766Spjd case 0: /* IP address or host name */ 162128766Spjd ip = g_gate_str2ip(strsep(&word, "/")); 163128766Spjd if (ip == INADDR_NONE) { 164128766Spjd g_gate_xlog("Invalid IP/host name at line %u.", 165128766Spjd lineno); 166128766Spjd } 167128766Spjd ip = ntohl(ip); 168128766Spjd if (word == NULL) 169128766Spjd vmask = 32; 170128766Spjd else { 171128766Spjd errno = 0; 172128766Spjd vmask = strtoul(word, NULL, 10); 173128766Spjd if (vmask == 0 && errno != 0) { 174128766Spjd g_gate_xlog("Invalid IP mask value at " 175128766Spjd "line %u.", lineno); 176128766Spjd } 177128766Spjd if ((unsigned)vmask > 32) { 178128766Spjd g_gate_xlog("Invalid IP mask value at line %u.", 179128766Spjd lineno); 180128766Spjd } 181128766Spjd } 182128766Spjd mask = countmask(vmask); 183128766Spjd break; 184128766Spjd case 1: /* flags */ 185128766Spjd if (strcasecmp("rd", word) == 0 || 186128766Spjd strcasecmp("ro", word) == 0) { 187128766Spjd flags = O_RDONLY; 188128766Spjd } else if (strcasecmp("wo", word) == 0) { 189128766Spjd flags = O_WRONLY; 190128766Spjd } else if (strcasecmp("rw", word) == 0) { 191128766Spjd flags = O_RDWR; 192128766Spjd } else { 193128766Spjd g_gate_xlog("Invalid value in flags field at " 194128766Spjd "line %u.", lineno); 195128766Spjd } 196128766Spjd sflags = word; 197128766Spjd break; 198128766Spjd case 2: /* path */ 199128766Spjd if (strlen(word) >= MAXPATHLEN) { 200128766Spjd g_gate_xlog("Path too long at line %u. ", 201128766Spjd lineno); 202128766Spjd } 203128766Spjd path = word; 204128766Spjd break; 205128766Spjd default: 206128766Spjd g_gate_xlog("Too many arguments at line %u. ", lineno); 207128766Spjd } 208128766Spjd } 209128766Spjd if (i != 3) 210128766Spjd g_gate_xlog("Too few arguments at line %u.", lineno); 211128766Spjd 212128766Spjd ex = malloc(sizeof(*ex)); 213128766Spjd if (ex == NULL) 214179900Sgonzo g_gate_xlog("Not enough memory."); 215128766Spjd ex->e_path = strdup(path); 216128766Spjd if (ex->e_path == NULL) 217179900Sgonzo g_gate_xlog("Not enough memory."); 218128766Spjd 219128766Spjd /* Made 'and' here. */ 220128766Spjd ex->e_ip = (ip & mask); 221128766Spjd ex->e_mask = mask; 222128766Spjd ex->e_flags = flags; 223128766Spjd 224147844Spjd SLIST_INSERT_HEAD(&exports, ex, e_next); 225128766Spjd 226128766Spjd g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.", 227128766Spjd ip2str(ex->e_ip), vmask, path, sflags); 228128766Spjd} 229128766Spjd 230128766Spjdstatic void 231128766Spjdexports_clear(void) 232128766Spjd{ 233147844Spjd struct ggd_export *ex; 234128766Spjd 235147844Spjd while (!SLIST_EMPTY(&exports)) { 236147844Spjd ex = SLIST_FIRST(&exports); 237147844Spjd SLIST_REMOVE_HEAD(&exports, e_next); 238128766Spjd free(ex); 239128766Spjd } 240128766Spjd} 241128766Spjd 242128766Spjd#define EXPORTS_LINE_SIZE 2048 243128766Spjdstatic void 244128766Spjdexports_get(void) 245128766Spjd{ 246128766Spjd char buf[EXPORTS_LINE_SIZE], *line; 247128766Spjd unsigned lineno = 0, objs = 0, len; 248128766Spjd FILE *fd; 249128766Spjd 250128766Spjd exports_clear(); 251128766Spjd 252147844Spjd fd = fopen(exports_file, "r"); 253128766Spjd if (fd == NULL) { 254147844Spjd g_gate_xlog("Cannot open exports file (%s): %s.", exports_file, 255128766Spjd strerror(errno)); 256128766Spjd } 257128766Spjd 258147844Spjd g_gate_log(LOG_INFO, "Reading exports file (%s).", exports_file); 259128766Spjd 260128766Spjd for (;;) { 261128766Spjd if (fgets(buf, sizeof(buf), fd) == NULL) { 262128766Spjd if (feof(fd)) 263128766Spjd break; 264128766Spjd 265128766Spjd g_gate_xlog("Error while reading exports file: %s.", 266128766Spjd strerror(errno)); 267128766Spjd } 268128766Spjd 269128766Spjd /* Increase line count. */ 270128766Spjd lineno++; 271128766Spjd 272128766Spjd /* Skip spaces and tabs. */ 273128766Spjd for (line = buf; *line == ' ' || *line == '\t'; ++line) 274128766Spjd ; 275128766Spjd 276128766Spjd /* Empty line, comment or empty line at the end of file. */ 277128766Spjd if (*line == '\n' || *line == '#' || *line == '\0') 278128766Spjd continue; 279128766Spjd 280128766Spjd len = strlen(line); 281128766Spjd if (line[len - 1] == '\n') { 282128766Spjd /* Remove new line char. */ 283128766Spjd line[len - 1] = '\0'; 284128766Spjd } else { 285128766Spjd if (!feof(fd)) 286128766Spjd g_gate_xlog("Line %u too long.", lineno); 287128766Spjd } 288128766Spjd 289128766Spjd line_parse(line, lineno); 290128766Spjd objs++; 291128766Spjd } 292128766Spjd 293128766Spjd fclose(fd); 294128766Spjd 295128766Spjd if (objs == 0) 296128766Spjd g_gate_xlog("There are no objects to export."); 297128766Spjd 298128766Spjd g_gate_log(LOG_INFO, "Exporting %u object(s).", objs); 299128766Spjd} 300128766Spjd 301147844Spjdstatic int 302147844Spjdexports_check(struct ggd_export *ex, struct g_gate_cinit *cinit, 303147844Spjd struct ggd_connection *conn) 304128766Spjd{ 305147844Spjd char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */ 306147844Spjd int error = 0, flags; 307147844Spjd 308147844Spjd strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask)); 309147844Spjd strlcat(ipmask, "/", sizeof(ipmask)); 310147844Spjd strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask)); 311147844Spjd if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) { 312147844Spjd if (ex->e_flags == O_WRONLY) { 313147844Spjd g_gate_log(LOG_WARNING, "Read-only access requested, " 314147844Spjd "but %s (%s) is exported write-only.", ex->e_path, 315147844Spjd ipmask); 316147844Spjd return (EPERM); 317147844Spjd } else { 318147844Spjd conn->c_flags |= GGATE_FLAG_RDONLY; 319147844Spjd } 320147844Spjd } else if ((cinit->gc_flags & GGATE_FLAG_WRONLY) != 0) { 321147844Spjd if (ex->e_flags == O_RDONLY) { 322147844Spjd g_gate_log(LOG_WARNING, "Write-only access requested, " 323147844Spjd "but %s (%s) is exported read-only.", ex->e_path, 324147844Spjd ipmask); 325147844Spjd return (EPERM); 326147844Spjd } else { 327147844Spjd conn->c_flags |= GGATE_FLAG_WRONLY; 328147844Spjd } 329147844Spjd } else { 330147844Spjd if (ex->e_flags == O_RDONLY) { 331147844Spjd g_gate_log(LOG_WARNING, "Read-write access requested, " 332147844Spjd "but %s (%s) is exported read-only.", ex->e_path, 333147844Spjd ipmask); 334147844Spjd return (EPERM); 335147844Spjd } else if (ex->e_flags == O_WRONLY) { 336147844Spjd g_gate_log(LOG_WARNING, "Read-write access requested, " 337147844Spjd "but %s (%s) is exported write-only.", ex->e_path, 338147844Spjd ipmask); 339147844Spjd return (EPERM); 340147844Spjd } 341147844Spjd } 342147844Spjd if ((conn->c_flags & GGATE_FLAG_RDONLY) != 0) 343147844Spjd flags = O_RDONLY; 344147844Spjd else if ((conn->c_flags & GGATE_FLAG_WRONLY) != 0) 345147844Spjd flags = O_WRONLY; 346147844Spjd else 347147844Spjd flags = O_RDWR; 348147844Spjd conn->c_diskfd = open(ex->e_path, flags); 349147844Spjd if (conn->c_diskfd == -1) { 350147844Spjd error = errno; 351147844Spjd g_gate_log(LOG_ERR, "Cannot open %s: %s.", ex->e_path, 352147844Spjd strerror(error)); 353147844Spjd return (error); 354147844Spjd } 355147844Spjd return (0); 356147844Spjd} 357147844Spjd 358147844Spjdstatic struct ggd_export * 359147844Spjdexports_find(struct sockaddr *s, struct g_gate_cinit *cinit, 360147844Spjd struct ggd_connection *conn) 361147844Spjd{ 362147844Spjd struct ggd_export *ex; 363128766Spjd in_addr_t ip; 364147844Spjd int error; 365128766Spjd 366128836Spjd ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); 367147844Spjd SLIST_FOREACH(ex, &exports, e_next) { 368147844Spjd if ((ip & ex->e_mask) != ex->e_ip) { 369147844Spjd g_gate_log(LOG_DEBUG, "exports[%s]: IP mismatch.", 370147844Spjd ex->e_path); 371128766Spjd continue; 372147844Spjd } 373147844Spjd if (strcmp(cinit->gc_path, ex->e_path) != 0) { 374147844Spjd g_gate_log(LOG_DEBUG, "exports[%s]: Path mismatch.", 375147844Spjd ex->e_path); 376128766Spjd continue; 377147844Spjd } 378147844Spjd error = exports_check(ex, cinit, conn); 379147844Spjd if (error == 0) 380147844Spjd return (ex); 381147844Spjd else { 382147844Spjd errno = error; 383147844Spjd return (NULL); 384147844Spjd } 385128766Spjd } 386147844Spjd g_gate_log(LOG_WARNING, "Unauthorized connection from: %s.", 387147844Spjd ip2str(ip)); 388147844Spjd errno = EPERM; 389128766Spjd return (NULL); 390128766Spjd} 391128766Spjd 392147844Spjd/* 393147844Spjd * Remove timed out connections. 394147844Spjd */ 395128766Spjdstatic void 396147844Spjdconnection_cleanups(void) 397128766Spjd{ 398147844Spjd struct ggd_connection *conn, *tconn; 399147844Spjd time_t now; 400128766Spjd 401147844Spjd time(&now); 402147844Spjd LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) { 403147844Spjd if (now - conn->c_birthtime > 10) { 404147844Spjd LIST_REMOVE(conn, c_next); 405147844Spjd g_gate_log(LOG_NOTICE, 406147844Spjd "Connection from %s [%s] removed.", 407147844Spjd ip2str(conn->c_srcip), conn->c_path); 408147844Spjd close(conn->c_diskfd); 409147844Spjd close(conn->c_sendfd); 410147844Spjd close(conn->c_recvfd); 411147844Spjd free(conn->c_path); 412147844Spjd free(conn); 413147844Spjd } 414128766Spjd } 415128766Spjd} 416128766Spjd 417147844Spjdstatic struct ggd_connection * 418147844Spjdconnection_find(struct g_gate_cinit *cinit) 419128766Spjd{ 420147844Spjd struct ggd_connection *conn; 421128766Spjd 422147844Spjd LIST_FOREACH(conn, &connections, c_next) { 423147844Spjd if (conn->c_token == cinit->gc_token) 424147844Spjd break; 425128766Spjd } 426147844Spjd return (conn); 427147844Spjd} 428128766Spjd 429147844Spjdstatic struct ggd_connection * 430147844Spjdconnection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) 431147844Spjd{ 432147844Spjd struct ggd_connection *conn; 433147844Spjd in_addr_t ip; 434147844Spjd 435147844Spjd /* 436147844Spjd * First, look for old connections. 437147844Spjd * We probably should do it every X seconds, but what for? 438147844Spjd * It is only dangerous if an attacker wants to overload connections 439147844Spjd * queue, so here is a good place to do the cleanups. 440147844Spjd */ 441147844Spjd connection_cleanups(); 442147844Spjd 443147844Spjd conn = malloc(sizeof(*conn)); 444147844Spjd if (conn == NULL) 445147844Spjd return (NULL); 446147844Spjd conn->c_path = strdup(cinit->gc_path); 447147844Spjd if (conn->c_path == NULL) { 448147844Spjd free(conn); 449147844Spjd return (NULL); 450128766Spjd } 451147844Spjd conn->c_token = cinit->gc_token; 452147844Spjd ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); 453147844Spjd conn->c_srcip = ip; 454147844Spjd conn->c_sendfd = conn->c_recvfd = -1; 455147844Spjd if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) 456147844Spjd conn->c_sendfd = sfd; 457147844Spjd else 458147844Spjd conn->c_recvfd = sfd; 459147844Spjd conn->c_mediasize = 0; 460147844Spjd conn->c_sectorsize = 0; 461147844Spjd time(&conn->c_birthtime); 462147844Spjd conn->c_flags = cinit->gc_flags; 463147844Spjd LIST_INSERT_HEAD(&connections, conn, c_next); 464147844Spjd g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip), 465147844Spjd conn->c_path); 466147844Spjd return (conn); 467147844Spjd} 468128766Spjd 469147844Spjdstatic int 470147844Spjdconnection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit, 471147844Spjd struct sockaddr *s, int sfd) 472147844Spjd{ 473147844Spjd in_addr_t ip; 474147844Spjd 475147844Spjd ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); 476147844Spjd if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) { 477147844Spjd if (conn->c_sendfd != -1) { 478147844Spjd g_gate_log(LOG_WARNING, 479147844Spjd "Send socket already exists [%s, %s].", ip2str(ip), 480147844Spjd conn->c_path); 481147844Spjd return (EEXIST); 482128766Spjd } 483147844Spjd conn->c_sendfd = sfd; 484128766Spjd } else { 485147844Spjd if (conn->c_recvfd != -1) { 486147844Spjd g_gate_log(LOG_WARNING, 487147844Spjd "Receive socket already exists [%s, %s].", 488147844Spjd ip2str(ip), conn->c_path); 489147844Spjd return (EEXIST); 490128766Spjd } 491147844Spjd conn->c_recvfd = sfd; 492128766Spjd } 493147844Spjd g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(ip), 494147844Spjd conn->c_path); 495147844Spjd return (0); 496147844Spjd} 497147844Spjd 498147844Spjd/* 499147844Spjd * Remove one socket from the given connection or the whole 500147844Spjd * connection if sfd == -1. 501147844Spjd */ 502147844Spjdstatic void 503147844Spjdconnection_remove(struct ggd_connection *conn) 504147844Spjd{ 505147844Spjd 506147844Spjd LIST_REMOVE(conn, c_next); 507147844Spjd g_gate_log(LOG_DEBUG, "Connection removed [%s %s].", 508147844Spjd ip2str(conn->c_srcip), conn->c_path); 509147844Spjd if (conn->c_sendfd != -1) 510147844Spjd close(conn->c_sendfd); 511147844Spjd if (conn->c_recvfd != -1) 512147844Spjd close(conn->c_recvfd); 513147844Spjd free(conn->c_path); 514147844Spjd free(conn); 515147844Spjd} 516147844Spjd 517147844Spjdstatic int 518147844Spjdconnection_ready(struct ggd_connection *conn) 519147844Spjd{ 520147844Spjd 521147844Spjd return (conn->c_sendfd != -1 && conn->c_recvfd != -1); 522147844Spjd} 523147844Spjd 524147844Spjdstatic void 525147844Spjdconnection_launch(struct ggd_connection *conn) 526147844Spjd{ 527147844Spjd pthread_t td; 528147844Spjd int error, pid; 529147844Spjd 530147844Spjd pid = fork(); 531147844Spjd if (pid > 0) 532147844Spjd return; 533147844Spjd else if (pid == -1) { 534147844Spjd g_gate_log(LOG_ERR, "Cannot fork: %s.", strerror(errno)); 535147844Spjd return; 536128766Spjd } 537147844Spjd g_gate_log(LOG_DEBUG, "Process created [%s].", conn->c_path); 538128766Spjd 539128766Spjd /* 540147844Spjd * Create condition variables and mutexes for in-queue and out-queue 541147844Spjd * synchronization. 542128766Spjd */ 543147844Spjd error = pthread_mutex_init(&inqueue_mtx, NULL); 544147844Spjd if (error != 0) { 545147844Spjd g_gate_xlog("pthread_mutex_init(inqueue_mtx): %s.", 546147844Spjd strerror(error)); 547147844Spjd } 548147844Spjd error = pthread_cond_init(&inqueue_cond, NULL); 549147844Spjd if (error != 0) { 550147844Spjd g_gate_xlog("pthread_cond_init(inqueue_cond): %s.", 551147844Spjd strerror(error)); 552147844Spjd } 553147844Spjd error = pthread_mutex_init(&outqueue_mtx, NULL); 554147844Spjd if (error != 0) { 555147844Spjd g_gate_xlog("pthread_mutex_init(outqueue_mtx): %s.", 556147844Spjd strerror(error)); 557147844Spjd } 558147844Spjd error = pthread_cond_init(&outqueue_cond, NULL); 559147844Spjd if (error != 0) { 560147844Spjd g_gate_xlog("pthread_cond_init(outqueue_cond): %s.", 561147844Spjd strerror(error)); 562147844Spjd } 563147844Spjd 564147844Spjd /* 565147844Spjd * Create threads: 566147844Spjd * recvtd - thread for receiving I/O request 567147844Spjd * diskio - thread for doing I/O request 568147844Spjd * sendtd - thread for sending I/O requests back 569147844Spjd */ 570147844Spjd error = pthread_create(&td, NULL, send_thread, conn); 571147844Spjd if (error != 0) { 572147844Spjd g_gate_xlog("pthread_create(send_thread): %s.", 573147844Spjd strerror(error)); 574147844Spjd } 575147844Spjd error = pthread_create(&td, NULL, recv_thread, conn); 576147844Spjd if (error != 0) { 577147844Spjd g_gate_xlog("pthread_create(recv_thread): %s.", 578147844Spjd strerror(error)); 579147844Spjd } 580147844Spjd disk_thread(conn); 581147844Spjd} 582147844Spjd 583147844Spjdstatic void 584147844Spjdsendfail(int sfd, int error, const char *fmt, ...) 585147844Spjd{ 586147844Spjd struct g_gate_sinit sinit; 587147844Spjd va_list ap; 588147844Spjd ssize_t data; 589147844Spjd 590147844Spjd sinit.gs_error = error; 591128766Spjd g_gate_swap2n_sinit(&sinit); 592147844Spjd data = g_gate_send(sfd, &sinit, sizeof(sinit), 0); 593128766Spjd g_gate_swap2h_sinit(&sinit); 594147844Spjd if (data != sizeof(sinit)) { 595147844Spjd g_gate_log(LOG_WARNING, "Cannot send initial packet: %s.", 596128766Spjd strerror(errno)); 597147844Spjd return; 598128766Spjd } 599147844Spjd if (fmt != NULL) { 600147844Spjd va_start(ap, fmt); 601147844Spjd g_gate_vlog(LOG_WARNING, fmt, ap); 602147844Spjd va_end(ap); 603147844Spjd } 604147844Spjd} 605128766Spjd 606147844Spjdstatic void * 607147844Spjdmalloc_waitok(size_t size) 608147844Spjd{ 609147844Spjd void *p; 610128766Spjd 611147844Spjd while ((p = malloc(size)) == NULL) { 612147844Spjd g_gate_log(LOG_DEBUG, "Cannot allocate %zu bytes.", size); 613147844Spjd sleep(1); 614147844Spjd } 615147844Spjd return (p); 616147844Spjd} 617128766Spjd 618147844Spjdstatic void * 619147844Spjdrecv_thread(void *arg) 620147844Spjd{ 621147844Spjd struct ggd_connection *conn; 622147844Spjd struct ggd_request *req; 623147844Spjd ssize_t data; 624147844Spjd int error, fd; 625147844Spjd 626147844Spjd conn = arg; 627147844Spjd g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); 628147844Spjd fd = conn->c_recvfd; 629128766Spjd for (;;) { 630128766Spjd /* 631147844Spjd * Get header packet. 632128766Spjd */ 633147844Spjd req = malloc_waitok(sizeof(*req)); 634147844Spjd data = g_gate_recv(fd, &req->r_hdr, sizeof(req->r_hdr), 635147844Spjd MSG_WAITALL); 636128766Spjd if (data == 0) { 637128766Spjd g_gate_log(LOG_DEBUG, "Process %u exiting.", getpid()); 638128766Spjd exit(EXIT_SUCCESS); 639128766Spjd } else if (data == -1) { 640128766Spjd g_gate_xlog("Error while receiving hdr packet: %s.", 641128766Spjd strerror(errno)); 642147844Spjd } else if (data != sizeof(req->r_hdr)) { 643128766Spjd g_gate_xlog("Malformed hdr packet received."); 644128766Spjd } 645128766Spjd g_gate_log(LOG_DEBUG, "Received hdr packet."); 646147844Spjd g_gate_swap2h_hdr(&req->r_hdr); 647128766Spjd 648147844Spjd g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, 649147844Spjd (intmax_t)req->r_offset, (unsigned)req->r_length); 650147844Spjd 651128766Spjd /* 652147844Spjd * Allocate memory for data. 653128766Spjd */ 654147844Spjd req->r_data = malloc_waitok(req->r_length); 655128766Spjd 656147844Spjd /* 657147844Spjd * Receive data to write for WRITE request. 658147844Spjd */ 659147844Spjd if (req->r_cmd == GGATE_CMD_WRITE) { 660128766Spjd g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data...", 661147844Spjd req->r_length); 662147844Spjd data = g_gate_recv(fd, req->r_data, req->r_length, 663147844Spjd MSG_WAITALL); 664128766Spjd if (data == -1) { 665128766Spjd g_gate_xlog("Error while receiving data: %s.", 666128766Spjd strerror(errno)); 667128766Spjd } 668147844Spjd } 669147844Spjd 670147844Spjd /* 671147844Spjd * Put the request onto the incoming queue. 672147844Spjd */ 673147844Spjd error = pthread_mutex_lock(&inqueue_mtx); 674147844Spjd assert(error == 0); 675147844Spjd TAILQ_INSERT_TAIL(&inqueue, req, r_next); 676147844Spjd error = pthread_cond_signal(&inqueue_cond); 677147844Spjd assert(error == 0); 678147844Spjd error = pthread_mutex_unlock(&inqueue_mtx); 679147844Spjd assert(error == 0); 680147844Spjd } 681147844Spjd} 682147844Spjd 683147844Spjdstatic void * 684147844Spjddisk_thread(void *arg) 685147844Spjd{ 686147844Spjd struct ggd_connection *conn; 687147844Spjd struct ggd_request *req; 688147844Spjd ssize_t data; 689147844Spjd int error, fd; 690147844Spjd 691147844Spjd conn = arg; 692147844Spjd g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); 693147844Spjd fd = conn->c_diskfd; 694147844Spjd for (;;) { 695147844Spjd /* 696147844Spjd * Get a request from the incoming queue. 697147844Spjd */ 698147844Spjd error = pthread_mutex_lock(&inqueue_mtx); 699147844Spjd assert(error == 0); 700147844Spjd while ((req = TAILQ_FIRST(&inqueue)) == NULL) { 701147844Spjd error = pthread_cond_wait(&inqueue_cond, &inqueue_mtx); 702147844Spjd assert(error == 0); 703147844Spjd } 704147844Spjd TAILQ_REMOVE(&inqueue, req, r_next); 705147844Spjd error = pthread_mutex_unlock(&inqueue_mtx); 706147844Spjd assert(error == 0); 707147844Spjd 708147844Spjd /* 709147844Spjd * Check the request. 710147844Spjd */ 711147844Spjd assert(req->r_cmd == GGATE_CMD_READ || req->r_cmd == GGATE_CMD_WRITE); 712147844Spjd assert(req->r_offset + req->r_length <= (uintmax_t)conn->c_mediasize); 713147844Spjd assert((req->r_offset % conn->c_sectorsize) == 0); 714147844Spjd assert((req->r_length % conn->c_sectorsize) == 0); 715147844Spjd 716147844Spjd g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, 717147844Spjd (intmax_t)req->r_offset, (unsigned)req->r_length); 718147844Spjd 719147844Spjd /* 720147844Spjd * Do the request. 721147844Spjd */ 722147844Spjd data = 0; 723147844Spjd switch (req->r_cmd) { 724147844Spjd case GGATE_CMD_READ: 725147844Spjd data = pread(fd, req->r_data, req->r_length, 726147844Spjd req->r_offset); 727147844Spjd break; 728147844Spjd case GGATE_CMD_WRITE: 729147844Spjd data = pwrite(fd, req->r_data, req->r_length, 730147844Spjd req->r_offset); 731147844Spjd /* Free data memory here - better sooner. */ 732147844Spjd free(req->r_data); 733147844Spjd req->r_data = NULL; 734147844Spjd break; 735147844Spjd } 736147844Spjd if (data != (ssize_t)req->r_length) { 737147844Spjd /* Report short reads/writes as I/O errors. */ 738147844Spjd if (errno == 0) 739147844Spjd errno = EIO; 740147844Spjd g_gate_log(LOG_ERR, "Disk error: %s", strerror(errno)); 741147844Spjd req->r_error = errno; 742147844Spjd if (req->r_data != NULL) { 743147844Spjd free(req->r_data); 744147844Spjd req->r_data = NULL; 745128766Spjd } 746147844Spjd } 747147844Spjd 748147844Spjd /* 749147844Spjd * Put the request onto the outgoing queue. 750147844Spjd */ 751147844Spjd error = pthread_mutex_lock(&outqueue_mtx); 752147844Spjd assert(error == 0); 753147844Spjd TAILQ_INSERT_TAIL(&outqueue, req, r_next); 754147844Spjd error = pthread_cond_signal(&outqueue_cond); 755147844Spjd assert(error == 0); 756147844Spjd error = pthread_mutex_unlock(&outqueue_mtx); 757147844Spjd assert(error == 0); 758147844Spjd } 759180020Smtm 760180020Smtm /* NOTREACHED */ 761180020Smtm return (NULL); 762147844Spjd} 763147844Spjd 764147844Spjdstatic void * 765147844Spjdsend_thread(void *arg) 766147844Spjd{ 767147844Spjd struct ggd_connection *conn; 768147844Spjd struct ggd_request *req; 769147844Spjd ssize_t data; 770147844Spjd int error, fd; 771147844Spjd 772147844Spjd conn = arg; 773147844Spjd g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); 774147844Spjd fd = conn->c_sendfd; 775147844Spjd for (;;) { 776147844Spjd /* 777147844Spjd * Get a request from the outgoing queue. 778147844Spjd */ 779147844Spjd error = pthread_mutex_lock(&outqueue_mtx); 780147844Spjd assert(error == 0); 781147844Spjd while ((req = TAILQ_FIRST(&outqueue)) == NULL) { 782147844Spjd error = pthread_cond_wait(&outqueue_cond, 783147844Spjd &outqueue_mtx); 784147844Spjd assert(error == 0); 785147844Spjd } 786147844Spjd TAILQ_REMOVE(&outqueue, req, r_next); 787147844Spjd error = pthread_mutex_unlock(&outqueue_mtx); 788147844Spjd assert(error == 0); 789147844Spjd 790147844Spjd g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, 791147844Spjd (intmax_t)req->r_offset, (unsigned)req->r_length); 792147844Spjd 793147844Spjd /* 794147844Spjd * Send the request. 795147844Spjd */ 796147844Spjd g_gate_swap2n_hdr(&req->r_hdr); 797147844Spjd if (g_gate_send(fd, &req->r_hdr, sizeof(req->r_hdr), 0) == -1) { 798147844Spjd g_gate_xlog("Error while sending hdr packet: %s.", 799147844Spjd strerror(errno)); 800147844Spjd } 801147844Spjd g_gate_log(LOG_DEBUG, "Sent hdr packet."); 802147844Spjd g_gate_swap2h_hdr(&req->r_hdr); 803147844Spjd if (req->r_data != NULL) { 804147844Spjd data = g_gate_send(fd, req->r_data, req->r_length, 0); 805147844Spjd if (data != (ssize_t)req->r_length) { 806147844Spjd g_gate_xlog("Error while sending data: %s.", 807128766Spjd strerror(errno)); 808128766Spjd } 809147844Spjd g_gate_log(LOG_DEBUG, 810147844Spjd "Sent %zd bytes (offset=%ju, size=%zu).", data, 811147844Spjd (uintmax_t)req->r_offset, (size_t)req->r_length); 812147844Spjd free(req->r_data); 813128766Spjd } 814147844Spjd free(req); 815128766Spjd } 816180020Smtm 817180020Smtm /* NOTREACHED */ 818180020Smtm return (NULL); 819128766Spjd} 820128766Spjd 821128766Spjdstatic void 822147844Spjdlog_connection(struct sockaddr *from) 823147844Spjd{ 824147844Spjd in_addr_t ip; 825147844Spjd 826147844Spjd ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr); 827147844Spjd g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); 828147844Spjd} 829147844Spjd 830147844Spjdstatic int 831147844Spjdhandshake(struct sockaddr *from, int sfd) 832147844Spjd{ 833147844Spjd struct g_gate_version ver; 834147844Spjd struct g_gate_cinit cinit; 835147844Spjd struct g_gate_sinit sinit; 836147844Spjd struct ggd_connection *conn; 837147844Spjd struct ggd_export *ex; 838147844Spjd ssize_t data; 839147844Spjd 840147844Spjd log_connection(from); 841147844Spjd /* 842147844Spjd * Phase 1: Version verification. 843147844Spjd */ 844147844Spjd g_gate_log(LOG_DEBUG, "Receiving version packet."); 845147844Spjd data = g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL); 846147844Spjd g_gate_swap2h_version(&ver); 847147844Spjd if (data != sizeof(ver)) { 848147844Spjd g_gate_log(LOG_WARNING, "Malformed version packet."); 849147844Spjd return (0); 850147844Spjd } 851147844Spjd g_gate_log(LOG_DEBUG, "Version packet received."); 852147844Spjd if (memcmp(ver.gv_magic, GGATE_MAGIC, strlen(GGATE_MAGIC)) != 0) { 853147844Spjd g_gate_log(LOG_WARNING, "Invalid magic field."); 854147844Spjd return (0); 855147844Spjd } 856147844Spjd if (ver.gv_version != GGATE_VERSION) { 857147844Spjd g_gate_log(LOG_WARNING, "Version %u is not supported.", 858147844Spjd ver.gv_version); 859147844Spjd return (0); 860147844Spjd } 861147844Spjd ver.gv_error = 0; 862147844Spjd g_gate_swap2n_version(&ver); 863147844Spjd data = g_gate_send(sfd, &ver, sizeof(ver), 0); 864147844Spjd g_gate_swap2h_version(&ver); 865147844Spjd if (data == -1) { 866147844Spjd sendfail(sfd, errno, "Error while sending version packet: %s.", 867147844Spjd strerror(errno)); 868147844Spjd return (0); 869147844Spjd } 870147844Spjd 871147844Spjd /* 872147844Spjd * Phase 2: Request verification. 873147844Spjd */ 874147844Spjd g_gate_log(LOG_DEBUG, "Receiving initial packet."); 875147844Spjd data = g_gate_recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL); 876147844Spjd g_gate_swap2h_cinit(&cinit); 877147844Spjd if (data != sizeof(cinit)) { 878147844Spjd g_gate_log(LOG_WARNING, "Malformed initial packet."); 879147844Spjd return (0); 880147844Spjd } 881147844Spjd g_gate_log(LOG_DEBUG, "Initial packet received."); 882147844Spjd conn = connection_find(&cinit); 883147844Spjd if (conn != NULL) { 884147844Spjd /* 885147844Spjd * Connection should already exists. 886147844Spjd */ 887147844Spjd g_gate_log(LOG_DEBUG, "Found existing connection (token=%lu).", 888147844Spjd (unsigned long)conn->c_token); 889147844Spjd if (connection_add(conn, &cinit, from, sfd) == -1) { 890147844Spjd connection_remove(conn); 891147844Spjd return (0); 892147844Spjd } 893147844Spjd } else { 894147844Spjd /* 895147844Spjd * New connection, allocate space. 896147844Spjd */ 897147844Spjd conn = connection_new(&cinit, from, sfd); 898147844Spjd if (conn == NULL) { 899147844Spjd sendfail(sfd, ENOMEM, 900147844Spjd "Cannot allocate new connection."); 901147844Spjd return (0); 902147844Spjd } 903147844Spjd g_gate_log(LOG_DEBUG, "New connection created (token=%lu).", 904147844Spjd (unsigned long)conn->c_token); 905147844Spjd } 906147844Spjd 907147844Spjd ex = exports_find(from, &cinit, conn); 908147844Spjd if (ex == NULL) { 909147844Spjd connection_remove(conn); 910147844Spjd sendfail(sfd, errno, NULL); 911147844Spjd return (0); 912147844Spjd } 913147844Spjd if (conn->c_mediasize == 0) { 914147844Spjd conn->c_mediasize = g_gate_mediasize(conn->c_diskfd); 915147844Spjd conn->c_sectorsize = g_gate_sectorsize(conn->c_diskfd); 916147844Spjd } 917147844Spjd sinit.gs_mediasize = conn->c_mediasize; 918147844Spjd sinit.gs_sectorsize = conn->c_sectorsize; 919147844Spjd sinit.gs_error = 0; 920147844Spjd 921147844Spjd g_gate_log(LOG_DEBUG, "Sending initial packet."); 922147844Spjd 923147844Spjd g_gate_swap2n_sinit(&sinit); 924147844Spjd data = g_gate_send(sfd, &sinit, sizeof(sinit), 0); 925147844Spjd g_gate_swap2h_sinit(&sinit); 926147844Spjd if (data == -1) { 927147844Spjd sendfail(sfd, errno, "Error while sending initial packet: %s.", 928147844Spjd strerror(errno)); 929147844Spjd return (0); 930147844Spjd } 931147844Spjd 932147844Spjd if (connection_ready(conn)) { 933147844Spjd connection_launch(conn); 934147844Spjd connection_remove(conn); 935147844Spjd } 936147844Spjd return (1); 937147844Spjd} 938147844Spjd 939147844Spjdstatic void 940128766Spjdhuphandler(int sig __unused) 941128766Spjd{ 942128766Spjd 943128766Spjd got_sighup = 1; 944128766Spjd} 945128766Spjd 946128766Spjdint 947128766Spjdmain(int argc, char *argv[]) 948128766Spjd{ 949128766Spjd struct sockaddr_in serv; 950128766Spjd struct sockaddr from; 951128766Spjd socklen_t fromlen; 952147844Spjd int sfd, tmpsfd; 953147844Spjd unsigned port; 954128766Spjd 955128766Spjd bindaddr = htonl(INADDR_ANY); 956128766Spjd port = G_GATE_PORT; 957128766Spjd for (;;) { 958128766Spjd int ch; 959128766Spjd 960128766Spjd ch = getopt(argc, argv, "a:hnp:R:S:v"); 961128766Spjd if (ch == -1) 962128766Spjd break; 963128766Spjd switch (ch) { 964128766Spjd case 'a': 965128766Spjd bindaddr = g_gate_str2ip(optarg); 966128766Spjd if (bindaddr == INADDR_NONE) { 967128766Spjd errx(EXIT_FAILURE, 968128766Spjd "Invalid IP/host name to bind to."); 969128766Spjd } 970128766Spjd break; 971128766Spjd case 'n': 972128766Spjd nagle = 0; 973128766Spjd break; 974128766Spjd case 'p': 975128766Spjd errno = 0; 976128766Spjd port = strtoul(optarg, NULL, 10); 977128766Spjd if (port == 0 && errno != 0) 978128766Spjd errx(EXIT_FAILURE, "Invalid port."); 979128766Spjd break; 980128766Spjd case 'R': 981128766Spjd errno = 0; 982128766Spjd rcvbuf = strtoul(optarg, NULL, 10); 983128766Spjd if (rcvbuf == 0 && errno != 0) 984128766Spjd errx(EXIT_FAILURE, "Invalid rcvbuf."); 985128766Spjd break; 986128766Spjd case 'S': 987128766Spjd errno = 0; 988128766Spjd sndbuf = strtoul(optarg, NULL, 10); 989128766Spjd if (sndbuf == 0 && errno != 0) 990128766Spjd errx(EXIT_FAILURE, "Invalid sndbuf."); 991128766Spjd break; 992128766Spjd case 'v': 993128766Spjd g_gate_verbose++; 994128766Spjd break; 995128766Spjd case 'h': 996128766Spjd default: 997128766Spjd usage(); 998128766Spjd } 999128766Spjd } 1000128766Spjd argc -= optind; 1001128766Spjd argv += optind; 1002128766Spjd 1003128766Spjd if (argv[0] != NULL) 1004147844Spjd exports_file = argv[0]; 1005128766Spjd exports_get(); 1006128766Spjd 1007128766Spjd if (!g_gate_verbose) { 1008128766Spjd /* Run in daemon mode. */ 1009134937Spjd if (daemon(0, 0) == -1) 1010147844Spjd g_gate_xlog("Cannot daemonize: %s", strerror(errno)); 1011128766Spjd } 1012128766Spjd 1013128766Spjd signal(SIGCHLD, SIG_IGN); 1014128766Spjd 1015128766Spjd sfd = socket(AF_INET, SOCK_STREAM, 0); 1016134937Spjd if (sfd == -1) 1017147844Spjd g_gate_xlog("Cannot open stream socket: %s.", strerror(errno)); 1018128766Spjd bzero(&serv, sizeof(serv)); 1019128766Spjd serv.sin_family = AF_INET; 1020128766Spjd serv.sin_addr.s_addr = bindaddr; 1021128766Spjd serv.sin_port = htons(port); 1022147844Spjd 1023147844Spjd g_gate_socket_settings(sfd); 1024147844Spjd 1025134937Spjd if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) 1026128766Spjd g_gate_xlog("bind(): %s.", strerror(errno)); 1027134937Spjd if (listen(sfd, 5) == -1) 1028128766Spjd g_gate_xlog("listen(): %s.", strerror(errno)); 1029128766Spjd 1030128766Spjd g_gate_log(LOG_INFO, "Listen on port: %d.", port); 1031128766Spjd 1032128766Spjd signal(SIGHUP, huphandler); 1033128766Spjd 1034128766Spjd for (;;) { 1035128766Spjd fromlen = sizeof(from); 1036128766Spjd tmpsfd = accept(sfd, &from, &fromlen); 1037134937Spjd if (tmpsfd == -1) 1038128766Spjd g_gate_xlog("accept(): %s.", strerror(errno)); 1039128766Spjd 1040128766Spjd if (got_sighup) { 1041128766Spjd got_sighup = 0; 1042128766Spjd exports_get(); 1043128766Spjd } 1044128766Spjd 1045147844Spjd if (!handshake(&from, tmpsfd)) 1046128766Spjd close(tmpsfd); 1047128766Spjd } 1048128766Spjd close(sfd); 1049128766Spjd exit(EXIT_SUCCESS); 1050128766Spjd} 1051