xref: /freebsd/sbin/ggate/ggated/ggated.c (revision 1b1e392aed4957a38c49599512b4f65b844a0772)
1d1d669bdSPawel Jakub Dawidek /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
31de7b4b8SPedro F. Giffuni  *
4d1d669bdSPawel Jakub Dawidek  * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5d1d669bdSPawel Jakub Dawidek  * All rights reserved.
6d1d669bdSPawel Jakub Dawidek  *
7d1d669bdSPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
8d1d669bdSPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
9d1d669bdSPawel Jakub Dawidek  * are met:
10d1d669bdSPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
11d1d669bdSPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
12d1d669bdSPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
13d1d669bdSPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
14d1d669bdSPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
15d1d669bdSPawel Jakub Dawidek  *
16d1d669bdSPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17d1d669bdSPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d1d669bdSPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d1d669bdSPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20d1d669bdSPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d1d669bdSPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d1d669bdSPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d1d669bdSPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d1d669bdSPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d1d669bdSPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d1d669bdSPawel Jakub Dawidek  * SUCH DAMAGE.
27d1d669bdSPawel Jakub Dawidek  */
28d1d669bdSPawel Jakub Dawidek 
29d1d669bdSPawel Jakub Dawidek #include <sys/param.h>
30fc7e71c5SEnji Cooper #include <sys/bio.h>
31fc7e71c5SEnji Cooper #include <sys/disk.h>
32d1d669bdSPawel Jakub Dawidek #include <sys/endian.h>
33d1d669bdSPawel Jakub Dawidek #include <sys/ioctl.h>
34fc7e71c5SEnji Cooper #include <sys/queue.h>
35fc7e71c5SEnji Cooper #include <sys/socket.h>
36d1d669bdSPawel Jakub Dawidek #include <sys/stat.h>
37b34d2de0SBruce Evans #include <sys/time.h>
38fc7e71c5SEnji 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>
44278847aeSMark Johnston #include <inttypes.h>
45fc7e71c5SEnji Cooper #include <fcntl.h>
46d1d669bdSPawel Jakub Dawidek #include <libgen.h>
47fc7e71c5SEnji Cooper #include <libutil.h>
48fc7e71c5SEnji Cooper #include <paths.h>
49fc7e71c5SEnji Cooper #include <pthread.h>
50fc7e71c5SEnji Cooper #include <signal.h>
51d1d669bdSPawel Jakub Dawidek #include <stdarg.h>
52fc7e71c5SEnji Cooper #include <stdio.h>
53fc7e71c5SEnji Cooper #include <stdlib.h>
54fc7e71c5SEnji Cooper #include <stdint.h>
55fc7e71c5SEnji Cooper #include <string.h>
56fc7e71c5SEnji Cooper #include <syslog.h>
57fc7e71c5SEnji Cooper #include <unistd.h>
58d1d669bdSPawel Jakub Dawidek 
59d1d669bdSPawel Jakub Dawidek #include "ggate.h"
60d1d669bdSPawel Jakub Dawidek 
61d1d669bdSPawel Jakub Dawidek 
627be67fe3SPawel Jakub Dawidek #define	GGATED_EXPORT_FILE	"/etc/gg.exports"
63d1d669bdSPawel Jakub Dawidek 
647be67fe3SPawel Jakub Dawidek struct ggd_connection {
657be67fe3SPawel Jakub Dawidek 	off_t		 c_mediasize;
6615c7f46bSPawel Jakub Dawidek 	unsigned	 c_sectorsize;
67*1b1e392aSDavid E. Cross 	int		 c_flags;	/* flags (RO/RW) */
687be67fe3SPawel Jakub Dawidek 	int		 c_diskfd;
697be67fe3SPawel Jakub Dawidek 	int		 c_sendfd;
707be67fe3SPawel Jakub Dawidek 	int		 c_recvfd;
717be67fe3SPawel Jakub Dawidek 	time_t		 c_birthtime;
727be67fe3SPawel Jakub Dawidek 	char		*c_path;
737be67fe3SPawel Jakub Dawidek 	uint64_t	 c_token;
747be67fe3SPawel Jakub Dawidek 	in_addr_t	 c_srcip;
757be67fe3SPawel Jakub Dawidek 	LIST_ENTRY(ggd_connection) c_next;
767be67fe3SPawel Jakub Dawidek };
77d1d669bdSPawel Jakub Dawidek 
787be67fe3SPawel Jakub Dawidek struct ggd_request {
797be67fe3SPawel Jakub Dawidek 	struct g_gate_hdr	 r_hdr;
807be67fe3SPawel Jakub Dawidek 	char			*r_data;
817be67fe3SPawel Jakub Dawidek 	TAILQ_ENTRY(ggd_request) r_next;
827be67fe3SPawel Jakub Dawidek };
837be67fe3SPawel Jakub Dawidek #define	r_cmd		r_hdr.gh_cmd
847be67fe3SPawel Jakub Dawidek #define	r_offset	r_hdr.gh_offset
857be67fe3SPawel Jakub Dawidek #define	r_length	r_hdr.gh_length
867be67fe3SPawel Jakub Dawidek #define	r_error		r_hdr.gh_error
877be67fe3SPawel Jakub Dawidek 
88*1b1e392aSDavid E. Cross #define EFLAGS_RDONLY	0x0000
89*1b1e392aSDavid E. Cross #define EFLAGS_WRONLY	0x0001
90*1b1e392aSDavid E. Cross #define EFLAGS_RDWR	0x0002
91*1b1e392aSDavid E. Cross #define EFLAGS_ACCMODE	0x0003
92*1b1e392aSDavid E. Cross #define EFLAGS_DIRECT	0x0004
93*1b1e392aSDavid E. Cross #define EFLAGS_NODIRECT	0x0008
94*1b1e392aSDavid E. Cross 
957be67fe3SPawel Jakub Dawidek struct ggd_export {
96d1d669bdSPawel Jakub Dawidek 	char		*e_path;	/* path to device/file */
97d1d669bdSPawel Jakub Dawidek 	in_addr_t	 e_ip;		/* remote IP address */
98d1d669bdSPawel Jakub Dawidek 	in_addr_t	 e_mask;	/* IP mask */
99*1b1e392aSDavid E. Cross 	int		 e_flags;	/* flags (WO/RO/RW/DIRECT/NODIRECT) */
1007be67fe3SPawel Jakub Dawidek 	SLIST_ENTRY(ggd_export) e_next;
101d1d669bdSPawel Jakub Dawidek };
1027be67fe3SPawel Jakub Dawidek 
1037be67fe3SPawel Jakub Dawidek static const char *exports_file = GGATED_EXPORT_FILE;
1047be67fe3SPawel Jakub Dawidek static int got_sighup = 0;
105ae824d80SEd Schouten static in_addr_t bindaddr;
1067be67fe3SPawel Jakub Dawidek 
1077be67fe3SPawel Jakub Dawidek static TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue);
1087be67fe3SPawel Jakub Dawidek static TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue);
109ae824d80SEd Schouten static pthread_mutex_t inqueue_mtx, outqueue_mtx;
110ae824d80SEd Schouten static pthread_cond_t inqueue_cond, outqueue_cond;
1117be67fe3SPawel Jakub Dawidek 
11213e403fdSAntoine Brodin static SLIST_HEAD(, ggd_export) exports = SLIST_HEAD_INITIALIZER(exports);
11313e403fdSAntoine Brodin static LIST_HEAD(, ggd_connection) connections = LIST_HEAD_INITIALIZER(connections);
1147be67fe3SPawel Jakub Dawidek 
1157be67fe3SPawel Jakub Dawidek static void *recv_thread(void *arg);
1167be67fe3SPawel Jakub Dawidek static void *disk_thread(void *arg);
1177be67fe3SPawel Jakub Dawidek static void *send_thread(void *arg);
118d1d669bdSPawel Jakub Dawidek 
119d1d669bdSPawel Jakub Dawidek static void
usage(void)120d1d669bdSPawel Jakub Dawidek usage(void)
121d1d669bdSPawel Jakub Dawidek {
122d1d669bdSPawel Jakub Dawidek 
123fc7e71c5SEnji Cooper 	fprintf(stderr, "usage: %s [-nv] [-a address] [-F pidfile] [-p port] "
124fc7e71c5SEnji Cooper 	    "[-R rcvbuf] [-S sndbuf] [exports file]\n", getprogname());
125d1d669bdSPawel Jakub Dawidek 	exit(EXIT_FAILURE);
126d1d669bdSPawel Jakub Dawidek }
127d1d669bdSPawel Jakub Dawidek 
128d1d669bdSPawel Jakub Dawidek static char *
ip2str(in_addr_t ip)129d1d669bdSPawel Jakub Dawidek ip2str(in_addr_t ip)
130d1d669bdSPawel Jakub Dawidek {
131d1d669bdSPawel Jakub Dawidek 	static char sip[16];
132d1d669bdSPawel Jakub Dawidek 
133d1d669bdSPawel Jakub Dawidek 	snprintf(sip, sizeof(sip), "%u.%u.%u.%u",
134d1d669bdSPawel Jakub Dawidek 	    ((ip >> 24) & 0xff),
135d1d669bdSPawel Jakub Dawidek 	    ((ip >> 16) & 0xff),
136d1d669bdSPawel Jakub Dawidek 	    ((ip >> 8) & 0xff),
137d1d669bdSPawel Jakub Dawidek 	    (ip & 0xff));
138d1d669bdSPawel Jakub Dawidek 	return (sip);
139d1d669bdSPawel Jakub Dawidek }
140d1d669bdSPawel Jakub Dawidek 
141d1d669bdSPawel Jakub Dawidek static in_addr_t
countmask(unsigned m)142d1d669bdSPawel Jakub Dawidek countmask(unsigned m)
143d1d669bdSPawel Jakub Dawidek {
144d1d669bdSPawel Jakub Dawidek 	in_addr_t mask;
145d1d669bdSPawel Jakub Dawidek 
146d1d669bdSPawel Jakub Dawidek 	if (m == 0) {
147d1d669bdSPawel Jakub Dawidek 		mask = 0x0;
148d1d669bdSPawel Jakub Dawidek 	} else {
149d1d669bdSPawel Jakub Dawidek 		mask = 1 << (32 - m);
150d1d669bdSPawel Jakub Dawidek 		mask--;
151d1d669bdSPawel Jakub Dawidek 		mask = ~mask;
152d1d669bdSPawel Jakub Dawidek 	}
153d1d669bdSPawel Jakub Dawidek 	return (mask);
154d1d669bdSPawel Jakub Dawidek }
155d1d669bdSPawel Jakub Dawidek 
156*1b1e392aSDavid E. Cross static int
parse_flags(const char * flagsstr,int lineno)157*1b1e392aSDavid E. Cross parse_flags(const char *flagsstr, int lineno)
158*1b1e392aSDavid E. Cross {
159*1b1e392aSDavid E. Cross 	char *flagscpy;
160*1b1e392aSDavid E. Cross 	char *word, *brkf;
161*1b1e392aSDavid E. Cross 	int access_flags = -1;
162*1b1e392aSDavid E. Cross 	int direct_flags = 0;
163*1b1e392aSDavid E. Cross 
164*1b1e392aSDavid E. Cross 	flagscpy = strdup(flagsstr);
165*1b1e392aSDavid E. Cross 	if (flagscpy == NULL) {
166*1b1e392aSDavid E. Cross 		g_gate_xlog("Not enough memory.");
167*1b1e392aSDavid E. Cross 	}
168*1b1e392aSDavid E. Cross 
169*1b1e392aSDavid E. Cross 	for (word = strtok_r(flagscpy, ",", &brkf);
170*1b1e392aSDavid E. Cross 	     word != NULL;
171*1b1e392aSDavid E. Cross 	     word = strtok_r(NULL, ",", &brkf)) {
172*1b1e392aSDavid E. Cross 		if (strcasecmp("ro", word) == 0 ||
173*1b1e392aSDavid E. Cross 		    strcasecmp("rd", word) == 0) {
174*1b1e392aSDavid E. Cross 			access_flags = EFLAGS_RDONLY;
175*1b1e392aSDavid E. Cross 		} else if (strcasecmp("wo", word) == 0) {
176*1b1e392aSDavid E. Cross 			access_flags = EFLAGS_WRONLY;
177*1b1e392aSDavid E. Cross 		} else if (strcasecmp("rw", word) == 0) {
178*1b1e392aSDavid E. Cross 			access_flags = EFLAGS_RDWR;
179*1b1e392aSDavid E. Cross 		} else if (strcasecmp("direct", word) == 0) {
180*1b1e392aSDavid E. Cross 			direct_flags = EFLAGS_DIRECT;
181*1b1e392aSDavid E. Cross 		} else if (strcasecmp("nodirect", word) == 0) {
182*1b1e392aSDavid E. Cross 			direct_flags = EFLAGS_NODIRECT;
183*1b1e392aSDavid E. Cross 		} else {
184*1b1e392aSDavid E. Cross 			g_gate_xlog("Invalid value (%s) in flags field at "
185*1b1e392aSDavid E. Cross                             "line %u.", word, lineno);
186*1b1e392aSDavid E. Cross 		}
187*1b1e392aSDavid E. Cross 	}
188*1b1e392aSDavid E. Cross 	free(flagscpy);
189*1b1e392aSDavid E. Cross 	if (access_flags == -1) {
190*1b1e392aSDavid E. Cross 		g_gate_xlog("Invalid value (%s) in flags field at "
191*1b1e392aSDavid E. Cross 		    "line %u.", flagsstr, lineno);
192*1b1e392aSDavid E. Cross 	}
193*1b1e392aSDavid E. Cross 	return (direct_flags | access_flags);
194*1b1e392aSDavid E. Cross }
195*1b1e392aSDavid E. Cross 
196d1d669bdSPawel Jakub Dawidek static void
line_parse(char * line,unsigned lineno)197d1d669bdSPawel Jakub Dawidek line_parse(char *line, unsigned lineno)
198d1d669bdSPawel Jakub Dawidek {
1997be67fe3SPawel Jakub Dawidek 	struct ggd_export *ex;
200*1b1e392aSDavid E. Cross 	char *word, *path, *sflags, *brkl;
201*1b1e392aSDavid E. Cross 	unsigned i, vmask;
202*1b1e392aSDavid E. Cross 	int flags;
203d1d669bdSPawel Jakub Dawidek 	in_addr_t ip, mask;
204d1d669bdSPawel Jakub Dawidek 
205d1d669bdSPawel Jakub Dawidek 	ip = mask = flags = vmask = 0;
206d1d669bdSPawel Jakub Dawidek 	path = NULL;
207d1d669bdSPawel Jakub Dawidek 	sflags = NULL;
208d1d669bdSPawel Jakub Dawidek 
209*1b1e392aSDavid E. Cross 	for (i = 0, word = strtok_r(line, " \t", &brkl); word != NULL;
210*1b1e392aSDavid E. Cross 	    i++, word = strtok_r(NULL, " \t", &brkl)) {
211d1d669bdSPawel Jakub Dawidek 		switch (i) {
212d1d669bdSPawel Jakub Dawidek 		case 0: /* IP address or host name */
213d1d669bdSPawel Jakub Dawidek 			ip = g_gate_str2ip(strsep(&word, "/"));
214d1d669bdSPawel Jakub Dawidek 			if (ip == INADDR_NONE) {
215d1d669bdSPawel Jakub Dawidek 				g_gate_xlog("Invalid IP/host name at line %u.",
216d1d669bdSPawel Jakub Dawidek 				    lineno);
217d1d669bdSPawel Jakub Dawidek 			}
218d1d669bdSPawel Jakub Dawidek 			ip = ntohl(ip);
219d1d669bdSPawel Jakub Dawidek 			if (word == NULL)
220d1d669bdSPawel Jakub Dawidek 				vmask = 32;
221d1d669bdSPawel Jakub Dawidek 			else {
222d1d669bdSPawel Jakub Dawidek 				errno = 0;
223d1d669bdSPawel Jakub Dawidek 				vmask = strtoul(word, NULL, 10);
224d1d669bdSPawel Jakub Dawidek 				if (vmask == 0 && errno != 0) {
225d1d669bdSPawel Jakub Dawidek 					g_gate_xlog("Invalid IP mask value at "
226d1d669bdSPawel Jakub Dawidek 					    "line %u.", lineno);
227d1d669bdSPawel Jakub Dawidek 				}
228d1d669bdSPawel Jakub Dawidek 				if ((unsigned)vmask > 32) {
229d1d669bdSPawel Jakub Dawidek 					g_gate_xlog("Invalid IP mask value at line %u.",
230d1d669bdSPawel Jakub Dawidek 					    lineno);
231d1d669bdSPawel Jakub Dawidek 				}
232d1d669bdSPawel Jakub Dawidek 			}
233d1d669bdSPawel Jakub Dawidek 			mask = countmask(vmask);
234d1d669bdSPawel Jakub Dawidek 			break;
235d1d669bdSPawel Jakub Dawidek 		case 1:	/* flags */
236*1b1e392aSDavid E. Cross 			flags = parse_flags(word, lineno);
237d1d669bdSPawel Jakub Dawidek 			sflags = word;
238d1d669bdSPawel Jakub Dawidek 			break;
239d1d669bdSPawel Jakub Dawidek 		case 2:	/* path */
240d1d669bdSPawel Jakub Dawidek 			if (strlen(word) >= MAXPATHLEN) {
241d1d669bdSPawel Jakub Dawidek 				g_gate_xlog("Path too long at line %u. ",
242d1d669bdSPawel Jakub Dawidek 				    lineno);
243d1d669bdSPawel Jakub Dawidek 			}
244d1d669bdSPawel Jakub Dawidek 			path = word;
245d1d669bdSPawel Jakub Dawidek 			break;
246d1d669bdSPawel Jakub Dawidek 		default:
247d1d669bdSPawel Jakub Dawidek 			g_gate_xlog("Too many arguments at line %u. ", lineno);
248d1d669bdSPawel Jakub Dawidek 		}
249d1d669bdSPawel Jakub Dawidek 	}
250d1d669bdSPawel Jakub Dawidek 	if (i != 3)
251d1d669bdSPawel Jakub Dawidek 		g_gate_xlog("Too few arguments at line %u.", lineno);
252d1d669bdSPawel Jakub Dawidek 
253d1d669bdSPawel Jakub Dawidek 	ex = malloc(sizeof(*ex));
254d1d669bdSPawel Jakub Dawidek 	if (ex == NULL)
255032de3f9SOleksandr Tymoshenko 		g_gate_xlog("Not enough memory.");
256d1d669bdSPawel Jakub Dawidek 	ex->e_path = strdup(path);
257d1d669bdSPawel Jakub Dawidek 	if (ex->e_path == NULL)
258032de3f9SOleksandr Tymoshenko 		g_gate_xlog("Not enough memory.");
259d1d669bdSPawel Jakub Dawidek 
260d1d669bdSPawel Jakub Dawidek 	/* Made 'and' here. */
261d1d669bdSPawel Jakub Dawidek 	ex->e_ip = (ip & mask);
262d1d669bdSPawel Jakub Dawidek 	ex->e_mask = mask;
263d1d669bdSPawel Jakub Dawidek 	ex->e_flags = flags;
264d1d669bdSPawel Jakub Dawidek 
2657be67fe3SPawel Jakub Dawidek 	SLIST_INSERT_HEAD(&exports, ex, e_next);
266d1d669bdSPawel Jakub Dawidek 
267d1d669bdSPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.",
268d1d669bdSPawel Jakub Dawidek 	    ip2str(ex->e_ip), vmask, path, sflags);
269d1d669bdSPawel Jakub Dawidek }
270d1d669bdSPawel Jakub Dawidek 
271d1d669bdSPawel Jakub Dawidek static void
exports_clear(void)272d1d669bdSPawel Jakub Dawidek exports_clear(void)
273d1d669bdSPawel Jakub Dawidek {
2747be67fe3SPawel Jakub Dawidek 	struct ggd_export *ex;
275d1d669bdSPawel Jakub Dawidek 
2767be67fe3SPawel Jakub Dawidek 	while (!SLIST_EMPTY(&exports)) {
2777be67fe3SPawel Jakub Dawidek 		ex = SLIST_FIRST(&exports);
2787be67fe3SPawel Jakub Dawidek 		SLIST_REMOVE_HEAD(&exports, e_next);
279d1d669bdSPawel Jakub Dawidek 		free(ex);
280d1d669bdSPawel Jakub Dawidek 	}
281d1d669bdSPawel Jakub Dawidek }
282d1d669bdSPawel Jakub Dawidek 
283d1d669bdSPawel Jakub Dawidek #define	EXPORTS_LINE_SIZE	2048
284d1d669bdSPawel Jakub Dawidek static void
exports_get(void)285d1d669bdSPawel Jakub Dawidek exports_get(void)
286d1d669bdSPawel Jakub Dawidek {
287d1d669bdSPawel Jakub Dawidek 	char buf[EXPORTS_LINE_SIZE], *line;
288d1d669bdSPawel Jakub Dawidek 	unsigned lineno = 0, objs = 0, len;
289d1d669bdSPawel Jakub Dawidek 	FILE *fd;
290d1d669bdSPawel Jakub Dawidek 
291d1d669bdSPawel Jakub Dawidek 	exports_clear();
292d1d669bdSPawel Jakub Dawidek 
2937be67fe3SPawel Jakub Dawidek 	fd = fopen(exports_file, "r");
294d1d669bdSPawel Jakub Dawidek 	if (fd == NULL) {
2957be67fe3SPawel Jakub Dawidek 		g_gate_xlog("Cannot open exports file (%s): %s.", exports_file,
296d1d669bdSPawel Jakub Dawidek 		    strerror(errno));
297d1d669bdSPawel Jakub Dawidek 	}
298d1d669bdSPawel Jakub Dawidek 
2997be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_INFO, "Reading exports file (%s).", exports_file);
300d1d669bdSPawel Jakub Dawidek 
301d1d669bdSPawel Jakub Dawidek 	for (;;) {
302d1d669bdSPawel Jakub Dawidek 		if (fgets(buf, sizeof(buf), fd) == NULL) {
303d1d669bdSPawel Jakub Dawidek 			if (feof(fd))
304d1d669bdSPawel Jakub Dawidek 				break;
305d1d669bdSPawel Jakub Dawidek 
306d1d669bdSPawel Jakub Dawidek 			g_gate_xlog("Error while reading exports file: %s.",
307d1d669bdSPawel Jakub Dawidek 			    strerror(errno));
308d1d669bdSPawel Jakub Dawidek 		}
309d1d669bdSPawel Jakub Dawidek 
310d1d669bdSPawel Jakub Dawidek 		/* Increase line count. */
311d1d669bdSPawel Jakub Dawidek 		lineno++;
312d1d669bdSPawel Jakub Dawidek 
313d1d669bdSPawel Jakub Dawidek 		/* Skip spaces and tabs. */
314d1d669bdSPawel Jakub Dawidek 		for (line = buf; *line == ' ' || *line == '\t'; ++line)
315d1d669bdSPawel Jakub Dawidek 			;
316d1d669bdSPawel Jakub Dawidek 
317d1d669bdSPawel Jakub Dawidek 		/* Empty line, comment or empty line at the end of file. */
318d1d669bdSPawel Jakub Dawidek 		if (*line == '\n' || *line == '#' || *line == '\0')
319d1d669bdSPawel Jakub Dawidek 			continue;
320d1d669bdSPawel Jakub Dawidek 
321d1d669bdSPawel Jakub Dawidek 		len = strlen(line);
322d1d669bdSPawel Jakub Dawidek 		if (line[len - 1] == '\n') {
323d1d669bdSPawel Jakub Dawidek 			/* Remove new line char. */
324d1d669bdSPawel Jakub Dawidek 			line[len - 1] = '\0';
325d1d669bdSPawel Jakub Dawidek 		} else {
326d1d669bdSPawel Jakub Dawidek 			if (!feof(fd))
327d1d669bdSPawel Jakub Dawidek 				g_gate_xlog("Line %u too long.", lineno);
328d1d669bdSPawel Jakub Dawidek 		}
329d1d669bdSPawel Jakub Dawidek 
330d1d669bdSPawel Jakub Dawidek 		line_parse(line, lineno);
331d1d669bdSPawel Jakub Dawidek 		objs++;
332d1d669bdSPawel Jakub Dawidek 	}
333d1d669bdSPawel Jakub Dawidek 
334d1d669bdSPawel Jakub Dawidek 	fclose(fd);
335d1d669bdSPawel Jakub Dawidek 
336d1d669bdSPawel Jakub Dawidek 	if (objs == 0)
337d1d669bdSPawel Jakub Dawidek 		g_gate_xlog("There are no objects to export.");
338d1d669bdSPawel Jakub Dawidek 
339d1d669bdSPawel Jakub Dawidek 	g_gate_log(LOG_INFO, "Exporting %u object(s).", objs);
340d1d669bdSPawel Jakub Dawidek }
341d1d669bdSPawel Jakub Dawidek 
3427be67fe3SPawel Jakub Dawidek static int
exports_check(struct ggd_export * ex,struct g_gate_cinit * cinit,struct ggd_connection * conn)3437be67fe3SPawel Jakub Dawidek exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit,
3447be67fe3SPawel Jakub Dawidek     struct ggd_connection *conn)
345d1d669bdSPawel Jakub Dawidek {
3467be67fe3SPawel Jakub Dawidek 	char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */
347*1b1e392aSDavid E. Cross 	int error = 0, flags, access_flags, direct_flags = 0;
3487be67fe3SPawel Jakub Dawidek 
3497be67fe3SPawel Jakub Dawidek 	strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask));
3507be67fe3SPawel Jakub Dawidek 	strlcat(ipmask, "/", sizeof(ipmask));
3517be67fe3SPawel Jakub Dawidek 	strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask));
352*1b1e392aSDavid E. Cross 
353*1b1e392aSDavid E. Cross 	access_flags = ex->e_flags & EFLAGS_ACCMODE;
354*1b1e392aSDavid E. Cross 
3557be67fe3SPawel Jakub Dawidek 	if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) {
356*1b1e392aSDavid E. Cross 		if (access_flags == EFLAGS_WRONLY) {
3577be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_WARNING, "Read-only access requested, "
3587be67fe3SPawel Jakub Dawidek 			    "but %s (%s) is exported write-only.", ex->e_path,
3597be67fe3SPawel Jakub Dawidek 			    ipmask);
3607be67fe3SPawel Jakub Dawidek 			return (EPERM);
3617be67fe3SPawel Jakub Dawidek 		} else {
3627be67fe3SPawel Jakub Dawidek 			conn->c_flags |= GGATE_FLAG_RDONLY;
3637be67fe3SPawel Jakub Dawidek 		}
3647be67fe3SPawel Jakub Dawidek 	} else if ((cinit->gc_flags & GGATE_FLAG_WRONLY) != 0) {
365*1b1e392aSDavid E. Cross 		if (access_flags == EFLAGS_RDONLY) {
3667be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_WARNING, "Write-only access requested, "
3677be67fe3SPawel Jakub Dawidek 			    "but %s (%s) is exported read-only.", ex->e_path,
3687be67fe3SPawel Jakub Dawidek 			    ipmask);
3697be67fe3SPawel Jakub Dawidek 			return (EPERM);
3707be67fe3SPawel Jakub Dawidek 		} else {
3717be67fe3SPawel Jakub Dawidek 			conn->c_flags |= GGATE_FLAG_WRONLY;
3727be67fe3SPawel Jakub Dawidek 		}
3737be67fe3SPawel Jakub Dawidek 	} else {
374*1b1e392aSDavid E. Cross 		if (access_flags == EFLAGS_RDONLY) {
3757be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_WARNING, "Read-write access requested, "
3767be67fe3SPawel Jakub Dawidek 			    "but %s (%s) is exported read-only.", ex->e_path,
3777be67fe3SPawel Jakub Dawidek 			    ipmask);
3787be67fe3SPawel Jakub Dawidek 			return (EPERM);
379*1b1e392aSDavid E. Cross 		} else if (access_flags == EFLAGS_WRONLY) {
3807be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_WARNING, "Read-write access requested, "
3817be67fe3SPawel Jakub Dawidek 			    "but %s (%s) is exported write-only.", ex->e_path,
3827be67fe3SPawel Jakub Dawidek 			    ipmask);
3837be67fe3SPawel Jakub Dawidek 			return (EPERM);
3847be67fe3SPawel Jakub Dawidek 		}
3857be67fe3SPawel Jakub Dawidek 	}
386*1b1e392aSDavid E. Cross 
387*1b1e392aSDavid E. Cross 	if ((cinit->gc_flags & GGATE_FLAG_DIRECT) != 0) {
388*1b1e392aSDavid E. Cross 		if (ex->e_flags & EFLAGS_NODIRECT) {
389*1b1e392aSDavid E. Cross 			g_gate_log(LOG_WARNING, "Direct IO requested, "
390*1b1e392aSDavid E. Cross 			    "but %s (%s) is exported NODIRECT.", ex->e_path,
391*1b1e392aSDavid E. Cross 			    ipmask);
392*1b1e392aSDavid E. Cross 		} else {
393*1b1e392aSDavid E. Cross 			conn->c_flags |= GGATE_FLAG_DIRECT;
394*1b1e392aSDavid E. Cross 			direct_flags = O_DIRECT;
395*1b1e392aSDavid E. Cross 		}
396*1b1e392aSDavid E. Cross 	}
397*1b1e392aSDavid E. Cross 
398*1b1e392aSDavid E. Cross 	if (ex->e_flags & EFLAGS_DIRECT) {
399*1b1e392aSDavid E. Cross 		direct_flags = O_DIRECT;
400*1b1e392aSDavid E. Cross 	}
401*1b1e392aSDavid E. Cross 
4027be67fe3SPawel Jakub Dawidek 	if ((conn->c_flags & GGATE_FLAG_RDONLY) != 0)
4037be67fe3SPawel Jakub Dawidek 		flags = O_RDONLY;
4047be67fe3SPawel Jakub Dawidek 	else if ((conn->c_flags & GGATE_FLAG_WRONLY) != 0)
4057be67fe3SPawel Jakub Dawidek 		flags = O_WRONLY;
4067be67fe3SPawel Jakub Dawidek 	else
4077be67fe3SPawel Jakub Dawidek 		flags = O_RDWR;
408*1b1e392aSDavid E. Cross 	flags |= direct_flags;
4090a01415eSMark Johnston 	if (conn->c_diskfd != -1) {
4100a01415eSMark Johnston 		if (strcmp(conn->c_path, ex->e_path) != 0) {
4110a01415eSMark Johnston 			g_gate_log(LOG_ERR, "old %s and new %s: "
4120a01415eSMark Johnston 			    "Path mismatch during handshakes.",
4130a01415eSMark Johnston 			    conn->c_path, ex->e_path);
4140a01415eSMark Johnston 			return (EPERM);
4150a01415eSMark Johnston 		}
4160a01415eSMark Johnston 		return (0);
4170a01415eSMark Johnston 	}
4180a01415eSMark Johnston 
4197be67fe3SPawel Jakub Dawidek 	conn->c_diskfd = open(ex->e_path, flags);
4207be67fe3SPawel Jakub Dawidek 	if (conn->c_diskfd == -1) {
4217be67fe3SPawel Jakub Dawidek 		error = errno;
4227be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_ERR, "Cannot open %s: %s.", ex->e_path,
4237be67fe3SPawel Jakub Dawidek 		    strerror(error));
4247be67fe3SPawel Jakub Dawidek 		return (error);
4257be67fe3SPawel Jakub Dawidek 	}
4267be67fe3SPawel Jakub Dawidek 	return (0);
4277be67fe3SPawel Jakub Dawidek }
4287be67fe3SPawel Jakub Dawidek 
4297be67fe3SPawel Jakub Dawidek static struct ggd_export *
exports_find(struct sockaddr * s,struct g_gate_cinit * cinit,struct ggd_connection * conn)4307be67fe3SPawel Jakub Dawidek exports_find(struct sockaddr *s, struct g_gate_cinit *cinit,
4317be67fe3SPawel Jakub Dawidek     struct ggd_connection *conn)
4327be67fe3SPawel Jakub Dawidek {
4337be67fe3SPawel Jakub Dawidek 	struct ggd_export *ex;
4347be67fe3SPawel Jakub Dawidek 	in_addr_t ip;
4357be67fe3SPawel Jakub Dawidek 	int error;
4367be67fe3SPawel Jakub Dawidek 
4377be67fe3SPawel Jakub Dawidek 	ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
4387be67fe3SPawel Jakub Dawidek 	SLIST_FOREACH(ex, &exports, e_next) {
4397be67fe3SPawel Jakub Dawidek 		if ((ip & ex->e_mask) != ex->e_ip) {
4407be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_DEBUG, "exports[%s]: IP mismatch.",
4417be67fe3SPawel Jakub Dawidek 			    ex->e_path);
4427be67fe3SPawel Jakub Dawidek 			continue;
4437be67fe3SPawel Jakub Dawidek 		}
4447be67fe3SPawel Jakub Dawidek 		if (strcmp(cinit->gc_path, ex->e_path) != 0) {
4457be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_DEBUG, "exports[%s]: Path mismatch.",
4467be67fe3SPawel Jakub Dawidek 			    ex->e_path);
4477be67fe3SPawel Jakub Dawidek 			continue;
4487be67fe3SPawel Jakub Dawidek 		}
4497be67fe3SPawel Jakub Dawidek 		error = exports_check(ex, cinit, conn);
4507be67fe3SPawel Jakub Dawidek 		if (error == 0)
4517be67fe3SPawel Jakub Dawidek 			return (ex);
4527be67fe3SPawel Jakub Dawidek 		else {
4537be67fe3SPawel Jakub Dawidek 			errno = error;
4547be67fe3SPawel Jakub Dawidek 			return (NULL);
4557be67fe3SPawel Jakub Dawidek 		}
4567be67fe3SPawel Jakub Dawidek 	}
4577be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_WARNING, "Unauthorized connection from: %s.",
4587be67fe3SPawel Jakub Dawidek 	    ip2str(ip));
4597be67fe3SPawel Jakub Dawidek 	errno = EPERM;
4607be67fe3SPawel Jakub Dawidek 	return (NULL);
4617be67fe3SPawel Jakub Dawidek }
4627be67fe3SPawel Jakub Dawidek 
4637be67fe3SPawel Jakub Dawidek /*
4647be67fe3SPawel Jakub Dawidek  * Remove timed out connections.
4657be67fe3SPawel Jakub Dawidek  */
4667be67fe3SPawel Jakub Dawidek static void
connection_cleanups(void)4677be67fe3SPawel Jakub Dawidek connection_cleanups(void)
4687be67fe3SPawel Jakub Dawidek {
4697be67fe3SPawel Jakub Dawidek 	struct ggd_connection *conn, *tconn;
4707be67fe3SPawel Jakub Dawidek 	time_t now;
4717be67fe3SPawel Jakub Dawidek 
4727be67fe3SPawel Jakub Dawidek 	time(&now);
4737be67fe3SPawel Jakub Dawidek 	LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) {
4747be67fe3SPawel Jakub Dawidek 		if (now - conn->c_birthtime > 10) {
4757be67fe3SPawel Jakub Dawidek 			LIST_REMOVE(conn, c_next);
4767be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_NOTICE,
4777be67fe3SPawel Jakub Dawidek 			    "Connection from %s [%s] removed.",
4787be67fe3SPawel Jakub Dawidek 			    ip2str(conn->c_srcip), conn->c_path);
4797be67fe3SPawel Jakub Dawidek 			close(conn->c_diskfd);
4807be67fe3SPawel Jakub Dawidek 			close(conn->c_sendfd);
4817be67fe3SPawel Jakub Dawidek 			close(conn->c_recvfd);
4827be67fe3SPawel Jakub Dawidek 			free(conn->c_path);
4837be67fe3SPawel Jakub Dawidek 			free(conn);
4847be67fe3SPawel Jakub Dawidek 		}
4857be67fe3SPawel Jakub Dawidek 	}
4867be67fe3SPawel Jakub Dawidek }
4877be67fe3SPawel Jakub Dawidek 
4887be67fe3SPawel Jakub Dawidek static struct ggd_connection *
connection_find(struct g_gate_cinit * cinit)4897be67fe3SPawel Jakub Dawidek connection_find(struct g_gate_cinit *cinit)
4907be67fe3SPawel Jakub Dawidek {
4917be67fe3SPawel Jakub Dawidek 	struct ggd_connection *conn;
4927be67fe3SPawel Jakub Dawidek 
4937be67fe3SPawel Jakub Dawidek 	LIST_FOREACH(conn, &connections, c_next) {
4947be67fe3SPawel Jakub Dawidek 		if (conn->c_token == cinit->gc_token)
4957be67fe3SPawel Jakub Dawidek 			break;
4967be67fe3SPawel Jakub Dawidek 	}
4977be67fe3SPawel Jakub Dawidek 	return (conn);
4987be67fe3SPawel Jakub Dawidek }
4997be67fe3SPawel Jakub Dawidek 
5007be67fe3SPawel Jakub Dawidek static struct ggd_connection *
connection_new(struct g_gate_cinit * cinit,struct sockaddr * s,int sfd)5017be67fe3SPawel Jakub Dawidek connection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd)
5027be67fe3SPawel Jakub Dawidek {
5037be67fe3SPawel Jakub Dawidek 	struct ggd_connection *conn;
5047be67fe3SPawel Jakub Dawidek 	in_addr_t ip;
5057be67fe3SPawel Jakub Dawidek 
5067be67fe3SPawel Jakub Dawidek 	/*
5077be67fe3SPawel Jakub Dawidek 	 * First, look for old connections.
5087be67fe3SPawel Jakub Dawidek 	 * We probably should do it every X seconds, but what for?
5097be67fe3SPawel Jakub Dawidek 	 * It is only dangerous if an attacker wants to overload connections
5107be67fe3SPawel Jakub Dawidek 	 * queue, so here is a good place to do the cleanups.
5117be67fe3SPawel Jakub Dawidek 	 */
5127be67fe3SPawel Jakub Dawidek 	connection_cleanups();
5137be67fe3SPawel Jakub Dawidek 
5147be67fe3SPawel Jakub Dawidek 	conn = malloc(sizeof(*conn));
5157be67fe3SPawel Jakub Dawidek 	if (conn == NULL)
5167be67fe3SPawel Jakub Dawidek 		return (NULL);
5177be67fe3SPawel Jakub Dawidek 	conn->c_path = strdup(cinit->gc_path);
5187be67fe3SPawel Jakub Dawidek 	if (conn->c_path == NULL) {
5197be67fe3SPawel Jakub Dawidek 		free(conn);
5207be67fe3SPawel Jakub Dawidek 		return (NULL);
5217be67fe3SPawel Jakub Dawidek 	}
5227be67fe3SPawel Jakub Dawidek 	conn->c_token = cinit->gc_token;
5237be67fe3SPawel Jakub Dawidek 	ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
5247be67fe3SPawel Jakub Dawidek 	conn->c_srcip = ip;
5250a01415eSMark Johnston 	conn->c_diskfd = conn->c_sendfd = conn->c_recvfd = -1;
5267be67fe3SPawel Jakub Dawidek 	if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0)
5277be67fe3SPawel Jakub Dawidek 		conn->c_sendfd = sfd;
5287be67fe3SPawel Jakub Dawidek 	else
5297be67fe3SPawel Jakub Dawidek 		conn->c_recvfd = sfd;
5307be67fe3SPawel Jakub Dawidek 	conn->c_mediasize = 0;
5317be67fe3SPawel Jakub Dawidek 	conn->c_sectorsize = 0;
5327be67fe3SPawel Jakub Dawidek 	time(&conn->c_birthtime);
5337be67fe3SPawel Jakub Dawidek 	conn->c_flags = cinit->gc_flags;
5347be67fe3SPawel Jakub Dawidek 	LIST_INSERT_HEAD(&connections, conn, c_next);
5357be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip),
5367be67fe3SPawel Jakub Dawidek 	    conn->c_path);
5377be67fe3SPawel Jakub Dawidek 	return (conn);
5387be67fe3SPawel Jakub Dawidek }
5397be67fe3SPawel Jakub Dawidek 
5407be67fe3SPawel Jakub Dawidek static int
connection_add(struct ggd_connection * conn,struct g_gate_cinit * cinit,struct sockaddr * s,int sfd)5417be67fe3SPawel Jakub Dawidek connection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit,
5427be67fe3SPawel Jakub Dawidek     struct sockaddr *s, int sfd)
5437be67fe3SPawel Jakub Dawidek {
544d1d669bdSPawel Jakub Dawidek 	in_addr_t ip;
545d1d669bdSPawel Jakub Dawidek 
54686bfa454SPawel Jakub Dawidek 	ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr);
5477be67fe3SPawel Jakub Dawidek 	if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) {
5487be67fe3SPawel Jakub Dawidek 		if (conn->c_sendfd != -1) {
5497be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_WARNING,
5507be67fe3SPawel Jakub Dawidek 			    "Send socket already exists [%s, %s].", ip2str(ip),
5517be67fe3SPawel Jakub Dawidek 			    conn->c_path);
5527be67fe3SPawel Jakub Dawidek 			return (EEXIST);
553d1d669bdSPawel Jakub Dawidek 		}
5547be67fe3SPawel Jakub Dawidek 		conn->c_sendfd = sfd;
5557be67fe3SPawel Jakub Dawidek 	} else {
5567be67fe3SPawel Jakub Dawidek 		if (conn->c_recvfd != -1) {
5577be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_WARNING,
5587be67fe3SPawel Jakub Dawidek 			    "Receive socket already exists [%s, %s].",
5597be67fe3SPawel Jakub Dawidek 			    ip2str(ip), conn->c_path);
5607be67fe3SPawel Jakub Dawidek 			return (EEXIST);
5617be67fe3SPawel Jakub Dawidek 		}
5627be67fe3SPawel Jakub Dawidek 		conn->c_recvfd = sfd;
5637be67fe3SPawel Jakub Dawidek 	}
5647be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(ip),
5657be67fe3SPawel Jakub Dawidek 	    conn->c_path);
5667be67fe3SPawel Jakub Dawidek 	return (0);
5677be67fe3SPawel Jakub Dawidek }
568d1d669bdSPawel Jakub Dawidek 
5697be67fe3SPawel Jakub Dawidek /*
5707be67fe3SPawel Jakub Dawidek  * Remove one socket from the given connection or the whole
5717be67fe3SPawel Jakub Dawidek  * connection if sfd == -1.
5727be67fe3SPawel Jakub Dawidek  */
5737be67fe3SPawel Jakub Dawidek static void
connection_remove(struct ggd_connection * conn)5747be67fe3SPawel Jakub Dawidek connection_remove(struct ggd_connection *conn)
5757be67fe3SPawel Jakub Dawidek {
5767be67fe3SPawel Jakub Dawidek 
5777be67fe3SPawel Jakub Dawidek 	LIST_REMOVE(conn, c_next);
5787be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Connection removed [%s %s].",
5797be67fe3SPawel Jakub Dawidek 	    ip2str(conn->c_srcip), conn->c_path);
5800a01415eSMark Johnston 	if (conn->c_diskfd != -1)
5810a01415eSMark Johnston 		close(conn->c_diskfd);
5827be67fe3SPawel Jakub Dawidek 	if (conn->c_sendfd != -1)
5837be67fe3SPawel Jakub Dawidek 		close(conn->c_sendfd);
5847be67fe3SPawel Jakub Dawidek 	if (conn->c_recvfd != -1)
5857be67fe3SPawel Jakub Dawidek 		close(conn->c_recvfd);
5867be67fe3SPawel Jakub Dawidek 	free(conn->c_path);
5877be67fe3SPawel Jakub Dawidek 	free(conn);
5887be67fe3SPawel Jakub Dawidek }
5897be67fe3SPawel Jakub Dawidek 
5907be67fe3SPawel Jakub Dawidek static int
connection_ready(struct ggd_connection * conn)5917be67fe3SPawel Jakub Dawidek connection_ready(struct ggd_connection *conn)
5927be67fe3SPawel Jakub Dawidek {
5937be67fe3SPawel Jakub Dawidek 
5947be67fe3SPawel Jakub Dawidek 	return (conn->c_sendfd != -1 && conn->c_recvfd != -1);
5957be67fe3SPawel Jakub Dawidek }
5967be67fe3SPawel Jakub Dawidek 
5977be67fe3SPawel Jakub Dawidek static void
connection_launch(struct ggd_connection * conn)5987be67fe3SPawel Jakub Dawidek connection_launch(struct ggd_connection *conn)
5997be67fe3SPawel Jakub Dawidek {
6007be67fe3SPawel Jakub Dawidek 	pthread_t td;
6017be67fe3SPawel Jakub Dawidek 	int error, pid;
6027be67fe3SPawel Jakub Dawidek 
6037be67fe3SPawel Jakub Dawidek 	pid = fork();
6047be67fe3SPawel Jakub Dawidek 	if (pid > 0)
6057be67fe3SPawel Jakub Dawidek 		return;
6067be67fe3SPawel Jakub Dawidek 	else if (pid == -1) {
6077be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_ERR, "Cannot fork: %s.", strerror(errno));
6087be67fe3SPawel Jakub Dawidek 		return;
6097be67fe3SPawel Jakub Dawidek 	}
6107be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Process created [%s].", conn->c_path);
6117be67fe3SPawel Jakub Dawidek 
6127be67fe3SPawel Jakub Dawidek 	/*
6137be67fe3SPawel Jakub Dawidek 	 * Create condition variables and mutexes for in-queue and out-queue
6147be67fe3SPawel Jakub Dawidek 	 * synchronization.
6157be67fe3SPawel Jakub Dawidek 	 */
6167be67fe3SPawel Jakub Dawidek 	error = pthread_mutex_init(&inqueue_mtx, NULL);
6177be67fe3SPawel Jakub Dawidek 	if (error != 0) {
6187be67fe3SPawel Jakub Dawidek 		g_gate_xlog("pthread_mutex_init(inqueue_mtx): %s.",
6197be67fe3SPawel Jakub Dawidek 		    strerror(error));
6207be67fe3SPawel Jakub Dawidek 	}
6217be67fe3SPawel Jakub Dawidek 	error = pthread_cond_init(&inqueue_cond, NULL);
6227be67fe3SPawel Jakub Dawidek 	if (error != 0) {
6237be67fe3SPawel Jakub Dawidek 		g_gate_xlog("pthread_cond_init(inqueue_cond): %s.",
6247be67fe3SPawel Jakub Dawidek 		    strerror(error));
6257be67fe3SPawel Jakub Dawidek 	}
6267be67fe3SPawel Jakub Dawidek 	error = pthread_mutex_init(&outqueue_mtx, NULL);
6277be67fe3SPawel Jakub Dawidek 	if (error != 0) {
6287be67fe3SPawel Jakub Dawidek 		g_gate_xlog("pthread_mutex_init(outqueue_mtx): %s.",
6297be67fe3SPawel Jakub Dawidek 		    strerror(error));
6307be67fe3SPawel Jakub Dawidek 	}
6317be67fe3SPawel Jakub Dawidek 	error = pthread_cond_init(&outqueue_cond, NULL);
6327be67fe3SPawel Jakub Dawidek 	if (error != 0) {
6337be67fe3SPawel Jakub Dawidek 		g_gate_xlog("pthread_cond_init(outqueue_cond): %s.",
6347be67fe3SPawel Jakub Dawidek 		    strerror(error));
6357be67fe3SPawel Jakub Dawidek 	}
6367be67fe3SPawel Jakub Dawidek 
6377be67fe3SPawel Jakub Dawidek 	/*
6387be67fe3SPawel Jakub Dawidek 	 * Create threads:
6397be67fe3SPawel Jakub Dawidek 	 * recvtd - thread for receiving I/O request
6407be67fe3SPawel Jakub Dawidek 	 * diskio - thread for doing I/O request
6417be67fe3SPawel Jakub Dawidek 	 * sendtd - thread for sending I/O requests back
6427be67fe3SPawel Jakub Dawidek 	 */
6437be67fe3SPawel Jakub Dawidek 	error = pthread_create(&td, NULL, send_thread, conn);
6447be67fe3SPawel Jakub Dawidek 	if (error != 0) {
6457be67fe3SPawel Jakub Dawidek 		g_gate_xlog("pthread_create(send_thread): %s.",
6467be67fe3SPawel Jakub Dawidek 		    strerror(error));
6477be67fe3SPawel Jakub Dawidek 	}
6487be67fe3SPawel Jakub Dawidek 	error = pthread_create(&td, NULL, recv_thread, conn);
6497be67fe3SPawel Jakub Dawidek 	if (error != 0) {
6507be67fe3SPawel Jakub Dawidek 		g_gate_xlog("pthread_create(recv_thread): %s.",
6517be67fe3SPawel Jakub Dawidek 		    strerror(error));
6527be67fe3SPawel Jakub Dawidek 	}
6537be67fe3SPawel Jakub Dawidek 	disk_thread(conn);
654d1d669bdSPawel Jakub Dawidek }
655d1d669bdSPawel Jakub Dawidek 
656d1d669bdSPawel Jakub Dawidek static void
sendfail(int sfd,int error,const char * fmt,...)657d1d669bdSPawel Jakub Dawidek sendfail(int sfd, int error, const char *fmt, ...)
658d1d669bdSPawel Jakub Dawidek {
659d1d669bdSPawel Jakub Dawidek 	struct g_gate_sinit sinit;
660d1d669bdSPawel Jakub Dawidek 	va_list ap;
6617be67fe3SPawel Jakub Dawidek 	ssize_t data;
662d1d669bdSPawel Jakub Dawidek 
663133f9fcfSEd Maste 	memset(&sinit, 0, sizeof(sinit));
664d1d669bdSPawel Jakub Dawidek 	sinit.gs_error = error;
665d1d669bdSPawel Jakub Dawidek 	g_gate_swap2n_sinit(&sinit);
6667be67fe3SPawel Jakub Dawidek 	data = g_gate_send(sfd, &sinit, sizeof(sinit), 0);
667d1d669bdSPawel Jakub Dawidek 	g_gate_swap2h_sinit(&sinit);
6687be67fe3SPawel Jakub Dawidek 	if (data != sizeof(sinit)) {
6697be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_WARNING, "Cannot send initial packet: %s.",
670d1d669bdSPawel Jakub Dawidek 		    strerror(errno));
6717be67fe3SPawel Jakub Dawidek 		return;
672d1d669bdSPawel Jakub Dawidek 	}
673d1d669bdSPawel Jakub Dawidek 	if (fmt != NULL) {
674d1d669bdSPawel Jakub Dawidek 		va_start(ap, fmt);
6757be67fe3SPawel Jakub Dawidek 		g_gate_vlog(LOG_WARNING, fmt, ap);
676d1d669bdSPawel Jakub Dawidek 		va_end(ap);
677d1d669bdSPawel Jakub Dawidek 	}
678d1d669bdSPawel Jakub Dawidek }
679d1d669bdSPawel Jakub Dawidek 
6807be67fe3SPawel Jakub Dawidek static void *
malloc_waitok(size_t size)6817be67fe3SPawel Jakub Dawidek malloc_waitok(size_t size)
682d1d669bdSPawel Jakub Dawidek {
6837be67fe3SPawel Jakub Dawidek 	void *p;
6847be67fe3SPawel Jakub Dawidek 
6857be67fe3SPawel Jakub Dawidek 	while ((p = malloc(size)) == NULL) {
6867be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_DEBUG, "Cannot allocate %zu bytes.", size);
6877be67fe3SPawel Jakub Dawidek 		sleep(1);
6887be67fe3SPawel Jakub Dawidek 	}
6897be67fe3SPawel Jakub Dawidek 	return (p);
6907be67fe3SPawel Jakub Dawidek }
6917be67fe3SPawel Jakub Dawidek 
6927be67fe3SPawel Jakub Dawidek static void *
recv_thread(void * arg)6937be67fe3SPawel Jakub Dawidek recv_thread(void *arg)
6947be67fe3SPawel Jakub Dawidek {
6957be67fe3SPawel Jakub Dawidek 	struct ggd_connection *conn;
6967be67fe3SPawel Jakub Dawidek 	struct ggd_request *req;
697d1d669bdSPawel Jakub Dawidek 	ssize_t data;
6987be67fe3SPawel Jakub Dawidek 	int error, fd;
699d1d669bdSPawel Jakub Dawidek 
7007be67fe3SPawel Jakub Dawidek 	conn = arg;
7017be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path);
7027be67fe3SPawel Jakub Dawidek 	fd = conn->c_recvfd;
703d1d669bdSPawel Jakub Dawidek 	for (;;) {
704d1d669bdSPawel Jakub Dawidek 		/*
7057be67fe3SPawel Jakub Dawidek 		 * Get header packet.
706d1d669bdSPawel Jakub Dawidek 		 */
7077be67fe3SPawel Jakub Dawidek 		req = malloc_waitok(sizeof(*req));
7087be67fe3SPawel Jakub Dawidek 		data = g_gate_recv(fd, &req->r_hdr, sizeof(req->r_hdr),
7097be67fe3SPawel Jakub Dawidek 		    MSG_WAITALL);
710d1d669bdSPawel Jakub Dawidek 		if (data == 0) {
711d1d669bdSPawel Jakub Dawidek 			g_gate_log(LOG_DEBUG, "Process %u exiting.", getpid());
712d1d669bdSPawel Jakub Dawidek 			exit(EXIT_SUCCESS);
713d1d669bdSPawel Jakub Dawidek 		} else if (data == -1) {
714d1d669bdSPawel Jakub Dawidek 			g_gate_xlog("Error while receiving hdr packet: %s.",
715d1d669bdSPawel Jakub Dawidek 			    strerror(errno));
7167be67fe3SPawel Jakub Dawidek 		} else if (data != sizeof(req->r_hdr)) {
717d1d669bdSPawel Jakub Dawidek 			g_gate_xlog("Malformed hdr packet received.");
718d1d669bdSPawel Jakub Dawidek 		}
719d1d669bdSPawel Jakub Dawidek 		g_gate_log(LOG_DEBUG, "Received hdr packet.");
7207be67fe3SPawel Jakub Dawidek 		g_gate_swap2h_hdr(&req->r_hdr);
7217be67fe3SPawel Jakub Dawidek 
722278847aeSMark Johnston 		g_gate_log(LOG_DEBUG, "%s: offset=%" PRIu64 " length=%" PRIu32,
723278847aeSMark Johnston 		    __func__, req->r_offset, req->r_length);
724d1d669bdSPawel Jakub Dawidek 
725d1d669bdSPawel Jakub Dawidek 		/*
7267be67fe3SPawel Jakub Dawidek 		 * Allocate memory for data.
727d1d669bdSPawel Jakub Dawidek 		 */
7287be67fe3SPawel Jakub Dawidek 		req->r_data = malloc_waitok(req->r_length);
729d1d669bdSPawel Jakub Dawidek 
7307be67fe3SPawel Jakub Dawidek 		/*
7317be67fe3SPawel Jakub Dawidek 		 * Receive data to write for WRITE request.
7327be67fe3SPawel Jakub Dawidek 		 */
7337be67fe3SPawel Jakub Dawidek 		if (req->r_cmd == GGATE_CMD_WRITE) {
734d1d669bdSPawel Jakub Dawidek 			g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data...",
7357be67fe3SPawel Jakub Dawidek 			    req->r_length);
7367be67fe3SPawel Jakub Dawidek 			data = g_gate_recv(fd, req->r_data, req->r_length,
7377be67fe3SPawel Jakub Dawidek 			    MSG_WAITALL);
738d1d669bdSPawel Jakub Dawidek 			if (data == -1) {
739d1d669bdSPawel Jakub Dawidek 				g_gate_xlog("Error while receiving data: %s.",
740d1d669bdSPawel Jakub Dawidek 				    strerror(errno));
741d1d669bdSPawel Jakub Dawidek 			}
742d1d669bdSPawel Jakub Dawidek 		}
7437be67fe3SPawel Jakub Dawidek 
7447be67fe3SPawel Jakub Dawidek 		/*
7457be67fe3SPawel Jakub Dawidek 		 * Put the request onto the incoming queue.
7467be67fe3SPawel Jakub Dawidek 		 */
7477be67fe3SPawel Jakub Dawidek 		error = pthread_mutex_lock(&inqueue_mtx);
7487be67fe3SPawel Jakub Dawidek 		assert(error == 0);
7497be67fe3SPawel Jakub Dawidek 		TAILQ_INSERT_TAIL(&inqueue, req, r_next);
7507be67fe3SPawel Jakub Dawidek 		error = pthread_cond_signal(&inqueue_cond);
7517be67fe3SPawel Jakub Dawidek 		assert(error == 0);
7527be67fe3SPawel Jakub Dawidek 		error = pthread_mutex_unlock(&inqueue_mtx);
7537be67fe3SPawel Jakub Dawidek 		assert(error == 0);
7547be67fe3SPawel Jakub Dawidek 	}
7557be67fe3SPawel Jakub Dawidek }
7567be67fe3SPawel Jakub Dawidek 
7577be67fe3SPawel Jakub Dawidek static void *
disk_thread(void * arg)7587be67fe3SPawel Jakub Dawidek disk_thread(void *arg)
7597be67fe3SPawel Jakub Dawidek {
7607be67fe3SPawel Jakub Dawidek 	struct ggd_connection *conn;
7617be67fe3SPawel Jakub Dawidek 	struct ggd_request *req;
7627be67fe3SPawel Jakub Dawidek 	ssize_t data;
7637be67fe3SPawel Jakub Dawidek 	int error, fd;
7647be67fe3SPawel Jakub Dawidek 
7657be67fe3SPawel Jakub Dawidek 	conn = arg;
7667be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path);
7677be67fe3SPawel Jakub Dawidek 	fd = conn->c_diskfd;
7687be67fe3SPawel Jakub Dawidek 	for (;;) {
7697be67fe3SPawel Jakub Dawidek 		/*
7707be67fe3SPawel Jakub Dawidek 		 * Get a request from the incoming queue.
7717be67fe3SPawel Jakub Dawidek 		 */
7727be67fe3SPawel Jakub Dawidek 		error = pthread_mutex_lock(&inqueue_mtx);
7737be67fe3SPawel Jakub Dawidek 		assert(error == 0);
7747be67fe3SPawel Jakub Dawidek 		while ((req = TAILQ_FIRST(&inqueue)) == NULL) {
7757be67fe3SPawel Jakub Dawidek 			error = pthread_cond_wait(&inqueue_cond, &inqueue_mtx);
7767be67fe3SPawel Jakub Dawidek 			assert(error == 0);
7777be67fe3SPawel Jakub Dawidek 		}
7787be67fe3SPawel Jakub Dawidek 		TAILQ_REMOVE(&inqueue, req, r_next);
7797be67fe3SPawel Jakub Dawidek 		error = pthread_mutex_unlock(&inqueue_mtx);
7807be67fe3SPawel Jakub Dawidek 		assert(error == 0);
7817be67fe3SPawel Jakub Dawidek 
7827be67fe3SPawel Jakub Dawidek 		/*
7837be67fe3SPawel Jakub Dawidek 		 * Check the request.
7847be67fe3SPawel Jakub Dawidek 		 */
7857be67fe3SPawel Jakub Dawidek 		assert(req->r_offset + req->r_length <= (uintmax_t)conn->c_mediasize);
7867be67fe3SPawel Jakub Dawidek 		assert((req->r_offset % conn->c_sectorsize) == 0);
7877be67fe3SPawel Jakub Dawidek 		assert((req->r_length % conn->c_sectorsize) == 0);
7887be67fe3SPawel Jakub Dawidek 
789278847aeSMark Johnston 		g_gate_log(LOG_DEBUG, "%s: offset=%" PRIu64 " length=%" PRIu32,
790278847aeSMark Johnston 		     __func__, req->r_offset, req->r_length);
7917be67fe3SPawel Jakub Dawidek 
7927be67fe3SPawel Jakub Dawidek 		/*
7937be67fe3SPawel Jakub Dawidek 		 * Do the request.
7947be67fe3SPawel Jakub Dawidek 		 */
7957be67fe3SPawel Jakub Dawidek 		data = 0;
7967be67fe3SPawel Jakub Dawidek 		switch (req->r_cmd) {
7977be67fe3SPawel Jakub Dawidek 		case GGATE_CMD_READ:
7987be67fe3SPawel Jakub Dawidek 			data = pread(fd, req->r_data, req->r_length,
7997be67fe3SPawel Jakub Dawidek 			    req->r_offset);
8007be67fe3SPawel Jakub Dawidek 			break;
8017be67fe3SPawel Jakub Dawidek 		case GGATE_CMD_WRITE:
8027be67fe3SPawel Jakub Dawidek 			data = pwrite(fd, req->r_data, req->r_length,
8037be67fe3SPawel Jakub Dawidek 			    req->r_offset);
8047be67fe3SPawel Jakub Dawidek 			/* Free data memory here - better sooner. */
8057be67fe3SPawel Jakub Dawidek 			free(req->r_data);
8067be67fe3SPawel Jakub Dawidek 			req->r_data = NULL;
8077be67fe3SPawel Jakub Dawidek 			break;
8086226477aSAlan Somers 		case GGATE_CMD_FLUSH:
8096226477aSAlan Somers 			data = fsync(fd);
8106226477aSAlan Somers 			if (data != 0)
8116226477aSAlan Somers 				req->r_error = errno;
8126226477aSAlan Somers 			break;
8136226477aSAlan Somers 		default:
8146226477aSAlan Somers 			g_gate_log(LOG_DEBUG, "Unsupported request: %i", req->r_cmd);
8156226477aSAlan Somers 			req->r_error = EOPNOTSUPP;
8166226477aSAlan Somers 			if (req->r_data != NULL) {
8176226477aSAlan Somers 				free(req->r_data);
8186226477aSAlan Somers 				req->r_data = NULL;
8196226477aSAlan Somers 			}
8206226477aSAlan Somers 			break;
8217be67fe3SPawel Jakub Dawidek 		}
8227be67fe3SPawel Jakub Dawidek 		if (data != (ssize_t)req->r_length) {
8237be67fe3SPawel Jakub Dawidek 			/* Report short reads/writes as I/O errors. */
8247be67fe3SPawel Jakub Dawidek 			if (errno == 0)
8257be67fe3SPawel Jakub Dawidek 				errno = EIO;
8267be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_ERR, "Disk error: %s", strerror(errno));
8277be67fe3SPawel Jakub Dawidek 			req->r_error = errno;
8287be67fe3SPawel Jakub Dawidek 			if (req->r_data != NULL) {
8297be67fe3SPawel Jakub Dawidek 				free(req->r_data);
8307be67fe3SPawel Jakub Dawidek 				req->r_data = NULL;
8317be67fe3SPawel Jakub Dawidek 			}
8327be67fe3SPawel Jakub Dawidek 		}
8337be67fe3SPawel Jakub Dawidek 
8347be67fe3SPawel Jakub Dawidek 		/*
8357be67fe3SPawel Jakub Dawidek 		 * Put the request onto the outgoing queue.
8367be67fe3SPawel Jakub Dawidek 		 */
8377be67fe3SPawel Jakub Dawidek 		error = pthread_mutex_lock(&outqueue_mtx);
8387be67fe3SPawel Jakub Dawidek 		assert(error == 0);
8397be67fe3SPawel Jakub Dawidek 		TAILQ_INSERT_TAIL(&outqueue, req, r_next);
8407be67fe3SPawel Jakub Dawidek 		error = pthread_cond_signal(&outqueue_cond);
8417be67fe3SPawel Jakub Dawidek 		assert(error == 0);
8427be67fe3SPawel Jakub Dawidek 		error = pthread_mutex_unlock(&outqueue_mtx);
8437be67fe3SPawel Jakub Dawidek 		assert(error == 0);
8447be67fe3SPawel Jakub Dawidek 	}
845186f2eeaSMike Makonnen 
846186f2eeaSMike Makonnen 	/* NOTREACHED */
847186f2eeaSMike Makonnen 	return (NULL);
8487be67fe3SPawel Jakub Dawidek }
8497be67fe3SPawel Jakub Dawidek 
8507be67fe3SPawel Jakub Dawidek static void *
send_thread(void * arg)8517be67fe3SPawel Jakub Dawidek send_thread(void *arg)
8527be67fe3SPawel Jakub Dawidek {
8537be67fe3SPawel Jakub Dawidek 	struct ggd_connection *conn;
8547be67fe3SPawel Jakub Dawidek 	struct ggd_request *req;
8557be67fe3SPawel Jakub Dawidek 	ssize_t data;
8567be67fe3SPawel Jakub Dawidek 	int error, fd;
8577be67fe3SPawel Jakub Dawidek 
8587be67fe3SPawel Jakub Dawidek 	conn = arg;
8597be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path);
8607be67fe3SPawel Jakub Dawidek 	fd = conn->c_sendfd;
8617be67fe3SPawel Jakub Dawidek 	for (;;) {
8627be67fe3SPawel Jakub Dawidek 		/*
8637be67fe3SPawel Jakub Dawidek 		 * Get a request from the outgoing queue.
8647be67fe3SPawel Jakub Dawidek 		 */
8657be67fe3SPawel Jakub Dawidek 		error = pthread_mutex_lock(&outqueue_mtx);
8667be67fe3SPawel Jakub Dawidek 		assert(error == 0);
8677be67fe3SPawel Jakub Dawidek 		while ((req = TAILQ_FIRST(&outqueue)) == NULL) {
8687be67fe3SPawel Jakub Dawidek 			error = pthread_cond_wait(&outqueue_cond,
8697be67fe3SPawel Jakub Dawidek 			    &outqueue_mtx);
8707be67fe3SPawel Jakub Dawidek 			assert(error == 0);
8717be67fe3SPawel Jakub Dawidek 		}
8727be67fe3SPawel Jakub Dawidek 		TAILQ_REMOVE(&outqueue, req, r_next);
8737be67fe3SPawel Jakub Dawidek 		error = pthread_mutex_unlock(&outqueue_mtx);
8747be67fe3SPawel Jakub Dawidek 		assert(error == 0);
8757be67fe3SPawel Jakub Dawidek 
876278847aeSMark Johnston 		g_gate_log(LOG_DEBUG, "%s: offset=%" PRIu64 " length=%" PRIu32,
877278847aeSMark Johnston 		    __func__, req->r_offset, req->r_length);
8787be67fe3SPawel Jakub Dawidek 
8797be67fe3SPawel Jakub Dawidek 		/*
8807be67fe3SPawel Jakub Dawidek 		 * Send the request.
8817be67fe3SPawel Jakub Dawidek 		 */
8827be67fe3SPawel Jakub Dawidek 		g_gate_swap2n_hdr(&req->r_hdr);
8837be67fe3SPawel Jakub Dawidek 		if (g_gate_send(fd, &req->r_hdr, sizeof(req->r_hdr), 0) == -1) {
8847be67fe3SPawel Jakub Dawidek 			g_gate_xlog("Error while sending hdr packet: %s.",
885d1d669bdSPawel Jakub Dawidek 			    strerror(errno));
886d1d669bdSPawel Jakub Dawidek 		}
8877be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_DEBUG, "Sent hdr packet.");
8887be67fe3SPawel Jakub Dawidek 		g_gate_swap2h_hdr(&req->r_hdr);
8897be67fe3SPawel Jakub Dawidek 		if (req->r_data != NULL) {
8907be67fe3SPawel Jakub Dawidek 			data = g_gate_send(fd, req->r_data, req->r_length, 0);
8917be67fe3SPawel Jakub Dawidek 			if (data != (ssize_t)req->r_length) {
8927be67fe3SPawel Jakub Dawidek 				g_gate_xlog("Error while sending data: %s.",
8937be67fe3SPawel Jakub Dawidek 				    strerror(errno));
894d1d669bdSPawel Jakub Dawidek 			}
8957be67fe3SPawel Jakub Dawidek 			g_gate_log(LOG_DEBUG,
896278847aeSMark Johnston 			    "Sent %zd bytes (offset=%" PRIu64 ", size=%" PRIu32
897278847aeSMark Johnston 			    ").", data, req->r_offset, req->r_length);
8987be67fe3SPawel Jakub Dawidek 			free(req->r_data);
899d1d669bdSPawel Jakub Dawidek 		}
9007be67fe3SPawel Jakub Dawidek 		free(req);
9017be67fe3SPawel Jakub Dawidek 	}
902186f2eeaSMike Makonnen 
903186f2eeaSMike Makonnen 	/* NOTREACHED */
904186f2eeaSMike Makonnen 	return (NULL);
9057be67fe3SPawel Jakub Dawidek }
9067be67fe3SPawel Jakub Dawidek 
9077be67fe3SPawel Jakub Dawidek static void
log_connection(struct sockaddr * from)9087be67fe3SPawel Jakub Dawidek log_connection(struct sockaddr *from)
9097be67fe3SPawel Jakub Dawidek {
9107be67fe3SPawel Jakub Dawidek 	in_addr_t ip;
9117be67fe3SPawel Jakub Dawidek 
9127be67fe3SPawel Jakub Dawidek 	ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr);
9137be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip));
9147be67fe3SPawel Jakub Dawidek }
9157be67fe3SPawel Jakub Dawidek 
9167be67fe3SPawel Jakub Dawidek static int
handshake(struct sockaddr * from,int sfd)9177be67fe3SPawel Jakub Dawidek handshake(struct sockaddr *from, int sfd)
9187be67fe3SPawel Jakub Dawidek {
9197be67fe3SPawel Jakub Dawidek 	struct g_gate_version ver;
9207be67fe3SPawel Jakub Dawidek 	struct g_gate_cinit cinit;
9217be67fe3SPawel Jakub Dawidek 	struct g_gate_sinit sinit;
9227be67fe3SPawel Jakub Dawidek 	struct ggd_connection *conn;
9237be67fe3SPawel Jakub Dawidek 	struct ggd_export *ex;
9247be67fe3SPawel Jakub Dawidek 	ssize_t data;
9257be67fe3SPawel Jakub Dawidek 
9267be67fe3SPawel Jakub Dawidek 	log_connection(from);
9277be67fe3SPawel Jakub Dawidek 	/*
9287be67fe3SPawel Jakub Dawidek 	 * Phase 1: Version verification.
9297be67fe3SPawel Jakub Dawidek 	 */
9307be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Receiving version packet.");
9317be67fe3SPawel Jakub Dawidek 	data = g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL);
9327be67fe3SPawel Jakub Dawidek 	g_gate_swap2h_version(&ver);
9337be67fe3SPawel Jakub Dawidek 	if (data != sizeof(ver)) {
9347be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_WARNING, "Malformed version packet.");
9357be67fe3SPawel Jakub Dawidek 		return (0);
9367be67fe3SPawel Jakub Dawidek 	}
9377be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Version packet received.");
9387be67fe3SPawel Jakub Dawidek 	if (memcmp(ver.gv_magic, GGATE_MAGIC, strlen(GGATE_MAGIC)) != 0) {
9397be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_WARNING, "Invalid magic field.");
9407be67fe3SPawel Jakub Dawidek 		return (0);
9417be67fe3SPawel Jakub Dawidek 	}
9427be67fe3SPawel Jakub Dawidek 	if (ver.gv_version != GGATE_VERSION) {
9437be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_WARNING, "Version %u is not supported.",
9447be67fe3SPawel Jakub Dawidek 		    ver.gv_version);
9457be67fe3SPawel Jakub Dawidek 		return (0);
9467be67fe3SPawel Jakub Dawidek 	}
9477be67fe3SPawel Jakub Dawidek 	ver.gv_error = 0;
9487be67fe3SPawel Jakub Dawidek 	g_gate_swap2n_version(&ver);
9497be67fe3SPawel Jakub Dawidek 	data = g_gate_send(sfd, &ver, sizeof(ver), 0);
9507be67fe3SPawel Jakub Dawidek 	g_gate_swap2h_version(&ver);
9517be67fe3SPawel Jakub Dawidek 	if (data == -1) {
9527be67fe3SPawel Jakub Dawidek 		sendfail(sfd, errno, "Error while sending version packet: %s.",
9537be67fe3SPawel Jakub Dawidek 		    strerror(errno));
9547be67fe3SPawel Jakub Dawidek 		return (0);
9557be67fe3SPawel Jakub Dawidek 	}
9567be67fe3SPawel Jakub Dawidek 
9577be67fe3SPawel Jakub Dawidek 	/*
9587be67fe3SPawel Jakub Dawidek 	 * Phase 2: Request verification.
9597be67fe3SPawel Jakub Dawidek 	 */
9607be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Receiving initial packet.");
9617be67fe3SPawel Jakub Dawidek 	data = g_gate_recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL);
9627be67fe3SPawel Jakub Dawidek 	g_gate_swap2h_cinit(&cinit);
9637be67fe3SPawel Jakub Dawidek 	if (data != sizeof(cinit)) {
9647be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_WARNING, "Malformed initial packet.");
9657be67fe3SPawel Jakub Dawidek 		return (0);
9667be67fe3SPawel Jakub Dawidek 	}
9677be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Initial packet received.");
9687be67fe3SPawel Jakub Dawidek 	conn = connection_find(&cinit);
9697be67fe3SPawel Jakub Dawidek 	if (conn != NULL) {
9707be67fe3SPawel Jakub Dawidek 		/*
9717be67fe3SPawel Jakub Dawidek 		 * Connection should already exists.
9727be67fe3SPawel Jakub Dawidek 		 */
9737be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_DEBUG, "Found existing connection (token=%lu).",
9747be67fe3SPawel Jakub Dawidek 		    (unsigned long)conn->c_token);
9757be67fe3SPawel Jakub Dawidek 		if (connection_add(conn, &cinit, from, sfd) == -1) {
9767be67fe3SPawel Jakub Dawidek 			connection_remove(conn);
9777be67fe3SPawel Jakub Dawidek 			return (0);
9787be67fe3SPawel Jakub Dawidek 		}
9797be67fe3SPawel Jakub Dawidek 	} else {
9807be67fe3SPawel Jakub Dawidek 		/*
9817be67fe3SPawel Jakub Dawidek 		 * New connection, allocate space.
9827be67fe3SPawel Jakub Dawidek 		 */
9837be67fe3SPawel Jakub Dawidek 		conn = connection_new(&cinit, from, sfd);
9847be67fe3SPawel Jakub Dawidek 		if (conn == NULL) {
9857be67fe3SPawel Jakub Dawidek 			sendfail(sfd, ENOMEM,
9867be67fe3SPawel Jakub Dawidek 			    "Cannot allocate new connection.");
9877be67fe3SPawel Jakub Dawidek 			return (0);
9887be67fe3SPawel Jakub Dawidek 		}
9897be67fe3SPawel Jakub Dawidek 		g_gate_log(LOG_DEBUG, "New connection created (token=%lu).",
9907be67fe3SPawel Jakub Dawidek 		    (unsigned long)conn->c_token);
9917be67fe3SPawel Jakub Dawidek 	}
9927be67fe3SPawel Jakub Dawidek 
9937be67fe3SPawel Jakub Dawidek 	ex = exports_find(from, &cinit, conn);
9947be67fe3SPawel Jakub Dawidek 	if (ex == NULL) {
9957be67fe3SPawel Jakub Dawidek 		sendfail(sfd, errno, NULL);
996b4a355e2SChristian Brueffer 		connection_remove(conn);
9977be67fe3SPawel Jakub Dawidek 		return (0);
9987be67fe3SPawel Jakub Dawidek 	}
9997be67fe3SPawel Jakub Dawidek 	if (conn->c_mediasize == 0) {
10007be67fe3SPawel Jakub Dawidek 		conn->c_mediasize = g_gate_mediasize(conn->c_diskfd);
10017be67fe3SPawel Jakub Dawidek 		conn->c_sectorsize = g_gate_sectorsize(conn->c_diskfd);
10027be67fe3SPawel Jakub Dawidek 	}
10037be67fe3SPawel Jakub Dawidek 	sinit.gs_mediasize = conn->c_mediasize;
10047be67fe3SPawel Jakub Dawidek 	sinit.gs_sectorsize = conn->c_sectorsize;
10057be67fe3SPawel Jakub Dawidek 	sinit.gs_error = 0;
10067be67fe3SPawel Jakub Dawidek 
10077be67fe3SPawel Jakub Dawidek 	g_gate_log(LOG_DEBUG, "Sending initial packet.");
10087be67fe3SPawel Jakub Dawidek 
10097be67fe3SPawel Jakub Dawidek 	g_gate_swap2n_sinit(&sinit);
10107be67fe3SPawel Jakub Dawidek 	data = g_gate_send(sfd, &sinit, sizeof(sinit), 0);
10117be67fe3SPawel Jakub Dawidek 	g_gate_swap2h_sinit(&sinit);
10127be67fe3SPawel Jakub Dawidek 	if (data == -1) {
10137be67fe3SPawel Jakub Dawidek 		sendfail(sfd, errno, "Error while sending initial packet: %s.",
10147be67fe3SPawel Jakub Dawidek 		    strerror(errno));
10157be67fe3SPawel Jakub Dawidek 		return (0);
10167be67fe3SPawel Jakub Dawidek 	}
10177be67fe3SPawel Jakub Dawidek 
10187be67fe3SPawel Jakub Dawidek 	if (connection_ready(conn)) {
10197be67fe3SPawel Jakub Dawidek 		connection_launch(conn);
10207be67fe3SPawel Jakub Dawidek 		connection_remove(conn);
10217be67fe3SPawel Jakub Dawidek 	}
10227be67fe3SPawel Jakub Dawidek 	return (1);
1023d1d669bdSPawel Jakub Dawidek }
1024d1d669bdSPawel Jakub Dawidek 
1025d1d669bdSPawel Jakub Dawidek static void
huphandler(int sig __unused)1026d1d669bdSPawel Jakub Dawidek huphandler(int sig __unused)
1027d1d669bdSPawel Jakub Dawidek {
1028d1d669bdSPawel Jakub Dawidek 
1029d1d669bdSPawel Jakub Dawidek 	got_sighup = 1;
1030d1d669bdSPawel Jakub Dawidek }
1031d1d669bdSPawel Jakub Dawidek 
1032d1d669bdSPawel Jakub Dawidek int
main(int argc,char * argv[])1033d1d669bdSPawel Jakub Dawidek main(int argc, char *argv[])
1034d1d669bdSPawel Jakub Dawidek {
1035fc7e71c5SEnji Cooper 	const char *ggated_pidfile = _PATH_VARRUN "/ggated.pid";
1036fc7e71c5SEnji Cooper 	struct pidfh *pfh;
1037d1d669bdSPawel Jakub Dawidek 	struct sockaddr_in serv;
1038d1d669bdSPawel Jakub Dawidek 	struct sockaddr from;
1039d1d669bdSPawel Jakub Dawidek 	socklen_t fromlen;
1040fc7e71c5SEnji Cooper 	pid_t otherpid;
1041fc7e71c5SEnji Cooper 	int ch, sfd, tmpsfd;
10427be67fe3SPawel Jakub Dawidek 	unsigned port;
1043d1d669bdSPawel Jakub Dawidek 
1044d1d669bdSPawel Jakub Dawidek 	bindaddr = htonl(INADDR_ANY);
1045d1d669bdSPawel Jakub Dawidek 	port = G_GATE_PORT;
1046fc7e71c5SEnji Cooper 	while ((ch = getopt(argc, argv, "a:hnp:F:R:S:v")) != -1) {
1047d1d669bdSPawel Jakub Dawidek 		switch (ch) {
1048d1d669bdSPawel Jakub Dawidek 		case 'a':
1049d1d669bdSPawel Jakub Dawidek 			bindaddr = g_gate_str2ip(optarg);
1050d1d669bdSPawel Jakub Dawidek 			if (bindaddr == INADDR_NONE) {
1051d1d669bdSPawel Jakub Dawidek 				errx(EXIT_FAILURE,
1052d1d669bdSPawel Jakub Dawidek 				    "Invalid IP/host name to bind to.");
1053d1d669bdSPawel Jakub Dawidek 			}
1054d1d669bdSPawel Jakub Dawidek 			break;
1055fc7e71c5SEnji Cooper 		case 'F':
1056fc7e71c5SEnji Cooper 			ggated_pidfile = optarg;
1057fc7e71c5SEnji Cooper 			break;
1058d1d669bdSPawel Jakub Dawidek 		case 'n':
1059d1d669bdSPawel Jakub Dawidek 			nagle = 0;
1060d1d669bdSPawel Jakub Dawidek 			break;
1061d1d669bdSPawel Jakub Dawidek 		case 'p':
1062d1d669bdSPawel Jakub Dawidek 			errno = 0;
1063d1d669bdSPawel Jakub Dawidek 			port = strtoul(optarg, NULL, 10);
1064d1d669bdSPawel Jakub Dawidek 			if (port == 0 && errno != 0)
1065d1d669bdSPawel Jakub Dawidek 				errx(EXIT_FAILURE, "Invalid port.");
1066d1d669bdSPawel Jakub Dawidek 			break;
1067d1d669bdSPawel Jakub Dawidek 		case 'R':
1068d1d669bdSPawel Jakub Dawidek 			errno = 0;
1069d1d669bdSPawel Jakub Dawidek 			rcvbuf = strtoul(optarg, NULL, 10);
1070d1d669bdSPawel Jakub Dawidek 			if (rcvbuf == 0 && errno != 0)
1071d1d669bdSPawel Jakub Dawidek 				errx(EXIT_FAILURE, "Invalid rcvbuf.");
1072d1d669bdSPawel Jakub Dawidek 			break;
1073d1d669bdSPawel Jakub Dawidek 		case 'S':
1074d1d669bdSPawel Jakub Dawidek 			errno = 0;
1075d1d669bdSPawel Jakub Dawidek 			sndbuf = strtoul(optarg, NULL, 10);
1076d1d669bdSPawel Jakub Dawidek 			if (sndbuf == 0 && errno != 0)
1077d1d669bdSPawel Jakub Dawidek 				errx(EXIT_FAILURE, "Invalid sndbuf.");
1078d1d669bdSPawel Jakub Dawidek 			break;
1079d1d669bdSPawel Jakub Dawidek 		case 'v':
1080d1d669bdSPawel Jakub Dawidek 			g_gate_verbose++;
1081d1d669bdSPawel Jakub Dawidek 			break;
1082d1d669bdSPawel Jakub Dawidek 		case 'h':
1083d1d669bdSPawel Jakub Dawidek 		default:
1084d1d669bdSPawel Jakub Dawidek 			usage();
1085d1d669bdSPawel Jakub Dawidek 		}
1086d1d669bdSPawel Jakub Dawidek 	}
1087d1d669bdSPawel Jakub Dawidek 	argc -= optind;
1088d1d669bdSPawel Jakub Dawidek 	argv += optind;
1089d1d669bdSPawel Jakub Dawidek 
1090d1d669bdSPawel Jakub Dawidek 	if (argv[0] != NULL)
10917be67fe3SPawel Jakub Dawidek 		exports_file = argv[0];
1092d1d669bdSPawel Jakub Dawidek 	exports_get();
1093d1d669bdSPawel Jakub Dawidek 
1094fc7e71c5SEnji Cooper 	pfh = pidfile_open(ggated_pidfile, 0600, &otherpid);
1095fc7e71c5SEnji Cooper 	if (pfh == NULL) {
1096fc7e71c5SEnji Cooper 		if (errno == EEXIST) {
1097fc7e71c5SEnji Cooper 			errx(EXIT_FAILURE, "Daemon already running, pid: %jd.",
1098fc7e71c5SEnji Cooper 			    (intmax_t)otherpid);
1099fc7e71c5SEnji Cooper 		}
1100fc7e71c5SEnji Cooper 		err(EXIT_FAILURE, "Cannot open/create pidfile");
1101fc7e71c5SEnji Cooper 	}
1102fc7e71c5SEnji Cooper 
1103d1d669bdSPawel Jakub Dawidek 	if (!g_gate_verbose) {
1104d1d669bdSPawel Jakub Dawidek 		/* Run in daemon mode. */
1105122abe03SPawel Jakub Dawidek 		if (daemon(0, 0) == -1)
11067be67fe3SPawel Jakub Dawidek 			g_gate_xlog("Cannot daemonize: %s", strerror(errno));
1107d1d669bdSPawel Jakub Dawidek 	}
1108d1d669bdSPawel Jakub Dawidek 
1109fc7e71c5SEnji Cooper 	pidfile_write(pfh);
1110fc7e71c5SEnji Cooper 
1111d1d669bdSPawel Jakub Dawidek 	signal(SIGCHLD, SIG_IGN);
1112d1d669bdSPawel Jakub Dawidek 
1113d1d669bdSPawel Jakub Dawidek 	sfd = socket(AF_INET, SOCK_STREAM, 0);
1114122abe03SPawel Jakub Dawidek 	if (sfd == -1)
11157be67fe3SPawel Jakub Dawidek 		g_gate_xlog("Cannot open stream socket: %s.", strerror(errno));
1116d1d669bdSPawel Jakub Dawidek 	bzero(&serv, sizeof(serv));
1117d1d669bdSPawel Jakub Dawidek 	serv.sin_family = AF_INET;
1118d1d669bdSPawel Jakub Dawidek 	serv.sin_addr.s_addr = bindaddr;
1119d1d669bdSPawel Jakub Dawidek 	serv.sin_port = htons(port);
11207be67fe3SPawel Jakub Dawidek 
11217be67fe3SPawel Jakub Dawidek 	g_gate_socket_settings(sfd);
11227be67fe3SPawel Jakub Dawidek 
1123122abe03SPawel Jakub Dawidek 	if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1)
1124d1d669bdSPawel Jakub Dawidek 		g_gate_xlog("bind(): %s.", strerror(errno));
1125122abe03SPawel Jakub Dawidek 	if (listen(sfd, 5) == -1)
1126d1d669bdSPawel Jakub Dawidek 		g_gate_xlog("listen(): %s.", strerror(errno));
1127d1d669bdSPawel Jakub Dawidek 
1128d1d669bdSPawel Jakub Dawidek 	g_gate_log(LOG_INFO, "Listen on port: %d.", port);
1129d1d669bdSPawel Jakub Dawidek 
1130d1d669bdSPawel Jakub Dawidek 	signal(SIGHUP, huphandler);
1131d1d669bdSPawel Jakub Dawidek 
1132d1d669bdSPawel Jakub Dawidek 	for (;;) {
1133d1d669bdSPawel Jakub Dawidek 		fromlen = sizeof(from);
1134d1d669bdSPawel Jakub Dawidek 		tmpsfd = accept(sfd, &from, &fromlen);
1135122abe03SPawel Jakub Dawidek 		if (tmpsfd == -1)
1136d1d669bdSPawel Jakub Dawidek 			g_gate_xlog("accept(): %s.", strerror(errno));
1137d1d669bdSPawel Jakub Dawidek 
1138d1d669bdSPawel Jakub Dawidek 		if (got_sighup) {
1139d1d669bdSPawel Jakub Dawidek 			got_sighup = 0;
1140d1d669bdSPawel Jakub Dawidek 			exports_get();
1141d1d669bdSPawel Jakub Dawidek 		}
1142d1d669bdSPawel Jakub Dawidek 
11437be67fe3SPawel Jakub Dawidek 		if (!handshake(&from, tmpsfd))
1144d1d669bdSPawel Jakub Dawidek 			close(tmpsfd);
1145d1d669bdSPawel Jakub Dawidek 	}
1146d1d669bdSPawel Jakub Dawidek 	close(sfd);
1147fc7e71c5SEnji Cooper 	pidfile_remove(pfh);
1148d1d669bdSPawel Jakub Dawidek 	exit(EXIT_SUCCESS);
1149d1d669bdSPawel Jakub Dawidek }
1150