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