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