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.
 
 
 
 

771 lines
18 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. * Copyright 2020 John-Mark Gurney <jmg@FreeBSD.org>
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in the
  15. * documentation and/or other materials provided with the distribution.
  16. *
  17. * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
  18. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
  21. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  22. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  23. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  24. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  25. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  26. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  27. * SUCH DAMAGE.
  28. *
  29. * $FreeBSD$
  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 <pthread_np.h>
  41. #include <signal.h>
  42. #include <err.h>
  43. #include <errno.h>
  44. #include <assert.h>
  45. #include <sys/param.h>
  46. #include <sys/ioctl.h>
  47. #include <sys/queue.h>
  48. #include <sys/socket.h>
  49. #include <sys/sysctl.h>
  50. #include <sys/syslog.h>
  51. #include <sys/time.h>
  52. #include <sys/bio.h>
  53. #include <sys/un.h>
  54. #include <netinet/in.h>
  55. #include <netinet/tcp.h>
  56. #include <arpa/inet.h>
  57. #include <semaphore.h>
  58. #include <curl/curl.h>
  59. #include <curl/multi.h>
  60. #include <geom/gate/g_gate.h>
  61. #include "ggate.h"
  62. static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
  63. static const char *url = 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 off_t mediasize;
  69. static unsigned sectorsize = 4096;
  70. static unsigned timeout = G_GATE_TIMEOUT;
  71. static int pushfd, popfd; /* work semaphore */
  72. static pthread_t reqtd, proctd;
  73. static unsigned maxconnections = 32;
  74. struct ggh_req {
  75. struct g_gate_ctl_io r_ggio;
  76. CURL *r_chandle;
  77. size_t r_bufoff;
  78. TAILQ_ENTRY(ggh_req) r_next;
  79. };
  80. static TAILQ_HEAD(, ggh_req) procqueue = TAILQ_HEAD_INITIALIZER(procqueue);
  81. static sem_t nconn_sem;
  82. static pthread_mutex_t procqueue_mtx;
  83. static void
  84. usage(void)
  85. {
  86. fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] "
  87. "[-q queue_size] [-s sectorsize] [-r nrequests] "
  88. "[-t timeout] [-u unit] <url>\n", getprogname());
  89. fprintf(stderr, " %s rescue [-v] [-o <ro|wo|rw>] "
  90. "[-r nrequests] <-u unit> <url>\n", getprogname());
  91. fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname());
  92. fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname());
  93. exit(EXIT_FAILURE);
  94. }
  95. static void *
  96. req_thread(void *arg __unused)
  97. {
  98. struct ggh_req *greq;
  99. static char *buf;
  100. int buflen = 1024*1024;
  101. int error;
  102. g_gate_log(LOG_NOTICE, "%s: started!", __func__);
  103. greq = NULL;
  104. for (;;) {
  105. if (greq == NULL)
  106. greq = malloc(sizeof *greq);
  107. if (buf == NULL)
  108. buf = malloc(buflen);
  109. if (greq == NULL || buf == NULL) {
  110. /* XXX */
  111. g_gate_log(LOG_ERR, "Unable to allocate memory.");
  112. exit(1);
  113. }
  114. greq->r_ggio.gctl_version = G_GATE_VERSION;
  115. greq->r_ggio.gctl_unit = unit;
  116. greq->r_ggio.gctl_data = buf;
  117. greq->r_ggio.gctl_length = buflen;
  118. greq->r_ggio.gctl_error = 0;
  119. //g_gate_log(LOG_DEBUG, "waiting for ioctl");
  120. g_gate_ioctl(G_GATE_CMD_START, &greq->r_ggio);
  121. //g_gate_log(LOG_DEBUG, "got ioctl");
  122. error = greq->r_ggio.gctl_error;
  123. switch (error) {
  124. case 0:
  125. break;
  126. case ECANCELED:
  127. /* Exit gracefully. */
  128. g_gate_close_device();
  129. exit(EXIT_SUCCESS);
  130. case ENXIO:
  131. default:
  132. g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
  133. strerror(error));
  134. }
  135. g_gate_log(LOG_DEBUG, "ggio, ver: %u, unit: %d, seq: %llu, "
  136. "cmd: %u, offset: %llu, len: %llu",
  137. greq->r_ggio.gctl_version, greq->r_ggio.gctl_unit,
  138. greq->r_ggio.gctl_seq, greq->r_ggio.gctl_cmd,
  139. greq->r_ggio.gctl_offset, greq->r_ggio.gctl_length);
  140. switch (greq->r_ggio.gctl_cmd) {
  141. case BIO_READ:
  142. /* use a correctly sized allocation */
  143. greq->r_ggio.gctl_data =
  144. malloc(greq->r_ggio.gctl_length);
  145. break;
  146. case BIO_WRITE:
  147. /* r_ggio takes ownership of buf now */
  148. buf = NULL;
  149. break;
  150. case BIO_DELETE:
  151. case BIO_FLUSH:
  152. default:
  153. greq->r_ggio.gctl_error = EOPNOTSUPP;
  154. g_gate_ioctl(G_GATE_CMD_DONE, &greq->r_ggio);
  155. continue; /* return EOPNOTSUPP */
  156. break;
  157. }
  158. //g_gate_log(LOG_DEBUG, "waiting for slot");
  159. sem_wait(&nconn_sem);
  160. #if 0
  161. int semval;
  162. sem_getvalue(&nconn_sem, &semval);
  163. g_gate_log(LOG_DEBUG, "slots: %d", semval);
  164. #endif
  165. error = pthread_mutex_lock(&procqueue_mtx);
  166. assert(error == 0);
  167. TAILQ_INSERT_TAIL(&procqueue, greq, r_next);
  168. error = pthread_mutex_unlock(&procqueue_mtx);
  169. assert(error == 0);
  170. /* notify processing thread a request is waiting */
  171. error = write(pushfd, "T", 1);
  172. if (error != 1)
  173. g_gate_xlog("write pushfd: %d, error: %s.", error,
  174. strerror(error));
  175. /* pass ownership */
  176. greq = NULL;
  177. }
  178. g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
  179. return (NULL);
  180. }
  181. /*
  182. * To support any auth:
  183. * https://curl.haxx.se/libcurl/c/anyauthput.html
  184. */
  185. static curlioerr
  186. curl_ioctl(CURL *hndl, curliocmd cmd, void *userdata)
  187. {
  188. struct ggh_req *greq;
  189. (void)hndl;
  190. greq = (struct ggh_req *)userdata;
  191. switch (cmd) {
  192. case CURLIOCMD_RESTARTREAD:
  193. greq->r_bufoff = 0;
  194. break;
  195. default:
  196. return CURLIOE_UNKNOWNCMD;
  197. }
  198. return CURLIOE_OK;
  199. }
  200. /*
  201. * file the curl buffer with data to send to remote server.
  202. */
  203. static size_t
  204. curl_readfun(char *buffer, size_t size, size_t nitems, void *userdata)
  205. {
  206. struct ggh_req *greq;
  207. size_t cnt;
  208. greq = (struct ggh_req *)userdata;
  209. cnt = MIN(size * nitems, greq->r_ggio.gctl_length - greq->r_bufoff);
  210. //g_gate_log(LOG_DEBUG, "sending %zd bytes on %p", cnt, greq);
  211. memcpy(buffer, (char *)greq->r_ggio.gctl_data + greq->r_bufoff, cnt);
  212. greq->r_bufoff += cnt;
  213. return cnt;
  214. }
  215. static size_t
  216. curl_writefun(char *buffer, size_t size, size_t nitems, void *userdata)
  217. {
  218. struct ggh_req *greq;
  219. size_t cnt;
  220. greq = (struct ggh_req *)userdata;
  221. cnt = size * nitems;
  222. assert((off_t)(greq->r_bufoff + cnt) <= greq->r_ggio.gctl_length);
  223. memcpy((char *)greq->r_ggio.gctl_data + greq->r_bufoff, buffer, cnt);
  224. greq->r_bufoff += cnt;
  225. return cnt;
  226. }
  227. static void
  228. process_greq(CURLM *cmulti, struct ggh_req *greq)
  229. {
  230. char range_header[256];
  231. off_t start, length, end;
  232. /* start processing */
  233. greq->r_chandle = curl_easy_init();
  234. curl_easy_setopt(greq->r_chandle, CURLOPT_URL, url);
  235. curl_easy_setopt(greq->r_chandle, CURLOPT_PRIVATE, (char *)greq);
  236. //curl_easy_setopt(greq->r_chandle, CURLOPT_VERBOSE, (long)1);
  237. start = greq->r_ggio.gctl_offset;
  238. length = greq->r_ggio.gctl_length;
  239. end = start + length;
  240. greq->r_bufoff = 0;
  241. switch (greq->r_ggio.gctl_cmd) {
  242. case BIO_READ:
  243. curl_easy_setopt(greq->r_chandle, CURLOPT_WRITEFUNCTION,
  244. curl_writefun);
  245. curl_easy_setopt(greq->r_chandle, CURLOPT_WRITEDATA, greq);
  246. sprintf(range_header, "%zd-%zd", start, end - 1);
  247. g_gate_log(LOG_DEBUG, "read range: %s", range_header);
  248. curl_easy_setopt(greq->r_chandle, CURLOPT_RANGE, range_header);
  249. curl_multi_add_handle(cmulti, greq->r_chandle);
  250. break;
  251. case BIO_WRITE:
  252. curl_easy_setopt(greq->r_chandle, CURLOPT_IOCTLFUNCTION,
  253. curl_ioctl);
  254. curl_easy_setopt(greq->r_chandle, CURLOPT_IOCTLDATA, greq);
  255. curl_easy_setopt(greq->r_chandle, CURLOPT_READFUNCTION,
  256. curl_readfun);
  257. curl_easy_setopt(greq->r_chandle, CURLOPT_READDATA, greq);
  258. curl_easy_setopt(greq->r_chandle, CURLOPT_UPLOAD, (long)1);
  259. /* XXX - support more than basic */
  260. //curl_easy_setopt(greq->r_chandle, CURLOPT_HTTPAUTH, (long)CURLAUTH_ANY);
  261. curl_easy_setopt(greq->r_chandle, CURLOPT_HTTPAUTH,
  262. (long)CURLAUTH_BASIC);
  263. //curl_easy_setopt(greq->r_chandle, CURLOPT_VERBOSE, (long)1);
  264. /* https://curl.haxx.se/mail/lib-2019-05/0012.html */
  265. curl_easy_setopt(greq->r_chandle, CURLOPT_INFILESIZE_LARGE,
  266. (curl_off_t)length);
  267. /* we don't need resume from as we don't seek */
  268. //curl_easy_setopt(greq->r_chandle, CURLOPT_RESUME_FROM_LARGE, (curl_off_t)start);
  269. sprintf(range_header, "Content-Range: bytes %zd-%zd/%zd",
  270. start, end - 1, mediasize);
  271. g_gate_log(LOG_DEBUG, "write range: %s", range_header);
  272. struct curl_slist *header_list;
  273. header_list = curl_slist_append(NULL, range_header);
  274. curl_easy_setopt(greq->r_chandle, CURLOPT_HTTPHEADER,
  275. header_list);
  276. #if 1
  277. curl_multi_add_handle(cmulti, greq->r_chandle);
  278. #else
  279. CURLcode res;
  280. res = curl_easy_perform(greq->r_chandle);
  281. curl_easy_getinfo(greq->r_chandle, CURLINFO_RESPONSE_CODE,
  282. &code);
  283. if (code != 200) {
  284. g_gate_log(LOG_ERR,
  285. "Got invalid response, HTTP code %03d.", code);
  286. }
  287. #endif
  288. break;
  289. }
  290. /* start processing */
  291. //curl_multi_add_handle(cmulti, greq->r_chandle);
  292. }
  293. static void *
  294. proc_thread(void *arg __unused)
  295. {
  296. char scratch[32];
  297. struct timeval to;
  298. CURLMsg *m;
  299. CURLM *cmulti;
  300. struct ggh_req *greq;
  301. fd_set fdread;
  302. fd_set fdwrite;
  303. fd_set fdexcep;
  304. CURLMcode mc;
  305. long curl_timeo;
  306. long code;
  307. int rc;
  308. int maxfd;
  309. int error;
  310. int still_running;
  311. g_gate_log(LOG_NOTICE, "%s: started!", __func__);
  312. /* make sure we don't block on reading */
  313. fcntl(popfd, F_SETFL, O_NONBLOCK);
  314. cmulti = curl_multi_init();
  315. //mc = curl_multi_setopt(cmulti, CURLOPT_VERBOSE, (long)1);
  316. for (;;) {
  317. //g_gate_log(LOG_DEBUG, "looping");
  318. /* setup polling loop */
  319. maxfd = -1;
  320. FD_ZERO(&fdread);
  321. FD_ZERO(&fdwrite);
  322. FD_ZERO(&fdexcep);
  323. to = (struct timeval){ .tv_sec = 1 };
  324. curl_timeo = -1;
  325. curl_multi_timeout(cmulti, &curl_timeo);
  326. if (curl_timeo >= 0) {
  327. to.tv_sec = curl_timeo / 1000;
  328. if (to.tv_sec > 1)
  329. to.tv_sec = 1;
  330. else
  331. to.tv_usec = (curl_timeo % 1000) * 1000;
  332. }
  333. mc = curl_multi_fdset(cmulti, &fdread, &fdwrite, &fdexcep, &maxfd);
  334. if (mc != CURLM_OK) {
  335. g_gate_log(LOG_ERR, "%s: fdset failed.", __func__);
  336. break;
  337. }
  338. /* add in the pop descriptor */
  339. FD_SET(popfd, &fdread);
  340. maxfd = MAX(popfd, maxfd);
  341. rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &to);
  342. switch (rc) {
  343. case -1:
  344. g_gate_log(LOG_ERR, "%s: select failed: %s", __func__,
  345. strerror(errno));
  346. break;
  347. case 0:
  348. default:
  349. curl_multi_perform(cmulti, &still_running);
  350. break;
  351. }
  352. /* Check for completed requests */
  353. do {
  354. int msgq = 0;
  355. m = curl_multi_info_read(cmulti, &msgq);
  356. if (m != NULL && m->msg == CURLMSG_DONE) {
  357. CURL *e = m->easy_handle;
  358. curl_easy_getinfo(e, CURLINFO_PRIVATE,
  359. (char *)&greq);
  360. curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE,
  361. &code);
  362. g_gate_log(LOG_DEBUG, "request code: %d", code);
  363. if (code != 206 && code != 204) {
  364. g_gate_log(LOG_ERR,
  365. "request failed: %d", code);
  366. greq->r_ggio.gctl_error = EIO;
  367. }
  368. g_gate_ioctl(G_GATE_CMD_DONE, &greq->r_ggio);
  369. //g_gate_log(LOG_DEBUG, "releasing slot");
  370. sem_post(&nconn_sem);
  371. curl_multi_remove_handle(cmulti, e);
  372. curl_easy_cleanup(e);
  373. free(greq->r_ggio.gctl_data);
  374. free(greq);
  375. } else if (m != NULL) {
  376. g_gate_log(LOG_ERR, "unknown curl msg: %d",
  377. m->msg);
  378. }
  379. } while (m != NULL);
  380. if (FD_ISSET(popfd, &fdread)) {
  381. /* read off the tokens */
  382. read(popfd, scratch, sizeof scratch);
  383. do {
  384. /* get the request */
  385. error = pthread_mutex_lock(&procqueue_mtx);
  386. assert(error == 0);
  387. greq = TAILQ_FIRST(&procqueue);
  388. if (greq != NULL)
  389. TAILQ_REMOVE(&procqueue, greq, r_next);
  390. error = pthread_mutex_unlock(&procqueue_mtx);
  391. assert(error == 0);
  392. /* no more to process */
  393. if (greq == NULL)
  394. break;
  395. process_greq(cmulti, greq);
  396. } while (greq != NULL);
  397. }
  398. }
  399. curl_multi_cleanup(cmulti);
  400. g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
  401. pthread_exit(NULL);
  402. }
  403. static void
  404. mydaemon(void)
  405. {
  406. if (g_gate_verbose > 0)
  407. return;
  408. if (daemon(0, 0) == 0)
  409. return;
  410. if (action == CREATE)
  411. g_gate_destroy(unit, 1);
  412. err(EXIT_FAILURE, "Cannot daemonize");
  413. }
  414. static int
  415. g_gatehttp_connect(void)
  416. {
  417. CURL *hndl;
  418. CURLcode cc;
  419. long code;
  420. curl_off_t cl;
  421. /* get the remote's size */
  422. hndl = curl_easy_init();
  423. curl_easy_setopt(hndl, CURLOPT_URL, url);
  424. curl_easy_setopt(hndl, CURLOPT_NOBODY, (long)1);
  425. //curl_easy_setopt(hndl, CURLOPT_VERBOSE, (long)1);
  426. cc = curl_easy_perform(hndl);
  427. if (cc != CURLE_OK) {
  428. g_gate_log(LOG_ERR, "curl request failed.");
  429. return 0;
  430. }
  431. curl_easy_getinfo(hndl, CURLINFO_RESPONSE_CODE, &code);
  432. if (code != 200) {
  433. g_gate_log(LOG_ERR, "Got invalid response, HTTP code %03d.", code);
  434. return 0;
  435. }
  436. curl_easy_getinfo(hndl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl);
  437. mediasize = cl;
  438. g_gate_log(LOG_DEBUG, "got mediasize: %zd", mediasize);
  439. curl_easy_cleanup(hndl);
  440. return 1;
  441. }
  442. static void
  443. g_gatehttp_start(void)
  444. {
  445. int filedes[2];
  446. int error;
  447. pipe(filedes);
  448. pushfd = filedes[1];
  449. popfd = filedes[0];
  450. error = pthread_mutex_init(&procqueue_mtx, NULL);
  451. if (error != 0) {
  452. g_gate_xlog("pthread_mutex_init(inqueue_mtx): %s.",
  453. strerror(error));
  454. }
  455. sem_init(&nconn_sem, 0, maxconnections);
  456. error = pthread_create(&proctd, NULL, proc_thread, NULL);
  457. if (error != 0) {
  458. g_gate_destroy(unit, 1);
  459. g_gate_xlog("pthread_create(proc_thread): %s.",
  460. strerror(error));
  461. }
  462. pthread_set_name_np(proctd, "proc");
  463. reqtd = pthread_self();
  464. pthread_set_name_np(reqtd, "req");
  465. req_thread(NULL);
  466. /* Disconnected. */
  467. close(pushfd);
  468. close(popfd);
  469. }
  470. static void
  471. signop(int sig __unused)
  472. {
  473. /* Do nothing. */
  474. }
  475. static void
  476. g_gatehttp_loop(void)
  477. {
  478. struct g_gate_ctl_cancel ggioc;
  479. signal(SIGUSR1, signop);
  480. for (;;) {
  481. g_gatehttp_start();
  482. g_gate_log(LOG_NOTICE, "Disconnected [%s]. Connecting...",
  483. url);
  484. ggioc.gctl_version = G_GATE_VERSION;
  485. ggioc.gctl_unit = unit;
  486. ggioc.gctl_seq = 0;
  487. g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
  488. }
  489. }
  490. static void
  491. g_gatehttp_create(void)
  492. {
  493. struct g_gate_ctl_create ggioc;
  494. if (!g_gatehttp_connect())
  495. g_gate_xlog("Cannot connect: %s.", strerror(errno));
  496. /*
  497. * Ok, got both sockets, time to create provider.
  498. */
  499. memset(&ggioc, 0, sizeof(ggioc));
  500. ggioc.gctl_version = G_GATE_VERSION;
  501. ggioc.gctl_mediasize = mediasize;
  502. ggioc.gctl_sectorsize = sectorsize;
  503. ggioc.gctl_flags = flags;
  504. ggioc.gctl_maxcount = queue_size;
  505. ggioc.gctl_timeout = timeout;
  506. ggioc.gctl_unit = unit;
  507. snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s", url);
  508. g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
  509. if (unit == -1) {
  510. printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
  511. fflush(stdout);
  512. }
  513. unit = ggioc.gctl_unit;
  514. mydaemon();
  515. g_gatehttp_loop();
  516. }
  517. static void
  518. g_gatehttp_rescue(void)
  519. {
  520. struct g_gate_ctl_cancel ggioc;
  521. if (!g_gatehttp_connect())
  522. g_gate_xlog("Cannot connect: %s.", strerror(errno));
  523. ggioc.gctl_version = G_GATE_VERSION;
  524. ggioc.gctl_unit = unit;
  525. ggioc.gctl_seq = 0;
  526. g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
  527. mydaemon();
  528. g_gatehttp_loop();
  529. }
  530. int
  531. main(int argc, char *argv[])
  532. {
  533. if (argc < 2)
  534. usage();
  535. if (strcasecmp(argv[1], "create") == 0)
  536. action = CREATE;
  537. else if (strcasecmp(argv[1], "destroy") == 0)
  538. action = DESTROY;
  539. else if (strcasecmp(argv[1], "list") == 0)
  540. action = LIST;
  541. else if (strcasecmp(argv[1], "rescue") == 0)
  542. action = RESCUE;
  543. else
  544. usage();
  545. argc -= 1;
  546. argv += 1;
  547. for (;;) {
  548. int ch;
  549. ch = getopt(argc, argv, "fo:q:r:s:t:u:v");
  550. if (ch == -1)
  551. break;
  552. switch (ch) {
  553. case 'f':
  554. if (action != DESTROY)
  555. usage();
  556. force = 1;
  557. break;
  558. case 'o':
  559. if (action != CREATE && action != RESCUE)
  560. usage();
  561. if (strcasecmp("ro", optarg) == 0)
  562. flags = G_GATE_FLAG_READONLY;
  563. else if (strcasecmp("wo", optarg) == 0)
  564. flags = G_GATE_FLAG_WRITEONLY;
  565. else if (strcasecmp("rw", optarg) == 0)
  566. flags = 0;
  567. else {
  568. errx(EXIT_FAILURE,
  569. "Invalid argument for '-o' option.");
  570. }
  571. break;
  572. case 'q':
  573. if (action != CREATE)
  574. usage();
  575. errno = 0;
  576. queue_size = strtoul(optarg, NULL, 10);
  577. if (queue_size == 0 && errno != 0)
  578. errx(EXIT_FAILURE, "Invalid queue_size.");
  579. break;
  580. case 'r':
  581. if (action != CREATE && action != RESCUE)
  582. usage();
  583. errno = 0;
  584. maxconnections = strtoul(optarg, NULL, 10);
  585. if (maxconnections == 0 && errno != 0)
  586. errx(EXIT_FAILURE, "Invalid queue_size.");
  587. break;
  588. case 's':
  589. if (action != CREATE)
  590. usage();
  591. errno = 0;
  592. sectorsize = strtoul(optarg, NULL, 10);
  593. if (sectorsize == 0 && errno != 0)
  594. errx(EXIT_FAILURE, "Invalid sectorsize.");
  595. break;
  596. case 't':
  597. if (action != CREATE)
  598. usage();
  599. errno = 0;
  600. timeout = strtoul(optarg, NULL, 10);
  601. if (timeout == 0 && errno != 0)
  602. errx(EXIT_FAILURE, "Invalid timeout.");
  603. break;
  604. case 'u':
  605. errno = 0;
  606. unit = strtol(optarg, NULL, 10);
  607. if (unit == 0 && errno != 0)
  608. errx(EXIT_FAILURE, "Invalid unit number.");
  609. break;
  610. case 'v':
  611. if (action == DESTROY)
  612. usage();
  613. g_gate_verbose++;
  614. break;
  615. default:
  616. usage();
  617. }
  618. }
  619. argc -= optind;
  620. argv += optind;
  621. switch (action) {
  622. case CREATE:
  623. if (argc != 1)
  624. usage();
  625. g_gate_load_module();
  626. g_gate_open_device();
  627. url = argv[0];
  628. g_gatehttp_create();
  629. break;
  630. case DESTROY:
  631. if (unit == -1) {
  632. fprintf(stderr, "Required unit number.\n");
  633. usage();
  634. }
  635. g_gate_verbose = 1;
  636. g_gate_open_device();
  637. g_gate_destroy(unit, force);
  638. break;
  639. case LIST:
  640. g_gate_list(unit, g_gate_verbose);
  641. break;
  642. case RESCUE:
  643. if (argc != 1)
  644. usage();
  645. if (unit == -1) {
  646. fprintf(stderr, "Required unit number.\n");
  647. usage();
  648. }
  649. g_gate_open_device();
  650. url = argv[0];
  651. g_gatehttp_rescue();
  652. break;
  653. case UNSET:
  654. default:
  655. usage();
  656. }
  657. g_gate_close_device();
  658. exit(EXIT_SUCCESS);
  659. }