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