geom_gate userland utility improvements
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

681 lines
16 KiB

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