1d1d669bdSPawel Jakub Dawidek /*- 2d1d669bdSPawel Jakub Dawidek * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3d1d669bdSPawel Jakub Dawidek * All rights reserved. 4d1d669bdSPawel Jakub Dawidek * 5d1d669bdSPawel Jakub Dawidek * Redistribution and use in source and binary forms, with or without 6d1d669bdSPawel Jakub Dawidek * modification, are permitted provided that the following conditions 7d1d669bdSPawel Jakub Dawidek * are met: 8d1d669bdSPawel Jakub Dawidek * 1. Redistributions of source code must retain the above copyright 9d1d669bdSPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer. 10d1d669bdSPawel Jakub Dawidek * 2. Redistributions in binary form must reproduce the above copyright 11d1d669bdSPawel Jakub Dawidek * notice, this list of conditions and the following disclaimer in the 12d1d669bdSPawel Jakub Dawidek * documentation and/or other materials provided with the distribution. 13d1d669bdSPawel Jakub Dawidek * 14d1d669bdSPawel Jakub Dawidek * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 15d1d669bdSPawel Jakub Dawidek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16d1d669bdSPawel Jakub Dawidek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17d1d669bdSPawel Jakub Dawidek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 18d1d669bdSPawel Jakub Dawidek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19d1d669bdSPawel Jakub Dawidek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20d1d669bdSPawel Jakub Dawidek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21d1d669bdSPawel Jakub Dawidek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22d1d669bdSPawel Jakub Dawidek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23d1d669bdSPawel Jakub Dawidek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24d1d669bdSPawel Jakub Dawidek * SUCH DAMAGE. 25d1d669bdSPawel Jakub Dawidek * 26d1d669bdSPawel Jakub Dawidek * $FreeBSD$ 27d1d669bdSPawel Jakub Dawidek */ 28d1d669bdSPawel Jakub Dawidek 29d1d669bdSPawel Jakub Dawidek #include <sys/param.h> 30*fc7e71c5SEnji Cooper #include <sys/bio.h> 31*fc7e71c5SEnji Cooper #include <sys/disk.h> 32d1d669bdSPawel Jakub Dawidek #include <sys/endian.h> 33d1d669bdSPawel Jakub Dawidek #include <sys/ioctl.h> 34*fc7e71c5SEnji Cooper #include <sys/queue.h> 35*fc7e71c5SEnji Cooper #include <sys/socket.h> 36d1d669bdSPawel Jakub Dawidek #include <sys/stat.h> 37b34d2de0SBruce Evans #include <sys/time.h> 38*fc7e71c5SEnji Cooper #include <arpa/inet.h> 39d1d669bdSPawel Jakub Dawidek #include <netinet/in.h> 40d1d669bdSPawel Jakub Dawidek #include <netinet/tcp.h> 417be67fe3SPawel Jakub Dawidek #include <assert.h> 42d1d669bdSPawel Jakub Dawidek #include <err.h> 43d1d669bdSPawel Jakub Dawidek #include <errno.h> 44*fc7e71c5SEnji Cooper #include <fcntl.h> 45d1d669bdSPawel Jakub Dawidek #include <libgen.h> 46*fc7e71c5SEnji Cooper #include <libutil.h> 47*fc7e71c5SEnji Cooper #include <paths.h> 48*fc7e71c5SEnji Cooper #include <pthread.h> 49*fc7e71c5SEnji Cooper #include <signal.h> 50d1d669bdSPawel Jakub Dawidek #include <stdarg.h> 51*fc7e71c5SEnji Cooper #include <stdio.h> 52*fc7e71c5SEnji Cooper #include <stdlib.h> 53*fc7e71c5SEnji Cooper #include <stdint.h> 54*fc7e71c5SEnji Cooper #include <string.h> 55*fc7e71c5SEnji Cooper #include <syslog.h> 56*fc7e71c5SEnji Cooper #include <unistd.h> 57d1d669bdSPawel Jakub Dawidek 58d1d669bdSPawel Jakub Dawidek #include "ggate.h" 59d1d669bdSPawel Jakub Dawidek 60d1d669bdSPawel Jakub Dawidek 617be67fe3SPawel Jakub Dawidek #define GGATED_EXPORT_FILE "/etc/gg.exports" 62d1d669bdSPawel Jakub Dawidek 637be67fe3SPawel Jakub Dawidek struct ggd_connection { 647be67fe3SPawel Jakub Dawidek off_t c_mediasize; 6515c7f46bSPawel Jakub Dawidek unsigned c_sectorsize; 667be67fe3SPawel Jakub Dawidek unsigned c_flags; /* flags (RO/RW) */ 677be67fe3SPawel Jakub Dawidek int c_diskfd; 687be67fe3SPawel Jakub Dawidek int c_sendfd; 697be67fe3SPawel Jakub Dawidek int c_recvfd; 707be67fe3SPawel Jakub Dawidek time_t c_birthtime; 717be67fe3SPawel Jakub Dawidek char *c_path; 727be67fe3SPawel Jakub Dawidek uint64_t c_token; 737be67fe3SPawel Jakub Dawidek in_addr_t c_srcip; 747be67fe3SPawel Jakub Dawidek LIST_ENTRY(ggd_connection) c_next; 757be67fe3SPawel Jakub Dawidek }; 76d1d669bdSPawel Jakub Dawidek 777be67fe3SPawel Jakub Dawidek struct ggd_request { 787be67fe3SPawel Jakub Dawidek struct g_gate_hdr r_hdr; 797be67fe3SPawel Jakub Dawidek char *r_data; 807be67fe3SPawel Jakub Dawidek TAILQ_ENTRY(ggd_request) r_next; 817be67fe3SPawel Jakub Dawidek }; 827be67fe3SPawel Jakub Dawidek #define r_cmd r_hdr.gh_cmd 837be67fe3SPawel Jakub Dawidek #define r_offset r_hdr.gh_offset 847be67fe3SPawel Jakub Dawidek #define r_length r_hdr.gh_length 857be67fe3SPawel Jakub Dawidek #define r_error r_hdr.gh_error 867be67fe3SPawel Jakub Dawidek 877be67fe3SPawel Jakub Dawidek struct ggd_export { 88d1d669bdSPawel Jakub Dawidek char *e_path; /* path to device/file */ 89d1d669bdSPawel Jakub Dawidek in_addr_t e_ip; /* remote IP address */ 90d1d669bdSPawel Jakub Dawidek in_addr_t e_mask; /* IP mask */ 91d1d669bdSPawel Jakub Dawidek unsigned e_flags; /* flags (RO/RW) */ 927be67fe3SPawel Jakub Dawidek SLIST_ENTRY(ggd_export) e_next; 93d1d669bdSPawel Jakub Dawidek }; 947be67fe3SPawel Jakub Dawidek 957be67fe3SPawel Jakub Dawidek static const char *exports_file = GGATED_EXPORT_FILE; 967be67fe3SPawel Jakub Dawidek static int got_sighup = 0; 97ae824d80SEd Schouten static in_addr_t bindaddr; 987be67fe3SPawel Jakub Dawidek 997be67fe3SPawel Jakub Dawidek static TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue); 1007be67fe3SPawel Jakub Dawidek static TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue); 101ae824d80SEd Schouten static pthread_mutex_t inqueue_mtx, outqueue_mtx; 102ae824d80SEd Schouten static pthread_cond_t inqueue_cond, outqueue_cond; 1037be67fe3SPawel Jakub Dawidek 10413e403fdSAntoine Brodin static SLIST_HEAD(, ggd_export) exports = SLIST_HEAD_INITIALIZER(exports); 10513e403fdSAntoine Brodin static LIST_HEAD(, ggd_connection) connections = LIST_HEAD_INITIALIZER(connections); 1067be67fe3SPawel Jakub Dawidek 1077be67fe3SPawel Jakub Dawidek static void *recv_thread(void *arg); 1087be67fe3SPawel Jakub Dawidek static void *disk_thread(void *arg); 1097be67fe3SPawel Jakub Dawidek static void *send_thread(void *arg); 110d1d669bdSPawel Jakub Dawidek 111d1d669bdSPawel Jakub Dawidek static void 112d1d669bdSPawel Jakub Dawidek usage(void) 113d1d669bdSPawel Jakub Dawidek { 114d1d669bdSPawel Jakub Dawidek 115*fc7e71c5SEnji Cooper fprintf(stderr, "usage: %s [-nv] [-a address] [-F pidfile] [-p port] " 116*fc7e71c5SEnji Cooper "[-R rcvbuf] [-S sndbuf] [exports file]\n", getprogname()); 117d1d669bdSPawel Jakub Dawidek exit(EXIT_FAILURE); 118d1d669bdSPawel Jakub Dawidek } 119d1d669bdSPawel Jakub Dawidek 120d1d669bdSPawel Jakub Dawidek static char * 121d1d669bdSPawel Jakub Dawidek ip2str(in_addr_t ip) 122d1d669bdSPawel Jakub Dawidek { 123d1d669bdSPawel Jakub Dawidek static char sip[16]; 124d1d669bdSPawel Jakub Dawidek 125d1d669bdSPawel Jakub Dawidek snprintf(sip, sizeof(sip), "%u.%u.%u.%u", 126d1d669bdSPawel Jakub Dawidek ((ip >> 24) & 0xff), 127d1d669bdSPawel Jakub Dawidek ((ip >> 16) & 0xff), 128d1d669bdSPawel Jakub Dawidek ((ip >> 8) & 0xff), 129d1d669bdSPawel Jakub Dawidek (ip & 0xff)); 130d1d669bdSPawel Jakub Dawidek return (sip); 131d1d669bdSPawel Jakub Dawidek } 132d1d669bdSPawel Jakub Dawidek 133d1d669bdSPawel Jakub Dawidek static in_addr_t 134d1d669bdSPawel Jakub Dawidek countmask(unsigned m) 135d1d669bdSPawel Jakub Dawidek { 136d1d669bdSPawel Jakub Dawidek in_addr_t mask; 137d1d669bdSPawel Jakub Dawidek 138d1d669bdSPawel Jakub Dawidek if (m == 0) { 139d1d669bdSPawel Jakub Dawidek mask = 0x0; 140d1d669bdSPawel Jakub Dawidek } else { 141d1d669bdSPawel Jakub Dawidek mask = 1 << (32 - m); 142d1d669bdSPawel Jakub Dawidek mask--; 143d1d669bdSPawel Jakub Dawidek mask = ~mask; 144d1d669bdSPawel Jakub Dawidek } 145d1d669bdSPawel Jakub Dawidek return (mask); 146d1d669bdSPawel Jakub Dawidek } 147d1d669bdSPawel Jakub Dawidek 148d1d669bdSPawel Jakub Dawidek static void 149d1d669bdSPawel Jakub Dawidek line_parse(char *line, unsigned lineno) 150d1d669bdSPawel Jakub Dawidek { 1517be67fe3SPawel Jakub Dawidek struct ggd_export *ex; 152d1d669bdSPawel Jakub Dawidek char *word, *path, *sflags; 153d1d669bdSPawel Jakub Dawidek unsigned flags, i, vmask; 154d1d669bdSPawel Jakub Dawidek in_addr_t ip, mask; 155d1d669bdSPawel Jakub Dawidek 156d1d669bdSPawel Jakub Dawidek ip = mask = flags = vmask = 0; 157d1d669bdSPawel Jakub Dawidek path = NULL; 158d1d669bdSPawel Jakub Dawidek sflags = NULL; 159d1d669bdSPawel Jakub Dawidek 160d1d669bdSPawel Jakub Dawidek for (i = 0, word = strtok(line, " \t"); word != NULL; 161d1d669bdSPawel Jakub Dawidek i++, word = strtok(NULL, " \t")) { 162d1d669bdSPawel Jakub Dawidek switch (i) { 163d1d669bdSPawel Jakub Dawidek case 0: /* IP address or host name */ 164d1d669bdSPawel Jakub Dawidek ip = g_gate_str2ip(strsep(&word, "/")); 165d1d669bdSPawel Jakub Dawidek if (ip == INADDR_NONE) { 166d1d669bdSPawel Jakub Dawidek g_gate_xlog("Invalid IP/host name at line %u.", 167d1d669bdSPawel Jakub Dawidek lineno); 168d1d669bdSPawel Jakub Dawidek } 169d1d669bdSPawel Jakub Dawidek ip = ntohl(ip); 170d1d669bdSPawel Jakub Dawidek if (word == NULL) 171d1d669bdSPawel Jakub Dawidek vmask = 32; 172d1d669bdSPawel Jakub Dawidek else { 173d1d669bdSPawel Jakub Dawidek errno = 0; 174d1d669bdSPawel Jakub Dawidek vmask = strtoul(word, NULL, 10); 175d1d669bdSPawel Jakub Dawidek if (vmask == 0 && errno != 0) { 176d1d669bdSPawel Jakub Dawidek g_gate_xlog("Invalid IP mask value at " 177d1d669bdSPawel Jakub Dawidek "line %u.", lineno); 178d1d669bdSPawel Jakub Dawidek } 179d1d669bdSPawel Jakub Dawidek if ((unsigned)vmask > 32) { 180d1d669bdSPawel Jakub Dawidek g_gate_xlog("Invalid IP mask value at line %u.", 181d1d669bdSPawel Jakub Dawidek lineno); 182d1d669bdSPawel Jakub Dawidek } 183d1d669bdSPawel Jakub Dawidek } 184d1d669bdSPawel Jakub Dawidek mask = countmask(vmask); 185d1d669bdSPawel Jakub Dawidek break; 186d1d669bdSPawel Jakub Dawidek case 1: /* flags */ 187d1d669bdSPawel Jakub Dawidek if (strcasecmp("rd", word) == 0 || 188d1d669bdSPawel Jakub Dawidek strcasecmp("ro", word) == 0) { 189d1d669bdSPawel Jakub Dawidek flags = O_RDONLY; 190d1d669bdSPawel Jakub Dawidek } else if (strcasecmp("wo", word) == 0) { 191d1d669bdSPawel Jakub Dawidek flags = O_WRONLY; 192d1d669bdSPawel Jakub Dawidek } else if (strcasecmp("rw", word) == 0) { 193d1d669bdSPawel Jakub Dawidek flags = O_RDWR; 194d1d669bdSPawel Jakub Dawidek } else { 195d1d669bdSPawel Jakub Dawidek g_gate_xlog("Invalid value in flags field at " 196d1d669bdSPawel Jakub Dawidek "line %u.", lineno); 197d1d669bdSPawel Jakub Dawidek } 198d1d669bdSPawel Jakub Dawidek sflags = word; 199d1d669bdSPawel Jakub Dawidek break; 200d1d669bdSPawel Jakub Dawidek case 2: /* path */ 201d1d669bdSPawel Jakub Dawidek if (strlen(word) >= MAXPATHLEN) { 202d1d669bdSPawel Jakub Dawidek g_gate_xlog("Path too long at line %u. ", 203d1d669bdSPawel Jakub Dawidek lineno); 204d1d669bdSPawel Jakub Dawidek } 205d1d669bdSPawel Jakub Dawidek path = word; 206d1d669bdSPawel Jakub Dawidek break; 207d1d669bdSPawel Jakub Dawidek default: 208d1d669bdSPawel Jakub Dawidek g_gate_xlog("Too many arguments at line %u. ", lineno); 209d1d669bdSPawel Jakub Dawidek } 210d1d669bdSPawel Jakub Dawidek } 211d1d669bdSPawel Jakub Dawidek if (i != 3) 212d1d669bdSPawel Jakub Dawidek g_gate_xlog("Too few arguments at line %u.", lineno); 213d1d669bdSPawel Jakub Dawidek 214d1d669bdSPawel Jakub Dawidek ex = malloc(sizeof(*ex)); 215d1d669bdSPawel Jakub Dawidek if (ex == NULL) 216032de3f9SOleksandr Tymoshenko g_gate_xlog("Not enough memory."); 217d1d669bdSPawel Jakub Dawidek ex->e_path = strdup(path); 218d1d669bdSPawel Jakub Dawidek if (ex->e_path == NULL) 219032de3f9SOleksandr Tymoshenko g_gate_xlog("Not enough memory."); 220d1d669bdSPawel Jakub Dawidek 221d1d669bdSPawel Jakub Dawidek /* Made 'and' here. */ 222d1d669bdSPawel Jakub Dawidek ex->e_ip = (ip & mask); 223d1d669bdSPawel Jakub Dawidek ex->e_mask = mask; 224d1d669bdSPawel Jakub Dawidek ex->e_flags = flags; 225d1d669bdSPawel Jakub Dawidek 2267be67fe3SPawel Jakub Dawidek SLIST_INSERT_HEAD(&exports, ex, e_next); 227d1d669bdSPawel Jakub Dawidek 228d1d669bdSPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.", 229d1d669bdSPawel Jakub Dawidek ip2str(ex->e_ip), vmask, path, sflags); 230d1d669bdSPawel Jakub Dawidek } 231d1d669bdSPawel Jakub Dawidek 232d1d669bdSPawel Jakub Dawidek static void 233d1d669bdSPawel Jakub Dawidek exports_clear(void) 234d1d669bdSPawel Jakub Dawidek { 2357be67fe3SPawel Jakub Dawidek struct ggd_export *ex; 236d1d669bdSPawel Jakub Dawidek 2377be67fe3SPawel Jakub Dawidek while (!SLIST_EMPTY(&exports)) { 2387be67fe3SPawel Jakub Dawidek ex = SLIST_FIRST(&exports); 2397be67fe3SPawel Jakub Dawidek SLIST_REMOVE_HEAD(&exports, e_next); 240d1d669bdSPawel Jakub Dawidek free(ex); 241d1d669bdSPawel Jakub Dawidek } 242d1d669bdSPawel Jakub Dawidek } 243d1d669bdSPawel Jakub Dawidek 244d1d669bdSPawel Jakub Dawidek #define EXPORTS_LINE_SIZE 2048 245d1d669bdSPawel Jakub Dawidek static void 246d1d669bdSPawel Jakub Dawidek exports_get(void) 247d1d669bdSPawel Jakub Dawidek { 248d1d669bdSPawel Jakub Dawidek char buf[EXPORTS_LINE_SIZE], *line; 249d1d669bdSPawel Jakub Dawidek unsigned lineno = 0, objs = 0, len; 250d1d669bdSPawel Jakub Dawidek FILE *fd; 251d1d669bdSPawel Jakub Dawidek 252d1d669bdSPawel Jakub Dawidek exports_clear(); 253d1d669bdSPawel Jakub Dawidek 2547be67fe3SPawel Jakub Dawidek fd = fopen(exports_file, "r"); 255d1d669bdSPawel Jakub Dawidek if (fd == NULL) { 2567be67fe3SPawel Jakub Dawidek g_gate_xlog("Cannot open exports file (%s): %s.", exports_file, 257d1d669bdSPawel Jakub Dawidek strerror(errno)); 258d1d669bdSPawel Jakub Dawidek } 259d1d669bdSPawel Jakub Dawidek 2607be67fe3SPawel Jakub Dawidek g_gate_log(LOG_INFO, "Reading exports file (%s).", exports_file); 261d1d669bdSPawel Jakub Dawidek 262d1d669bdSPawel Jakub Dawidek for (;;) { 263d1d669bdSPawel Jakub Dawidek if (fgets(buf, sizeof(buf), fd) == NULL) { 264d1d669bdSPawel Jakub Dawidek if (feof(fd)) 265d1d669bdSPawel Jakub Dawidek break; 266d1d669bdSPawel Jakub Dawidek 267d1d669bdSPawel Jakub Dawidek g_gate_xlog("Error while reading exports file: %s.", 268d1d669bdSPawel Jakub Dawidek strerror(errno)); 269d1d669bdSPawel Jakub Dawidek } 270d1d669bdSPawel Jakub Dawidek 271d1d669bdSPawel Jakub Dawidek /* Increase line count. */ 272d1d669bdSPawel Jakub Dawidek lineno++; 273d1d669bdSPawel Jakub Dawidek 274d1d669bdSPawel Jakub Dawidek /* Skip spaces and tabs. */ 275d1d669bdSPawel Jakub Dawidek for (line = buf; *line == ' ' || *line == '\t'; ++line) 276d1d669bdSPawel Jakub Dawidek ; 277d1d669bdSPawel Jakub Dawidek 278d1d669bdSPawel Jakub Dawidek /* Empty line, comment or empty line at the end of file. */ 279d1d669bdSPawel Jakub Dawidek if (*line == '\n' || *line == '#' || *line == '\0') 280d1d669bdSPawel Jakub Dawidek continue; 281d1d669bdSPawel Jakub Dawidek 282d1d669bdSPawel Jakub Dawidek len = strlen(line); 283d1d669bdSPawel Jakub Dawidek if (line[len - 1] == '\n') { 284d1d669bdSPawel Jakub Dawidek /* Remove new line char. */ 285d1d669bdSPawel Jakub Dawidek line[len - 1] = '\0'; 286d1d669bdSPawel Jakub Dawidek } else { 287d1d669bdSPawel Jakub Dawidek if (!feof(fd)) 288d1d669bdSPawel Jakub Dawidek g_gate_xlog("Line %u too long.", lineno); 289d1d669bdSPawel Jakub Dawidek } 290d1d669bdSPawel Jakub Dawidek 291d1d669bdSPawel Jakub Dawidek line_parse(line, lineno); 292d1d669bdSPawel Jakub Dawidek objs++; 293d1d669bdSPawel Jakub Dawidek } 294d1d669bdSPawel Jakub Dawidek 295d1d669bdSPawel Jakub Dawidek fclose(fd); 296d1d669bdSPawel Jakub Dawidek 297d1d669bdSPawel Jakub Dawidek if (objs == 0) 298d1d669bdSPawel Jakub Dawidek g_gate_xlog("There are no objects to export."); 299d1d669bdSPawel Jakub Dawidek 300d1d669bdSPawel Jakub Dawidek g_gate_log(LOG_INFO, "Exporting %u object(s).", objs); 301d1d669bdSPawel Jakub Dawidek } 302d1d669bdSPawel Jakub Dawidek 3037be67fe3SPawel Jakub Dawidek static int 3047be67fe3SPawel Jakub Dawidek exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit, 3057be67fe3SPawel Jakub Dawidek struct ggd_connection *conn) 306d1d669bdSPawel Jakub Dawidek { 3077be67fe3SPawel Jakub Dawidek char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */ 3087be67fe3SPawel Jakub Dawidek int error = 0, flags; 3097be67fe3SPawel Jakub Dawidek 3107be67fe3SPawel Jakub Dawidek strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask)); 3117be67fe3SPawel Jakub Dawidek strlcat(ipmask, "/", sizeof(ipmask)); 3127be67fe3SPawel Jakub Dawidek strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask)); 3137be67fe3SPawel Jakub Dawidek if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) { 3147be67fe3SPawel Jakub Dawidek if (ex->e_flags == O_WRONLY) { 3157be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Read-only access requested, " 3167be67fe3SPawel Jakub Dawidek "but %s (%s) is exported write-only.", ex->e_path, 3177be67fe3SPawel Jakub Dawidek ipmask); 3187be67fe3SPawel Jakub Dawidek return (EPERM); 3197be67fe3SPawel Jakub Dawidek } else { 3207be67fe3SPawel Jakub Dawidek conn->c_flags |= GGATE_FLAG_RDONLY; 3217be67fe3SPawel Jakub Dawidek } 3227be67fe3SPawel Jakub Dawidek } else if ((cinit->gc_flags & GGATE_FLAG_WRONLY) != 0) { 3237be67fe3SPawel Jakub Dawidek if (ex->e_flags == O_RDONLY) { 3247be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Write-only access requested, " 3257be67fe3SPawel Jakub Dawidek "but %s (%s) is exported read-only.", ex->e_path, 3267be67fe3SPawel Jakub Dawidek ipmask); 3277be67fe3SPawel Jakub Dawidek return (EPERM); 3287be67fe3SPawel Jakub Dawidek } else { 3297be67fe3SPawel Jakub Dawidek conn->c_flags |= GGATE_FLAG_WRONLY; 3307be67fe3SPawel Jakub Dawidek } 3317be67fe3SPawel Jakub Dawidek } else { 3327be67fe3SPawel Jakub Dawidek if (ex->e_flags == O_RDONLY) { 3337be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Read-write access requested, " 3347be67fe3SPawel Jakub Dawidek "but %s (%s) is exported read-only.", ex->e_path, 3357be67fe3SPawel Jakub Dawidek ipmask); 3367be67fe3SPawel Jakub Dawidek return (EPERM); 3377be67fe3SPawel Jakub Dawidek } else if (ex->e_flags == O_WRONLY) { 3387be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Read-write access requested, " 3397be67fe3SPawel Jakub Dawidek "but %s (%s) is exported write-only.", ex->e_path, 3407be67fe3SPawel Jakub Dawidek ipmask); 3417be67fe3SPawel Jakub Dawidek return (EPERM); 3427be67fe3SPawel Jakub Dawidek } 3437be67fe3SPawel Jakub Dawidek } 3447be67fe3SPawel Jakub Dawidek if ((conn->c_flags & GGATE_FLAG_RDONLY) != 0) 3457be67fe3SPawel Jakub Dawidek flags = O_RDONLY; 3467be67fe3SPawel Jakub Dawidek else if ((conn->c_flags & GGATE_FLAG_WRONLY) != 0) 3477be67fe3SPawel Jakub Dawidek flags = O_WRONLY; 3487be67fe3SPawel Jakub Dawidek else 3497be67fe3SPawel Jakub Dawidek flags = O_RDWR; 3507be67fe3SPawel Jakub Dawidek conn->c_diskfd = open(ex->e_path, flags); 3517be67fe3SPawel Jakub Dawidek if (conn->c_diskfd == -1) { 3527be67fe3SPawel Jakub Dawidek error = errno; 3537be67fe3SPawel Jakub Dawidek g_gate_log(LOG_ERR, "Cannot open %s: %s.", ex->e_path, 3547be67fe3SPawel Jakub Dawidek strerror(error)); 3557be67fe3SPawel Jakub Dawidek return (error); 3567be67fe3SPawel Jakub Dawidek } 3577be67fe3SPawel Jakub Dawidek return (0); 3587be67fe3SPawel Jakub Dawidek } 3597be67fe3SPawel Jakub Dawidek 3607be67fe3SPawel Jakub Dawidek static struct ggd_export * 3617be67fe3SPawel Jakub Dawidek exports_find(struct sockaddr *s, struct g_gate_cinit *cinit, 3627be67fe3SPawel Jakub Dawidek struct ggd_connection *conn) 3637be67fe3SPawel Jakub Dawidek { 3647be67fe3SPawel Jakub Dawidek struct ggd_export *ex; 3657be67fe3SPawel Jakub Dawidek in_addr_t ip; 3667be67fe3SPawel Jakub Dawidek int error; 3677be67fe3SPawel Jakub Dawidek 3687be67fe3SPawel Jakub Dawidek ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); 3697be67fe3SPawel Jakub Dawidek SLIST_FOREACH(ex, &exports, e_next) { 3707be67fe3SPawel Jakub Dawidek if ((ip & ex->e_mask) != ex->e_ip) { 3717be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "exports[%s]: IP mismatch.", 3727be67fe3SPawel Jakub Dawidek ex->e_path); 3737be67fe3SPawel Jakub Dawidek continue; 3747be67fe3SPawel Jakub Dawidek } 3757be67fe3SPawel Jakub Dawidek if (strcmp(cinit->gc_path, ex->e_path) != 0) { 3767be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "exports[%s]: Path mismatch.", 3777be67fe3SPawel Jakub Dawidek ex->e_path); 3787be67fe3SPawel Jakub Dawidek continue; 3797be67fe3SPawel Jakub Dawidek } 3807be67fe3SPawel Jakub Dawidek error = exports_check(ex, cinit, conn); 3817be67fe3SPawel Jakub Dawidek if (error == 0) 3827be67fe3SPawel Jakub Dawidek return (ex); 3837be67fe3SPawel Jakub Dawidek else { 3847be67fe3SPawel Jakub Dawidek errno = error; 3857be67fe3SPawel Jakub Dawidek return (NULL); 3867be67fe3SPawel Jakub Dawidek } 3877be67fe3SPawel Jakub Dawidek } 3887be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Unauthorized connection from: %s.", 3897be67fe3SPawel Jakub Dawidek ip2str(ip)); 3907be67fe3SPawel Jakub Dawidek errno = EPERM; 3917be67fe3SPawel Jakub Dawidek return (NULL); 3927be67fe3SPawel Jakub Dawidek } 3937be67fe3SPawel Jakub Dawidek 3947be67fe3SPawel Jakub Dawidek /* 3957be67fe3SPawel Jakub Dawidek * Remove timed out connections. 3967be67fe3SPawel Jakub Dawidek */ 3977be67fe3SPawel Jakub Dawidek static void 3987be67fe3SPawel Jakub Dawidek connection_cleanups(void) 3997be67fe3SPawel Jakub Dawidek { 4007be67fe3SPawel Jakub Dawidek struct ggd_connection *conn, *tconn; 4017be67fe3SPawel Jakub Dawidek time_t now; 4027be67fe3SPawel Jakub Dawidek 4037be67fe3SPawel Jakub Dawidek time(&now); 4047be67fe3SPawel Jakub Dawidek LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) { 4057be67fe3SPawel Jakub Dawidek if (now - conn->c_birthtime > 10) { 4067be67fe3SPawel Jakub Dawidek LIST_REMOVE(conn, c_next); 4077be67fe3SPawel Jakub Dawidek g_gate_log(LOG_NOTICE, 4087be67fe3SPawel Jakub Dawidek "Connection from %s [%s] removed.", 4097be67fe3SPawel Jakub Dawidek ip2str(conn->c_srcip), conn->c_path); 4107be67fe3SPawel Jakub Dawidek close(conn->c_diskfd); 4117be67fe3SPawel Jakub Dawidek close(conn->c_sendfd); 4127be67fe3SPawel Jakub Dawidek close(conn->c_recvfd); 4137be67fe3SPawel Jakub Dawidek free(conn->c_path); 4147be67fe3SPawel Jakub Dawidek free(conn); 4157be67fe3SPawel Jakub Dawidek } 4167be67fe3SPawel Jakub Dawidek } 4177be67fe3SPawel Jakub Dawidek } 4187be67fe3SPawel Jakub Dawidek 4197be67fe3SPawel Jakub Dawidek static struct ggd_connection * 4207be67fe3SPawel Jakub Dawidek connection_find(struct g_gate_cinit *cinit) 4217be67fe3SPawel Jakub Dawidek { 4227be67fe3SPawel Jakub Dawidek struct ggd_connection *conn; 4237be67fe3SPawel Jakub Dawidek 4247be67fe3SPawel Jakub Dawidek LIST_FOREACH(conn, &connections, c_next) { 4257be67fe3SPawel Jakub Dawidek if (conn->c_token == cinit->gc_token) 4267be67fe3SPawel Jakub Dawidek break; 4277be67fe3SPawel Jakub Dawidek } 4287be67fe3SPawel Jakub Dawidek return (conn); 4297be67fe3SPawel Jakub Dawidek } 4307be67fe3SPawel Jakub Dawidek 4317be67fe3SPawel Jakub Dawidek static struct ggd_connection * 4327be67fe3SPawel Jakub Dawidek connection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) 4337be67fe3SPawel Jakub Dawidek { 4347be67fe3SPawel Jakub Dawidek struct ggd_connection *conn; 4357be67fe3SPawel Jakub Dawidek in_addr_t ip; 4367be67fe3SPawel Jakub Dawidek 4377be67fe3SPawel Jakub Dawidek /* 4387be67fe3SPawel Jakub Dawidek * First, look for old connections. 4397be67fe3SPawel Jakub Dawidek * We probably should do it every X seconds, but what for? 4407be67fe3SPawel Jakub Dawidek * It is only dangerous if an attacker wants to overload connections 4417be67fe3SPawel Jakub Dawidek * queue, so here is a good place to do the cleanups. 4427be67fe3SPawel Jakub Dawidek */ 4437be67fe3SPawel Jakub Dawidek connection_cleanups(); 4447be67fe3SPawel Jakub Dawidek 4457be67fe3SPawel Jakub Dawidek conn = malloc(sizeof(*conn)); 4467be67fe3SPawel Jakub Dawidek if (conn == NULL) 4477be67fe3SPawel Jakub Dawidek return (NULL); 4487be67fe3SPawel Jakub Dawidek conn->c_path = strdup(cinit->gc_path); 4497be67fe3SPawel Jakub Dawidek if (conn->c_path == NULL) { 4507be67fe3SPawel Jakub Dawidek free(conn); 4517be67fe3SPawel Jakub Dawidek return (NULL); 4527be67fe3SPawel Jakub Dawidek } 4537be67fe3SPawel Jakub Dawidek conn->c_token = cinit->gc_token; 4547be67fe3SPawel Jakub Dawidek ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); 4557be67fe3SPawel Jakub Dawidek conn->c_srcip = ip; 4567be67fe3SPawel Jakub Dawidek conn->c_sendfd = conn->c_recvfd = -1; 4577be67fe3SPawel Jakub Dawidek if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) 4587be67fe3SPawel Jakub Dawidek conn->c_sendfd = sfd; 4597be67fe3SPawel Jakub Dawidek else 4607be67fe3SPawel Jakub Dawidek conn->c_recvfd = sfd; 4617be67fe3SPawel Jakub Dawidek conn->c_mediasize = 0; 4627be67fe3SPawel Jakub Dawidek conn->c_sectorsize = 0; 4637be67fe3SPawel Jakub Dawidek time(&conn->c_birthtime); 4647be67fe3SPawel Jakub Dawidek conn->c_flags = cinit->gc_flags; 4657be67fe3SPawel Jakub Dawidek LIST_INSERT_HEAD(&connections, conn, c_next); 4667be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip), 4677be67fe3SPawel Jakub Dawidek conn->c_path); 4687be67fe3SPawel Jakub Dawidek return (conn); 4697be67fe3SPawel Jakub Dawidek } 4707be67fe3SPawel Jakub Dawidek 4717be67fe3SPawel Jakub Dawidek static int 4727be67fe3SPawel Jakub Dawidek connection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit, 4737be67fe3SPawel Jakub Dawidek struct sockaddr *s, int sfd) 4747be67fe3SPawel Jakub Dawidek { 475d1d669bdSPawel Jakub Dawidek in_addr_t ip; 476d1d669bdSPawel Jakub Dawidek 47786bfa454SPawel Jakub Dawidek ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); 4787be67fe3SPawel Jakub Dawidek if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) { 4797be67fe3SPawel Jakub Dawidek if (conn->c_sendfd != -1) { 4807be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, 4817be67fe3SPawel Jakub Dawidek "Send socket already exists [%s, %s].", ip2str(ip), 4827be67fe3SPawel Jakub Dawidek conn->c_path); 4837be67fe3SPawel Jakub Dawidek return (EEXIST); 484d1d669bdSPawel Jakub Dawidek } 4857be67fe3SPawel Jakub Dawidek conn->c_sendfd = sfd; 4867be67fe3SPawel Jakub Dawidek } else { 4877be67fe3SPawel Jakub Dawidek if (conn->c_recvfd != -1) { 4887be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, 4897be67fe3SPawel Jakub Dawidek "Receive socket already exists [%s, %s].", 4907be67fe3SPawel Jakub Dawidek ip2str(ip), conn->c_path); 4917be67fe3SPawel Jakub Dawidek return (EEXIST); 4927be67fe3SPawel Jakub Dawidek } 4937be67fe3SPawel Jakub Dawidek conn->c_recvfd = sfd; 4947be67fe3SPawel Jakub Dawidek } 4957be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(ip), 4967be67fe3SPawel Jakub Dawidek conn->c_path); 4977be67fe3SPawel Jakub Dawidek return (0); 4987be67fe3SPawel Jakub Dawidek } 499d1d669bdSPawel Jakub Dawidek 5007be67fe3SPawel Jakub Dawidek /* 5017be67fe3SPawel Jakub Dawidek * Remove one socket from the given connection or the whole 5027be67fe3SPawel Jakub Dawidek * connection if sfd == -1. 5037be67fe3SPawel Jakub Dawidek */ 5047be67fe3SPawel Jakub Dawidek static void 5057be67fe3SPawel Jakub Dawidek connection_remove(struct ggd_connection *conn) 5067be67fe3SPawel Jakub Dawidek { 5077be67fe3SPawel Jakub Dawidek 5087be67fe3SPawel Jakub Dawidek LIST_REMOVE(conn, c_next); 5097be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Connection removed [%s %s].", 5107be67fe3SPawel Jakub Dawidek ip2str(conn->c_srcip), conn->c_path); 5117be67fe3SPawel Jakub Dawidek if (conn->c_sendfd != -1) 5127be67fe3SPawel Jakub Dawidek close(conn->c_sendfd); 5137be67fe3SPawel Jakub Dawidek if (conn->c_recvfd != -1) 5147be67fe3SPawel Jakub Dawidek close(conn->c_recvfd); 5157be67fe3SPawel Jakub Dawidek free(conn->c_path); 5167be67fe3SPawel Jakub Dawidek free(conn); 5177be67fe3SPawel Jakub Dawidek } 5187be67fe3SPawel Jakub Dawidek 5197be67fe3SPawel Jakub Dawidek static int 5207be67fe3SPawel Jakub Dawidek connection_ready(struct ggd_connection *conn) 5217be67fe3SPawel Jakub Dawidek { 5227be67fe3SPawel Jakub Dawidek 5237be67fe3SPawel Jakub Dawidek return (conn->c_sendfd != -1 && conn->c_recvfd != -1); 5247be67fe3SPawel Jakub Dawidek } 5257be67fe3SPawel Jakub Dawidek 5267be67fe3SPawel Jakub Dawidek static void 5277be67fe3SPawel Jakub Dawidek connection_launch(struct ggd_connection *conn) 5287be67fe3SPawel Jakub Dawidek { 5297be67fe3SPawel Jakub Dawidek pthread_t td; 5307be67fe3SPawel Jakub Dawidek int error, pid; 5317be67fe3SPawel Jakub Dawidek 5327be67fe3SPawel Jakub Dawidek pid = fork(); 5337be67fe3SPawel Jakub Dawidek if (pid > 0) 5347be67fe3SPawel Jakub Dawidek return; 5357be67fe3SPawel Jakub Dawidek else if (pid == -1) { 5367be67fe3SPawel Jakub Dawidek g_gate_log(LOG_ERR, "Cannot fork: %s.", strerror(errno)); 5377be67fe3SPawel Jakub Dawidek return; 5387be67fe3SPawel Jakub Dawidek } 5397be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Process created [%s].", conn->c_path); 5407be67fe3SPawel Jakub Dawidek 5417be67fe3SPawel Jakub Dawidek /* 5427be67fe3SPawel Jakub Dawidek * Create condition variables and mutexes for in-queue and out-queue 5437be67fe3SPawel Jakub Dawidek * synchronization. 5447be67fe3SPawel Jakub Dawidek */ 5457be67fe3SPawel Jakub Dawidek error = pthread_mutex_init(&inqueue_mtx, NULL); 5467be67fe3SPawel Jakub Dawidek if (error != 0) { 5477be67fe3SPawel Jakub Dawidek g_gate_xlog("pthread_mutex_init(inqueue_mtx): %s.", 5487be67fe3SPawel Jakub Dawidek strerror(error)); 5497be67fe3SPawel Jakub Dawidek } 5507be67fe3SPawel Jakub Dawidek error = pthread_cond_init(&inqueue_cond, NULL); 5517be67fe3SPawel Jakub Dawidek if (error != 0) { 5527be67fe3SPawel Jakub Dawidek g_gate_xlog("pthread_cond_init(inqueue_cond): %s.", 5537be67fe3SPawel Jakub Dawidek strerror(error)); 5547be67fe3SPawel Jakub Dawidek } 5557be67fe3SPawel Jakub Dawidek error = pthread_mutex_init(&outqueue_mtx, NULL); 5567be67fe3SPawel Jakub Dawidek if (error != 0) { 5577be67fe3SPawel Jakub Dawidek g_gate_xlog("pthread_mutex_init(outqueue_mtx): %s.", 5587be67fe3SPawel Jakub Dawidek strerror(error)); 5597be67fe3SPawel Jakub Dawidek } 5607be67fe3SPawel Jakub Dawidek error = pthread_cond_init(&outqueue_cond, NULL); 5617be67fe3SPawel Jakub Dawidek if (error != 0) { 5627be67fe3SPawel Jakub Dawidek g_gate_xlog("pthread_cond_init(outqueue_cond): %s.", 5637be67fe3SPawel Jakub Dawidek strerror(error)); 5647be67fe3SPawel Jakub Dawidek } 5657be67fe3SPawel Jakub Dawidek 5667be67fe3SPawel Jakub Dawidek /* 5677be67fe3SPawel Jakub Dawidek * Create threads: 5687be67fe3SPawel Jakub Dawidek * recvtd - thread for receiving I/O request 5697be67fe3SPawel Jakub Dawidek * diskio - thread for doing I/O request 5707be67fe3SPawel Jakub Dawidek * sendtd - thread for sending I/O requests back 5717be67fe3SPawel Jakub Dawidek */ 5727be67fe3SPawel Jakub Dawidek error = pthread_create(&td, NULL, send_thread, conn); 5737be67fe3SPawel Jakub Dawidek if (error != 0) { 5747be67fe3SPawel Jakub Dawidek g_gate_xlog("pthread_create(send_thread): %s.", 5757be67fe3SPawel Jakub Dawidek strerror(error)); 5767be67fe3SPawel Jakub Dawidek } 5777be67fe3SPawel Jakub Dawidek error = pthread_create(&td, NULL, recv_thread, conn); 5787be67fe3SPawel Jakub Dawidek if (error != 0) { 5797be67fe3SPawel Jakub Dawidek g_gate_xlog("pthread_create(recv_thread): %s.", 5807be67fe3SPawel Jakub Dawidek strerror(error)); 5817be67fe3SPawel Jakub Dawidek } 5827be67fe3SPawel Jakub Dawidek disk_thread(conn); 583d1d669bdSPawel Jakub Dawidek } 584d1d669bdSPawel Jakub Dawidek 585d1d669bdSPawel Jakub Dawidek static void 586d1d669bdSPawel Jakub Dawidek sendfail(int sfd, int error, const char *fmt, ...) 587d1d669bdSPawel Jakub Dawidek { 588d1d669bdSPawel Jakub Dawidek struct g_gate_sinit sinit; 589d1d669bdSPawel Jakub Dawidek va_list ap; 5907be67fe3SPawel Jakub Dawidek ssize_t data; 591d1d669bdSPawel Jakub Dawidek 592d1d669bdSPawel Jakub Dawidek sinit.gs_error = error; 593d1d669bdSPawel Jakub Dawidek g_gate_swap2n_sinit(&sinit); 5947be67fe3SPawel Jakub Dawidek data = g_gate_send(sfd, &sinit, sizeof(sinit), 0); 595d1d669bdSPawel Jakub Dawidek g_gate_swap2h_sinit(&sinit); 5967be67fe3SPawel Jakub Dawidek if (data != sizeof(sinit)) { 5977be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Cannot send initial packet: %s.", 598d1d669bdSPawel Jakub Dawidek strerror(errno)); 5997be67fe3SPawel Jakub Dawidek return; 600d1d669bdSPawel Jakub Dawidek } 601d1d669bdSPawel Jakub Dawidek if (fmt != NULL) { 602d1d669bdSPawel Jakub Dawidek va_start(ap, fmt); 6037be67fe3SPawel Jakub Dawidek g_gate_vlog(LOG_WARNING, fmt, ap); 604d1d669bdSPawel Jakub Dawidek va_end(ap); 605d1d669bdSPawel Jakub Dawidek } 606d1d669bdSPawel Jakub Dawidek } 607d1d669bdSPawel Jakub Dawidek 6087be67fe3SPawel Jakub Dawidek static void * 6097be67fe3SPawel Jakub Dawidek malloc_waitok(size_t size) 610d1d669bdSPawel Jakub Dawidek { 6117be67fe3SPawel Jakub Dawidek void *p; 6127be67fe3SPawel Jakub Dawidek 6137be67fe3SPawel Jakub Dawidek while ((p = malloc(size)) == NULL) { 6147be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Cannot allocate %zu bytes.", size); 6157be67fe3SPawel Jakub Dawidek sleep(1); 6167be67fe3SPawel Jakub Dawidek } 6177be67fe3SPawel Jakub Dawidek return (p); 6187be67fe3SPawel Jakub Dawidek } 6197be67fe3SPawel Jakub Dawidek 6207be67fe3SPawel Jakub Dawidek static void * 6217be67fe3SPawel Jakub Dawidek recv_thread(void *arg) 6227be67fe3SPawel Jakub Dawidek { 6237be67fe3SPawel Jakub Dawidek struct ggd_connection *conn; 6247be67fe3SPawel Jakub Dawidek struct ggd_request *req; 625d1d669bdSPawel Jakub Dawidek ssize_t data; 6267be67fe3SPawel Jakub Dawidek int error, fd; 627d1d669bdSPawel Jakub Dawidek 6287be67fe3SPawel Jakub Dawidek conn = arg; 6297be67fe3SPawel Jakub Dawidek g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); 6307be67fe3SPawel Jakub Dawidek fd = conn->c_recvfd; 631d1d669bdSPawel Jakub Dawidek for (;;) { 632d1d669bdSPawel Jakub Dawidek /* 6337be67fe3SPawel Jakub Dawidek * Get header packet. 634d1d669bdSPawel Jakub Dawidek */ 6357be67fe3SPawel Jakub Dawidek req = malloc_waitok(sizeof(*req)); 6367be67fe3SPawel Jakub Dawidek data = g_gate_recv(fd, &req->r_hdr, sizeof(req->r_hdr), 6377be67fe3SPawel Jakub Dawidek MSG_WAITALL); 638d1d669bdSPawel Jakub Dawidek if (data == 0) { 639d1d669bdSPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Process %u exiting.", getpid()); 640d1d669bdSPawel Jakub Dawidek exit(EXIT_SUCCESS); 641d1d669bdSPawel Jakub Dawidek } else if (data == -1) { 642d1d669bdSPawel Jakub Dawidek g_gate_xlog("Error while receiving hdr packet: %s.", 643d1d669bdSPawel Jakub Dawidek strerror(errno)); 6447be67fe3SPawel Jakub Dawidek } else if (data != sizeof(req->r_hdr)) { 645d1d669bdSPawel Jakub Dawidek g_gate_xlog("Malformed hdr packet received."); 646d1d669bdSPawel Jakub Dawidek } 647d1d669bdSPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Received hdr packet."); 6487be67fe3SPawel Jakub Dawidek g_gate_swap2h_hdr(&req->r_hdr); 6497be67fe3SPawel Jakub Dawidek 6507be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, 6517be67fe3SPawel Jakub Dawidek (intmax_t)req->r_offset, (unsigned)req->r_length); 652d1d669bdSPawel Jakub Dawidek 653d1d669bdSPawel Jakub Dawidek /* 6547be67fe3SPawel Jakub Dawidek * Allocate memory for data. 655d1d669bdSPawel Jakub Dawidek */ 6567be67fe3SPawel Jakub Dawidek req->r_data = malloc_waitok(req->r_length); 657d1d669bdSPawel Jakub Dawidek 6587be67fe3SPawel Jakub Dawidek /* 6597be67fe3SPawel Jakub Dawidek * Receive data to write for WRITE request. 6607be67fe3SPawel Jakub Dawidek */ 6617be67fe3SPawel Jakub Dawidek if (req->r_cmd == GGATE_CMD_WRITE) { 662d1d669bdSPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data...", 6637be67fe3SPawel Jakub Dawidek req->r_length); 6647be67fe3SPawel Jakub Dawidek data = g_gate_recv(fd, req->r_data, req->r_length, 6657be67fe3SPawel Jakub Dawidek MSG_WAITALL); 666d1d669bdSPawel Jakub Dawidek if (data == -1) { 667d1d669bdSPawel Jakub Dawidek g_gate_xlog("Error while receiving data: %s.", 668d1d669bdSPawel Jakub Dawidek strerror(errno)); 669d1d669bdSPawel Jakub Dawidek } 670d1d669bdSPawel Jakub Dawidek } 6717be67fe3SPawel Jakub Dawidek 6727be67fe3SPawel Jakub Dawidek /* 6737be67fe3SPawel Jakub Dawidek * Put the request onto the incoming queue. 6747be67fe3SPawel Jakub Dawidek */ 6757be67fe3SPawel Jakub Dawidek error = pthread_mutex_lock(&inqueue_mtx); 6767be67fe3SPawel Jakub Dawidek assert(error == 0); 6777be67fe3SPawel Jakub Dawidek TAILQ_INSERT_TAIL(&inqueue, req, r_next); 6787be67fe3SPawel Jakub Dawidek error = pthread_cond_signal(&inqueue_cond); 6797be67fe3SPawel Jakub Dawidek assert(error == 0); 6807be67fe3SPawel Jakub Dawidek error = pthread_mutex_unlock(&inqueue_mtx); 6817be67fe3SPawel Jakub Dawidek assert(error == 0); 6827be67fe3SPawel Jakub Dawidek } 6837be67fe3SPawel Jakub Dawidek } 6847be67fe3SPawel Jakub Dawidek 6857be67fe3SPawel Jakub Dawidek static void * 6867be67fe3SPawel Jakub Dawidek disk_thread(void *arg) 6877be67fe3SPawel Jakub Dawidek { 6887be67fe3SPawel Jakub Dawidek struct ggd_connection *conn; 6897be67fe3SPawel Jakub Dawidek struct ggd_request *req; 6907be67fe3SPawel Jakub Dawidek ssize_t data; 6917be67fe3SPawel Jakub Dawidek int error, fd; 6927be67fe3SPawel Jakub Dawidek 6937be67fe3SPawel Jakub Dawidek conn = arg; 6947be67fe3SPawel Jakub Dawidek g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); 6957be67fe3SPawel Jakub Dawidek fd = conn->c_diskfd; 6967be67fe3SPawel Jakub Dawidek for (;;) { 6977be67fe3SPawel Jakub Dawidek /* 6987be67fe3SPawel Jakub Dawidek * Get a request from the incoming queue. 6997be67fe3SPawel Jakub Dawidek */ 7007be67fe3SPawel Jakub Dawidek error = pthread_mutex_lock(&inqueue_mtx); 7017be67fe3SPawel Jakub Dawidek assert(error == 0); 7027be67fe3SPawel Jakub Dawidek while ((req = TAILQ_FIRST(&inqueue)) == NULL) { 7037be67fe3SPawel Jakub Dawidek error = pthread_cond_wait(&inqueue_cond, &inqueue_mtx); 7047be67fe3SPawel Jakub Dawidek assert(error == 0); 7057be67fe3SPawel Jakub Dawidek } 7067be67fe3SPawel Jakub Dawidek TAILQ_REMOVE(&inqueue, req, r_next); 7077be67fe3SPawel Jakub Dawidek error = pthread_mutex_unlock(&inqueue_mtx); 7087be67fe3SPawel Jakub Dawidek assert(error == 0); 7097be67fe3SPawel Jakub Dawidek 7107be67fe3SPawel Jakub Dawidek /* 7117be67fe3SPawel Jakub Dawidek * Check the request. 7127be67fe3SPawel Jakub Dawidek */ 7137be67fe3SPawel Jakub Dawidek assert(req->r_cmd == GGATE_CMD_READ || req->r_cmd == GGATE_CMD_WRITE); 7147be67fe3SPawel Jakub Dawidek assert(req->r_offset + req->r_length <= (uintmax_t)conn->c_mediasize); 7157be67fe3SPawel Jakub Dawidek assert((req->r_offset % conn->c_sectorsize) == 0); 7167be67fe3SPawel Jakub Dawidek assert((req->r_length % conn->c_sectorsize) == 0); 7177be67fe3SPawel Jakub Dawidek 7187be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, 7197be67fe3SPawel Jakub Dawidek (intmax_t)req->r_offset, (unsigned)req->r_length); 7207be67fe3SPawel Jakub Dawidek 7217be67fe3SPawel Jakub Dawidek /* 7227be67fe3SPawel Jakub Dawidek * Do the request. 7237be67fe3SPawel Jakub Dawidek */ 7247be67fe3SPawel Jakub Dawidek data = 0; 7257be67fe3SPawel Jakub Dawidek switch (req->r_cmd) { 7267be67fe3SPawel Jakub Dawidek case GGATE_CMD_READ: 7277be67fe3SPawel Jakub Dawidek data = pread(fd, req->r_data, req->r_length, 7287be67fe3SPawel Jakub Dawidek req->r_offset); 7297be67fe3SPawel Jakub Dawidek break; 7307be67fe3SPawel Jakub Dawidek case GGATE_CMD_WRITE: 7317be67fe3SPawel Jakub Dawidek data = pwrite(fd, req->r_data, req->r_length, 7327be67fe3SPawel Jakub Dawidek req->r_offset); 7337be67fe3SPawel Jakub Dawidek /* Free data memory here - better sooner. */ 7347be67fe3SPawel Jakub Dawidek free(req->r_data); 7357be67fe3SPawel Jakub Dawidek req->r_data = NULL; 7367be67fe3SPawel Jakub Dawidek break; 7377be67fe3SPawel Jakub Dawidek } 7387be67fe3SPawel Jakub Dawidek if (data != (ssize_t)req->r_length) { 7397be67fe3SPawel Jakub Dawidek /* Report short reads/writes as I/O errors. */ 7407be67fe3SPawel Jakub Dawidek if (errno == 0) 7417be67fe3SPawel Jakub Dawidek errno = EIO; 7427be67fe3SPawel Jakub Dawidek g_gate_log(LOG_ERR, "Disk error: %s", strerror(errno)); 7437be67fe3SPawel Jakub Dawidek req->r_error = errno; 7447be67fe3SPawel Jakub Dawidek if (req->r_data != NULL) { 7457be67fe3SPawel Jakub Dawidek free(req->r_data); 7467be67fe3SPawel Jakub Dawidek req->r_data = NULL; 7477be67fe3SPawel Jakub Dawidek } 7487be67fe3SPawel Jakub Dawidek } 7497be67fe3SPawel Jakub Dawidek 7507be67fe3SPawel Jakub Dawidek /* 7517be67fe3SPawel Jakub Dawidek * Put the request onto the outgoing queue. 7527be67fe3SPawel Jakub Dawidek */ 7537be67fe3SPawel Jakub Dawidek error = pthread_mutex_lock(&outqueue_mtx); 7547be67fe3SPawel Jakub Dawidek assert(error == 0); 7557be67fe3SPawel Jakub Dawidek TAILQ_INSERT_TAIL(&outqueue, req, r_next); 7567be67fe3SPawel Jakub Dawidek error = pthread_cond_signal(&outqueue_cond); 7577be67fe3SPawel Jakub Dawidek assert(error == 0); 7587be67fe3SPawel Jakub Dawidek error = pthread_mutex_unlock(&outqueue_mtx); 7597be67fe3SPawel Jakub Dawidek assert(error == 0); 7607be67fe3SPawel Jakub Dawidek } 761186f2eeaSMike Makonnen 762186f2eeaSMike Makonnen /* NOTREACHED */ 763186f2eeaSMike Makonnen return (NULL); 7647be67fe3SPawel Jakub Dawidek } 7657be67fe3SPawel Jakub Dawidek 7667be67fe3SPawel Jakub Dawidek static void * 7677be67fe3SPawel Jakub Dawidek send_thread(void *arg) 7687be67fe3SPawel Jakub Dawidek { 7697be67fe3SPawel Jakub Dawidek struct ggd_connection *conn; 7707be67fe3SPawel Jakub Dawidek struct ggd_request *req; 7717be67fe3SPawel Jakub Dawidek ssize_t data; 7727be67fe3SPawel Jakub Dawidek int error, fd; 7737be67fe3SPawel Jakub Dawidek 7747be67fe3SPawel Jakub Dawidek conn = arg; 7757be67fe3SPawel Jakub Dawidek g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); 7767be67fe3SPawel Jakub Dawidek fd = conn->c_sendfd; 7777be67fe3SPawel Jakub Dawidek for (;;) { 7787be67fe3SPawel Jakub Dawidek /* 7797be67fe3SPawel Jakub Dawidek * Get a request from the outgoing queue. 7807be67fe3SPawel Jakub Dawidek */ 7817be67fe3SPawel Jakub Dawidek error = pthread_mutex_lock(&outqueue_mtx); 7827be67fe3SPawel Jakub Dawidek assert(error == 0); 7837be67fe3SPawel Jakub Dawidek while ((req = TAILQ_FIRST(&outqueue)) == NULL) { 7847be67fe3SPawel Jakub Dawidek error = pthread_cond_wait(&outqueue_cond, 7857be67fe3SPawel Jakub Dawidek &outqueue_mtx); 7867be67fe3SPawel Jakub Dawidek assert(error == 0); 7877be67fe3SPawel Jakub Dawidek } 7887be67fe3SPawel Jakub Dawidek TAILQ_REMOVE(&outqueue, req, r_next); 7897be67fe3SPawel Jakub Dawidek error = pthread_mutex_unlock(&outqueue_mtx); 7907be67fe3SPawel Jakub Dawidek assert(error == 0); 7917be67fe3SPawel Jakub Dawidek 7927be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, 7937be67fe3SPawel Jakub Dawidek (intmax_t)req->r_offset, (unsigned)req->r_length); 7947be67fe3SPawel Jakub Dawidek 7957be67fe3SPawel Jakub Dawidek /* 7967be67fe3SPawel Jakub Dawidek * Send the request. 7977be67fe3SPawel Jakub Dawidek */ 7987be67fe3SPawel Jakub Dawidek g_gate_swap2n_hdr(&req->r_hdr); 7997be67fe3SPawel Jakub Dawidek if (g_gate_send(fd, &req->r_hdr, sizeof(req->r_hdr), 0) == -1) { 8007be67fe3SPawel Jakub Dawidek g_gate_xlog("Error while sending hdr packet: %s.", 801d1d669bdSPawel Jakub Dawidek strerror(errno)); 802d1d669bdSPawel Jakub Dawidek } 8037be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Sent hdr packet."); 8047be67fe3SPawel Jakub Dawidek g_gate_swap2h_hdr(&req->r_hdr); 8057be67fe3SPawel Jakub Dawidek if (req->r_data != NULL) { 8067be67fe3SPawel Jakub Dawidek data = g_gate_send(fd, req->r_data, req->r_length, 0); 8077be67fe3SPawel Jakub Dawidek if (data != (ssize_t)req->r_length) { 8087be67fe3SPawel Jakub Dawidek g_gate_xlog("Error while sending data: %s.", 8097be67fe3SPawel Jakub Dawidek strerror(errno)); 810d1d669bdSPawel Jakub Dawidek } 8117be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, 8127be67fe3SPawel Jakub Dawidek "Sent %zd bytes (offset=%ju, size=%zu).", data, 8137be67fe3SPawel Jakub Dawidek (uintmax_t)req->r_offset, (size_t)req->r_length); 8147be67fe3SPawel Jakub Dawidek free(req->r_data); 815d1d669bdSPawel Jakub Dawidek } 8167be67fe3SPawel Jakub Dawidek free(req); 8177be67fe3SPawel Jakub Dawidek } 818186f2eeaSMike Makonnen 819186f2eeaSMike Makonnen /* NOTREACHED */ 820186f2eeaSMike Makonnen return (NULL); 8217be67fe3SPawel Jakub Dawidek } 8227be67fe3SPawel Jakub Dawidek 8237be67fe3SPawel Jakub Dawidek static void 8247be67fe3SPawel Jakub Dawidek log_connection(struct sockaddr *from) 8257be67fe3SPawel Jakub Dawidek { 8267be67fe3SPawel Jakub Dawidek in_addr_t ip; 8277be67fe3SPawel Jakub Dawidek 8287be67fe3SPawel Jakub Dawidek ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr); 8297be67fe3SPawel Jakub Dawidek g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); 8307be67fe3SPawel Jakub Dawidek } 8317be67fe3SPawel Jakub Dawidek 8327be67fe3SPawel Jakub Dawidek static int 8337be67fe3SPawel Jakub Dawidek handshake(struct sockaddr *from, int sfd) 8347be67fe3SPawel Jakub Dawidek { 8357be67fe3SPawel Jakub Dawidek struct g_gate_version ver; 8367be67fe3SPawel Jakub Dawidek struct g_gate_cinit cinit; 8377be67fe3SPawel Jakub Dawidek struct g_gate_sinit sinit; 8387be67fe3SPawel Jakub Dawidek struct ggd_connection *conn; 8397be67fe3SPawel Jakub Dawidek struct ggd_export *ex; 8407be67fe3SPawel Jakub Dawidek ssize_t data; 8417be67fe3SPawel Jakub Dawidek 8427be67fe3SPawel Jakub Dawidek log_connection(from); 8437be67fe3SPawel Jakub Dawidek /* 8447be67fe3SPawel Jakub Dawidek * Phase 1: Version verification. 8457be67fe3SPawel Jakub Dawidek */ 8467be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Receiving version packet."); 8477be67fe3SPawel Jakub Dawidek data = g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL); 8487be67fe3SPawel Jakub Dawidek g_gate_swap2h_version(&ver); 8497be67fe3SPawel Jakub Dawidek if (data != sizeof(ver)) { 8507be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Malformed version packet."); 8517be67fe3SPawel Jakub Dawidek return (0); 8527be67fe3SPawel Jakub Dawidek } 8537be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Version packet received."); 8547be67fe3SPawel Jakub Dawidek if (memcmp(ver.gv_magic, GGATE_MAGIC, strlen(GGATE_MAGIC)) != 0) { 8557be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Invalid magic field."); 8567be67fe3SPawel Jakub Dawidek return (0); 8577be67fe3SPawel Jakub Dawidek } 8587be67fe3SPawel Jakub Dawidek if (ver.gv_version != GGATE_VERSION) { 8597be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Version %u is not supported.", 8607be67fe3SPawel Jakub Dawidek ver.gv_version); 8617be67fe3SPawel Jakub Dawidek return (0); 8627be67fe3SPawel Jakub Dawidek } 8637be67fe3SPawel Jakub Dawidek ver.gv_error = 0; 8647be67fe3SPawel Jakub Dawidek g_gate_swap2n_version(&ver); 8657be67fe3SPawel Jakub Dawidek data = g_gate_send(sfd, &ver, sizeof(ver), 0); 8667be67fe3SPawel Jakub Dawidek g_gate_swap2h_version(&ver); 8677be67fe3SPawel Jakub Dawidek if (data == -1) { 8687be67fe3SPawel Jakub Dawidek sendfail(sfd, errno, "Error while sending version packet: %s.", 8697be67fe3SPawel Jakub Dawidek strerror(errno)); 8707be67fe3SPawel Jakub Dawidek return (0); 8717be67fe3SPawel Jakub Dawidek } 8727be67fe3SPawel Jakub Dawidek 8737be67fe3SPawel Jakub Dawidek /* 8747be67fe3SPawel Jakub Dawidek * Phase 2: Request verification. 8757be67fe3SPawel Jakub Dawidek */ 8767be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Receiving initial packet."); 8777be67fe3SPawel Jakub Dawidek data = g_gate_recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL); 8787be67fe3SPawel Jakub Dawidek g_gate_swap2h_cinit(&cinit); 8797be67fe3SPawel Jakub Dawidek if (data != sizeof(cinit)) { 8807be67fe3SPawel Jakub Dawidek g_gate_log(LOG_WARNING, "Malformed initial packet."); 8817be67fe3SPawel Jakub Dawidek return (0); 8827be67fe3SPawel Jakub Dawidek } 8837be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Initial packet received."); 8847be67fe3SPawel Jakub Dawidek conn = connection_find(&cinit); 8857be67fe3SPawel Jakub Dawidek if (conn != NULL) { 8867be67fe3SPawel Jakub Dawidek /* 8877be67fe3SPawel Jakub Dawidek * Connection should already exists. 8887be67fe3SPawel Jakub Dawidek */ 8897be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Found existing connection (token=%lu).", 8907be67fe3SPawel Jakub Dawidek (unsigned long)conn->c_token); 8917be67fe3SPawel Jakub Dawidek if (connection_add(conn, &cinit, from, sfd) == -1) { 8927be67fe3SPawel Jakub Dawidek connection_remove(conn); 8937be67fe3SPawel Jakub Dawidek return (0); 8947be67fe3SPawel Jakub Dawidek } 8957be67fe3SPawel Jakub Dawidek } else { 8967be67fe3SPawel Jakub Dawidek /* 8977be67fe3SPawel Jakub Dawidek * New connection, allocate space. 8987be67fe3SPawel Jakub Dawidek */ 8997be67fe3SPawel Jakub Dawidek conn = connection_new(&cinit, from, sfd); 9007be67fe3SPawel Jakub Dawidek if (conn == NULL) { 9017be67fe3SPawel Jakub Dawidek sendfail(sfd, ENOMEM, 9027be67fe3SPawel Jakub Dawidek "Cannot allocate new connection."); 9037be67fe3SPawel Jakub Dawidek return (0); 9047be67fe3SPawel Jakub Dawidek } 9057be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "New connection created (token=%lu).", 9067be67fe3SPawel Jakub Dawidek (unsigned long)conn->c_token); 9077be67fe3SPawel Jakub Dawidek } 9087be67fe3SPawel Jakub Dawidek 9097be67fe3SPawel Jakub Dawidek ex = exports_find(from, &cinit, conn); 9107be67fe3SPawel Jakub Dawidek if (ex == NULL) { 9117be67fe3SPawel Jakub Dawidek sendfail(sfd, errno, NULL); 912b4a355e2SChristian Brueffer connection_remove(conn); 9137be67fe3SPawel Jakub Dawidek return (0); 9147be67fe3SPawel Jakub Dawidek } 9157be67fe3SPawel Jakub Dawidek if (conn->c_mediasize == 0) { 9167be67fe3SPawel Jakub Dawidek conn->c_mediasize = g_gate_mediasize(conn->c_diskfd); 9177be67fe3SPawel Jakub Dawidek conn->c_sectorsize = g_gate_sectorsize(conn->c_diskfd); 9187be67fe3SPawel Jakub Dawidek } 9197be67fe3SPawel Jakub Dawidek sinit.gs_mediasize = conn->c_mediasize; 9207be67fe3SPawel Jakub Dawidek sinit.gs_sectorsize = conn->c_sectorsize; 9217be67fe3SPawel Jakub Dawidek sinit.gs_error = 0; 9227be67fe3SPawel Jakub Dawidek 9237be67fe3SPawel Jakub Dawidek g_gate_log(LOG_DEBUG, "Sending initial packet."); 9247be67fe3SPawel Jakub Dawidek 9257be67fe3SPawel Jakub Dawidek g_gate_swap2n_sinit(&sinit); 9267be67fe3SPawel Jakub Dawidek data = g_gate_send(sfd, &sinit, sizeof(sinit), 0); 9277be67fe3SPawel Jakub Dawidek g_gate_swap2h_sinit(&sinit); 9287be67fe3SPawel Jakub Dawidek if (data == -1) { 9297be67fe3SPawel Jakub Dawidek sendfail(sfd, errno, "Error while sending initial packet: %s.", 9307be67fe3SPawel Jakub Dawidek strerror(errno)); 9317be67fe3SPawel Jakub Dawidek return (0); 9327be67fe3SPawel Jakub Dawidek } 9337be67fe3SPawel Jakub Dawidek 9347be67fe3SPawel Jakub Dawidek if (connection_ready(conn)) { 9357be67fe3SPawel Jakub Dawidek connection_launch(conn); 9367be67fe3SPawel Jakub Dawidek connection_remove(conn); 9377be67fe3SPawel Jakub Dawidek } 9387be67fe3SPawel Jakub Dawidek return (1); 939d1d669bdSPawel Jakub Dawidek } 940d1d669bdSPawel Jakub Dawidek 941d1d669bdSPawel Jakub Dawidek static void 942d1d669bdSPawel Jakub Dawidek huphandler(int sig __unused) 943d1d669bdSPawel Jakub Dawidek { 944d1d669bdSPawel Jakub Dawidek 945d1d669bdSPawel Jakub Dawidek got_sighup = 1; 946d1d669bdSPawel Jakub Dawidek } 947d1d669bdSPawel Jakub Dawidek 948d1d669bdSPawel Jakub Dawidek int 949d1d669bdSPawel Jakub Dawidek main(int argc, char *argv[]) 950d1d669bdSPawel Jakub Dawidek { 951*fc7e71c5SEnji Cooper const char *ggated_pidfile = _PATH_VARRUN "/ggated.pid"; 952*fc7e71c5SEnji Cooper struct pidfh *pfh; 953d1d669bdSPawel Jakub Dawidek struct sockaddr_in serv; 954d1d669bdSPawel Jakub Dawidek struct sockaddr from; 955d1d669bdSPawel Jakub Dawidek socklen_t fromlen; 956*fc7e71c5SEnji Cooper pid_t otherpid; 957*fc7e71c5SEnji Cooper int ch, sfd, tmpsfd; 9587be67fe3SPawel Jakub Dawidek unsigned port; 959d1d669bdSPawel Jakub Dawidek 960d1d669bdSPawel Jakub Dawidek bindaddr = htonl(INADDR_ANY); 961d1d669bdSPawel Jakub Dawidek port = G_GATE_PORT; 962*fc7e71c5SEnji Cooper while ((ch = getopt(argc, argv, "a:hnp:F:R:S:v")) != -1) { 963d1d669bdSPawel Jakub Dawidek switch (ch) { 964d1d669bdSPawel Jakub Dawidek case 'a': 965d1d669bdSPawel Jakub Dawidek bindaddr = g_gate_str2ip(optarg); 966d1d669bdSPawel Jakub Dawidek if (bindaddr == INADDR_NONE) { 967d1d669bdSPawel Jakub Dawidek errx(EXIT_FAILURE, 968d1d669bdSPawel Jakub Dawidek "Invalid IP/host name to bind to."); 969d1d669bdSPawel Jakub Dawidek } 970d1d669bdSPawel Jakub Dawidek break; 971*fc7e71c5SEnji Cooper case 'F': 972*fc7e71c5SEnji Cooper ggated_pidfile = optarg; 973*fc7e71c5SEnji Cooper break; 974d1d669bdSPawel Jakub Dawidek case 'n': 975d1d669bdSPawel Jakub Dawidek nagle = 0; 976d1d669bdSPawel Jakub Dawidek break; 977d1d669bdSPawel Jakub Dawidek case 'p': 978d1d669bdSPawel Jakub Dawidek errno = 0; 979d1d669bdSPawel Jakub Dawidek port = strtoul(optarg, NULL, 10); 980d1d669bdSPawel Jakub Dawidek if (port == 0 && errno != 0) 981d1d669bdSPawel Jakub Dawidek errx(EXIT_FAILURE, "Invalid port."); 982d1d669bdSPawel Jakub Dawidek break; 983d1d669bdSPawel Jakub Dawidek case 'R': 984d1d669bdSPawel Jakub Dawidek errno = 0; 985d1d669bdSPawel Jakub Dawidek rcvbuf = strtoul(optarg, NULL, 10); 986d1d669bdSPawel Jakub Dawidek if (rcvbuf == 0 && errno != 0) 987d1d669bdSPawel Jakub Dawidek errx(EXIT_FAILURE, "Invalid rcvbuf."); 988d1d669bdSPawel Jakub Dawidek break; 989d1d669bdSPawel Jakub Dawidek case 'S': 990d1d669bdSPawel Jakub Dawidek errno = 0; 991d1d669bdSPawel Jakub Dawidek sndbuf = strtoul(optarg, NULL, 10); 992d1d669bdSPawel Jakub Dawidek if (sndbuf == 0 && errno != 0) 993d1d669bdSPawel Jakub Dawidek errx(EXIT_FAILURE, "Invalid sndbuf."); 994d1d669bdSPawel Jakub Dawidek break; 995d1d669bdSPawel Jakub Dawidek case 'v': 996d1d669bdSPawel Jakub Dawidek g_gate_verbose++; 997d1d669bdSPawel Jakub Dawidek break; 998d1d669bdSPawel Jakub Dawidek case 'h': 999d1d669bdSPawel Jakub Dawidek default: 1000d1d669bdSPawel Jakub Dawidek usage(); 1001d1d669bdSPawel Jakub Dawidek } 1002d1d669bdSPawel Jakub Dawidek } 1003d1d669bdSPawel Jakub Dawidek argc -= optind; 1004d1d669bdSPawel Jakub Dawidek argv += optind; 1005d1d669bdSPawel Jakub Dawidek 1006d1d669bdSPawel Jakub Dawidek if (argv[0] != NULL) 10077be67fe3SPawel Jakub Dawidek exports_file = argv[0]; 1008d1d669bdSPawel Jakub Dawidek exports_get(); 1009d1d669bdSPawel Jakub Dawidek 1010*fc7e71c5SEnji Cooper pfh = pidfile_open(ggated_pidfile, 0600, &otherpid); 1011*fc7e71c5SEnji Cooper if (pfh == NULL) { 1012*fc7e71c5SEnji Cooper if (errno == EEXIST) { 1013*fc7e71c5SEnji Cooper errx(EXIT_FAILURE, "Daemon already running, pid: %jd.", 1014*fc7e71c5SEnji Cooper (intmax_t)otherpid); 1015*fc7e71c5SEnji Cooper } 1016*fc7e71c5SEnji Cooper err(EXIT_FAILURE, "Cannot open/create pidfile"); 1017*fc7e71c5SEnji Cooper } 1018*fc7e71c5SEnji Cooper 1019d1d669bdSPawel Jakub Dawidek if (!g_gate_verbose) { 1020d1d669bdSPawel Jakub Dawidek /* Run in daemon mode. */ 1021122abe03SPawel Jakub Dawidek if (daemon(0, 0) == -1) 10227be67fe3SPawel Jakub Dawidek g_gate_xlog("Cannot daemonize: %s", strerror(errno)); 1023d1d669bdSPawel Jakub Dawidek } 1024d1d669bdSPawel Jakub Dawidek 1025*fc7e71c5SEnji Cooper pidfile_write(pfh); 1026*fc7e71c5SEnji Cooper 1027d1d669bdSPawel Jakub Dawidek signal(SIGCHLD, SIG_IGN); 1028d1d669bdSPawel Jakub Dawidek 1029d1d669bdSPawel Jakub Dawidek sfd = socket(AF_INET, SOCK_STREAM, 0); 1030122abe03SPawel Jakub Dawidek if (sfd == -1) 10317be67fe3SPawel Jakub Dawidek g_gate_xlog("Cannot open stream socket: %s.", strerror(errno)); 1032d1d669bdSPawel Jakub Dawidek bzero(&serv, sizeof(serv)); 1033d1d669bdSPawel Jakub Dawidek serv.sin_family = AF_INET; 1034d1d669bdSPawel Jakub Dawidek serv.sin_addr.s_addr = bindaddr; 1035d1d669bdSPawel Jakub Dawidek serv.sin_port = htons(port); 10367be67fe3SPawel Jakub Dawidek 10377be67fe3SPawel Jakub Dawidek g_gate_socket_settings(sfd); 10387be67fe3SPawel Jakub Dawidek 1039122abe03SPawel Jakub Dawidek if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) 1040d1d669bdSPawel Jakub Dawidek g_gate_xlog("bind(): %s.", strerror(errno)); 1041122abe03SPawel Jakub Dawidek if (listen(sfd, 5) == -1) 1042d1d669bdSPawel Jakub Dawidek g_gate_xlog("listen(): %s.", strerror(errno)); 1043d1d669bdSPawel Jakub Dawidek 1044d1d669bdSPawel Jakub Dawidek g_gate_log(LOG_INFO, "Listen on port: %d.", port); 1045d1d669bdSPawel Jakub Dawidek 1046d1d669bdSPawel Jakub Dawidek signal(SIGHUP, huphandler); 1047d1d669bdSPawel Jakub Dawidek 1048d1d669bdSPawel Jakub Dawidek for (;;) { 1049d1d669bdSPawel Jakub Dawidek fromlen = sizeof(from); 1050d1d669bdSPawel Jakub Dawidek tmpsfd = accept(sfd, &from, &fromlen); 1051122abe03SPawel Jakub Dawidek if (tmpsfd == -1) 1052d1d669bdSPawel Jakub Dawidek g_gate_xlog("accept(): %s.", strerror(errno)); 1053d1d669bdSPawel Jakub Dawidek 1054d1d669bdSPawel Jakub Dawidek if (got_sighup) { 1055d1d669bdSPawel Jakub Dawidek got_sighup = 0; 1056d1d669bdSPawel Jakub Dawidek exports_get(); 1057d1d669bdSPawel Jakub Dawidek } 1058d1d669bdSPawel Jakub Dawidek 10597be67fe3SPawel Jakub Dawidek if (!handshake(&from, tmpsfd)) 1060d1d669bdSPawel Jakub Dawidek close(tmpsfd); 1061d1d669bdSPawel Jakub Dawidek } 1062d1d669bdSPawel Jakub Dawidek close(sfd); 1063*fc7e71c5SEnji Cooper pidfile_remove(pfh); 1064d1d669bdSPawel Jakub Dawidek exit(EXIT_SUCCESS); 1065d1d669bdSPawel Jakub Dawidek } 1066