1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
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
29#include <stdio.h>
30#include <stdlib.h>
31#include <stdint.h>
32#include <fcntl.h>
33#include <unistd.h>
34#include <string.h>
35#include <ctype.h>
36#include <inttypes.h>
37#include <libgen.h>
38#include <pthread.h>
39#include <signal.h>
40#include <err.h>
41#include <errno.h>
42#include <assert.h>
43
44#include <sys/param.h>
45#include <sys/ioctl.h>
46#include <sys/socket.h>
47#include <sys/sysctl.h>
48#include <sys/syslog.h>
49#include <sys/time.h>
50#include <sys/bio.h>
51#include <netinet/in.h>
52#include <netinet/tcp.h>
53#include <arpa/inet.h>
54
55#include <geom/gate/g_gate.h>
56#include "ggate.h"
57
58
59static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
60
61static const char *path = NULL;
62static const char *host = NULL;
63static int unit = G_GATE_UNIT_AUTO;
64static unsigned flags = 0;
65static int force = 0;
66static unsigned queue_size = G_GATE_QUEUE_SIZE;
67static unsigned port = G_GATE_PORT;
68static off_t mediasize;
69static unsigned sectorsize = 0;
70static unsigned timeout = G_GATE_TIMEOUT;
71static int sendfd, recvfd;
72static uint32_t token;
73static pthread_t sendtd, recvtd;
74static int reconnect;
75static int initialbuffersize = 131072;
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	size_t buf_capacity;
97	ssize_t numbytesprocd;
98	int error;
99	char *newbuf;
100
101	g_gate_log(LOG_NOTICE, "%s: started!", __func__);
102
103	buf_capacity = initialbuffersize;
104
105	ggio.gctl_version = G_GATE_VERSION;
106	ggio.gctl_unit = unit;
107	ggio.gctl_data = malloc(buf_capacity);
108	if (ggio.gctl_data == NULL) {
109		g_gate_log(LOG_ERR, "%s: Cannot alloc buffer.", __func__);
110		pthread_exit(NULL);
111	}
112
113	for (;;) {
114		ggio.gctl_length = buf_capacity;
115		ggio.gctl_error = 0;
116		g_gate_ioctl(G_GATE_CMD_START, &ggio);
117		error = ggio.gctl_error;
118		switch (error) {
119		case 0:
120			break;
121		case ECANCELED:
122			if (reconnect)
123				break;
124			/* Exit gracefully. */
125			g_gate_close_device();
126			exit(EXIT_SUCCESS);
127
128		case ENOMEM:
129		{
130			/* Buffer too small. */
131			g_gate_log(LOG_DEBUG, "buffer too small. new size: %u",
132			    ggio.gctl_length);
133			newbuf = malloc(ggio.gctl_length);
134			if (newbuf != NULL) {
135				free(ggio.gctl_data);
136				ggio.gctl_data = newbuf;
137				buf_capacity = ggio.gctl_length;
138				continue;
139			}
140			/* FALLTHROUGH */
141		}
142
143		case ENXIO:
144		default:
145			g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
146			    strerror(error));
147		}
148
149		if (reconnect)
150			break;
151
152		switch (ggio.gctl_cmd) {
153		case BIO_READ:
154			hdr.gh_cmd = GGATE_CMD_READ;
155			break;
156		case BIO_WRITE:
157			hdr.gh_cmd = GGATE_CMD_WRITE;
158			break;
159		case BIO_FLUSH:
160			hdr.gh_cmd = GGATE_CMD_FLUSH;
161			break;
162		default:
163			g_gate_log(LOG_NOTICE, "Unknown gctl_cmd: %i",
164			    ggio.gctl_cmd);
165			ggio.gctl_error = EOPNOTSUPP;
166			g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
167			continue;
168		}
169
170		hdr.gh_seq = ggio.gctl_seq;
171		hdr.gh_offset = ggio.gctl_offset;
172		hdr.gh_length = ggio.gctl_length;
173		hdr.gh_error = 0;
174		g_gate_swap2n_hdr(&hdr);
175
176		numbytesprocd = g_gate_send(sendfd, &hdr, sizeof(hdr), MSG_NOSIGNAL);
177		g_gate_log(LOG_DEBUG, "Sent hdr packet.");
178		g_gate_swap2h_hdr(&hdr);
179		if (reconnect)
180			break;
181		if (numbytesprocd != sizeof(hdr)) {
182			g_gate_log(LOG_ERR, "Lost connection 1.");
183			reconnect = 1;
184			pthread_kill(recvtd, SIGUSR1);
185			break;
186		}
187
188		if (hdr.gh_cmd == GGATE_CMD_WRITE) {
189			numbytesprocd = g_gate_send(sendfd, ggio.gctl_data,
190			    ggio.gctl_length, MSG_NOSIGNAL);
191			if (reconnect)
192				break;
193			if (numbytesprocd != ggio.gctl_length) {
194				g_gate_log(LOG_ERR, "Lost connection 2 (%zd != %zd).",
195				    numbytesprocd, (ssize_t)ggio.gctl_length);
196				reconnect = 1;
197				pthread_kill(recvtd, SIGUSR1);
198				break;
199			}
200			g_gate_log(LOG_DEBUG, "Sent %zd bytes (offset=%"
201			    PRIu64 ", length=%" PRIu32 ").", numbytesprocd,
202			    hdr.gh_offset, hdr.gh_length);
203		}
204	}
205	g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
206	return (NULL);
207}
208
209static void *
210recv_thread(void *arg __unused)
211{
212	struct g_gate_ctl_io ggio;
213	struct g_gate_hdr hdr;
214	ssize_t buf_capacity;
215	ssize_t numbytesprocd;
216	char *newbuf;
217
218	g_gate_log(LOG_NOTICE, "%s: started!", __func__);
219
220	buf_capacity = initialbuffersize;
221
222	ggio.gctl_version = G_GATE_VERSION;
223	ggio.gctl_unit = unit;
224	ggio.gctl_data = malloc(buf_capacity);
225	if (ggio.gctl_data == NULL) {
226		g_gate_log(LOG_ERR, "%s: Cannot alloc buffer.", __func__);
227		pthread_exit(NULL);
228	}
229
230	for (;;) {
231		numbytesprocd = g_gate_recv(recvfd, &hdr, sizeof(hdr), MSG_WAITALL);
232		if (reconnect)
233			break;
234		g_gate_swap2h_hdr(&hdr);
235		if (numbytesprocd != sizeof(hdr)) {
236			if (numbytesprocd == -1 && errno == EAGAIN)
237				continue;
238			g_gate_log(LOG_ERR, "Lost connection 3.");
239			reconnect = 1;
240			pthread_kill(sendtd, SIGUSR1);
241			break;
242		}
243		g_gate_log(LOG_DEBUG, "Received hdr packet.");
244
245		ggio.gctl_seq = hdr.gh_seq;
246		ggio.gctl_cmd = hdr.gh_cmd;
247		ggio.gctl_offset = hdr.gh_offset;
248		ggio.gctl_length = hdr.gh_length;
249		ggio.gctl_error = hdr.gh_error;
250
251		if (ggio.gctl_length > buf_capacity) {
252			newbuf = malloc(ggio.gctl_length);
253			if (newbuf != NULL) {
254				free(ggio.gctl_data);
255				ggio.gctl_data = newbuf;
256				buf_capacity = ggio.gctl_length;
257			} else {
258				g_gate_log(LOG_ERR, "Received too big response: %zd",
259				    ggio.gctl_length);
260				break;
261			}
262		}
263
264		if (ggio.gctl_error == 0 && ggio.gctl_cmd == GGATE_CMD_READ) {
265			numbytesprocd = g_gate_recv(recvfd, ggio.gctl_data,
266			    ggio.gctl_length, MSG_WAITALL);
267			if (reconnect)
268				break;
269			g_gate_log(LOG_DEBUG, "Received data packet.");
270			if (numbytesprocd != ggio.gctl_length) {
271				g_gate_log(LOG_ERR, "Lost connection 4.");
272				reconnect = 1;
273				pthread_kill(sendtd, SIGUSR1);
274				break;
275			}
276			g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%"
277			    PRIu64 ", length=%" PRIu32 ").", numbytesprocd,
278			    hdr.gh_offset, hdr.gh_length);
279		}
280
281		g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
282	}
283	g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
284	pthread_exit(NULL);
285}
286
287static int
288handshake(int dir)
289{
290	struct g_gate_version ver;
291	struct g_gate_cinit cinit;
292	struct g_gate_sinit sinit;
293	struct sockaddr_in serv;
294	int sfd;
295
296	/*
297	 * Do the network stuff.
298	 */
299	bzero(&serv, sizeof(serv));
300	serv.sin_family = AF_INET;
301	serv.sin_addr.s_addr = g_gate_str2ip(host);
302	if (serv.sin_addr.s_addr == INADDR_NONE) {
303		g_gate_log(LOG_DEBUG, "Invalid IP/host name: %s.", host);
304		return (-1);
305	}
306	serv.sin_port = htons(port);
307	sfd = socket(AF_INET, SOCK_STREAM, 0);
308	if (sfd == -1) {
309		g_gate_log(LOG_DEBUG, "Cannot open socket: %s.",
310		    strerror(errno));
311		return (-1);
312	}
313
314	g_gate_socket_settings(sfd);
315
316	if (connect(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) {
317		g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.",
318		    strerror(errno));
319		close(sfd);
320		return (-1);
321	}
322
323	g_gate_log(LOG_INFO, "Connected to the server: %s:%d.", host, port);
324
325	/*
326	 * Create and send version packet.
327	 */
328	g_gate_log(LOG_DEBUG, "Sending version packet.");
329	assert(strlen(GGATE_MAGIC) == sizeof(ver.gv_magic));
330	bcopy(GGATE_MAGIC, ver.gv_magic, sizeof(ver.gv_magic));
331	ver.gv_version = GGATE_VERSION;
332	ver.gv_error = 0;
333	g_gate_swap2n_version(&ver);
334	if (g_gate_send(sfd, &ver, sizeof(ver), MSG_NOSIGNAL) == -1) {
335		g_gate_log(LOG_DEBUG, "Error while sending version packet: %s.",
336		    strerror(errno));
337		close(sfd);
338		return (-1);
339	}
340	bzero(&ver, sizeof(ver));
341	if (g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL) == -1) {
342		g_gate_log(LOG_DEBUG, "Error while receiving data: %s.",
343		    strerror(errno));
344		close(sfd);
345		return (-1);
346	}
347	if (ver.gv_error != 0) {
348		g_gate_log(LOG_DEBUG, "Version verification problem: %s.",
349		    strerror(errno));
350		close(sfd);
351		return (-1);
352	}
353
354	/*
355	 * Create and send initial packet.
356	 */
357	g_gate_log(LOG_DEBUG, "Sending initial packet.");
358	if (strlcpy(cinit.gc_path, path, sizeof(cinit.gc_path)) >=
359	    sizeof(cinit.gc_path)) {
360		g_gate_log(LOG_DEBUG, "Path name too long.");
361		close(sfd);
362		return (-1);
363	}
364	cinit.gc_flags = flags | dir;
365	cinit.gc_token = token;
366	cinit.gc_nconn = 2;
367	g_gate_swap2n_cinit(&cinit);
368	if (g_gate_send(sfd, &cinit, sizeof(cinit), MSG_NOSIGNAL) == -1) {
369	        g_gate_log(LOG_DEBUG, "Error while sending initial packet: %s.",
370		    strerror(errno));
371		close(sfd);
372		return (-1);
373	}
374	g_gate_swap2h_cinit(&cinit);
375
376	/*
377	 * Receiving initial packet from server.
378	 */
379	g_gate_log(LOG_DEBUG, "Receiving initial packet.");
380	if (g_gate_recv(sfd, &sinit, sizeof(sinit), MSG_WAITALL) == -1) {
381		g_gate_log(LOG_DEBUG, "Error while receiving data: %s.",
382		    strerror(errno));
383		close(sfd);
384		return (-1);
385	}
386	g_gate_swap2h_sinit(&sinit);
387	if (sinit.gs_error != 0) {
388	        g_gate_log(LOG_DEBUG, "Error from server: %s.",
389		    strerror(sinit.gs_error));
390		close(sfd);
391		return (-1);
392	}
393	g_gate_log(LOG_DEBUG, "Received initial packet.");
394
395	mediasize = sinit.gs_mediasize;
396	if (sectorsize == 0)
397		sectorsize = sinit.gs_sectorsize;
398
399	return (sfd);
400}
401
402static void
403mydaemon(void)
404{
405
406	if (g_gate_verbose > 0)
407		return;
408	if (daemon(0, 0) == 0)
409		return;
410	if (action == CREATE)
411		g_gate_destroy(unit, 1);
412	err(EXIT_FAILURE, "Cannot daemonize");
413}
414
415static int
416g_gatec_connect(void)
417{
418
419	token = arc4random();
420	/*
421	 * Our receive descriptor is connected to the send descriptor on the
422	 * server side.
423	 */
424	recvfd = handshake(GGATE_FLAG_SEND);
425	if (recvfd == -1)
426		return (0);
427	/*
428	 * Our send descriptor is connected to the receive descriptor on the
429	 * server side.
430	 */
431	sendfd = handshake(GGATE_FLAG_RECV);
432	if (sendfd == -1)
433		return (0);
434	return (1);
435}
436
437static void
438g_gatec_start(void)
439{
440	int error;
441
442	reconnect = 0;
443	error = pthread_create(&recvtd, NULL, recv_thread, NULL);
444	if (error != 0) {
445		g_gate_destroy(unit, 1);
446		g_gate_xlog("pthread_create(recv_thread): %s.",
447		    strerror(error));
448	}
449	sendtd = pthread_self();
450	send_thread(NULL);
451	/* Disconnected. */
452	close(sendfd);
453	close(recvfd);
454}
455
456static void
457signop(int sig __unused)
458{
459
460	/* Do nothing. */
461}
462
463static void
464g_gatec_loop(void)
465{
466	struct g_gate_ctl_cancel ggioc;
467
468	signal(SIGUSR1, signop);
469	for (;;) {
470		g_gatec_start();
471		g_gate_log(LOG_NOTICE, "Disconnected [%s %s]. Connecting...",
472		    host, path);
473		while (!g_gatec_connect()) {
474			sleep(2);
475			g_gate_log(LOG_NOTICE, "Connecting [%s %s]...", host,
476			    path);
477		}
478		ggioc.gctl_version = G_GATE_VERSION;
479		ggioc.gctl_unit = unit;
480		ggioc.gctl_seq = 0;
481		g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
482	}
483}
484
485static void
486g_gatec_create(void)
487{
488	struct g_gate_ctl_create ggioc;
489
490	if (!g_gatec_connect())
491		g_gate_xlog("Cannot connect: %s.", strerror(errno));
492
493	/*
494	 * Ok, got both sockets, time to create provider.
495	 */
496	memset(&ggioc, 0, sizeof(ggioc));
497	ggioc.gctl_version = G_GATE_VERSION;
498	ggioc.gctl_mediasize = mediasize;
499	ggioc.gctl_sectorsize = sectorsize;
500	ggioc.gctl_flags = flags;
501	ggioc.gctl_maxcount = queue_size;
502	ggioc.gctl_timeout = timeout;
503	ggioc.gctl_unit = unit;
504	snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s:%u %s", host,
505	    port, path);
506	g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
507	if (unit == -1) {
508		printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
509		fflush(stdout);
510	}
511	unit = ggioc.gctl_unit;
512
513	mydaemon();
514	g_gatec_loop();
515}
516
517static void
518g_gatec_rescue(void)
519{
520	struct g_gate_ctl_cancel ggioc;
521
522	if (!g_gatec_connect())
523		g_gate_xlog("Cannot connect: %s.", strerror(errno));
524
525	ggioc.gctl_version = G_GATE_VERSION;
526	ggioc.gctl_unit = unit;
527	ggioc.gctl_seq = 0;
528	g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
529
530	mydaemon();
531	g_gatec_loop();
532}
533
534static void
535init_initial_buffer_size(void)
536{
537	int value;
538	size_t intsize;
539	intsize = sizeof(initialbuffersize);
540	if (sysctlbyname("kern.maxphys", &value, &intsize, NULL, 0) == 0)
541		initialbuffersize = value;
542}
543
544int
545main(int argc, char *argv[])
546{
547
548	if (argc < 2)
549		usage();
550	if (strcasecmp(argv[1], "create") == 0)
551		action = CREATE;
552	else if (strcasecmp(argv[1], "destroy") == 0)
553		action = DESTROY;
554	else if (strcasecmp(argv[1], "list") == 0)
555		action = LIST;
556	else if (strcasecmp(argv[1], "rescue") == 0)
557		action = RESCUE;
558	else
559		usage();
560	argc -= 1;
561	argv += 1;
562	for (;;) {
563		int ch;
564
565		ch = getopt(argc, argv, "fno:p:q:R:S:s:t:u:v");
566		if (ch == -1)
567			break;
568		switch (ch) {
569		case 'f':
570			if (action != DESTROY)
571				usage();
572			force = 1;
573			break;
574		case 'n':
575			if (action != CREATE && action != RESCUE)
576				usage();
577			nagle = 0;
578			break;
579		case 'o':
580			if (action != CREATE && action != RESCUE)
581				usage();
582			if (strcasecmp("ro", optarg) == 0)
583				flags = G_GATE_FLAG_READONLY;
584			else if (strcasecmp("wo", optarg) == 0)
585				flags = G_GATE_FLAG_WRITEONLY;
586			else if (strcasecmp("rw", optarg) == 0)
587				flags = 0;
588			else {
589				errx(EXIT_FAILURE,
590				    "Invalid argument for '-o' option.");
591			}
592			break;
593		case 'p':
594			if (action != CREATE && action != RESCUE)
595				usage();
596			errno = 0;
597			port = strtoul(optarg, NULL, 10);
598			if (port == 0 && errno != 0)
599				errx(EXIT_FAILURE, "Invalid port.");
600			break;
601		case 'q':
602			if (action != CREATE)
603				usage();
604			errno = 0;
605			queue_size = strtoul(optarg, NULL, 10);
606			if (queue_size == 0 && errno != 0)
607				errx(EXIT_FAILURE, "Invalid queue_size.");
608			break;
609		case 'R':
610			if (action != CREATE && action != RESCUE)
611				usage();
612			errno = 0;
613			rcvbuf = strtoul(optarg, NULL, 10);
614			if (rcvbuf == 0 && errno != 0)
615				errx(EXIT_FAILURE, "Invalid rcvbuf.");
616			break;
617		case 'S':
618			if (action != CREATE && action != RESCUE)
619				usage();
620			errno = 0;
621			sndbuf = strtoul(optarg, NULL, 10);
622			if (sndbuf == 0 && errno != 0)
623				errx(EXIT_FAILURE, "Invalid sndbuf.");
624			break;
625		case 's':
626			if (action != CREATE)
627				usage();
628			errno = 0;
629			sectorsize = strtoul(optarg, NULL, 10);
630			if (sectorsize == 0 && errno != 0)
631				errx(EXIT_FAILURE, "Invalid sectorsize.");
632			break;
633		case 't':
634			if (action != CREATE)
635				usage();
636			errno = 0;
637			timeout = strtoul(optarg, NULL, 10);
638			if (timeout == 0 && errno != 0)
639				errx(EXIT_FAILURE, "Invalid timeout.");
640			break;
641		case 'u':
642			errno = 0;
643			unit = strtol(optarg, NULL, 10);
644			if (unit == 0 && errno != 0)
645				errx(EXIT_FAILURE, "Invalid unit number.");
646			break;
647		case 'v':
648			if (action == DESTROY)
649				usage();
650			g_gate_verbose++;
651			break;
652		default:
653			usage();
654		}
655	}
656	argc -= optind;
657	argv += optind;
658
659	init_initial_buffer_size();
660
661	switch (action) {
662	case CREATE:
663		if (argc != 2)
664			usage();
665		g_gate_load_module();
666		g_gate_open_device();
667		host = argv[0];
668		path = argv[1];
669		g_gatec_create();
670		break;
671	case DESTROY:
672		if (unit == -1) {
673			fprintf(stderr, "Required unit number.\n");
674			usage();
675		}
676		g_gate_verbose = 1;
677		g_gate_open_device();
678		g_gate_destroy(unit, force);
679		break;
680	case LIST:
681		g_gate_list(unit, g_gate_verbose);
682		break;
683	case RESCUE:
684		if (argc != 2)
685			usage();
686		if (unit == -1) {
687			fprintf(stderr, "Required unit number.\n");
688			usage();
689		}
690		g_gate_open_device();
691		host = argv[0];
692		path = argv[1];
693		g_gatec_rescue();
694		break;
695	case UNSET:
696	default:
697		usage();
698	}
699	g_gate_close_device();
700	exit(EXIT_SUCCESS);
701}
702