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