1/*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 *
28 * $FreeBSD: stable/11/sbin/ggate/ggatec/ggatec.c 370381 2021-08-24 18:29:35Z gordon $
29 */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <stdint.h>
34#include <fcntl.h>
35#include <unistd.h>
36#include <string.h>
37#include <ctype.h>
38#include <libgen.h>
39#include <pthread.h>
40#include <signal.h>
41#include <err.h>
42#include <errno.h>
43#include <assert.h>
44
45#include <sys/param.h>
46#include <sys/ioctl.h>
47#include <sys/socket.h>
48#include <sys/sysctl.h>
49#include <sys/syslog.h>
50#include <sys/time.h>
51#include <sys/bio.h>
52#include <netinet/in.h>
53#include <netinet/tcp.h>
54#include <arpa/inet.h>
55
56#include <geom/gate/g_gate.h>
57#include "ggate.h"
58
59
60static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
61
62static const char *path = NULL;
63static const char *host = NULL;
64static int unit = G_GATE_UNIT_AUTO;
65static unsigned flags = 0;
66static int force = 0;
67static unsigned queue_size = G_GATE_QUEUE_SIZE;
68static unsigned port = G_GATE_PORT;
69static off_t mediasize;
70static unsigned sectorsize = 0;
71static unsigned timeout = G_GATE_TIMEOUT;
72static int sendfd, recvfd;
73static uint32_t token;
74static pthread_t sendtd, recvtd;
75static int reconnect;
76
77static void
78usage(void)
79{
80
81	fprintf(stderr, "usage: %s create [-nv] [-o <ro|wo|rw>] [-p port] "
82	    "[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] "
83	    "[-t timeout] [-u unit] <host> <path>\n", getprogname());
84	fprintf(stderr, "       %s rescue [-nv] [-o <ro|wo|rw>] [-p port] "
85	    "[-R rcvbuf] [-S sndbuf] <-u unit> <host> <path>\n", getprogname());
86	fprintf(stderr, "       %s destroy [-f] <-u unit>\n", getprogname());
87	fprintf(stderr, "       %s list [-v] [-u unit]\n", getprogname());
88	exit(EXIT_FAILURE);
89}
90
91static void *
92send_thread(void *arg __unused)
93{
94	struct g_gate_ctl_io ggio;
95	struct g_gate_hdr hdr;
96	char buf[MAXPHYS];
97	ssize_t data;
98	int error;
99
100	g_gate_log(LOG_NOTICE, "%s: started!", __func__);
101
102	ggio.gctl_version = G_GATE_VERSION;
103	ggio.gctl_unit = unit;
104	ggio.gctl_data = buf;
105
106	for (;;) {
107		ggio.gctl_length = sizeof(buf);
108		ggio.gctl_error = 0;
109		g_gate_ioctl(G_GATE_CMD_START, &ggio);
110		error = ggio.gctl_error;
111		switch (error) {
112		case 0:
113			break;
114		case ECANCELED:
115			if (reconnect)
116				break;
117			/* Exit gracefully. */
118			g_gate_close_device();
119			exit(EXIT_SUCCESS);
120#if 0
121		case ENOMEM:
122			/* Buffer too small. */
123			ggio.gctl_data = realloc(ggio.gctl_data,
124			    ggio.gctl_length);
125			if (ggio.gctl_data != NULL) {
126				bsize = ggio.gctl_length;
127				goto once_again;
128			}
129			/* FALLTHROUGH */
130#endif
131		case ENXIO:
132		default:
133			g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
134			    strerror(error));
135		}
136
137		if (reconnect)
138			break;
139
140		switch (ggio.gctl_cmd) {
141		case BIO_READ:
142			hdr.gh_cmd = GGATE_CMD_READ;
143			break;
144		case BIO_WRITE:
145			hdr.gh_cmd = GGATE_CMD_WRITE;
146			break;
147		default:
148			g_gate_log(LOG_NOTICE, "Unknown gctl_cmd: %i", ggio.gctl_cmd);
149			ggio.gctl_error = EOPNOTSUPP;
150			g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
151			continue;
152		}
153
154		/* Don't send requests for more data than we can handle the response for! */
155		if (ggio.gctl_length > MAXPHYS) {
156			g_gate_log(LOG_ERR, "Request too big: %zd", ggio.gctl_length);
157			ggio.gctl_error = EOPNOTSUPP;
158			g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
159			continue;
160		}
161
162		hdr.gh_seq = ggio.gctl_seq;
163		hdr.gh_offset = ggio.gctl_offset;
164		hdr.gh_length = ggio.gctl_length;
165		hdr.gh_error = 0;
166		g_gate_swap2n_hdr(&hdr);
167
168		data = g_gate_send(sendfd, &hdr, sizeof(hdr), MSG_NOSIGNAL);
169		g_gate_log(LOG_DEBUG, "Sent hdr packet.");
170		g_gate_swap2h_hdr(&hdr);
171		if (reconnect)
172			break;
173		if (data != sizeof(hdr)) {
174			g_gate_log(LOG_ERR, "Lost connection 1.");
175			reconnect = 1;
176			pthread_kill(recvtd, SIGUSR1);
177			break;
178		}
179
180		if (hdr.gh_cmd == GGATE_CMD_WRITE) {
181			data = g_gate_send(sendfd, ggio.gctl_data,
182			    ggio.gctl_length, MSG_NOSIGNAL);
183			if (reconnect)
184				break;
185			if (data != ggio.gctl_length) {
186				g_gate_log(LOG_ERR, "Lost connection 2 (%zd != %zd).", data, (ssize_t)ggio.gctl_length);
187				reconnect = 1;
188				pthread_kill(recvtd, SIGUSR1);
189				break;
190			}
191			g_gate_log(LOG_DEBUG, "Sent %zd bytes (offset=%llu, "
192			    "size=%u).", data, hdr.gh_offset, hdr.gh_length);
193		}
194	}
195	g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
196	return (NULL);
197}
198
199static void *
200recv_thread(void *arg __unused)
201{
202	struct g_gate_ctl_io ggio;
203	struct g_gate_hdr hdr;
204	char buf[MAXPHYS];
205	ssize_t data;
206
207	g_gate_log(LOG_NOTICE, "%s: started!", __func__);
208
209	ggio.gctl_version = G_GATE_VERSION;
210	ggio.gctl_unit = unit;
211	ggio.gctl_data = buf;
212
213	for (;;) {
214		data = g_gate_recv(recvfd, &hdr, sizeof(hdr), MSG_WAITALL);
215		if (reconnect)
216			break;
217		g_gate_swap2h_hdr(&hdr);
218		if (data != sizeof(hdr)) {
219			if (data == -1 && errno == EAGAIN)
220				continue;
221			g_gate_log(LOG_ERR, "Lost connection 3.");
222			reconnect = 1;
223			pthread_kill(sendtd, SIGUSR1);
224			break;
225		}
226		g_gate_log(LOG_DEBUG, "Received hdr packet.");
227
228		ggio.gctl_seq = hdr.gh_seq;
229		ggio.gctl_cmd = hdr.gh_cmd;
230		ggio.gctl_offset = hdr.gh_offset;
231		ggio.gctl_length = hdr.gh_length;
232		ggio.gctl_error = hdr.gh_error;
233
234		/* Do not overflow our buffer if there is a bogus response. */
235		if (ggio.gctl_length > (off_t) sizeof(buf)) {
236			g_gate_log(LOG_ERR, "Received too big response: %zd", ggio.gctl_length);
237			break;
238		}
239
240		if (ggio.gctl_error == 0 && ggio.gctl_cmd == GGATE_CMD_READ) {
241			data = g_gate_recv(recvfd, ggio.gctl_data,
242			    ggio.gctl_length, MSG_WAITALL);
243			if (reconnect)
244				break;
245			g_gate_log(LOG_DEBUG, "Received data packet.");
246			if (data != ggio.gctl_length) {
247				g_gate_log(LOG_ERR, "Lost connection 4.");
248				reconnect = 1;
249				pthread_kill(sendtd, SIGUSR1);
250				break;
251			}
252			g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%ju, "
253			    "size=%zu).", data, (uintmax_t)hdr.gh_offset,
254			    (size_t)hdr.gh_length);
255		}
256
257		g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
258	}
259	g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
260	pthread_exit(NULL);
261}
262
263static int
264handshake(int dir)
265{
266	struct g_gate_version ver;
267	struct g_gate_cinit cinit;
268	struct g_gate_sinit sinit;
269	struct sockaddr_in serv;
270	int sfd;
271
272	/*
273	 * Do the network stuff.
274	 */
275	bzero(&serv, sizeof(serv));
276	serv.sin_family = AF_INET;
277	serv.sin_addr.s_addr = g_gate_str2ip(host);
278	if (serv.sin_addr.s_addr == INADDR_NONE) {
279		g_gate_log(LOG_DEBUG, "Invalid IP/host name: %s.", host);
280		return (-1);
281	}
282	serv.sin_port = htons(port);
283	sfd = socket(AF_INET, SOCK_STREAM, 0);
284	if (sfd == -1) {
285		g_gate_log(LOG_DEBUG, "Cannot open socket: %s.",
286		    strerror(errno));
287		return (-1);
288	}
289
290	g_gate_socket_settings(sfd);
291
292	if (connect(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) {
293		g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.",
294		    strerror(errno));
295		close(sfd);
296		return (-1);
297	}
298
299	g_gate_log(LOG_INFO, "Connected to the server: %s:%d.", host, port);
300
301	/*
302	 * Create and send version packet.
303	 */
304	g_gate_log(LOG_DEBUG, "Sending version packet.");
305	assert(strlen(GGATE_MAGIC) == sizeof(ver.gv_magic));
306	bcopy(GGATE_MAGIC, ver.gv_magic, sizeof(ver.gv_magic));
307	ver.gv_version = GGATE_VERSION;
308	ver.gv_error = 0;
309	g_gate_swap2n_version(&ver);
310	if (g_gate_send(sfd, &ver, sizeof(ver), MSG_NOSIGNAL) == -1) {
311		g_gate_log(LOG_DEBUG, "Error while sending version packet: %s.",
312		    strerror(errno));
313		close(sfd);
314		return (-1);
315	}
316	bzero(&ver, sizeof(ver));
317	if (g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL) == -1) {
318		g_gate_log(LOG_DEBUG, "Error while receiving data: %s.",
319		    strerror(errno));
320		close(sfd);
321		return (-1);
322	}
323	if (ver.gv_error != 0) {
324		g_gate_log(LOG_DEBUG, "Version verification problem: %s.",
325		    strerror(errno));
326		close(sfd);
327		return (-1);
328	}
329
330	/*
331	 * Create and send initial packet.
332	 */
333	g_gate_log(LOG_DEBUG, "Sending initial packet.");
334	if (strlcpy(cinit.gc_path, path, sizeof(cinit.gc_path)) >=
335	    sizeof(cinit.gc_path)) {
336		g_gate_log(LOG_DEBUG, "Path name too long.");
337		close(sfd);
338		return (-1);
339	}
340	cinit.gc_flags = flags | dir;
341	cinit.gc_token = token;
342	cinit.gc_nconn = 2;
343	g_gate_swap2n_cinit(&cinit);
344	if (g_gate_send(sfd, &cinit, sizeof(cinit), MSG_NOSIGNAL) == -1) {
345	        g_gate_log(LOG_DEBUG, "Error while sending initial packet: %s.",
346		    strerror(errno));
347		close(sfd);
348		return (-1);
349	}
350	g_gate_swap2h_cinit(&cinit);
351
352	/*
353	 * Receiving initial packet from server.
354	 */
355	g_gate_log(LOG_DEBUG, "Receiving initial packet.");
356	if (g_gate_recv(sfd, &sinit, sizeof(sinit), MSG_WAITALL) == -1) {
357		g_gate_log(LOG_DEBUG, "Error while receiving data: %s.",
358		    strerror(errno));
359		close(sfd);
360		return (-1);
361	}
362	g_gate_swap2h_sinit(&sinit);
363	if (sinit.gs_error != 0) {
364	        g_gate_log(LOG_DEBUG, "Error from server: %s.",
365		    strerror(sinit.gs_error));
366		close(sfd);
367		return (-1);
368	}
369	g_gate_log(LOG_DEBUG, "Received initial packet.");
370
371	mediasize = sinit.gs_mediasize;
372	if (sectorsize == 0)
373		sectorsize = sinit.gs_sectorsize;
374
375	return (sfd);
376}
377
378static void
379mydaemon(void)
380{
381
382	if (g_gate_verbose > 0)
383		return;
384	if (daemon(0, 0) == 0)
385		return;
386	if (action == CREATE)
387		g_gate_destroy(unit, 1);
388	err(EXIT_FAILURE, "Cannot daemonize");
389}
390
391static int
392g_gatec_connect(void)
393{
394
395	token = arc4random();
396	/*
397	 * Our receive descriptor is connected to the send descriptor on the
398	 * server side.
399	 */
400	recvfd = handshake(GGATE_FLAG_SEND);
401	if (recvfd == -1)
402		return (0);
403	/*
404	 * Our send descriptor is connected to the receive descriptor on the
405	 * server side.
406	 */
407	sendfd = handshake(GGATE_FLAG_RECV);
408	if (sendfd == -1)
409		return (0);
410	return (1);
411}
412
413static void
414g_gatec_start(void)
415{
416	int error;
417
418	reconnect = 0;
419	error = pthread_create(&recvtd, NULL, recv_thread, NULL);
420	if (error != 0) {
421		g_gate_destroy(unit, 1);
422		g_gate_xlog("pthread_create(recv_thread): %s.",
423		    strerror(error));
424	}
425	sendtd = pthread_self();
426	send_thread(NULL);
427	/* Disconnected. */
428	close(sendfd);
429	close(recvfd);
430}
431
432static void
433signop(int sig __unused)
434{
435
436	/* Do nothing. */
437}
438
439static void
440g_gatec_loop(void)
441{
442	struct g_gate_ctl_cancel ggioc;
443
444	signal(SIGUSR1, signop);
445	for (;;) {
446		g_gatec_start();
447		g_gate_log(LOG_NOTICE, "Disconnected [%s %s]. Connecting...",
448		    host, path);
449		while (!g_gatec_connect()) {
450			sleep(2);
451			g_gate_log(LOG_NOTICE, "Connecting [%s %s]...", host,
452			    path);
453		}
454		ggioc.gctl_version = G_GATE_VERSION;
455		ggioc.gctl_unit = unit;
456		ggioc.gctl_seq = 0;
457		g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
458	}
459}
460
461static void
462g_gatec_create(void)
463{
464	struct g_gate_ctl_create ggioc;
465
466	if (!g_gatec_connect())
467		g_gate_xlog("Cannot connect: %s.", strerror(errno));
468
469	/*
470	 * Ok, got both sockets, time to create provider.
471	 */
472	memset(&ggioc, 0, sizeof(ggioc));
473	ggioc.gctl_version = G_GATE_VERSION;
474	ggioc.gctl_mediasize = mediasize;
475	ggioc.gctl_sectorsize = sectorsize;
476	ggioc.gctl_flags = flags;
477	ggioc.gctl_maxcount = queue_size;
478	ggioc.gctl_timeout = timeout;
479	ggioc.gctl_unit = unit;
480	snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s:%u %s", host,
481	    port, path);
482	g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
483	if (unit == -1) {
484		printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
485		fflush(stdout);
486	}
487	unit = ggioc.gctl_unit;
488
489	mydaemon();
490	g_gatec_loop();
491}
492
493static void
494g_gatec_rescue(void)
495{
496	struct g_gate_ctl_cancel ggioc;
497
498	if (!g_gatec_connect())
499		g_gate_xlog("Cannot connect: %s.", strerror(errno));
500
501	ggioc.gctl_version = G_GATE_VERSION;
502	ggioc.gctl_unit = unit;
503	ggioc.gctl_seq = 0;
504	g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
505
506	mydaemon();
507	g_gatec_loop();
508}
509
510int
511main(int argc, char *argv[])
512{
513
514	if (argc < 2)
515		usage();
516	if (strcasecmp(argv[1], "create") == 0)
517		action = CREATE;
518	else if (strcasecmp(argv[1], "destroy") == 0)
519		action = DESTROY;
520	else if (strcasecmp(argv[1], "list") == 0)
521		action = LIST;
522	else if (strcasecmp(argv[1], "rescue") == 0)
523		action = RESCUE;
524	else
525		usage();
526	argc -= 1;
527	argv += 1;
528	for (;;) {
529		int ch;
530
531		ch = getopt(argc, argv, "fno:p:q:R:S:s:t:u:v");
532		if (ch == -1)
533			break;
534		switch (ch) {
535		case 'f':
536			if (action != DESTROY)
537				usage();
538			force = 1;
539			break;
540		case 'n':
541			if (action != CREATE && action != RESCUE)
542				usage();
543			nagle = 0;
544			break;
545		case 'o':
546			if (action != CREATE && action != RESCUE)
547				usage();
548			if (strcasecmp("ro", optarg) == 0)
549				flags = G_GATE_FLAG_READONLY;
550			else if (strcasecmp("wo", optarg) == 0)
551				flags = G_GATE_FLAG_WRITEONLY;
552			else if (strcasecmp("rw", optarg) == 0)
553				flags = 0;
554			else {
555				errx(EXIT_FAILURE,
556				    "Invalid argument for '-o' option.");
557			}
558			break;
559		case 'p':
560			if (action != CREATE && action != RESCUE)
561				usage();
562			errno = 0;
563			port = strtoul(optarg, NULL, 10);
564			if (port == 0 && errno != 0)
565				errx(EXIT_FAILURE, "Invalid port.");
566			break;
567		case 'q':
568			if (action != CREATE)
569				usage();
570			errno = 0;
571			queue_size = strtoul(optarg, NULL, 10);
572			if (queue_size == 0 && errno != 0)
573				errx(EXIT_FAILURE, "Invalid queue_size.");
574			break;
575		case 'R':
576			if (action != CREATE && action != RESCUE)
577				usage();
578			errno = 0;
579			rcvbuf = strtoul(optarg, NULL, 10);
580			if (rcvbuf == 0 && errno != 0)
581				errx(EXIT_FAILURE, "Invalid rcvbuf.");
582			break;
583		case 'S':
584			if (action != CREATE && action != RESCUE)
585				usage();
586			errno = 0;
587			sndbuf = strtoul(optarg, NULL, 10);
588			if (sndbuf == 0 && errno != 0)
589				errx(EXIT_FAILURE, "Invalid sndbuf.");
590			break;
591		case 's':
592			if (action != CREATE)
593				usage();
594			errno = 0;
595			sectorsize = strtoul(optarg, NULL, 10);
596			if (sectorsize == 0 && errno != 0)
597				errx(EXIT_FAILURE, "Invalid sectorsize.");
598			break;
599		case 't':
600			if (action != CREATE)
601				usage();
602			errno = 0;
603			timeout = strtoul(optarg, NULL, 10);
604			if (timeout == 0 && errno != 0)
605				errx(EXIT_FAILURE, "Invalid timeout.");
606			break;
607		case 'u':
608			errno = 0;
609			unit = strtol(optarg, NULL, 10);
610			if (unit == 0 && errno != 0)
611				errx(EXIT_FAILURE, "Invalid unit number.");
612			break;
613		case 'v':
614			if (action == DESTROY)
615				usage();
616			g_gate_verbose++;
617			break;
618		default:
619			usage();
620		}
621	}
622	argc -= optind;
623	argv += optind;
624
625	switch (action) {
626	case CREATE:
627		if (argc != 2)
628			usage();
629		g_gate_load_module();
630		g_gate_open_device();
631		host = argv[0];
632		path = argv[1];
633		g_gatec_create();
634		break;
635	case DESTROY:
636		if (unit == -1) {
637			fprintf(stderr, "Required unit number.\n");
638			usage();
639		}
640		g_gate_verbose = 1;
641		g_gate_open_device();
642		g_gate_destroy(unit, force);
643		break;
644	case LIST:
645		g_gate_list(unit, g_gate_verbose);
646		break;
647	case RESCUE:
648		if (argc != 2)
649			usage();
650		if (unit == -1) {
651			fprintf(stderr, "Required unit number.\n");
652			usage();
653		}
654		g_gate_open_device();
655		host = argv[0];
656		path = argv[1];
657		g_gatec_rescue();
658		break;
659	case UNSET:
660	default:
661		usage();
662	}
663	g_gate_close_device();
664	exit(EXIT_SUCCESS);
665}
666