|
- /*-
- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
- *
- * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org>
- * All rights reserved.
- * Copyright 2020 John-Mark Gurney <jmg@FreeBSD.org>
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
- * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
- * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
- * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
- * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- *
- * $FreeBSD$
- */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdint.h>
- #include <fcntl.h>
- #include <unistd.h>
- #include <string.h>
- #include <ctype.h>
- #include <libgen.h>
- #include <pthread.h>
- #include <pthread_np.h>
- #include <signal.h>
- #include <err.h>
- #include <errno.h>
- #include <assert.h>
-
- #include <sys/param.h>
- #include <sys/ioctl.h>
- #include <sys/queue.h>
- #include <sys/socket.h>
- #include <sys/sysctl.h>
- #include <sys/syslog.h>
- #include <sys/time.h>
- #include <sys/bio.h>
- #include <sys/un.h>
- #include <netinet/in.h>
- #include <netinet/tcp.h>
- #include <arpa/inet.h>
-
- #include <semaphore.h>
-
- #include <curl/curl.h>
- #include <curl/multi.h>
-
- #include <geom/gate/g_gate.h>
- #include "ggate.h"
-
- static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET;
-
- static const char *url = NULL;
- static int unit = G_GATE_UNIT_AUTO;
- static unsigned flags = 0;
- static int force = 0;
- static unsigned queue_size = G_GATE_QUEUE_SIZE;
- static off_t mediasize;
- static unsigned sectorsize = 4096;
- static unsigned timeout = G_GATE_TIMEOUT;
- static int pushfd, popfd; /* work semaphore */
- static pthread_t reqtd, proctd;
- static unsigned maxconnections = 32;
-
- struct ggh_req {
- struct g_gate_ctl_io r_ggio;
- CURL *r_chandle;
- size_t r_bufoff;
- TAILQ_ENTRY(ggh_req) r_next;
- };
-
- static TAILQ_HEAD(, ggh_req) procqueue = TAILQ_HEAD_INITIALIZER(procqueue);
- static sem_t nconn_sem;
- static pthread_mutex_t procqueue_mtx;
-
- static void
- usage(void)
- {
-
- fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] "
- "[-q queue_size] [-s sectorsize] [-r nrequests] "
- "[-t timeout] [-u unit] <url>\n", getprogname());
- fprintf(stderr, " %s rescue [-v] [-o <ro|wo|rw>] "
- "[-r nrequests] <-u unit> <url>\n", getprogname());
- fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname());
- fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname());
- exit(EXIT_FAILURE);
- }
-
- static void *
- req_thread(void *arg __unused)
- {
- struct ggh_req *greq;
- static char *buf;
- int buflen = 1024*1024;
- int error;
-
- g_gate_log(LOG_NOTICE, "%s: started!", __func__);
-
- greq = NULL;
-
- for (;;) {
- if (greq == NULL)
- greq = malloc(sizeof *greq);
-
- if (buf == NULL)
- buf = malloc(buflen);
-
- if (greq == NULL || buf == NULL) {
- /* XXX */
- g_gate_log(LOG_ERR, "Unable to allocate memory.");
- exit(1);
- }
-
- greq->r_ggio.gctl_version = G_GATE_VERSION;
- greq->r_ggio.gctl_unit = unit;
- greq->r_ggio.gctl_data = buf;
- greq->r_ggio.gctl_length = buflen;
- greq->r_ggio.gctl_error = 0;
-
- //g_gate_log(LOG_DEBUG, "waiting for ioctl");
- g_gate_ioctl(G_GATE_CMD_START, &greq->r_ggio);
- //g_gate_log(LOG_DEBUG, "got ioctl");
-
- error = greq->r_ggio.gctl_error;
- switch (error) {
- case 0:
- break;
- case ECANCELED:
- /* Exit gracefully. */
- g_gate_close_device();
- exit(EXIT_SUCCESS);
- case ENXIO:
- default:
- g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME,
- strerror(error));
- }
-
- g_gate_log(LOG_DEBUG, "ggio, ver: %u, unit: %d, seq: %llu, "
- "cmd: %u, offset: %llu, len: %llu",
- greq->r_ggio.gctl_version, greq->r_ggio.gctl_unit,
- greq->r_ggio.gctl_seq, greq->r_ggio.gctl_cmd,
- greq->r_ggio.gctl_offset, greq->r_ggio.gctl_length);
-
- switch (greq->r_ggio.gctl_cmd) {
- case BIO_READ:
- /* use a correctly sized allocation */
- greq->r_ggio.gctl_data =
- malloc(greq->r_ggio.gctl_length);
- break;
- case BIO_WRITE:
- /* r_ggio takes ownership of buf now */
- buf = NULL;
- break;
-
- case BIO_DELETE:
- case BIO_FLUSH:
- default:
- greq->r_ggio.gctl_error = EOPNOTSUPP;
- g_gate_ioctl(G_GATE_CMD_DONE, &greq->r_ggio);
- continue; /* return EOPNOTSUPP */
- break;
- }
-
- //g_gate_log(LOG_DEBUG, "waiting for slot");
- sem_wait(&nconn_sem);
- #if 0
- int semval;
- sem_getvalue(&nconn_sem, &semval);
- g_gate_log(LOG_DEBUG, "slots: %d", semval);
- #endif
-
- error = pthread_mutex_lock(&procqueue_mtx);
- assert(error == 0);
- TAILQ_INSERT_TAIL(&procqueue, greq, r_next);
- error = pthread_mutex_unlock(&procqueue_mtx);
- assert(error == 0);
-
- /* notify processing thread a request is waiting */
- error = write(pushfd, "T", 1);
- if (error != 1)
- g_gate_xlog("write pushfd: %d, error: %s.", error,
- strerror(error));
-
- /* pass ownership */
- greq = NULL;
- }
- g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
- return (NULL);
- }
-
- /*
- * To support any auth:
- * https://curl.haxx.se/libcurl/c/anyauthput.html
- */
- static curlioerr
- curl_ioctl(CURL *hndl, curliocmd cmd, void *userdata)
- {
- struct ggh_req *greq;
-
- (void)hndl;
-
- greq = (struct ggh_req *)userdata;
-
- switch (cmd) {
- case CURLIOCMD_RESTARTREAD:
- greq->r_bufoff = 0;
- break;
-
- default:
- return CURLIOE_UNKNOWNCMD;
- }
-
- return CURLIOE_OK;
- }
-
- /*
- * file the curl buffer with data to send to remote server.
- */
- static size_t
- curl_readfun(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- struct ggh_req *greq;
- size_t cnt;
-
- greq = (struct ggh_req *)userdata;
-
- cnt = MIN(size * nitems, greq->r_ggio.gctl_length - greq->r_bufoff);
-
- //g_gate_log(LOG_DEBUG, "sending %zd bytes on %p", cnt, greq);
-
- memcpy(buffer, (char *)greq->r_ggio.gctl_data + greq->r_bufoff, cnt);
-
- greq->r_bufoff += cnt;
-
- return cnt;
- }
-
- static size_t
- curl_writefun(char *buffer, size_t size, size_t nitems, void *userdata)
- {
- struct ggh_req *greq;
- size_t cnt;
-
- greq = (struct ggh_req *)userdata;
-
- cnt = size * nitems;
-
- assert((off_t)(greq->r_bufoff + cnt) <= greq->r_ggio.gctl_length);
-
- memcpy((char *)greq->r_ggio.gctl_data + greq->r_bufoff, buffer, cnt);
-
- greq->r_bufoff += cnt;
-
- return cnt;
- }
-
- static void
- process_greq(CURLM *cmulti, struct ggh_req *greq)
- {
- char range_header[256];
- off_t start, length, end;
-
- /* start processing */
- greq->r_chandle = curl_easy_init();
-
- curl_easy_setopt(greq->r_chandle, CURLOPT_URL, url);
- curl_easy_setopt(greq->r_chandle, CURLOPT_PRIVATE, (char *)greq);
- //curl_easy_setopt(greq->r_chandle, CURLOPT_VERBOSE, (long)1);
-
- start = greq->r_ggio.gctl_offset;
- length = greq->r_ggio.gctl_length;
- end = start + length;
-
- greq->r_bufoff = 0;
- switch (greq->r_ggio.gctl_cmd) {
- case BIO_READ:
- curl_easy_setopt(greq->r_chandle, CURLOPT_WRITEFUNCTION,
- curl_writefun);
- curl_easy_setopt(greq->r_chandle, CURLOPT_WRITEDATA, greq);
-
- sprintf(range_header, "%zd-%zd", start, end - 1);
- g_gate_log(LOG_DEBUG, "read range: %s", range_header);
- curl_easy_setopt(greq->r_chandle, CURLOPT_RANGE, range_header);
- curl_multi_add_handle(cmulti, greq->r_chandle);
- break;
-
- case BIO_WRITE:
- curl_easy_setopt(greq->r_chandle, CURLOPT_IOCTLFUNCTION,
- curl_ioctl);
- curl_easy_setopt(greq->r_chandle, CURLOPT_IOCTLDATA, greq);
- curl_easy_setopt(greq->r_chandle, CURLOPT_READFUNCTION,
- curl_readfun);
- curl_easy_setopt(greq->r_chandle, CURLOPT_READDATA, greq);
- curl_easy_setopt(greq->r_chandle, CURLOPT_UPLOAD, (long)1);
- /* XXX - support more than basic */
- //curl_easy_setopt(greq->r_chandle, CURLOPT_HTTPAUTH, (long)CURLAUTH_ANY);
- curl_easy_setopt(greq->r_chandle, CURLOPT_HTTPAUTH,
- (long)CURLAUTH_BASIC);
-
- //curl_easy_setopt(greq->r_chandle, CURLOPT_VERBOSE, (long)1);
-
- /* https://curl.haxx.se/mail/lib-2019-05/0012.html */
- curl_easy_setopt(greq->r_chandle, CURLOPT_INFILESIZE_LARGE,
- (curl_off_t)length);
- /* we don't need resume from as we don't seek */
- //curl_easy_setopt(greq->r_chandle, CURLOPT_RESUME_FROM_LARGE, (curl_off_t)start);
- sprintf(range_header, "Content-Range: bytes %zd-%zd/%zd",
- start, end - 1, mediasize);
- g_gate_log(LOG_DEBUG, "write range: %s", range_header);
-
- struct curl_slist *header_list;
- header_list = curl_slist_append(NULL, range_header);
- curl_easy_setopt(greq->r_chandle, CURLOPT_HTTPHEADER,
- header_list);
-
- #if 1
- curl_multi_add_handle(cmulti, greq->r_chandle);
- #else
- CURLcode res;
- res = curl_easy_perform(greq->r_chandle);
- curl_easy_getinfo(greq->r_chandle, CURLINFO_RESPONSE_CODE,
- &code);
- if (code != 200) {
- g_gate_log(LOG_ERR,
- "Got invalid response, HTTP code %03d.", code);
- }
- #endif
- break;
- }
-
- /* start processing */
- //curl_multi_add_handle(cmulti, greq->r_chandle);
- }
-
- static void *
- proc_thread(void *arg __unused)
- {
- char scratch[32];
- struct timeval to;
- CURLMsg *m;
- CURLM *cmulti;
- struct ggh_req *greq;
- fd_set fdread;
- fd_set fdwrite;
- fd_set fdexcep;
- CURLMcode mc;
- long curl_timeo;
- long code;
- int rc;
- int maxfd;
- int error;
- int still_running;
-
- g_gate_log(LOG_NOTICE, "%s: started!", __func__);
-
- /* make sure we don't block on reading */
- fcntl(popfd, F_SETFL, O_NONBLOCK);
-
- cmulti = curl_multi_init();
- //mc = curl_multi_setopt(cmulti, CURLOPT_VERBOSE, (long)1);
- for (;;) {
- //g_gate_log(LOG_DEBUG, "looping");
-
- /* setup polling loop */
- maxfd = -1;
- FD_ZERO(&fdread);
- FD_ZERO(&fdwrite);
- FD_ZERO(&fdexcep);
- to = (struct timeval){ .tv_sec = 1 };
- curl_timeo = -1;
- curl_multi_timeout(cmulti, &curl_timeo);
- if (curl_timeo >= 0) {
- to.tv_sec = curl_timeo / 1000;
- if (to.tv_sec > 1)
- to.tv_sec = 1;
- else
- to.tv_usec = (curl_timeo % 1000) * 1000;
- }
- mc = curl_multi_fdset(cmulti, &fdread, &fdwrite, &fdexcep, &maxfd);
- if (mc != CURLM_OK) {
- g_gate_log(LOG_ERR, "%s: fdset failed.", __func__);
- break;
- }
-
- /* add in the pop descriptor */
- FD_SET(popfd, &fdread);
- maxfd = MAX(popfd, maxfd);
-
- rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &to);
- switch (rc) {
- case -1:
- g_gate_log(LOG_ERR, "%s: select failed: %s", __func__,
- strerror(errno));
- break;
- case 0:
- default:
- curl_multi_perform(cmulti, &still_running);
- break;
- }
-
- /* Check for completed requests */
- do {
- int msgq = 0;
- m = curl_multi_info_read(cmulti, &msgq);
- if (m != NULL && m->msg == CURLMSG_DONE) {
- CURL *e = m->easy_handle;
-
- curl_easy_getinfo(e, CURLINFO_PRIVATE,
- (char *)&greq);
- curl_easy_getinfo(e, CURLINFO_RESPONSE_CODE,
- &code);
- g_gate_log(LOG_DEBUG, "request code: %d", code);
- if (code != 206 && code != 204) {
- g_gate_log(LOG_ERR,
- "request failed: %d", code);
- greq->r_ggio.gctl_error = EIO;
- }
-
- g_gate_ioctl(G_GATE_CMD_DONE, &greq->r_ggio);
-
- //g_gate_log(LOG_DEBUG, "releasing slot");
- sem_post(&nconn_sem);
-
- curl_multi_remove_handle(cmulti, e);
- curl_easy_cleanup(e);
-
- free(greq->r_ggio.gctl_data);
- free(greq);
- } else if (m != NULL) {
- g_gate_log(LOG_ERR, "unknown curl msg: %d",
- m->msg);
- }
- } while (m != NULL);
-
- if (FD_ISSET(popfd, &fdread)) {
- /* read off the tokens */
- read(popfd, scratch, sizeof scratch);
-
- do {
- /* get the request */
- error = pthread_mutex_lock(&procqueue_mtx);
- assert(error == 0);
- greq = TAILQ_FIRST(&procqueue);
- if (greq != NULL)
- TAILQ_REMOVE(&procqueue, greq, r_next);
- error = pthread_mutex_unlock(&procqueue_mtx);
- assert(error == 0);
-
- /* no more to process */
- if (greq == NULL)
- break;
-
- process_greq(cmulti, greq);
- } while (greq != NULL);
- }
- }
-
- curl_multi_cleanup(cmulti);
- g_gate_log(LOG_DEBUG, "%s: Died.", __func__);
- pthread_exit(NULL);
- }
-
- static void
- mydaemon(void)
- {
-
- if (g_gate_verbose > 0)
- return;
- if (daemon(0, 0) == 0)
- return;
- if (action == CREATE)
- g_gate_destroy(unit, 1);
- err(EXIT_FAILURE, "Cannot daemonize");
- }
-
- static int
- g_gatehttp_connect(void)
- {
- CURL *hndl;
- CURLcode cc;
- long code;
- curl_off_t cl;
-
- /* get the remote's size */
- hndl = curl_easy_init();
- curl_easy_setopt(hndl, CURLOPT_URL, url);
- curl_easy_setopt(hndl, CURLOPT_NOBODY, (long)1);
- //curl_easy_setopt(hndl, CURLOPT_VERBOSE, (long)1);
-
- cc = curl_easy_perform(hndl);
-
- if (cc != CURLE_OK) {
- g_gate_log(LOG_ERR, "curl request failed.");
- return 0;
- }
-
- curl_easy_getinfo(hndl, CURLINFO_RESPONSE_CODE, &code);
- if (code != 200) {
- g_gate_log(LOG_ERR, "Got invalid response, HTTP code %03d.", code);
- return 0;
- }
-
- curl_easy_getinfo(hndl, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &cl);
- mediasize = cl;
- g_gate_log(LOG_DEBUG, "got mediasize: %zd", mediasize);
-
- curl_easy_cleanup(hndl);
-
- return 1;
- }
-
- static void
- g_gatehttp_start(void)
- {
- int filedes[2];
- int error;
-
- pipe(filedes);
- pushfd = filedes[1];
- popfd = filedes[0];
-
- error = pthread_mutex_init(&procqueue_mtx, NULL);
- if (error != 0) {
- g_gate_xlog("pthread_mutex_init(inqueue_mtx): %s.",
- strerror(error));
- }
-
- sem_init(&nconn_sem, 0, maxconnections);
-
- error = pthread_create(&proctd, NULL, proc_thread, NULL);
- if (error != 0) {
- g_gate_destroy(unit, 1);
- g_gate_xlog("pthread_create(proc_thread): %s.",
- strerror(error));
- }
- pthread_set_name_np(proctd, "proc");
-
- reqtd = pthread_self();
- pthread_set_name_np(reqtd, "req");
- req_thread(NULL);
-
- /* Disconnected. */
- close(pushfd);
- close(popfd);
- }
-
- static void
- signop(int sig __unused)
- {
-
- /* Do nothing. */
- }
-
- static void
- g_gatehttp_loop(void)
- {
- struct g_gate_ctl_cancel ggioc;
-
- signal(SIGUSR1, signop);
- for (;;) {
- g_gatehttp_start();
- g_gate_log(LOG_NOTICE, "Disconnected [%s]. Connecting...",
- url);
-
- ggioc.gctl_version = G_GATE_VERSION;
- ggioc.gctl_unit = unit;
- ggioc.gctl_seq = 0;
- g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
- }
- }
-
- static void
- g_gatehttp_create(void)
- {
- struct g_gate_ctl_create ggioc;
-
- if (!g_gatehttp_connect())
- g_gate_xlog("Cannot connect: %s.", strerror(errno));
-
- /*
- * Ok, got both sockets, time to create provider.
- */
- memset(&ggioc, 0, sizeof(ggioc));
- ggioc.gctl_version = G_GATE_VERSION;
- ggioc.gctl_mediasize = mediasize;
- ggioc.gctl_sectorsize = sectorsize;
- ggioc.gctl_flags = flags;
- ggioc.gctl_maxcount = queue_size;
- ggioc.gctl_timeout = timeout;
- ggioc.gctl_unit = unit;
- snprintf(ggioc.gctl_info, sizeof(ggioc.gctl_info), "%s", url);
- g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc);
- if (unit == -1) {
- printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit);
- fflush(stdout);
- }
- unit = ggioc.gctl_unit;
-
- mydaemon();
- g_gatehttp_loop();
- }
-
- static void
- g_gatehttp_rescue(void)
- {
- struct g_gate_ctl_cancel ggioc;
-
- if (!g_gatehttp_connect())
- g_gate_xlog("Cannot connect: %s.", strerror(errno));
-
- ggioc.gctl_version = G_GATE_VERSION;
- ggioc.gctl_unit = unit;
- ggioc.gctl_seq = 0;
- g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc);
-
- mydaemon();
- g_gatehttp_loop();
- }
-
- int
- main(int argc, char *argv[])
- {
-
- if (argc < 2)
- usage();
- if (strcasecmp(argv[1], "create") == 0)
- action = CREATE;
- else if (strcasecmp(argv[1], "destroy") == 0)
- action = DESTROY;
- else if (strcasecmp(argv[1], "list") == 0)
- action = LIST;
- else if (strcasecmp(argv[1], "rescue") == 0)
- action = RESCUE;
- else
- usage();
- argc -= 1;
- argv += 1;
- for (;;) {
- int ch;
-
- ch = getopt(argc, argv, "fo:q:r:s:t:u:v");
- if (ch == -1)
- break;
- switch (ch) {
- case 'f':
- if (action != DESTROY)
- usage();
- force = 1;
- break;
- case 'o':
- if (action != CREATE && action != RESCUE)
- usage();
- if (strcasecmp("ro", optarg) == 0)
- flags = G_GATE_FLAG_READONLY;
- else if (strcasecmp("wo", optarg) == 0)
- flags = G_GATE_FLAG_WRITEONLY;
- else if (strcasecmp("rw", optarg) == 0)
- flags = 0;
- else {
- errx(EXIT_FAILURE,
- "Invalid argument for '-o' option.");
- }
- break;
- case 'q':
- if (action != CREATE)
- usage();
- errno = 0;
- queue_size = strtoul(optarg, NULL, 10);
- if (queue_size == 0 && errno != 0)
- errx(EXIT_FAILURE, "Invalid queue_size.");
- break;
- case 'r':
- if (action != CREATE && action != RESCUE)
- usage();
- errno = 0;
- maxconnections = strtoul(optarg, NULL, 10);
- if (maxconnections == 0 && errno != 0)
- errx(EXIT_FAILURE, "Invalid queue_size.");
- break;
- case 's':
- if (action != CREATE)
- usage();
- errno = 0;
- sectorsize = strtoul(optarg, NULL, 10);
- if (sectorsize == 0 && errno != 0)
- errx(EXIT_FAILURE, "Invalid sectorsize.");
- break;
- case 't':
- if (action != CREATE)
- usage();
- errno = 0;
- timeout = strtoul(optarg, NULL, 10);
- if (timeout == 0 && errno != 0)
- errx(EXIT_FAILURE, "Invalid timeout.");
- break;
- case 'u':
- errno = 0;
- unit = strtol(optarg, NULL, 10);
- if (unit == 0 && errno != 0)
- errx(EXIT_FAILURE, "Invalid unit number.");
- break;
- case 'v':
- if (action == DESTROY)
- usage();
- g_gate_verbose++;
- break;
- default:
- usage();
- }
- }
- argc -= optind;
- argv += optind;
-
- switch (action) {
- case CREATE:
- if (argc != 1)
- usage();
- g_gate_load_module();
- g_gate_open_device();
- url = argv[0];
- g_gatehttp_create();
- break;
- case DESTROY:
- if (unit == -1) {
- fprintf(stderr, "Required unit number.\n");
- usage();
- }
- g_gate_verbose = 1;
- g_gate_open_device();
- g_gate_destroy(unit, force);
- break;
- case LIST:
- g_gate_list(unit, g_gate_verbose);
- break;
- case RESCUE:
- if (argc != 1)
- usage();
- if (unit == -1) {
- fprintf(stderr, "Required unit number.\n");
- usage();
- }
- g_gate_open_device();
- url = argv[0];
- g_gatehttp_rescue();
- break;
- case UNSET:
- default:
- usage();
- }
- g_gate_close_device();
- exit(EXIT_SUCCESS);
- }
|