Compare commits

...

9 Commits

Author SHA1 Message Date
  John-Mark Gurney 8c7209fe1b get things mostly working.. 4 years ago
  John-Mark Gurney e1c3f9d200 guess this isn't useful, wsgidav doesn't function.. 4 years ago
  John-Mark Gurney e0703efbd3 add discussion about issues w/ WebDAV/REST and this.. 4 years ago
  John-Mark Gurney 5afc427d47 only display the first few erros, and fail if everything didn't get 4 years ago
  John-Mark Gurney b9ca728a15 update tests to new name and create the starting test... 4 years ago
  John-Mark Gurney acb98a9b09 add docs about running up a simple WebDAV server.. 4 years ago
  John-Mark Gurney 7d19a61c4f rename ggatec to ggatehttp so that it won't overwrite ggatec 4 years ago
  John-Mark Gurney a26a992b6e forgot to cleanup Makefile 4 years ago
  John-Mark Gurney 1cb4afd5fb start on a variant for https instead... 4 years ago
20 changed files with 964 additions and 2838 deletions
Split View
  1. +2
    -4
      .gitignore
  2. +3
    -6
      Makefile
  3. +18
    -2
      README.md
  4. +0
    -15
      ggatec/Makefile
  5. +0
    -710
      ggatec/ggatec.c
  6. +0
    -13
      ggated/Makefile
  7. +0
    -21
      ggated/Makefile.depend
  8. +0
    -134
      ggated/ggated.8
  9. +0
    -1133
      ggated/ggated.c
  10. +19
    -0
      ggatehttp/Makefile
  11. +0
    -0
      ggatehttp/Makefile.depend
  12. +0
    -0
      ggatehttp/ggatehttp.8
  13. +770
    -0
      ggatehttp/ggatehttp.c
  14. +0
    -14
      ggatel/Makefile
  15. +0
    -22
      ggatel/Makefile.depend
  16. +0
    -164
      ggatel/ggatel.8
  17. +0
    -330
      ggatel/ggatel.c
  18. +1
    -1
      tests/Makefile
  19. +0
    -269
      tests/ggate_test.sh
  20. +151
    -0
      tests/ggatehttp_test.sh

+ 2
- 4
.gitignore View File

@@ -3,8 +3,6 @@
*.8.gz
*.full
*.debug
ggatec/ggatec
ggated/ggated
ggatel/ggatel
ggatehttp/ggatehttp
tests/Kyuafile
tests/ggate_test
tests/ggatehttp_test

+ 3
- 6
Makefile View File

@@ -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>

+ 18
- 2
README.md View File

@@ -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

+ 0
- 15
ggatec/Makefile View File

@@ -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>

+ 0
- 710
ggatec/ggatec.c View File

@@ -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);
}

+ 0
- 13
ggated/Makefile View File

@@ -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>

+ 0
- 21
ggated/Makefile.depend View File

@@ -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

+ 0
- 134
ggated/ggated.8 View File

@@ -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
- 1133
ggated/ggated.c
File diff suppressed because it is too large
View File


+ 19
- 0
ggatehttp/Makefile View File

@@ -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>

ggatec/Makefile.depend → ggatehttp/Makefile.depend View File


ggatec/ggatec.8 → ggatehttp/ggatehttp.8 View File


+ 770
- 0
ggatehttp/ggatehttp.c View File

@@ -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);
}

+ 0
- 14
ggatel/Makefile View File

@@ -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>

+ 0
- 22
ggatel/Makefile.depend View File

@@ -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

+ 0
- 164
ggatel/ggatel.8 View File

@@ -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 .

+ 0
- 330
ggatel/ggatel.c View File

@@ -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);
}

+ 1
- 1
tests/Makefile View File

@@ -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>

+ 0
- 269
tests/ggate_test.sh View File

@@ -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
}

+ 151
- 0
tests/ggatehttp_test.sh View File

@@ -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
}

Loading…
Cancel
Save