]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sbin/ggate/ggatec/ggatec.c
Import PCG-C into sys/contrib
[FreeBSD/FreeBSD.git] / sbin / ggate / ggatec / ggatec.c
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$
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
60 static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
61
62 static const char *path = NULL;
63 static const char *host = NULL;
64 static int unit = G_GATE_UNIT_AUTO;
65 static unsigned flags = 0;
66 static int force = 0;
67 static unsigned queue_size = G_GATE_QUEUE_SIZE;
68 static unsigned port = G_GATE_PORT;
69 static off_t mediasize;
70 static unsigned sectorsize = 0;
71 static unsigned timeout = G_GATE_TIMEOUT;
72 static int sendfd, recvfd;
73 static uint32_t token;
74 static pthread_t sendtd, recvtd;
75 static int reconnect;
76
77 static void
78 usage(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
91 static void *
92 send_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                 }
148                 hdr.gh_seq = ggio.gctl_seq;
149                 hdr.gh_offset = ggio.gctl_offset;
150                 hdr.gh_length = ggio.gctl_length;
151                 hdr.gh_error = 0;
152                 g_gate_swap2n_hdr(&hdr);
153
154                 data = g_gate_send(sendfd, &hdr, sizeof(hdr), MSG_NOSIGNAL);
155                 g_gate_log(LOG_DEBUG, "Sent hdr packet.");
156                 g_gate_swap2h_hdr(&hdr);
157                 if (reconnect)
158                         break;
159                 if (data != sizeof(hdr)) {
160                         g_gate_log(LOG_ERR, "Lost connection 1.");
161                         reconnect = 1;
162                         pthread_kill(recvtd, SIGUSR1);
163                         break;
164                 }
165
166                 if (hdr.gh_cmd == GGATE_CMD_WRITE) {
167                         data = g_gate_send(sendfd, ggio.gctl_data,
168                             ggio.gctl_length, MSG_NOSIGNAL);
169                         if (reconnect)
170                                 break;
171                         if (data != ggio.gctl_length) {
172                                 g_gate_log(LOG_ERR, "Lost connection 2 (%zd != %zd).", data, (ssize_t)ggio.gctl_length);
173                                 reconnect = 1;
174                                 pthread_kill(recvtd, SIGUSR1);
175                                 break;
176                         }
177                         g_gate_log(LOG_DEBUG, "Sent %zd bytes (offset=%llu, "
178                             "size=%u).", data, hdr.gh_offset, hdr.gh_length);
179                 }
180         }
181         g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
182         return (NULL);
183 }
184
185 static void *
186 recv_thread(void *arg __unused)
187 {
188         struct g_gate_ctl_io ggio;
189         struct g_gate_hdr hdr;
190         char buf[MAXPHYS];
191         ssize_t data;
192
193         g_gate_log(LOG_NOTICE, "%s: started!", __func__);
194
195         ggio.gctl_version = G_GATE_VERSION;
196         ggio.gctl_unit = unit;
197         ggio.gctl_data = buf;
198
199         for (;;) {
200                 data = g_gate_recv(recvfd, &hdr, sizeof(hdr), MSG_WAITALL);
201                 if (reconnect)
202                         break;
203                 g_gate_swap2h_hdr(&hdr);
204                 if (data != sizeof(hdr)) {
205                         if (data == -1 && errno == EAGAIN)
206                                 continue;
207                         g_gate_log(LOG_ERR, "Lost connection 3.");
208                         reconnect = 1;
209                         pthread_kill(sendtd, SIGUSR1);
210                         break;
211                 }
212                 g_gate_log(LOG_DEBUG, "Received hdr packet.");
213
214                 ggio.gctl_seq = hdr.gh_seq;
215                 ggio.gctl_cmd = hdr.gh_cmd;
216                 ggio.gctl_offset = hdr.gh_offset;
217                 ggio.gctl_length = hdr.gh_length;
218                 ggio.gctl_error = hdr.gh_error;
219
220                 if (ggio.gctl_error == 0 && ggio.gctl_cmd == GGATE_CMD_READ) {
221                         data = g_gate_recv(recvfd, ggio.gctl_data,
222                             ggio.gctl_length, MSG_WAITALL);
223                         if (reconnect)
224                                 break;
225                         g_gate_log(LOG_DEBUG, "Received data packet.");
226                         if (data != ggio.gctl_length) {
227                                 g_gate_log(LOG_ERR, "Lost connection 4.");
228                                 reconnect = 1;
229                                 pthread_kill(sendtd, SIGUSR1);
230                                 break;
231                         }
232                         g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%ju, "
233                             "size=%zu).", data, (uintmax_t)hdr.gh_offset,
234                             (size_t)hdr.gh_length);
235                 }
236
237                 g_gate_ioctl(G_GATE_CMD_DONE, &ggio);
238         }
239         g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
240         pthread_exit(NULL);
241 }
242
243 static int
244 handshake(int dir)
245 {
246         struct g_gate_version ver;
247         struct g_gate_cinit cinit;
248         struct g_gate_sinit sinit;
249         struct sockaddr_in serv;
250         int sfd;
251
252         /*
253          * Do the network stuff.
254          */
255         bzero(&serv, sizeof(serv));
256         serv.sin_family = AF_INET;
257         serv.sin_addr.s_addr = g_gate_str2ip(host);
258         if (serv.sin_addr.s_addr == INADDR_NONE) {
259                 g_gate_log(LOG_DEBUG, "Invalid IP/host name: %s.", host);
260                 return (-1);
261         }
262         serv.sin_port = htons(port);
263         sfd = socket(AF_INET, SOCK_STREAM, 0);
264         if (sfd == -1) {
265                 g_gate_log(LOG_DEBUG, "Cannot open socket: %s.",
266                     strerror(errno));
267                 return (-1);
268         }
269
270         g_gate_socket_settings(sfd);
271
272         if (connect(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) {
273                 g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.",
274                     strerror(errno));
275                 close(sfd);
276                 return (-1);
277         }
278
279         g_gate_log(LOG_INFO, "Connected to the server: %s:%d.", host, port);
280
281         /*
282          * Create and send version packet.
283          */
284         g_gate_log(LOG_DEBUG, "Sending version packet.");
285         assert(strlen(GGATE_MAGIC) == sizeof(ver.gv_magic));
286         bcopy(GGATE_MAGIC, ver.gv_magic, sizeof(ver.gv_magic));
287         ver.gv_version = GGATE_VERSION;
288         ver.gv_error = 0;
289         g_gate_swap2n_version(&ver);
290         if (g_gate_send(sfd, &ver, sizeof(ver), MSG_NOSIGNAL) == -1) {
291                 g_gate_log(LOG_DEBUG, "Error while sending version packet: %s.",
292                     strerror(errno));
293                 close(sfd);
294                 return (-1);
295         }
296         bzero(&ver, sizeof(ver));
297         if (g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL) == -1) {
298                 g_gate_log(LOG_DEBUG, "Error while receiving data: %s.",
299                     strerror(errno));
300                 close(sfd);
301                 return (-1);
302         }
303         if (ver.gv_error != 0) {
304                 g_gate_log(LOG_DEBUG, "Version verification problem: %s.",
305                     strerror(errno));
306                 close(sfd);
307                 return (-1);
308         }
309
310         /*
311          * Create and send initial packet.
312          */
313         g_gate_log(LOG_DEBUG, "Sending initial packet.");
314         if (strlcpy(cinit.gc_path, path, sizeof(cinit.gc_path)) >=
315             sizeof(cinit.gc_path)) {
316                 g_gate_log(LOG_DEBUG, "Path name too long.");
317                 close(sfd);
318                 return (-1);
319         }
320         cinit.gc_flags = flags | dir;
321         cinit.gc_token = token;
322         cinit.gc_nconn = 2;
323         g_gate_swap2n_cinit(&cinit);
324         if (g_gate_send(sfd, &cinit, sizeof(cinit), MSG_NOSIGNAL) == -1) {
325                 g_gate_log(LOG_DEBUG, "Error while sending initial packet: %s.",
326                     strerror(errno));
327                 close(sfd);
328                 return (-1);
329         }
330         g_gate_swap2h_cinit(&cinit);
331
332         /*
333          * Receiving initial packet from server.
334          */
335         g_gate_log(LOG_DEBUG, "Receiving initial packet.");
336         if (g_gate_recv(sfd, &sinit, sizeof(sinit), MSG_WAITALL) == -1) {
337                 g_gate_log(LOG_DEBUG, "Error while receiving data: %s.",
338                     strerror(errno));
339                 close(sfd);
340                 return (-1);
341         }
342         g_gate_swap2h_sinit(&sinit);
343         if (sinit.gs_error != 0) {
344                 g_gate_log(LOG_DEBUG, "Error from server: %s.",
345                     strerror(sinit.gs_error));
346                 close(sfd);
347                 return (-1);
348         }
349         g_gate_log(LOG_DEBUG, "Received initial packet.");
350
351         mediasize = sinit.gs_mediasize;
352         if (sectorsize == 0)
353                 sectorsize = sinit.gs_sectorsize;
354
355         return (sfd);
356 }
357
358 static void
359 mydaemon(void)
360 {
361
362         if (g_gate_verbose > 0)
363                 return;
364         if (daemon(0, 0) == 0)
365                 return;
366         if (action == CREATE)
367                 g_gate_destroy(unit, 1);
368         err(EXIT_FAILURE, "Cannot daemonize");
369 }
370
371 static int
372 g_gatec_connect(void)
373 {
374
375         token = arc4random();
376         /*
377          * Our receive descriptor is connected to the send descriptor on the
378          * server side.
379          */
380         recvfd = handshake(GGATE_FLAG_SEND);
381         if (recvfd == -1)
382                 return (0);
383         /*
384          * Our send descriptor is connected to the receive descriptor on the
385          * server side.
386          */
387         sendfd = handshake(GGATE_FLAG_RECV);
388         if (sendfd == -1)
389                 return (0);
390         return (1);
391 }
392
393 static void
394 g_gatec_start(void)
395 {
396         int error;
397
398         reconnect = 0;
399         error = pthread_create(&recvtd, NULL, recv_thread, NULL);
400         if (error != 0) {
401                 g_gate_destroy(unit, 1);
402                 g_gate_xlog("pthread_create(recv_thread): %s.",
403                     strerror(error));
404         }
405         sendtd = pthread_self();
406         send_thread(NULL);
407         /* Disconnected. */
408         close(sendfd);
409         close(recvfd);
410 }
411
412 static void
413 signop(int sig __unused)
414 {
415
416         /* Do nothing. */
417 }
418
419 static void
420 g_gatec_loop(void)
421 {
422         struct g_gate_ctl_cancel ggioc;
423
424         signal(SIGUSR1, signop);
425         for (;;) {
426                 g_gatec_start();
427                 g_gate_log(LOG_NOTICE, "Disconnected [%s %s]. Connecting...",
428                     host, path);
429                 while (!g_gatec_connect()) {
430                         sleep(2);
431                         g_gate_log(LOG_NOTICE, "Connecting [%s %s]...", host,
432                             path);
433                 }
434                 ggioc.gctl_version = G_GATE_VERSION;
435                 ggioc.gctl_unit = unit;
436                 ggioc.gctl_seq = 0;
437                 g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
438         }
439 }
440
441 static void
442 g_gatec_create(void)
443 {
444         struct g_gate_ctl_create ggioc;
445
446         if (!g_gatec_connect())
447                 g_gate_xlog("Cannot connect: %s.", strerror(errno));
448
449         /*
450          * Ok, got both sockets, time to create provider.
451          */
452         memset(&ggioc, 0, sizeof(ggioc));
453         ggioc.gctl_version = G_GATE_VERSION;
454         ggioc.gctl_mediasize = mediasize;
455         ggioc.gctl_sectorsize = sectorsize;
456         ggioc.gctl_flags = flags;
457         ggioc.gctl_maxcount = queue_size;
458         ggioc.gctl_timeout = timeout;
459         ggioc.gctl_unit = unit;
460         snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s:%u %s", host,
461             port, path);
462         g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
463         if (unit == -1) {
464                 printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
465                 fflush(stdout);
466         }
467         unit = ggioc.gctl_unit;
468
469         mydaemon();
470         g_gatec_loop();
471 }
472
473 static void
474 g_gatec_rescue(void)
475 {
476         struct g_gate_ctl_cancel ggioc;
477
478         if (!g_gatec_connect())
479                 g_gate_xlog("Cannot connect: %s.", strerror(errno));
480
481         ggioc.gctl_version = G_GATE_VERSION;
482         ggioc.gctl_unit = unit;
483         ggioc.gctl_seq = 0;
484         g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
485
486         mydaemon();
487         g_gatec_loop();
488 }
489
490 int
491 main(int argc, char *argv[])
492 {
493
494         if (argc < 2)
495                 usage();
496         if (strcasecmp(argv[1], "create") == 0)
497                 action = CREATE;
498         else if (strcasecmp(argv[1], "destroy") == 0)
499                 action = DESTROY;
500         else if (strcasecmp(argv[1], "list") == 0)
501                 action = LIST;
502         else if (strcasecmp(argv[1], "rescue") == 0)
503                 action = RESCUE;
504         else
505                 usage();
506         argc -= 1;
507         argv += 1;
508         for (;;) {
509                 int ch;
510
511                 ch = getopt(argc, argv, "fno:p:q:R:S:s:t:u:v");
512                 if (ch == -1)
513                         break;
514                 switch (ch) {
515                 case 'f':
516                         if (action != DESTROY)
517                                 usage();
518                         force = 1;
519                         break;
520                 case 'n':
521                         if (action != CREATE && action != RESCUE)
522                                 usage();
523                         nagle = 0;
524                         break;
525                 case 'o':
526                         if (action != CREATE && action != RESCUE)
527                                 usage();
528                         if (strcasecmp("ro", optarg) == 0)
529                                 flags = G_GATE_FLAG_READONLY;
530                         else if (strcasecmp("wo", optarg) == 0)
531                                 flags = G_GATE_FLAG_WRITEONLY;
532                         else if (strcasecmp("rw", optarg) == 0)
533                                 flags = 0;
534                         else {
535                                 errx(EXIT_FAILURE,
536                                     "Invalid argument for '-o' option.");
537                         }
538                         break;
539                 case 'p':
540                         if (action != CREATE && action != RESCUE)
541                                 usage();
542                         errno = 0;
543                         port = strtoul(optarg, NULL, 10);
544                         if (port == 0 && errno != 0)
545                                 errx(EXIT_FAILURE, "Invalid port.");
546                         break;
547                 case 'q':
548                         if (action != CREATE)
549                                 usage();
550                         errno = 0;
551                         queue_size = strtoul(optarg, NULL, 10);
552                         if (queue_size == 0 && errno != 0)
553                                 errx(EXIT_FAILURE, "Invalid queue_size.");
554                         break;
555                 case 'R':
556                         if (action != CREATE && action != RESCUE)
557                                 usage();
558                         errno = 0;
559                         rcvbuf = strtoul(optarg, NULL, 10);
560                         if (rcvbuf == 0 && errno != 0)
561                                 errx(EXIT_FAILURE, "Invalid rcvbuf.");
562                         break;
563                 case 'S':
564                         if (action != CREATE && action != RESCUE)
565                                 usage();
566                         errno = 0;
567                         sndbuf = strtoul(optarg, NULL, 10);
568                         if (sndbuf == 0 && errno != 0)
569                                 errx(EXIT_FAILURE, "Invalid sndbuf.");
570                         break;
571                 case 's':
572                         if (action != CREATE)
573                                 usage();
574                         errno = 0;
575                         sectorsize = strtoul(optarg, NULL, 10);
576                         if (sectorsize == 0 && errno != 0)
577                                 errx(EXIT_FAILURE, "Invalid sectorsize.");
578                         break;
579                 case 't':
580                         if (action != CREATE)
581                                 usage();
582                         errno = 0;
583                         timeout = strtoul(optarg, NULL, 10);
584                         if (timeout == 0 && errno != 0)
585                                 errx(EXIT_FAILURE, "Invalid timeout.");
586                         break;
587                 case 'u':
588                         errno = 0;
589                         unit = strtol(optarg, NULL, 10);
590                         if (unit == 0 && errno != 0)
591                                 errx(EXIT_FAILURE, "Invalid unit number.");
592                         break;
593                 case 'v':
594                         if (action == DESTROY)
595                                 usage();
596                         g_gate_verbose++;
597                         break;
598                 default:
599                         usage();
600                 }
601         }
602         argc -= optind;
603         argv += optind;
604
605         switch (action) {
606         case CREATE:
607                 if (argc != 2)
608                         usage();
609                 g_gate_load_module();
610                 g_gate_open_device();
611                 host = argv[0];
612                 path = argv[1];
613                 g_gatec_create();
614                 break;
615         case DESTROY:
616                 if (unit == -1) {
617                         fprintf(stderr, "Required unit number.\n");
618                         usage();
619                 }
620                 g_gate_verbose = 1;
621                 g_gate_open_device();
622                 g_gate_destroy(unit, force);
623                 break;
624         case LIST:
625                 g_gate_list(unit, g_gate_verbose);
626                 break;
627         case RESCUE:
628                 if (argc != 2)
629                         usage();
630                 if (unit == -1) {
631                         fprintf(stderr, "Required unit number.\n");
632                         usage();
633                 }
634                 g_gate_open_device();
635                 host = argv[0];
636                 path = argv[1];
637                 g_gatec_rescue();
638                 break;
639         case UNSET:
640         default:
641                 usage();
642         }
643         g_gate_close_device();
644         exit(EXIT_SUCCESS);
645 }