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 } 646