| @@ -3,8 +3,6 @@ | |||
| *.8.gz | |||
| *.full | |||
| *.debug | |||
| ggatec/ggatec | |||
| ggated/ggated | |||
| ggatel/ggatel | |||
| ggatehttp/ggatehttp | |||
| tests/Kyuafile | |||
| tests/ggate_test | |||
| tests/ggatehttp_test | |||
| @@ -1,16 +1,13 @@ | |||
| # $FreeBSD$ | |||
| SUBDIR= ${_ggatec} \ | |||
| ${_ggated} \ | |||
| ggatel \ | |||
| SUBDIR= ${_ggatehttp} \ | |||
| tests | |||
| _ggatec= ggatec | |||
| _ggated= ggated | |||
| _ggatehttp= ggatehttp | |||
| .PHONY: devtest | |||
| devtest: | |||
| find [gst]* -type f -name '*.c' -o -name 'Makefile*' -o -name '*.sh' -o -name '*.8' | entr sh -c 'make -j 4 && make install -j 4 && (kyua test -k /usr/tests/Kyuafile sys/geom/class/gate || (cd /usr/tests && kyua report --verbose))' | |||
| find [gst]* -type f -name '*.c' -o -name 'Makefile*' -o -name '*.sh' -o -name '*.8' | entr sh -c '(set -o pipefail; make -j 4 | head -n 30) && make install -j 4 && (kyua test -k /usr/tests/Kyuafile sys/geom/class/gate || (cd /usr/tests && kyua report --verbose))' | |||
| .include <bsd.subdir.mk> | |||
| @@ -1,4 +1,20 @@ | |||
| ggate working tree | |||
| ================== | |||
| https ggate working tree | |||
| ======================== | |||
| This is a working tree for ggate work. | |||
| This is a variant of ggatec using http(s) GET/PUT instead of talking | |||
| to ggated. | |||
| Note that when I started on this project, that this would be completely | |||
| standards complaint. After running into an issue w/ the wsgidav server | |||
| not supporting partial PUTs, I did some research, and came across this | |||
| [post](https://blog.sphere.chronosempire.org.uk/2012/11/21/webdav-and-the-http-patch-nightmare) | |||
| that talks about how the IETF intentionally broke partial PUTs, despite | |||
| having the same problems w/ other parts of their spec. | |||
| Servers known to work: | |||
| - apache 2.2.x: can truncate file under some conditions | |||
| Services known to not work: | |||
| - wsgidav (Python): Does not implement partial PUT | |||
| @@ -1,15 +0,0 @@ | |||
| # $FreeBSD$ | |||
| .PATH: ${.CURDIR:H}/shared | |||
| PROG= ggatec | |||
| MAN= ggatec.8 | |||
| SRCS= ggatec.c ggate.c | |||
| CFLAGS+= -DMAX_SEND_SIZE=32768 | |||
| CFLAGS+= -DLIBGEOM | |||
| CFLAGS+= -I${.CURDIR:H}/shared | |||
| LDADD= -lgeom -lutil -lpthread | |||
| .include <bsd.prog.mk> | |||
| @@ -1,710 +0,0 @@ | |||
| /*- | |||
| * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||
| * | |||
| * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> | |||
| * All rights reserved. | |||
| * | |||
| * 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 <signal.h> | |||
| #include <err.h> | |||
| #include <errno.h> | |||
| #include <assert.h> | |||
| #include <sys/param.h> | |||
| #include <sys/ioctl.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 <geom/gate/g_gate.h> | |||
| #include "ggate.h" | |||
| static const char *sockprefix = "sock:"; | |||
| static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET; | |||
| static const char *path = NULL; | |||
| static const char *host = 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 unsigned port = G_GATE_PORT; | |||
| static off_t mediasize; | |||
| static unsigned sectorsize = 0; | |||
| static unsigned timeout = G_GATE_TIMEOUT; | |||
| static int sendfd, recvfd; | |||
| static uint32_t token; | |||
| static pthread_t sendtd, recvtd; | |||
| static int reconnect; | |||
| static void | |||
| usage(void) | |||
| { | |||
| fprintf(stderr, "usage: %s create [-nv] [-o <ro|wo|rw>] [-p port] " | |||
| "[-q queue_size] [-R rcvbuf] [-S sndbuf] [-s sectorsize] " | |||
| "[-t timeout] [-u unit] <host> <path>\n", getprogname()); | |||
| fprintf(stderr, " %s rescue [-nv] [-o <ro|wo|rw>] [-p port] " | |||
| "[-R rcvbuf] [-S sndbuf] <-u unit> <host> <path>\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 * | |||
| send_thread(void *arg __unused) | |||
| { | |||
| struct g_gate_ctl_io ggio; | |||
| struct g_gate_hdr hdr; | |||
| static char *buf; | |||
| ssize_t data; | |||
| int buflen = 1024*1024; | |||
| int error; | |||
| if (buf == NULL) | |||
| buf = malloc(buflen); | |||
| g_gate_log(LOG_NOTICE, "%s: started!", __func__); | |||
| ggio.gctl_version = G_GATE_VERSION; | |||
| ggio.gctl_unit = unit; | |||
| ggio.gctl_data = buf; | |||
| for (;;) { | |||
| ggio.gctl_length = buflen; | |||
| ggio.gctl_error = 0; | |||
| g_gate_ioctl(G_GATE_CMD_START, &ggio); | |||
| error = ggio.gctl_error; | |||
| switch (error) { | |||
| case 0: | |||
| break; | |||
| case ECANCELED: | |||
| if (reconnect) | |||
| break; | |||
| /* Exit gracefully. */ | |||
| g_gate_close_device(); | |||
| exit(EXIT_SUCCESS); | |||
| #if 0 | |||
| case ENOMEM: | |||
| /* Buffer too small. */ | |||
| ggio.gctl_data = realloc(ggio.gctl_data, | |||
| ggio.gctl_length); | |||
| if (ggio.gctl_data != NULL) { | |||
| bsize = ggio.gctl_length; | |||
| goto once_again; | |||
| } | |||
| /* FALLTHROUGH */ | |||
| #endif | |||
| case ENXIO: | |||
| default: | |||
| g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME, | |||
| strerror(error)); | |||
| } | |||
| if (reconnect) | |||
| break; | |||
| g_gate_log(LOG_DEBUG, "ggio, ver: %u, unit: %d, seq: %llu, cmd: %u, offset: %llu, len: %llu", ggio.gctl_version, ggio.gctl_unit, ggio.gctl_seq, ggio.gctl_cmd, ggio.gctl_offset, ggio.gctl_length); | |||
| switch (ggio.gctl_cmd) { | |||
| case BIO_READ: | |||
| hdr.gh_cmd = GGATE_CMD_READ; | |||
| break; | |||
| case BIO_WRITE: | |||
| hdr.gh_cmd = GGATE_CMD_WRITE; | |||
| break; | |||
| case BIO_DELETE: | |||
| hdr.gh_cmd = GGATE_CMD_DELETE; | |||
| break; | |||
| case BIO_FLUSH: | |||
| hdr.gh_cmd = GGATE_CMD_FLUSH; | |||
| break; | |||
| default: | |||
| /* XXX - how to handle this? */ | |||
| g_gate_log(LOG_ERR, "Got unhandled cmd: %d", ggio.gctl_cmd); | |||
| reconnect = 1; | |||
| pthread_kill(recvtd, SIGUSR1); | |||
| break; | |||
| } | |||
| hdr.gh_seq = ggio.gctl_seq; | |||
| hdr.gh_offset = ggio.gctl_offset; | |||
| hdr.gh_length = ggio.gctl_length; | |||
| hdr.gh_error = 0; | |||
| g_gate_log(LOG_DEBUG, "hdr packet, cmd: %hhu, off: %llu, len: %u, seq: %u", hdr.gh_cmd, hdr.gh_offset, hdr.gh_length, hdr.gh_seq); | |||
| g_gate_swap2n_hdr(&hdr); | |||
| data = g_gate_send(sendfd, &hdr, sizeof(hdr), MSG_NOSIGNAL); | |||
| g_gate_log(LOG_DEBUG, "Sent hdr packet."); | |||
| g_gate_swap2h_hdr(&hdr); | |||
| if (reconnect) | |||
| break; | |||
| if (data != sizeof(hdr)) { | |||
| g_gate_log(LOG_ERR, "Lost connection 1."); | |||
| reconnect = 1; | |||
| pthread_kill(recvtd, SIGUSR1); | |||
| break; | |||
| } | |||
| if (hdr.gh_cmd == GGATE_CMD_WRITE) { | |||
| data = g_gate_send(sendfd, ggio.gctl_data, | |||
| ggio.gctl_length, MSG_NOSIGNAL); | |||
| if (reconnect) | |||
| break; | |||
| if (data != ggio.gctl_length) { | |||
| g_gate_log(LOG_ERR, "Lost connection 2 (%zd != %zd).", data, (ssize_t)ggio.gctl_length); | |||
| reconnect = 1; | |||
| pthread_kill(recvtd, SIGUSR1); | |||
| break; | |||
| } | |||
| g_gate_log(LOG_DEBUG, "Sent %zd bytes (offset=%llu, " | |||
| "size=%u).", data, hdr.gh_offset, hdr.gh_length); | |||
| } | |||
| } | |||
| g_gate_log(LOG_DEBUG, "%s: Died.", __func__); | |||
| return (NULL); | |||
| } | |||
| static void * | |||
| recv_thread(void *arg __unused) | |||
| { | |||
| struct g_gate_ctl_io ggio; | |||
| struct g_gate_hdr hdr; | |||
| static char *buf; | |||
| int buflen; | |||
| ssize_t data; | |||
| buflen = 1024*1024; | |||
| if (buf == NULL) | |||
| buf = malloc(buflen); | |||
| g_gate_log(LOG_NOTICE, "%s: started!", __func__); | |||
| ggio.gctl_version = G_GATE_VERSION; | |||
| ggio.gctl_unit = unit; | |||
| ggio.gctl_data = buf; | |||
| for (;;) { | |||
| data = g_gate_recv(recvfd, &hdr, sizeof(hdr), MSG_WAITALL); | |||
| if (reconnect) | |||
| break; | |||
| g_gate_swap2h_hdr(&hdr); | |||
| if (data != sizeof(hdr)) { | |||
| if (data == -1 && errno == EAGAIN) | |||
| continue; | |||
| g_gate_log(LOG_ERR, "Lost connection 3."); | |||
| reconnect = 1; | |||
| pthread_kill(sendtd, SIGUSR1); | |||
| break; | |||
| } | |||
| g_gate_log(LOG_DEBUG, "Received hdr packet."); | |||
| ggio.gctl_seq = hdr.gh_seq; | |||
| ggio.gctl_cmd = hdr.gh_cmd; | |||
| ggio.gctl_offset = hdr.gh_offset; | |||
| ggio.gctl_length = hdr.gh_length; | |||
| ggio.gctl_error = hdr.gh_error; | |||
| if (ggio.gctl_error == 0 && ggio.gctl_cmd == GGATE_CMD_READ) { | |||
| if (ggio.gctl_length > buflen) { | |||
| g_gate_log(LOG_ERR, "Received too large data, %llu.", ggio.gctl_length); | |||
| reconnect = 1; | |||
| pthread_kill(sendtd, SIGUSR1); | |||
| break; | |||
| } | |||
| data = g_gate_recv(recvfd, ggio.gctl_data, | |||
| ggio.gctl_length, MSG_WAITALL); | |||
| if (reconnect) | |||
| break; | |||
| g_gate_log(LOG_DEBUG, "Received data packet."); | |||
| if (data != ggio.gctl_length) { | |||
| g_gate_log(LOG_ERR, "Lost connection 4."); | |||
| reconnect = 1; | |||
| pthread_kill(sendtd, SIGUSR1); | |||
| break; | |||
| } | |||
| g_gate_log(LOG_DEBUG, "Received %d bytes (offset=%ju, " | |||
| "size=%zu).", data, (uintmax_t)hdr.gh_offset, | |||
| (size_t)hdr.gh_length); | |||
| } | |||
| g_gate_ioctl(G_GATE_CMD_DONE, &ggio); | |||
| } | |||
| g_gate_log(LOG_DEBUG, "%s: Died.", __func__); | |||
| pthread_exit(NULL); | |||
| } | |||
| static int | |||
| handshake(int dir) | |||
| { | |||
| struct g_gate_version ver; | |||
| struct g_gate_cinit cinit; | |||
| struct g_gate_sinit sinit; | |||
| struct sockaddr *serv; | |||
| struct sockaddr_in inaddr; | |||
| struct sockaddr_un unixaddr; | |||
| socklen_t addrlen; | |||
| int sfd; | |||
| const char *sockname; | |||
| sockname = NULL; | |||
| /* | |||
| * Do the network stuff. | |||
| */ | |||
| if (strncmp(host, sockprefix, strlen(sockprefix)) == 0) { | |||
| sockname = host + strlen(sockprefix); | |||
| if (strlen(sockname) + 1 > sizeof(unixaddr.sun_path)) { | |||
| g_gate_log(LOG_DEBUG, "Socket path is too long."); | |||
| return (-1); | |||
| } | |||
| unixaddr = (struct sockaddr_un) { | |||
| .sun_len = sizeof(unixaddr), | |||
| .sun_family = AF_UNIX, | |||
| }; | |||
| strncpy(unixaddr.sun_path, sockname, sizeof(unixaddr.sun_path)); | |||
| sfd = socket(AF_UNIX, SOCK_STREAM, 0); | |||
| if (sfd == -1) { | |||
| g_gate_log(LOG_DEBUG, "Cannot open socket: %s.", | |||
| strerror(errno)); | |||
| return (-1); | |||
| } | |||
| serv = (struct sockaddr *)&unixaddr; | |||
| addrlen = sizeof unixaddr; | |||
| } else { | |||
| bzero(&inaddr, sizeof(inaddr)); | |||
| inaddr.sin_family = AF_INET; | |||
| inaddr.sin_addr.s_addr = g_gate_str2ip(host); | |||
| if (inaddr.sin_addr.s_addr == INADDR_NONE) { | |||
| g_gate_log(LOG_DEBUG, "Invalid IP/host name: %s.", host); | |||
| return (-1); | |||
| } | |||
| inaddr.sin_port = htons(port); | |||
| sfd = socket(AF_INET, SOCK_STREAM, 0); | |||
| if (sfd == -1) { | |||
| g_gate_log(LOG_DEBUG, "Cannot open socket: %s.", | |||
| strerror(errno)); | |||
| return (-1); | |||
| } | |||
| serv = (struct sockaddr *)&inaddr; | |||
| addrlen = sizeof inaddr; | |||
| g_gate_socket_settings(sfd); | |||
| } | |||
| if (connect(sfd, serv, addrlen) == -1) { | |||
| g_gate_log(LOG_DEBUG, "Cannot connect to server: %s.", | |||
| strerror(errno)); | |||
| close(sfd); | |||
| return (-1); | |||
| } | |||
| if (sockname != NULL) | |||
| g_gate_log(LOG_INFO, "Connected to socket: %s.", sockname); | |||
| else | |||
| g_gate_log(LOG_INFO, "Connected to the server: %s:%d.", host, port); | |||
| /* | |||
| * Create and send version packet. | |||
| */ | |||
| g_gate_log(LOG_DEBUG, "Sending version packet."); | |||
| assert(strlen(GGATE_MAGIC) == sizeof(ver.gv_magic)); | |||
| bcopy(GGATE_MAGIC, ver.gv_magic, sizeof(ver.gv_magic)); | |||
| ver.gv_version = GGATE_VERSION; | |||
| ver.gv_error = 0; | |||
| g_gate_swap2n_version(&ver); | |||
| if (g_gate_send(sfd, &ver, sizeof(ver), MSG_NOSIGNAL) == -1) { | |||
| g_gate_log(LOG_DEBUG, "Error while sending version packet: %s.", | |||
| strerror(errno)); | |||
| close(sfd); | |||
| return (-1); | |||
| } | |||
| bzero(&ver, sizeof(ver)); | |||
| if (g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL) == -1) { | |||
| g_gate_log(LOG_DEBUG, "Error while receiving data: %s.", | |||
| strerror(errno)); | |||
| close(sfd); | |||
| return (-1); | |||
| } | |||
| if (ver.gv_error != 0) { | |||
| g_gate_log(LOG_DEBUG, "Version verification problem: %s.", | |||
| strerror(errno)); | |||
| close(sfd); | |||
| return (-1); | |||
| } | |||
| /* | |||
| * Create and send initial packet. | |||
| */ | |||
| g_gate_log(LOG_DEBUG, "Sending initial packet."); | |||
| if (strlcpy(cinit.gc_path, path, sizeof(cinit.gc_path)) >= | |||
| sizeof(cinit.gc_path)) { | |||
| g_gate_log(LOG_DEBUG, "Path name too long."); | |||
| close(sfd); | |||
| return (-1); | |||
| } | |||
| cinit.gc_flags = flags | dir; | |||
| cinit.gc_token = token; | |||
| cinit.gc_nconn = 2; | |||
| g_gate_swap2n_cinit(&cinit); | |||
| if (g_gate_send(sfd, &cinit, sizeof(cinit), MSG_NOSIGNAL) == -1) { | |||
| g_gate_log(LOG_DEBUG, "Error while sending initial packet: %s.", | |||
| strerror(errno)); | |||
| close(sfd); | |||
| return (-1); | |||
| } | |||
| g_gate_swap2h_cinit(&cinit); | |||
| /* | |||
| * Receiving initial packet from server. | |||
| */ | |||
| g_gate_log(LOG_DEBUG, "Receiving initial packet."); | |||
| if (g_gate_recv(sfd, &sinit, sizeof(sinit), MSG_WAITALL) == -1) { | |||
| g_gate_log(LOG_DEBUG, "Error while receiving data: %s.", | |||
| strerror(errno)); | |||
| close(sfd); | |||
| return (-1); | |||
| } | |||
| g_gate_swap2h_sinit(&sinit); | |||
| if (sinit.gs_error != 0) { | |||
| g_gate_log(LOG_DEBUG, "Error from server: %s.", | |||
| strerror(sinit.gs_error)); | |||
| close(sfd); | |||
| return (-1); | |||
| } | |||
| g_gate_log(LOG_DEBUG, "Received initial packet."); | |||
| mediasize = sinit.gs_mediasize; | |||
| if (sectorsize == 0) | |||
| sectorsize = sinit.gs_sectorsize; | |||
| return (sfd); | |||
| } | |||
| 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_gatec_connect(void) | |||
| { | |||
| token = arc4random(); | |||
| /* | |||
| * Our receive descriptor is connected to the send descriptor on the | |||
| * server side. | |||
| */ | |||
| recvfd = handshake(GGATE_FLAG_SEND); | |||
| if (recvfd == -1) | |||
| return (0); | |||
| /* | |||
| * Our send descriptor is connected to the receive descriptor on the | |||
| * server side. | |||
| */ | |||
| sendfd = handshake(GGATE_FLAG_RECV); | |||
| if (sendfd == -1) | |||
| return (0); | |||
| return (1); | |||
| } | |||
| static void | |||
| g_gatec_start(void) | |||
| { | |||
| int error; | |||
| reconnect = 0; | |||
| error = pthread_create(&recvtd, NULL, recv_thread, NULL); | |||
| if (error != 0) { | |||
| g_gate_destroy(unit, 1); | |||
| g_gate_xlog("pthread_create(recv_thread): %s.", | |||
| strerror(error)); | |||
| } | |||
| sendtd = pthread_self(); | |||
| send_thread(NULL); | |||
| /* Disconnected. */ | |||
| close(sendfd); | |||
| close(recvfd); | |||
| } | |||
| static void | |||
| signop(int sig __unused) | |||
| { | |||
| /* Do nothing. */ | |||
| } | |||
| static void | |||
| g_gatec_loop(void) | |||
| { | |||
| struct g_gate_ctl_cancel ggioc; | |||
| signal(SIGUSR1, signop); | |||
| for (;;) { | |||
| g_gatec_start(); | |||
| g_gate_log(LOG_NOTICE, "Disconnected [%s %s]. Connecting...", | |||
| host, path); | |||
| while (!g_gatec_connect()) { | |||
| sleep(2); | |||
| g_gate_log(LOG_NOTICE, "Connecting [%s %s]...", host, | |||
| path); | |||
| } | |||
| 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_gatec_create(void) | |||
| { | |||
| struct g_gate_ctl_create ggioc; | |||
| if (!g_gatec_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:%u %s", host, | |||
| port, path); | |||
| 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_gatec_loop(); | |||
| } | |||
| static void | |||
| g_gatec_rescue(void) | |||
| { | |||
| struct g_gate_ctl_cancel ggioc; | |||
| if (!g_gatec_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_gatec_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, "fno:p:q:R:S:s:t:u:v"); | |||
| if (ch == -1) | |||
| break; | |||
| switch (ch) { | |||
| case 'f': | |||
| if (action != DESTROY) | |||
| usage(); | |||
| force = 1; | |||
| break; | |||
| case 'n': | |||
| if (action != CREATE && action != RESCUE) | |||
| usage(); | |||
| nagle = 0; | |||
| 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 'p': | |||
| if (action != CREATE && action != RESCUE) | |||
| usage(); | |||
| errno = 0; | |||
| port = strtoul(optarg, NULL, 10); | |||
| if (port == 0 && errno != 0) | |||
| errx(EXIT_FAILURE, "Invalid port."); | |||
| 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; | |||
| rcvbuf = strtoul(optarg, NULL, 10); | |||
| if (rcvbuf == 0 && errno != 0) | |||
| errx(EXIT_FAILURE, "Invalid rcvbuf."); | |||
| break; | |||
| case 'S': | |||
| if (action != CREATE && action != RESCUE) | |||
| usage(); | |||
| errno = 0; | |||
| sndbuf = strtoul(optarg, NULL, 10); | |||
| if (sndbuf == 0 && errno != 0) | |||
| errx(EXIT_FAILURE, "Invalid sndbuf."); | |||
| 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 != 2) | |||
| usage(); | |||
| g_gate_load_module(); | |||
| g_gate_open_device(); | |||
| host = argv[0]; | |||
| path = argv[1]; | |||
| g_gatec_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 != 2) | |||
| usage(); | |||
| if (unit == -1) { | |||
| fprintf(stderr, "Required unit number.\n"); | |||
| usage(); | |||
| } | |||
| g_gate_open_device(); | |||
| host = argv[0]; | |||
| path = argv[1]; | |||
| g_gatec_rescue(); | |||
| break; | |||
| case UNSET: | |||
| default: | |||
| usage(); | |||
| } | |||
| g_gate_close_device(); | |||
| exit(EXIT_SUCCESS); | |||
| } | |||
| @@ -1,13 +0,0 @@ | |||
| # $FreeBSD$ | |||
| .PATH: ${.CURDIR:H}/shared | |||
| PROG= ggated | |||
| MAN= ggated.8 | |||
| SRCS= ggated.c ggate.c | |||
| LDADD= -lpthread -lutil | |||
| CFLAGS+= -I${.CURDIR:H}/shared | |||
| .include <bsd.prog.mk> | |||
| @@ -1,21 +0,0 @@ | |||
| # $FreeBSD$ | |||
| # Autogenerated - do NOT edit! | |||
| DIRDEPS = \ | |||
| gnu/lib/csu \ | |||
| include \ | |||
| include/arpa \ | |||
| include/xlocale \ | |||
| lib/${CSU_DIR} \ | |||
| lib/libc \ | |||
| lib/libcompiler_rt \ | |||
| lib/libgeom \ | |||
| lib/libthr \ | |||
| lib/libutil \ | |||
| .include <dirdeps.mk> | |||
| .if ${DEP_RELDIR} == ${_DEP_RELDIR} | |||
| # local dependencies - needed for -jN in clean tree | |||
| .endif | |||
| @@ -1,134 +0,0 @@ | |||
| .\" Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> | |||
| .\" All rights reserved. | |||
| .\" | |||
| .\" 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$ | |||
| .\" | |||
| .Dd September 8, 2016 | |||
| .Dt GGATED 8 | |||
| .Os | |||
| .Sh NAME | |||
| .Nm ggated | |||
| .Nd "GEOM Gate network daemon" | |||
| .Sh SYNOPSIS | |||
| .Nm | |||
| .Op Fl h | |||
| .Op Fl n | |||
| .Op Fl v | |||
| .Op Fl a Ar address | |||
| .Op Fl p Ar port | |||
| .Op Fl q Ar queue_size | |||
| .Op Fl F Ar pidfile | |||
| .Op Fl R Ar rcvbuf | |||
| .Op Fl S Ar sndbuf | |||
| .Op Ar "exports file" | |||
| .Sh DESCRIPTION | |||
| The | |||
| .Nm | |||
| utility is a network server for the GEOM Gate class. | |||
| It runs on a server machine to service GEOM Gate requests from workers | |||
| placed on a client machine. | |||
| Keep in mind, that connections between | |||
| .Xr ggatec 8 | |||
| and | |||
| .Nm | |||
| are not encrypted. | |||
| .Pp | |||
| Available options: | |||
| .Bl -tag -width ".Ar exports\ file" | |||
| .It Fl a Ar address | |||
| Specifies an IP address to bind to. | |||
| .It Fl h | |||
| Print available options. | |||
| .It Fl n | |||
| Do not use | |||
| .Dv TCP_NODELAY | |||
| option on TCP sockets. | |||
| .It Fl p Ar port | |||
| Port on which | |||
| .Nm | |||
| listens for connections. | |||
| Default is 3080. | |||
| .It Fl q Ar queue_size | |||
| The number of IOs to queue to the disks at once. | |||
| The default is 20. | |||
| If you have a large number of disks and/or you are not seeing the expected | |||
| number of IOPS, increase this parameter. | |||
| .It Fl F Ar pidfile | |||
| PID file that | |||
| .Nm | |||
| uses. | |||
| .It Fl R Ar rcvbuf | |||
| Size of receive buffer to use. | |||
| When not specified, the system default is used. | |||
| .It Fl S Ar sndbuf | |||
| Size of send buffer to use. | |||
| When not specified, the system default is used. | |||
| .It Fl v | |||
| Do not fork, run in foreground and print debug information on standard | |||
| output. | |||
| .It Ar "exports file" | |||
| An alternate location for the exports file. | |||
| .El | |||
| .Pp | |||
| The format of an exports file is as follows: | |||
| .Bd -literal -offset indent | |||
| 1.2.3.4 RO /dev/cd0 | |||
| 1.2.3.0/24 RW /tmp/test.img | |||
| hostname WO /tmp/image | |||
| .Ed | |||
| .Sh FILES | |||
| .Bl -tag -width ".Pa /var/run/ggated.pid" -compact | |||
| .It Pa /var/run/ggated.pid | |||
| The default location of the | |||
| .Nm | |||
| PID file. | |||
| .El | |||
| .Sh EXIT STATUS | |||
| Exit status is 0 on success, or 1 if the command fails. | |||
| To get details about the failure, | |||
| .Nm | |||
| should be called with the | |||
| .Fl v | |||
| option. | |||
| .Sh EXAMPLES | |||
| Export CD-ROM device and a file: | |||
| .Bd -literal -offset indent | |||
| # echo "1.2.3.0/24 RO /dev/cd0" > /etc/gg.exports | |||
| # echo "client RW /image" >> /etc/gg.exports | |||
| # ggated | |||
| .Ed | |||
| .Sh SEE ALSO | |||
| .Xr geom 4 , | |||
| .Xr ggatec 8 , | |||
| .Xr ggatel 8 | |||
| .Sh HISTORY | |||
| The | |||
| .Nm | |||
| utility appeared in | |||
| .Fx 5.3 . | |||
| .Sh AUTHORS | |||
| The | |||
| .Nm | |||
| utility as well as this manual page was written by | |||
| .An Pawel Jakub Dawidek Aq Mt pjd@FreeBSD.org . | |||
| @@ -0,0 +1,19 @@ | |||
| # $FreeBSD$ | |||
| .PATH: ${.CURDIR:H}/shared | |||
| PROG= ggatehttp | |||
| MAN= ggatehttp.8 | |||
| SRCS= ggatehttp.c ggate.c | |||
| CFLAGS+= -DMAX_SEND_SIZE=32768 | |||
| CFLAGS+= -DLIBGEOM | |||
| CFLAGS+= -I${.CURDIR:H}/shared | |||
| CFLAGS+= -I/usr/local/include | |||
| #CFLAGS+= -O0 | |||
| #CFLAGS+= -fprofile-instr-generate | |||
| LDFLAGS+= -L/usr/local/lib | |||
| LDADD= -lgeom -lutil -lpthread -lcurl | |||
| .include <bsd.prog.mk> | |||
| @@ -0,0 +1,770 @@ | |||
| /*- | |||
| * 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); | |||
| } | |||
| @@ -1,14 +0,0 @@ | |||
| # $FreeBSD$ | |||
| .PATH: ${.CURDIR:H}/shared | |||
| PROG= ggatel | |||
| MAN= ggatel.8 | |||
| SRCS= ggatel.c ggate.c | |||
| CFLAGS+= -DLIBGEOM | |||
| CFLAGS+= -I${.CURDIR:H}/shared | |||
| LDADD= -lgeom -lutil | |||
| .include <bsd.prog.mk> | |||
| @@ -1,22 +0,0 @@ | |||
| # $FreeBSD$ | |||
| # Autogenerated - do NOT edit! | |||
| DIRDEPS = \ | |||
| gnu/lib/csu \ | |||
| include \ | |||
| include/arpa \ | |||
| include/xlocale \ | |||
| lib/${CSU_DIR} \ | |||
| lib/libc \ | |||
| lib/libcompiler_rt \ | |||
| lib/libexpat \ | |||
| lib/libgeom \ | |||
| lib/libsbuf \ | |||
| lib/libutil \ | |||
| .include <dirdeps.mk> | |||
| .if ${DEP_RELDIR} == ${_DEP_RELDIR} | |||
| # local dependencies - needed for -jN in clean tree | |||
| .endif | |||
| @@ -1,164 +0,0 @@ | |||
| .\" Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> | |||
| .\" All rights reserved. | |||
| .\" | |||
| .\" 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$ | |||
| .\" | |||
| .Dd September 8, 2016 | |||
| .Dt GGATEL 8 | |||
| .Os | |||
| .Sh NAME | |||
| .Nm ggatel | |||
| .Nd "GEOM Gate local control utility" | |||
| .Sh SYNOPSIS | |||
| .Nm | |||
| .Cm create | |||
| .Op Fl v | |||
| .Op Fl o Cm ro | wo | rw | |||
| .Op Fl s Ar sectorsize | |||
| .Op Fl t Ar timeout | |||
| .Op Fl u Ar unit | |||
| .Ar path | |||
| .Nm | |||
| .Cm destroy | |||
| .Op Fl f | |||
| .Fl u Ar unit | |||
| .Nm | |||
| .Cm list | |||
| .Op Fl v | |||
| .Op Fl u Ar unit | |||
| .Nm | |||
| .Cm rescue | |||
| .Op Fl v | |||
| .Op Fl o Cm ro | wo | rw | |||
| .Fl u Ar unit | |||
| .Ar path | |||
| .Sh DESCRIPTION | |||
| The | |||
| .Nm | |||
| utility is a local GEOM Gate class consumer. | |||
| It can be used as a replacement for | |||
| .Xr md 4 | |||
| devices or as a | |||
| .Dq GEOMificator | |||
| for non GEOM-aware devices, but it was mainly created as an example | |||
| on how to use and how to communicate with the GEOM Gate kernel subsystem. | |||
| .Pp | |||
| Available commands: | |||
| .Bl -tag -width ".Cm destroy" | |||
| .It Cm create | |||
| Create a | |||
| .Nm ggate | |||
| provider related to the given regular file or device. | |||
| .It Cm destroy | |||
| Destroy the given | |||
| .Nm ggate | |||
| provider. | |||
| .It Cm list | |||
| List | |||
| .Nm ggate | |||
| providers. | |||
| .It Cm rescue | |||
| Take over a previously created provider and handle pending and future | |||
| requests. | |||
| This is useful if the initial | |||
| .Nm | |||
| process died. | |||
| To prevent data loss, the given path must lead to the | |||
| regular file or device that was used to create the provider. | |||
| .El | |||
| .Pp | |||
| Available options: | |||
| .Bl -tag -width ".Fl s Cm ro | wo | rw" | |||
| .It Fl f | |||
| Forcibly destroy | |||
| .Nm ggate | |||
| provider (cancels all pending requests). | |||
| .It Fl o Cm ro | wo | rw | |||
| Specify permissions to use when opening the file or device: read-only | |||
| .Pq Cm ro , | |||
| write-only | |||
| .Pq Cm wo , | |||
| or read-write | |||
| .Pq Cm rw . | |||
| Default is | |||
| .Cm rw . | |||
| .It Fl s Ar sectorsize | |||
| Sector size for | |||
| .Nm ggate | |||
| provider. | |||
| If not specified, it is taken from the device, or set to 512 bytes for files. | |||
| .It Fl t Ar timeout | |||
| Number of seconds to wait before an I/O request will be canceled. | |||
| 0 means no timeout. | |||
| Default is 30. | |||
| .It Fl u Ar unit | |||
| Unit number to use. | |||
| .It Fl v | |||
| Do not fork, run in foreground and print debug information on standard | |||
| output. | |||
| .It Ar path | |||
| Path to a regular file or device. | |||
| .El | |||
| .Sh EXIT STATUS | |||
| Exit status is 0 on success, or 1 if the command fails. | |||
| To get details about the failure, | |||
| .Nm | |||
| should be called with the | |||
| .Fl v | |||
| option. | |||
| .Sh EXAMPLES | |||
| .Dq GEOMify | |||
| the | |||
| .Dq Li fd0 | |||
| device and use | |||
| .Xr gbde 8 | |||
| to encrypt data on a floppy disk. | |||
| .Bd -literal -offset indent | |||
| ggatel create -u 5 /dev/fd0 | |||
| gbde init /dev/ggate5 | |||
| gbde attach ggate5 | |||
| newfs /dev/ggate5.bde | |||
| mount /dev/ggate5.bde /secret | |||
| cp /private/foo /secret/ | |||
| umount /secret | |||
| gbde detach ggate5 | |||
| ggatel destroy -u 5 | |||
| .Ed | |||
| .Sh SEE ALSO | |||
| .Xr geom 4 , | |||
| .Xr gbde 8 , | |||
| .Xr ggatec 8 , | |||
| .Xr ggated 8 , | |||
| .Xr mount 8 , | |||
| .Xr newfs 8 | |||
| .Sh HISTORY | |||
| The | |||
| .Nm | |||
| utility appeared in | |||
| .Fx 5.3 . | |||
| .Sh AUTHORS | |||
| The | |||
| .Nm | |||
| utility as well as this manual page was written by | |||
| .An Pawel Jakub Dawidek Aq Mt pjd@FreeBSD.org . | |||
| @@ -1,330 +0,0 @@ | |||
| /*- | |||
| * SPDX-License-Identifier: BSD-2-Clause-FreeBSD | |||
| * | |||
| * Copyright (c) 2004 Pawel Jakub Dawidek <pjd@FreeBSD.org> | |||
| * All rights reserved. | |||
| * | |||
| * 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 <err.h> | |||
| #include <errno.h> | |||
| #include <assert.h> | |||
| #include <sys/param.h> | |||
| #include <sys/time.h> | |||
| #include <sys/bio.h> | |||
| #include <sys/disk.h> | |||
| #include <sys/ioctl.h> | |||
| #include <sys/stat.h> | |||
| #include <sys/syslog.h> | |||
| #include <geom/gate/g_gate.h> | |||
| #include "ggate.h" | |||
| static enum { UNSET, CREATE, DESTROY, LIST, RESCUE } action = UNSET; | |||
| static const char *path = NULL; | |||
| static int unit = G_GATE_UNIT_AUTO; | |||
| static unsigned flags = 0; | |||
| static int force = 0; | |||
| static unsigned sectorsize = 0; | |||
| static unsigned timeout = G_GATE_TIMEOUT; | |||
| static void | |||
| usage(void) | |||
| { | |||
| fprintf(stderr, "usage: %s create [-v] [-o <ro|wo|rw>] " | |||
| "[-s sectorsize] [-t timeout] [-u unit] <path>\n", getprogname()); | |||
| fprintf(stderr, " %s rescue [-v] [-o <ro|wo|rw>] <-u unit> " | |||
| "<path>\n", getprogname()); | |||
| fprintf(stderr, " %s destroy [-f] <-u unit>\n", getprogname()); | |||
| fprintf(stderr, " %s list [-v] [-u unit]\n", getprogname()); | |||
| exit(EXIT_FAILURE); | |||
| } | |||
| static int | |||
| g_gate_openflags(unsigned ggflags) | |||
| { | |||
| if ((ggflags & G_GATE_FLAG_READONLY) != 0) | |||
| return (O_RDONLY); | |||
| else if ((ggflags & G_GATE_FLAG_WRITEONLY) != 0) | |||
| return (O_WRONLY); | |||
| return (O_RDWR); | |||
| } | |||
| static void | |||
| g_gatel_serve(int fd) | |||
| { | |||
| struct g_gate_ctl_io ggio; | |||
| size_t bsize; | |||
| if (g_gate_verbose == 0) { | |||
| if (daemon(0, 0) == -1) { | |||
| g_gate_destroy(unit, 1); | |||
| err(EXIT_FAILURE, "Cannot daemonize"); | |||
| } | |||
| } | |||
| g_gate_log(LOG_DEBUG, "Worker created: %u.", getpid()); | |||
| ggio.gctl_version = G_GATE_VERSION; | |||
| ggio.gctl_unit = unit; | |||
| bsize = sectorsize; | |||
| ggio.gctl_data = malloc(bsize); | |||
| for (;;) { | |||
| int error; | |||
| once_again: | |||
| ggio.gctl_length = bsize; | |||
| ggio.gctl_error = 0; | |||
| g_gate_ioctl(G_GATE_CMD_START, &ggio); | |||
| error = ggio.gctl_error; | |||
| switch (error) { | |||
| case 0: | |||
| break; | |||
| case ECANCELED: | |||
| /* Exit gracefully. */ | |||
| free(ggio.gctl_data); | |||
| g_gate_close_device(); | |||
| close(fd); | |||
| exit(EXIT_SUCCESS); | |||
| case ENOMEM: | |||
| /* Buffer too small. */ | |||
| assert(ggio.gctl_cmd == BIO_DELETE || | |||
| ggio.gctl_cmd == BIO_WRITE); | |||
| ggio.gctl_data = realloc(ggio.gctl_data, | |||
| ggio.gctl_length); | |||
| if (ggio.gctl_data != NULL) { | |||
| bsize = ggio.gctl_length; | |||
| goto once_again; | |||
| } | |||
| /* FALLTHROUGH */ | |||
| case ENXIO: | |||
| default: | |||
| g_gate_xlog("ioctl(/dev/%s): %s.", G_GATE_CTL_NAME, | |||
| strerror(error)); | |||
| } | |||
| error = 0; | |||
| switch (ggio.gctl_cmd) { | |||
| case BIO_READ: | |||
| if ((size_t)ggio.gctl_length > bsize) { | |||
| ggio.gctl_data = realloc(ggio.gctl_data, | |||
| ggio.gctl_length); | |||
| if (ggio.gctl_data != NULL) | |||
| bsize = ggio.gctl_length; | |||
| else | |||
| error = ENOMEM; | |||
| } | |||
| if (error == 0) { | |||
| if (pread(fd, ggio.gctl_data, ggio.gctl_length, | |||
| ggio.gctl_offset) == -1) { | |||
| error = errno; | |||
| } | |||
| } | |||
| break; | |||
| case BIO_DELETE: | |||
| case BIO_WRITE: | |||
| if (pwrite(fd, ggio.gctl_data, ggio.gctl_length, | |||
| ggio.gctl_offset) == -1) { | |||
| error = errno; | |||
| } | |||
| break; | |||
| default: | |||
| error = EOPNOTSUPP; | |||
| } | |||
| ggio.gctl_error = error; | |||
| g_gate_ioctl(G_GATE_CMD_DONE, &ggio); | |||
| } | |||
| } | |||
| static void | |||
| g_gatel_create(void) | |||
| { | |||
| struct g_gate_ctl_create ggioc; | |||
| int fd; | |||
| fd = open(path, g_gate_openflags(flags) | O_DIRECT | O_FSYNC); | |||
| if (fd == -1) | |||
| err(EXIT_FAILURE, "Cannot open %s", path); | |||
| memset(&ggioc, 0, sizeof(ggioc)); | |||
| ggioc.gctl_version = G_GATE_VERSION; | |||
| ggioc.gctl_unit = unit; | |||
| ggioc.gctl_mediasize = g_gate_mediasize(fd); | |||
| if (sectorsize == 0) | |||
| sectorsize = g_gate_sectorsize(fd); | |||
| ggioc.gctl_sectorsize = sectorsize; | |||
| ggioc.gctl_timeout = timeout; | |||
| ggioc.gctl_flags = flags; | |||
| ggioc.gctl_maxcount = 0; | |||
| strlcpy(ggioc.gctl_info, path, sizeof(ggioc.gctl_info)); | |||
| g_gate_ioctl(G_GATE_CMD_CREATE, &ggioc); | |||
| if (unit == -1) | |||
| printf("%s%u\n", G_GATE_PROVIDER_NAME, ggioc.gctl_unit); | |||
| unit = ggioc.gctl_unit; | |||
| g_gatel_serve(fd); | |||
| } | |||
| static void | |||
| g_gatel_rescue(void) | |||
| { | |||
| struct g_gate_ctl_cancel ggioc; | |||
| int fd; | |||
| fd = open(path, g_gate_openflags(flags)); | |||
| if (fd == -1) | |||
| err(EXIT_FAILURE, "Cannot open %s", path); | |||
| ggioc.gctl_version = G_GATE_VERSION; | |||
| ggioc.gctl_unit = unit; | |||
| ggioc.gctl_seq = 0; | |||
| g_gate_ioctl(G_GATE_CMD_CANCEL, &ggioc); | |||
| g_gatel_serve(fd); | |||
| } | |||
| int | |||
| main(int argc, char *argv[]) | |||
| { | |||
| if (argc < 2) | |||
| usage(); | |||
| if (strcasecmp(argv[1], "create") == 0) | |||
| action = CREATE; | |||
| else if (strcasecmp(argv[1], "rescue") == 0) | |||
| action = RESCUE; | |||
| else if (strcasecmp(argv[1], "destroy") == 0) | |||
| action = DESTROY; | |||
| else if (strcasecmp(argv[1], "list") == 0) | |||
| action = LIST; | |||
| else | |||
| usage(); | |||
| argc -= 1; | |||
| argv += 1; | |||
| for (;;) { | |||
| int ch; | |||
| ch = getopt(argc, argv, "fo: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 '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(); | |||
| path = argv[0]; | |||
| g_gatel_create(); | |||
| break; | |||
| case RESCUE: | |||
| if (argc != 1) | |||
| usage(); | |||
| if (unit == -1) { | |||
| fprintf(stderr, "Required unit number.\n"); | |||
| usage(); | |||
| } | |||
| g_gate_open_device(); | |||
| path = argv[0]; | |||
| g_gatel_rescue(); | |||
| 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 UNSET: | |||
| default: | |||
| usage(); | |||
| } | |||
| g_gate_close_device(); | |||
| exit(EXIT_SUCCESS); | |||
| } | |||
| @@ -4,6 +4,6 @@ PACKAGE= tests | |||
| TESTSDIR= ${TESTSBASE}/sys/geom/class/gate | |||
| ATF_TESTS_SH+= ggate_test | |||
| ATF_TESTS_SH+= ggatehttp_test | |||
| .include <bsd.test.mk> | |||
| @@ -1,269 +0,0 @@ | |||
| # $FreeBSD$ | |||
| PIDFILE=ggated.pid | |||
| PLAINFILES=plainfiles | |||
| PORT=33080 | |||
| CONF=gg.exports | |||
| atf_test_case ggated cleanup | |||
| ggated_head() | |||
| { | |||
| atf_set "descr" "ggated can proxy geoms" | |||
| atf_set "require.progs" "ggatec ggated" | |||
| atf_set "require.user" "root" | |||
| atf_set "timeout" 60 | |||
| } | |||
| ggated_body() | |||
| { | |||
| load_ggate | |||
| us=$(alloc_ggate_dev) | |||
| work=$(alloc_md) | |||
| src=$(alloc_md) | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/random of=/dev/$work bs=1m count=1 conv=notrunc | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/random of=/dev/$src bs=1m count=1 conv=notrunc | |||
| echo $CONF >> $PLAINFILES | |||
| echo "127.0.0.1 RW /dev/$work" > $CONF | |||
| atf_check ggated -p $PORT -F $PIDFILE $CONF | |||
| atf_check ggatec create -p $PORT -u $us 127.0.0.1 /dev/$work | |||
| ggate_dev=/dev/ggate${us} | |||
| wait_for_ggate_device ${ggate_dev} | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/${src} of=${ggate_dev} bs=1m count=1 conv=notrunc | |||
| checksum /dev/$src /dev/$work | |||
| } | |||
| ggated_cleanup() | |||
| { | |||
| common_cleanup | |||
| } | |||
| atf_test_case ggatel_file cleanup | |||
| ggatel_file_head() | |||
| { | |||
| atf_set "descr" "ggatel can proxy files" | |||
| atf_set "require.progs" "ggatel" | |||
| atf_set "require.user" "root" | |||
| atf_set "timeout" 15 | |||
| } | |||
| ggatel_file_body() | |||
| { | |||
| load_ggate | |||
| us=$(alloc_ggate_dev) | |||
| echo src work >> ${PLAINFILES} | |||
| dd if=/dev/random of=work bs=1m count=1 | |||
| dd if=/dev/random of=src bs=1m count=1 | |||
| atf_check ggatel create -u $us work | |||
| ggate_dev=/dev/ggate${us} | |||
| wait_for_ggate_device ${ggate_dev} | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=src of=${ggate_dev} bs=1m count=1 conv=notrunc | |||
| checksum src work | |||
| } | |||
| ggatel_file_cleanup() | |||
| { | |||
| common_cleanup | |||
| } | |||
| atf_test_case ggatel_md cleanup | |||
| ggatel_md_head() | |||
| { | |||
| atf_set "descr" "ggatel can proxy files" | |||
| atf_set "require.progs" "ggatel" | |||
| atf_set "require.user" "root" | |||
| atf_set "timeout" 15 | |||
| } | |||
| ggatel_md_body() | |||
| { | |||
| load_ggate | |||
| us=$(alloc_ggate_dev) | |||
| work=$(alloc_md) | |||
| src=$(alloc_md) | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/random of=$work bs=1m count=1 conv=notrunc | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/random of=$src bs=1m count=1 conv=notrunc | |||
| atf_check ggatel create -u $us /dev/$work | |||
| ggate_dev=/dev/ggate${us} | |||
| wait_for_ggate_device ${ggate_dev} | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/$src of=${ggate_dev} bs=1m count=1 conv=notrunc | |||
| checksum /dev/$src /dev/$work | |||
| } | |||
| ggatel_md_cleanup() | |||
| { | |||
| common_cleanup | |||
| } | |||
| atf_test_case ggate_sock cleanup | |||
| ggate_sock_head() | |||
| { | |||
| atf_set "descr" "ggatec can connect to ggated over unix domain socket" | |||
| atf_set "require.progs" "ggatec ggated" | |||
| atf_set "require.user" "root" | |||
| atf_set "timeout" 5 | |||
| } | |||
| ggate_sock_body() | |||
| { | |||
| load_ggate | |||
| us=$(alloc_ggate_dev) | |||
| work=$(alloc_md) | |||
| src=$(alloc_md) | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/random of=/dev/$work bs=1m count=1 conv=notrunc | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/random of=/dev/$src bs=1m count=1 conv=notrunc | |||
| echo $CONF >> $PLAINFILES | |||
| echo "0.0.0.0 RW /dev/$work" > $CONF | |||
| atf_check ggated -q 10 -s local.sock -F $PIDFILE $CONF | |||
| atf_check ggatec create -u $us sock:"$(pwd)/local.sock" /dev/$work | |||
| ggate_dev=/dev/ggate${us} | |||
| wait_for_ggate_device ${ggate_dev} | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/${src} of=${ggate_dev} bs=1m count=1 conv=notrunc | |||
| checksum /dev/$src /dev/$work | |||
| } | |||
| ggate_sock_cleanup() | |||
| { | |||
| common_cleanup | |||
| } | |||
| atf_init_test_cases() | |||
| { | |||
| atf_add_test_case ggated | |||
| atf_add_test_case ggatel_file | |||
| atf_add_test_case ggatel_md | |||
| atf_add_test_case ggate_sock | |||
| } | |||
| alloc_ggate_dev() | |||
| { | |||
| local us | |||
| us=0 | |||
| while [ -c /dev/ggate${us} ]; do | |||
| : $(( us += 1 )) | |||
| done | |||
| echo ${us} > ggate.devs | |||
| echo ${us} | |||
| } | |||
| alloc_md() | |||
| { | |||
| local md | |||
| md=$(mdconfig -a -t malloc -s 1M) || \ | |||
| atf_fail "failed to allocate md device" | |||
| echo ${md} >> md.devs | |||
| echo ${md} | |||
| } | |||
| checksum() | |||
| { | |||
| local src work | |||
| src=$1 | |||
| work=$2 | |||
| src_checksum=$(md5 -q $src) | |||
| work_checksum=$(md5 -q $work) | |||
| if [ "$work_checksum" != "$src_checksum" ]; then | |||
| atf_fail "work md5 checksum didn't match" | |||
| fi | |||
| ggate_checksum=$(md5 -q /dev/ggate${us}) | |||
| if [ "$ggate_checksum" != "$src_checksum" ]; then | |||
| atf_fail "ggate md5 checksum didn't match" | |||
| fi | |||
| } | |||
| common_cleanup() | |||
| { | |||
| if [ -f "ggate.devs" ]; then | |||
| while read test_ggate; do | |||
| ggatec destroy -f -u $test_ggate >/dev/null | |||
| done < ggate.devs | |||
| rm ggate.devs | |||
| fi | |||
| if [ -f "$PIDFILE" ]; then | |||
| pkill -F "$PIDFILE" | |||
| rm $PIDFILE | |||
| fi | |||
| if [ -f "PLAINFILES" ]; then | |||
| while read f; do | |||
| rm -f ${f} | |||
| done < ${PLAINFILES} | |||
| rm ${PLAINFILES} | |||
| fi | |||
| if [ -f "md.devs" ]; then | |||
| while read test_md; do | |||
| mdconfig -d -u $test_md 2>/dev/null | |||
| done < md.devs | |||
| rm md.devs | |||
| fi | |||
| true | |||
| } | |||
| load_ggate() | |||
| { | |||
| local class=gate | |||
| # If the geom class isn't already loaded, try loading it. | |||
| if ! kldstat -q -m g_${class}; then | |||
| if ! geom ${class} load; then | |||
| atf_skip "could not load module for geom class=${class}" | |||
| fi | |||
| fi | |||
| } | |||
| # Bug 204616: ggatel(8) creates /dev/ggate* asynchronously if `ggatel create` | |||
| # isn't called with `-v`. | |||
| wait_for_ggate_device() | |||
| { | |||
| ggate_device=$1 | |||
| while [ ! -c $ggate_device ]; do | |||
| sleep 0.5 | |||
| done | |||
| } | |||
| @@ -0,0 +1,151 @@ | |||
| # $FreeBSD$ | |||
| PIDFILE=ggated.pid | |||
| TESTURL="$GGATEHTTP_URL" | |||
| TEMPFILE="random.data" | |||
| atf_test_case ggatehttp cleanup | |||
| ggatehttp_head() | |||
| { | |||
| atf_set "descr" "ggatehttp can proxy to http" | |||
| atf_set "require.progs" "ggatehttp" | |||
| atf_set "require.user" "root" | |||
| atf_set "timeout" 10 | |||
| } | |||
| ggatehttp_body() | |||
| { | |||
| load_ggate | |||
| us=$(alloc_ggate_dev) | |||
| src=$(alloc_md) | |||
| n1mchunks=10 | |||
| atf_check -e ignore -o ignore \ | |||
| dd if=/dev/random of="$TEMPFILE" bs=1m count=$n1mchunks conv=notrunc | |||
| atf_check ggatehttp create -u $us "$TESTURL" | |||
| ggate_dev=/dev/ggate${us} | |||
| wait_for_ggate_device ${ggate_dev} | |||
| # Test writing | |||
| atf_check -e ignore -o ignore \ | |||
| dd if="$TEMPFILE" of=${ggate_dev} bs=1m count=$n1mchunks conv=notrunc | |||
| # Test reading | |||
| atf_check -e ignore -o ignore \ | |||
| dd of="$TEMPFILE"2 if=${ggate_dev} bs=1m count=$n1mchunks conv=notrunc | |||
| ls -l "$TEMPFILE" "$TEMPFILE"2 | |||
| # Verify that we read what we wrote | |||
| atf_check cmp "$TEMPFILE" "$TEMPFILE"2 | |||
| rm "$TEMPFILE" "$TEMPFILE"2 | |||
| } | |||
| ggatehttp_cleanup() | |||
| { | |||
| common_cleanup | |||
| } | |||
| atf_init_test_cases() | |||
| { | |||
| atf_add_test_case ggatehttp | |||
| } | |||
| alloc_ggate_dev() | |||
| { | |||
| local us | |||
| us=0 | |||
| while [ -c /dev/ggate${us} ]; do | |||
| : $(( us += 1 )) | |||
| done | |||
| echo ${us} > ggate.devs | |||
| echo ${us} | |||
| } | |||
| alloc_md() | |||
| { | |||
| local md | |||
| md=$(mdconfig -a -t malloc -s 1M) || \ | |||
| atf_fail "failed to allocate md device" | |||
| echo ${md} >> md.devs | |||
| echo ${md} | |||
| } | |||
| checksum() | |||
| { | |||
| local src work | |||
| src=$1 | |||
| work=$2 | |||
| src_checksum=$(md5 -q $src) | |||
| work_checksum=$(md5 -q $work) | |||
| if [ "$work_checksum" != "$src_checksum" ]; then | |||
| atf_fail "work md5 checksum didn't match" | |||
| fi | |||
| ggate_checksum=$(md5 -q /dev/ggate${us}) | |||
| if [ "$ggate_checksum" != "$src_checksum" ]; then | |||
| atf_fail "ggate md5 checksum didn't match" | |||
| fi | |||
| } | |||
| common_cleanup() | |||
| { | |||
| if [ -f "ggate.devs" ]; then | |||
| while read test_ggate; do | |||
| ggatec destroy -f -u $test_ggate >/dev/null | |||
| done < ggate.devs | |||
| rm ggate.devs | |||
| fi | |||
| if [ -f "$PIDFILE" ]; then | |||
| pkill -F "$PIDFILE" | |||
| rm $PIDFILE | |||
| fi | |||
| if [ -f "PLAINFILES" ]; then | |||
| while read f; do | |||
| rm -f ${f} | |||
| done < ${PLAINFILES} | |||
| rm ${PLAINFILES} | |||
| fi | |||
| if [ -f "md.devs" ]; then | |||
| while read test_md; do | |||
| mdconfig -d -u $test_md 2>/dev/null | |||
| done < md.devs | |||
| rm md.devs | |||
| fi | |||
| true | |||
| } | |||
| load_ggate() | |||
| { | |||
| local class=gate | |||
| # If the geom class isn't already loaded, try loading it. | |||
| if ! kldstat -q -m g_${class}; then | |||
| if ! geom ${class} load; then | |||
| atf_skip "could not load module for geom class=${class}" | |||
| fi | |||
| fi | |||
| } | |||
| # Bug 204616: ggatel(8) creates /dev/ggate* asynchronously if `ggatel create` | |||
| # isn't called with `-v`. | |||
| wait_for_ggate_device() | |||
| { | |||
| ggate_device=$1 | |||
| while [ ! -c $ggate_device ]; do | |||
| sleep 0.5 | |||
| done | |||
| } | |||