xref: /freebsd/sbin/ggate/ggatec/ggatec.c (revision 2546665afcaf0d53dc2c7058fee96354b3680f5a)
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$
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 <err.h>
38 #include <errno.h>
39 #include <sys/param.h>
40 #include <sys/ioctl.h>
41 #include <sys/socket.h>
42 #include <sys/sysctl.h>
43 #include <sys/syslog.h>
44 #include <sys/time.h>
45 #include <sys/bio.h>
46 #include <netinet/in.h>
47 #include <netinet/tcp.h>
48 #include <arpa/inet.h>
49 
50 #include <geom/gate/g_gate.h>
51 #include "ggate.h"
52 
53 
54 enum { UNSET, ATTACH, CREATE, DESTROY, LIST } action = UNSET;
55 
56 static const char *path = NULL;
57 static const char *host = NULL;
58 static int unit = -1;
59 static unsigned flags = 0;
60 static int force = 0;
61 static int nagle = 1;
62 static unsigned queue_size = G_GATE_QUEUE_SIZE;
63 static unsigned port = G_GATE_PORT;
64 static off_t mediasize;
65 static unsigned sectorsize = 0;
66 static unsigned timeout = G_GATE_TIMEOUT;
67 static unsigned rcvbuf = G_GATE_RCVBUF;
68 static unsigned sndbuf = G_GATE_SNDBUF;
69 
70 static void
71 usage(void)
72 {
73 
74 	fprintf(stderr, "usage: %s create [-nv] [-o <ro|wo|rw>] [-p port] "
75 	    "[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] "
76 	    "[-t timeout] [-u unit] <host> <path>\n", getprogname());
77 	fprintf(stderr, "       %s attach [-nv] [-o <ro|wo|rw>] [-p port] "
78 	    "[-R rcvbuf] [-S sndbuf] <-u unit> <host> <path>\n", getprogname());
79 	fprintf(stderr, "       %s destroy [-f] <-u unit>\n", getprogname());
80 	fprintf(stderr, "       %s list [-v] [-u unit]\n", getprogname());
81 	exit(EXIT_FAILURE);
82 }
83 
84 static int
85 handshake(void)
86 {
87 	struct g_gate_cinit cinit;
88 	struct g_gate_sinit sinit;
89 	struct sockaddr_in serv;
90 	struct timeval tv;
91 	size_t bsize;
92 	int sfd;
93 
94 	/*
95 	 * Do the network stuff.
96 	 */
97 	bzero(&serv, sizeof(serv));
98 	serv.sin_family = AF_INET;
99 	serv.sin_addr.s_addr = g_gate_str2ip(host);
100 	if (serv.sin_addr.s_addr == INADDR_NONE) {
101 		g_gate_log(LOG_ERR, "Invalid IP/host name: %s.", host);
102 		return (-1);
103 	}
104 	serv.sin_port = htons(port);
105 	sfd = socket(AF_INET, SOCK_STREAM, 0);
106 	if (sfd < 0)
107 		g_gate_xlog("Can't open socket: %s.", strerror(errno));
108 	/*
109 	 * Some trivial network optimalization.
110 	 * This should be much more advanced.
111 	 */
112 	if (nagle) {
113 		int on = 1;
114 
115 		if (setsockopt(sfd, IPPROTO_TCP, TCP_NODELAY, &on,
116 		    sizeof(on)) < 0) {
117 			g_gate_xlog("setsockopt() error: %s.", strerror(errno));
118 		}
119 	}
120 	bsize = rcvbuf;
121 	if (setsockopt(sfd, SOL_SOCKET, SO_RCVBUF, &bsize, sizeof(bsize)))
122 		g_gate_xlog("setsockopt() error: %s.", strerror(errno));
123 	bsize = sndbuf;
124 	if (setsockopt(sfd, SOL_SOCKET, SO_SNDBUF, &bsize, sizeof(bsize)))
125 		g_gate_xlog("setsockopt() error: %s.", strerror(errno));
126 	tv.tv_sec = timeout;
127 	tv.tv_usec = 0;
128 	if (setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) ||
129 	    setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) {
130 		g_gate_xlog("setsockopt() error: %s.", strerror(errno));
131 	}
132 	if (connect(sfd, (struct sockaddr *)&serv, sizeof(serv)) < 0) {
133 		g_gate_log(LOG_ERR, "Can't connect to server: %s.",
134 		    strerror(errno));
135 		return (-1);
136 	}
137 
138 	g_gate_log(LOG_INFO, "Connected to the server: %s:%d.", host, port);
139 
140 	/*
141 	 * Creating and sending initial packet.
142 	 */
143 	if (strlcpy(cinit.gc_path, path, sizeof(cinit.gc_path)) >=
144 	    sizeof(cinit.gc_path)) {
145 	        g_gate_xlog("Path name too long.");
146 	}
147 	cinit.gc_flags = flags;
148 	g_gate_log(LOG_DEBUG, "Sending initial packet.");
149 	g_gate_swap2n_cinit(&cinit);
150 	if (send(sfd, &cinit, sizeof(cinit), 0) == -1) {
151 	        g_gate_log(LOG_ERR, "Error while sending initial packet: %s.",
152 		    strerror(errno));
153 		return (-1);
154 	}
155 	g_gate_swap2h_cinit(&cinit);
156 
157 	/*
158 	 * Receiving initial packet from server.
159 	 */
160 	g_gate_log(LOG_DEBUG, "Receiving initial packet.");
161 	if (recv(sfd, &sinit, sizeof(sinit), MSG_WAITALL) == -1) {
162 		g_gate_log(LOG_ERR, "Error while receiving data: %s.",
163 		    strerror(errno));
164 		return (-1);
165 	}
166 	g_gate_swap2h_sinit(&sinit);
167 	if (sinit.gs_error != 0)
168 	        g_gate_xlog("Error from server: %s.", strerror(sinit.gs_error));
169 
170 	mediasize = sinit.gs_mediasize;
171 	if (sectorsize == 0)
172 		sectorsize = sinit.gs_sectorsize;
173 	return (sfd);
174 }
175 
176 static int
177 serve(int sfd)
178 {
179 	struct g_gate_ctl_io ggio;
180 	size_t bsize;
181 	char *buf;
182 
183 	bsize = G_GATE_BUFSIZE_START;
184 	buf = malloc(bsize);
185 	if (buf == NULL) {
186 		if (action == CREATE)
187 			g_gate_destroy(unit, 1);
188 		g_gate_xlog("No enough memory");
189 	}
190 
191 	ggio.gctl_version = G_GATE_VERSION;
192 	ggio.gctl_unit = unit;
193 	bsize = sectorsize;
194 	ggio.gctl_data = malloc(bsize);
195 	for (;;) {
196 		struct g_gate_hdr hdr;
197 		int data, error;
198 once_again:
199 		ggio.gctl_length = bsize;
200 		ggio.gctl_error = 0;
201 		g_gate_ioctl(G_GATE_CMD_START, &ggio);
202 		error = ggio.gctl_error;
203 		switch (error) {
204 		case 0:
205 			break;
206 		case ECANCELED:
207 			/* Exit gracefully. */
208 			free(ggio.gctl_data);
209 			g_gate_close_device();
210 			close(sfd);
211 			exit(EXIT_SUCCESS);
212 		case ENOMEM:
213 			/* Buffer too small. */
214 			ggio.gctl_data = realloc(ggio.gctl_data,
215 			    ggio.gctl_length);
216 			if (ggio.gctl_data != NULL) {
217 				bsize = ggio.gctl_length;
218 				goto once_again;
219 			}
220 			/* FALLTHROUGH */
221 		case ENXIO:
222 		default:
223 			g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
224 			    strerror(error));
225 		}
226 
227 		hdr.gh_cmd = ggio.gctl_cmd;
228 		hdr.gh_offset = ggio.gctl_offset;
229 		hdr.gh_length = ggio.gctl_length;
230 		hdr.gh_error = 0;
231 		g_gate_swap2n_hdr(&hdr);
232 		data = send(sfd, &hdr, sizeof(hdr), 0);
233 		g_gate_log(LOG_DEBUG, "Sent hdr packet.");
234 		g_gate_swap2h_hdr(&hdr);
235 		if (data != sizeof(hdr)) {
236 			ggio.gctl_error = EAGAIN;
237 			goto done;
238 		}
239 		if (ggio.gctl_cmd == BIO_DELETE || ggio.gctl_cmd == BIO_WRITE) {
240 			data = send(sfd, ggio.gctl_data, ggio.gctl_length, 0);
241 			g_gate_log(LOG_DEBUG, "Sent data packet.");
242 			if (data != ggio.gctl_length) {
243 				ggio.gctl_error = EAGAIN;
244 				goto done;
245 			}
246 			g_gate_log(LOG_DEBUG, "Sent %d bytes (offset=%llu, "
247 			    "size=%u).", data, hdr.gh_offset, hdr.gh_length);
248 		}
249 		data = recv(sfd, &hdr, sizeof(hdr), MSG_WAITALL);
250 		g_gate_log(LOG_DEBUG, "Received hdr packet.");
251 		g_gate_swap2h_hdr(&hdr);
252 		if (data != sizeof(hdr)) {
253 			ggio.gctl_error = EIO;
254 			goto done;
255 		}
256 		if (ggio.gctl_cmd == BIO_READ) {
257 			if (bsize < (size_t)ggio.gctl_length) {
258 				ggio.gctl_data = realloc(ggio.gctl_data,
259 				    ggio.gctl_length);
260 				if (ggio.gctl_data != NULL)
261 					bsize = ggio.gctl_length;
262 				else
263 					g_gate_xlog("No memory.");
264 			}
265 			data = recv(sfd, ggio.gctl_data, ggio.gctl_length,
266 			    MSG_WAITALL);
267 			g_gate_log(LOG_DEBUG, "Received data packet.");
268 			if (data != ggio.gctl_length) {
269 				ggio.gctl_error = EAGAIN;
270 				goto done;
271 			}
272 			g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%ju, "
273 			    "size=%zu).", data, (uintmax_t)hdr.gh_offset,
274 			    (size_t)hdr.gh_length);
275 		}
276 done:
277 		g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
278 		if (ggio.gctl_error == EAGAIN)
279 			return (ggio.gctl_error);
280 	}
281 	/* NOTREACHED */
282 	return (0);
283 }
284 
285 static void
286 serve_loop(int sfd)
287 {
288 
289 	for (;;) {
290 		int error;
291 
292 		error = serve(sfd);
293 		close(sfd);
294 		if (error != EAGAIN)
295 			g_gate_xlog("%s.", strerror(error));
296 		sfd = handshake();
297 		if (sfd < 0) {
298 			sleep(2);
299 			continue;
300 		}
301 	}
302 }
303 
304 static void
305 mydaemon(void)
306 {
307 
308 	if (g_gate_verbose > 0)
309 		return;
310 	if (daemon(0, 0) == 0)
311 		return;
312 	if (action == CREATE)
313 		g_gate_destroy(unit, 1);
314 	err(EXIT_FAILURE, "Cannot daemonize");
315 }
316 
317 static void
318 g_gatec_attach(void)
319 {
320 	int sfd;
321 
322 	sfd = handshake();
323 	g_gate_log(LOG_DEBUG, "Worker created: %u.", getpid());
324 	mydaemon();
325 	serve_loop(sfd);
326 }
327 
328 static void
329 g_gatec_create(void)
330 {
331 	struct g_gate_ctl_create ggioc;
332 	int sfd;
333 
334 	sfd = handshake();
335 	if (sfd < 0)
336 		exit(EXIT_FAILURE);
337 	ggioc.gctl_version = G_GATE_VERSION;
338 	ggioc.gctl_mediasize = mediasize;
339 	ggioc.gctl_sectorsize = sectorsize;
340 	ggioc.gctl_flags = flags;
341 	ggioc.gctl_maxcount = queue_size;
342 	ggioc.gctl_timeout = timeout;
343 	ggioc.gctl_unit = unit;
344 	snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s:%u %s", host,
345 	    port, path);
346 	g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
347 	g_gate_log(LOG_DEBUG, "Worker created: %u.", getpid());
348 	if (unit == -1)
349 		printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
350 	unit = ggioc.gctl_unit;
351 	mydaemon();
352 	serve_loop(sfd);
353 }
354 
355 int
356 main(int argc, char *argv[])
357 {
358 
359 	if (argc < 2)
360 		usage();
361 	if (strcasecmp(argv[1], "attach") == 0)
362 		action = ATTACH;
363 	else if (strcasecmp(argv[1], "create") == 0)
364 		action = CREATE;
365 	else if (strcasecmp(argv[1], "destroy") == 0)
366 		action = DESTROY;
367 	else if (strcasecmp(argv[1], "list") == 0)
368 		action = LIST;
369 	else
370 		usage();
371 	argc -= 1;
372 	argv += 1;
373 	for (;;) {
374 		int ch;
375 
376 		ch = getopt(argc, argv, "fno:p:q:R:S:s:t:u:v");
377 		if (ch == -1)
378 			break;
379 		switch (ch) {
380 		case 'f':
381 			if (action != DESTROY)
382 				usage();
383 			force = 1;
384 			break;
385 		case 'n':
386 			if (action != ATTACH && action != CREATE)
387 				usage();
388 			nagle = 0;
389 			break;
390 		case 'o':
391 			if (action != ATTACH && action != CREATE)
392 				usage();
393 			if (strcasecmp("ro", optarg) == 0)
394 				flags = G_GATE_FLAG_READONLY;
395 			else if (strcasecmp("wo", optarg) == 0)
396 				flags = G_GATE_FLAG_WRITEONLY;
397 			else if (strcasecmp("rw", optarg) == 0)
398 				flags = 0;
399 			else {
400 				errx(EXIT_FAILURE,
401 				    "Invalid argument for '-o' option.");
402 			}
403 			break;
404 		case 'p':
405 			if (action != ATTACH && action != CREATE)
406 				usage();
407 			errno = 0;
408 			port = strtoul(optarg, NULL, 10);
409 			if (port == 0 && errno != 0)
410 				errx(EXIT_FAILURE, "Invalid port.");
411 			break;
412 		case 'q':
413 			if (action != CREATE)
414 				usage();
415 			errno = 0;
416 			queue_size = strtoul(optarg, NULL, 10);
417 			if (queue_size == 0 && errno != 0)
418 				errx(EXIT_FAILURE, "Invalid queue_size.");
419 			break;
420 		case 'R':
421 			if (action != ATTACH && action != CREATE)
422 				usage();
423 			errno = 0;
424 			rcvbuf = strtoul(optarg, NULL, 10);
425 			if (rcvbuf == 0 && errno != 0)
426 				errx(EXIT_FAILURE, "Invalid rcvbuf.");
427 			break;
428 		case 'S':
429 			if (action != ATTACH && action != CREATE)
430 				usage();
431 			errno = 0;
432 			sndbuf = strtoul(optarg, NULL, 10);
433 			if (sndbuf == 0 && errno != 0)
434 				errx(EXIT_FAILURE, "Invalid sndbuf.");
435 			break;
436 		case 's':
437 			if (action != CREATE)
438 				usage();
439 			errno = 0;
440 			sectorsize = strtoul(optarg, NULL, 10);
441 			if (sectorsize == 0 && errno != 0)
442 				errx(EXIT_FAILURE, "Invalid sectorsize.");
443 			break;
444 		case 't':
445 			if (action != CREATE)
446 				usage();
447 			errno = 0;
448 			timeout = strtoul(optarg, NULL, 10);
449 			if (timeout == 0 && errno != 0)
450 				errx(EXIT_FAILURE, "Invalid timeout.");
451 			break;
452 		case 'u':
453 			errno = 0;
454 			unit = strtol(optarg, NULL, 10);
455 			if (unit == 0 && errno != 0)
456 				errx(EXIT_FAILURE, "Invalid unit number.");
457 			break;
458 		case 'v':
459 			if (action == DESTROY)
460 				usage();
461 			g_gate_verbose++;
462 			break;
463 		default:
464 			usage();
465 		}
466 	}
467 	argc -= optind;
468 	argv += optind;
469 
470 	switch (action) {
471 	case ATTACH:
472 		if (argc != 2)
473 			usage();
474 		if (unit == -1) {
475 			fprintf(stderr, "Required unit number.\n");
476 			usage();
477 		}
478 		g_gate_open_device();
479 		host = argv[0];
480 		path = argv[1];
481 		g_gatec_attach();
482 		break;
483 	case CREATE:
484 		if (argc != 2)
485 			usage();
486 		g_gate_load_module();
487 		g_gate_open_device();
488 		host = argv[0];
489 		path = argv[1];
490 		g_gatec_create();
491 		break;
492 	case DESTROY:
493 		if (unit == -1) {
494 			fprintf(stderr, "Required unit number.\n");
495 			usage();
496 		}
497 		g_gate_verbose = 1;
498 		g_gate_open_device();
499 		g_gate_destroy(unit, force);
500 		break;
501 	case LIST:
502 		g_gate_list(unit, g_gate_verbose);
503 		break;
504 	case UNSET:
505 	default:
506 		usage();
507 	}
508 	g_gate_close_device();
509 	exit(EXIT_SUCCESS);
510 }
511