diff --git a/README.md b/README.md index b985789..82ac109 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ -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. diff --git a/ggated/Makefile b/ggated/Makefile deleted file mode 100644 index db1749f..0000000 --- a/ggated/Makefile +++ /dev/null @@ -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 diff --git a/ggated/Makefile.depend b/ggated/Makefile.depend deleted file mode 100644 index 408a628..0000000 --- a/ggated/Makefile.depend +++ /dev/null @@ -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 - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/ggated/ggated.8 b/ggated/ggated.8 deleted file mode 100644 index be6f230..0000000 --- a/ggated/ggated.8 +++ /dev/null @@ -1,134 +0,0 @@ -.\" Copyright (c) 2004 Pawel Jakub Dawidek -.\" 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 . diff --git a/ggated/ggated.c b/ggated/ggated.c deleted file mode 100644 index badba44..0000000 --- a/ggated/ggated.c +++ /dev/null @@ -1,1133 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2004 Pawel Jakub Dawidek - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ggate.h" - - -#define GGATED_EXPORT_FILE "/etc/gg.exports" - -struct ggd_connection { - off_t c_mediasize; - unsigned c_sectorsize; - unsigned c_flags; /* flags (RO/RW) */ - int c_diskfd; - int c_sendfd; - int c_recvfd; - time_t c_birthtime; - char *c_path; - uint64_t c_token; - in_addr_t c_srcip; - LIST_ENTRY(ggd_connection) c_next; -}; - -struct ggd_request { - struct g_gate_hdr r_hdr; - char *r_data; - TAILQ_ENTRY(ggd_request) r_next; -}; -#define r_cmd r_hdr.gh_cmd -#define r_offset r_hdr.gh_offset -#define r_length r_hdr.gh_length -#define r_error r_hdr.gh_error - -struct ggd_export { - char *e_path; /* path to device/file */ - in_addr_t e_ip; /* remote IP address */ - in_addr_t e_mask; /* IP mask */ - unsigned e_flags; /* flags (RO/RW) */ - SLIST_ENTRY(ggd_export) e_next; -}; - -static long niothreads = 20; -static const char *exports_file = GGATED_EXPORT_FILE; -static int got_sighup = 0; - -static TAILQ_HEAD(, ggd_request) inqueue = TAILQ_HEAD_INITIALIZER(inqueue); -static TAILQ_HEAD(, ggd_request) outqueue = TAILQ_HEAD_INITIALIZER(outqueue); -static pthread_mutex_t inqueue_mtx, outqueue_mtx; -static pthread_cond_t inqueue_cond, outqueue_cond; - -static SLIST_HEAD(, ggd_export) exports = SLIST_HEAD_INITIALIZER(exports); -static LIST_HEAD(, ggd_connection) connections = LIST_HEAD_INITIALIZER(connections); - -static void *recv_thread(void *arg); -static void *disk_thread(void *arg); -static void *send_thread(void *arg); - -static void -usage(void) -{ - - fprintf(stderr, "usage: %s [-nv] [-a address] [-F pidfile] [-p port] " - "[-R rcvbuf] [-S sndbuf] [-s socket] [exports file]\n", getprogname()); - exit(EXIT_FAILURE); -} - -static char * -ip2str(in_addr_t ip) -{ - static char sip[16]; - - snprintf(sip, sizeof(sip), "%u.%u.%u.%u", - ((ip >> 24) & 0xff), - ((ip >> 16) & 0xff), - ((ip >> 8) & 0xff), - (ip & 0xff)); - return (sip); -} - -static in_addr_t -countmask(unsigned m) -{ - in_addr_t mask; - - if (m == 0) { - mask = 0x0; - } else { - mask = 1 << (32 - m); - mask--; - mask = ~mask; - } - return (mask); -} - -static void -line_parse(char *line, unsigned lineno) -{ - struct ggd_export *ex; - char *word, *path, *sflags; - unsigned flags, i, vmask; - in_addr_t ip, mask; - - ip = mask = flags = vmask = 0; - path = NULL; - sflags = NULL; - - for (i = 0, word = strtok(line, " \t"); word != NULL; - i++, word = strtok(NULL, " \t")) { - switch (i) { - case 0: /* IP address or host name */ - ip = g_gate_str2ip(strsep(&word, "/")); - if (ip == INADDR_NONE) { - g_gate_xlog("Invalid IP/host name at line %u.", - lineno); - } - ip = ntohl(ip); - if (word == NULL) - vmask = 32; - else { - errno = 0; - vmask = strtoul(word, NULL, 10); - if (vmask == 0 && errno != 0) { - g_gate_xlog("Invalid IP mask value at " - "line %u.", lineno); - } - if ((unsigned)vmask > 32) { - g_gate_xlog("Invalid IP mask value at line %u.", - lineno); - } - } - mask = countmask(vmask); - break; - case 1: /* flags */ - if (strcasecmp("rd", word) == 0 || - strcasecmp("ro", word) == 0) { - flags = O_RDONLY; - } else if (strcasecmp("wo", word) == 0) { - flags = O_WRONLY; - } else if (strcasecmp("rw", word) == 0) { - flags = O_RDWR; - } else { - g_gate_xlog("Invalid value in flags field at " - "line %u.", lineno); - } - sflags = word; - break; - case 2: /* path */ - if (strlen(word) >= MAXPATHLEN) { - g_gate_xlog("Path too long at line %u. ", - lineno); - } - path = word; - break; - default: - g_gate_xlog("Too many arguments at line %u. ", lineno); - } - } - if (i != 3) - g_gate_xlog("Too few arguments at line %u.", lineno); - - ex = malloc(sizeof(*ex)); - if (ex == NULL) - g_gate_xlog("Not enough memory."); - ex->e_path = strdup(path); - if (ex->e_path == NULL) - g_gate_xlog("Not enough memory."); - - /* Made 'and' here. */ - ex->e_ip = (ip & mask); - ex->e_mask = mask; - ex->e_flags = flags; - - SLIST_INSERT_HEAD(&exports, ex, e_next); - - g_gate_log(LOG_DEBUG, "Added %s/%u %s %s to exports list.", - ip2str(ex->e_ip), vmask, path, sflags); -} - -static void -exports_clear(void) -{ - struct ggd_export *ex; - - while (!SLIST_EMPTY(&exports)) { - ex = SLIST_FIRST(&exports); - SLIST_REMOVE_HEAD(&exports, e_next); - free(ex); - } -} - -#define EXPORTS_LINE_SIZE 2048 -static void -exports_get(void) -{ - char buf[EXPORTS_LINE_SIZE], *line; - unsigned lineno = 0, objs = 0, len; - FILE *fd; - - exports_clear(); - - fd = fopen(exports_file, "r"); - if (fd == NULL) { - g_gate_xlog("Cannot open exports file (%s): %s.", exports_file, - strerror(errno)); - } - - g_gate_log(LOG_INFO, "Reading exports file (%s).", exports_file); - - for (;;) { - if (fgets(buf, sizeof(buf), fd) == NULL) { - if (feof(fd)) - break; - - g_gate_xlog("Error while reading exports file: %s.", - strerror(errno)); - } - - /* Increase line count. */ - lineno++; - - /* Skip spaces and tabs. */ - for (line = buf; *line == ' ' || *line == '\t'; ++line) - ; - - /* Empty line, comment or empty line at the end of file. */ - if (*line == '\n' || *line == '#' || *line == '\0') - continue; - - len = strlen(line); - if (line[len - 1] == '\n') { - /* Remove new line char. */ - line[len - 1] = '\0'; - } else { - if (!feof(fd)) - g_gate_xlog("Line %u too long.", lineno); - } - - line_parse(line, lineno); - objs++; - } - - fclose(fd); - - if (objs == 0) - g_gate_xlog("There are no objects to export."); - - g_gate_log(LOG_INFO, "Exporting %u object(s).", objs); -} - -static int -exports_check(struct ggd_export *ex, struct g_gate_cinit *cinit, - struct ggd_connection *conn) -{ - char ipmask[32]; /* 32 == strlen("xxx.xxx.xxx.xxx/xxx.xxx.xxx.xxx")+1 */ - int error = 0, flags; - - strlcpy(ipmask, ip2str(ex->e_ip), sizeof(ipmask)); - strlcat(ipmask, "/", sizeof(ipmask)); - strlcat(ipmask, ip2str(ex->e_mask), sizeof(ipmask)); - if ((cinit->gc_flags & GGATE_FLAG_RDONLY) != 0) { - if (ex->e_flags == O_WRONLY) { - g_gate_log(LOG_WARNING, "Read-only access requested, " - "but %s (%s) is exported write-only.", ex->e_path, - ipmask); - return (EPERM); - } else { - conn->c_flags |= GGATE_FLAG_RDONLY; - } - } else if ((cinit->gc_flags & GGATE_FLAG_WRONLY) != 0) { - if (ex->e_flags == O_RDONLY) { - g_gate_log(LOG_WARNING, "Write-only access requested, " - "but %s (%s) is exported read-only.", ex->e_path, - ipmask); - return (EPERM); - } else { - conn->c_flags |= GGATE_FLAG_WRONLY; - } - } else { - if (ex->e_flags == O_RDONLY) { - g_gate_log(LOG_WARNING, "Read-write access requested, " - "but %s (%s) is exported read-only.", ex->e_path, - ipmask); - return (EPERM); - } else if (ex->e_flags == O_WRONLY) { - g_gate_log(LOG_WARNING, "Read-write access requested, " - "but %s (%s) is exported write-only.", ex->e_path, - ipmask); - return (EPERM); - } - } - if ((conn->c_flags & GGATE_FLAG_RDONLY) != 0) - flags = O_RDONLY; - else if ((conn->c_flags & GGATE_FLAG_WRONLY) != 0) - flags = O_WRONLY; - else - flags = O_RDWR; - conn->c_diskfd = open(ex->e_path, flags); - if (conn->c_diskfd == -1) { - error = errno; - g_gate_log(LOG_ERR, "Cannot open %s: %s.", ex->e_path, - strerror(error)); - return (error); - } - return (0); -} - -static struct ggd_export * -exports_find(struct sockaddr *s, struct g_gate_cinit *cinit, - struct ggd_connection *conn) -{ - struct ggd_export *ex; - in_addr_t ip; - int error; - - ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); - SLIST_FOREACH(ex, &exports, e_next) { - if ((ip & ex->e_mask) != ex->e_ip) { - g_gate_log(LOG_DEBUG, "exports[%s]: IP mismatch.", - ex->e_path); - continue; - } - if (strcmp(cinit->gc_path, ex->e_path) != 0) { - g_gate_log(LOG_DEBUG, "exports[%s]: Path mismatch.", - ex->e_path); - continue; - } - error = exports_check(ex, cinit, conn); - if (error == 0) - return (ex); - else { - errno = error; - return (NULL); - } - } - g_gate_log(LOG_WARNING, "Unauthorized connection from: %s.", - ip2str(ip)); - errno = EPERM; - return (NULL); -} - -/* - * Remove timed out connections. - */ -static void -connection_cleanups(void) -{ - struct ggd_connection *conn, *tconn; - time_t now; - - time(&now); - LIST_FOREACH_SAFE(conn, &connections, c_next, tconn) { - if (now - conn->c_birthtime > 10) { - LIST_REMOVE(conn, c_next); - g_gate_log(LOG_NOTICE, - "Connection from %s [%s] removed.", - ip2str(conn->c_srcip), conn->c_path); - close(conn->c_diskfd); - close(conn->c_sendfd); - close(conn->c_recvfd); - free(conn->c_path); - free(conn); - } - } -} - -static struct ggd_connection * -connection_find(struct g_gate_cinit *cinit) -{ - struct ggd_connection *conn; - - LIST_FOREACH(conn, &connections, c_next) { - if (conn->c_token == cinit->gc_token) - break; - } - return (conn); -} - -static struct ggd_connection * -connection_new(struct g_gate_cinit *cinit, struct sockaddr *s, int sfd) -{ - struct ggd_connection *conn; - in_addr_t ip; - - /* - * First, look for old connections. - * We probably should do it every X seconds, but what for? - * It is only dangerous if an attacker wants to overload connections - * queue, so here is a good place to do the cleanups. - */ - connection_cleanups(); - - conn = malloc(sizeof(*conn)); - if (conn == NULL) - return (NULL); - conn->c_path = strdup(cinit->gc_path); - if (conn->c_path == NULL) { - free(conn); - return (NULL); - } - conn->c_token = cinit->gc_token; - ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); - conn->c_srcip = ip; - conn->c_sendfd = conn->c_recvfd = -1; - if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) - conn->c_sendfd = sfd; - else - conn->c_recvfd = sfd; - conn->c_mediasize = 0; - conn->c_sectorsize = 0; - time(&conn->c_birthtime); - conn->c_flags = cinit->gc_flags; - LIST_INSERT_HEAD(&connections, conn, c_next); - g_gate_log(LOG_DEBUG, "Connection created [%s, %s].", ip2str(ip), - conn->c_path); - return (conn); -} - -static int -connection_add(struct ggd_connection *conn, struct g_gate_cinit *cinit, - struct sockaddr *s, int sfd) -{ - in_addr_t ip; - - ip = htonl(((struct sockaddr_in *)(void *)s)->sin_addr.s_addr); - if ((cinit->gc_flags & GGATE_FLAG_SEND) != 0) { - if (conn->c_sendfd != -1) { - g_gate_log(LOG_WARNING, - "Send socket already exists [%s, %s].", ip2str(ip), - conn->c_path); - return (EEXIST); - } - conn->c_sendfd = sfd; - } else { - if (conn->c_recvfd != -1) { - g_gate_log(LOG_WARNING, - "Receive socket already exists [%s, %s].", - ip2str(ip), conn->c_path); - return (EEXIST); - } - conn->c_recvfd = sfd; - } - g_gate_log(LOG_DEBUG, "Connection added [%s, %s].", ip2str(ip), - conn->c_path); - return (0); -} - -/* - * Remove one socket from the given connection or the whole - * connection if sfd == -1. - */ -static void -connection_remove(struct ggd_connection *conn) -{ - - LIST_REMOVE(conn, c_next); - g_gate_log(LOG_DEBUG, "Connection removed [%s %s].", - ip2str(conn->c_srcip), conn->c_path); - if (conn->c_sendfd != -1) - close(conn->c_sendfd); - if (conn->c_recvfd != -1) - close(conn->c_recvfd); - free(conn->c_path); - free(conn); -} - -static int -connection_ready(struct ggd_connection *conn) -{ - - return (conn->c_sendfd != -1 && conn->c_recvfd != -1); -} - -static void -connection_launch(struct ggd_connection *conn) -{ - pthread_t td; - int error, pid; - int i; - - pid = fork(); - if (pid > 0) - return; - else if (pid == -1) { - g_gate_log(LOG_ERR, "Cannot fork: %s.", strerror(errno)); - return; - } - g_gate_log(LOG_DEBUG, "Process created [%s], pid: %d.", conn->c_path, getpid()); - - /* - * Create condition variables and mutexes for in-queue and out-queue - * synchronization. - */ - error = pthread_mutex_init(&inqueue_mtx, NULL); - if (error != 0) { - g_gate_xlog("pthread_mutex_init(inqueue_mtx): %s.", - strerror(error)); - } - error = pthread_cond_init(&inqueue_cond, NULL); - if (error != 0) { - g_gate_xlog("pthread_cond_init(inqueue_cond): %s.", - strerror(error)); - } - error = pthread_mutex_init(&outqueue_mtx, NULL); - if (error != 0) { - g_gate_xlog("pthread_mutex_init(outqueue_mtx): %s.", - strerror(error)); - } - error = pthread_cond_init(&outqueue_cond, NULL); - if (error != 0) { - g_gate_xlog("pthread_cond_init(outqueue_cond): %s.", - strerror(error)); - } - - /* - * Create threads: - * recvtd - thread for receiving I/O request - * diskio - thread for doing I/O request - * sendtd - thread for sending I/O requests back - */ - error = pthread_create(&td, NULL, send_thread, conn); - if (error != 0) { - g_gate_xlog("pthread_create(send_thread): %s.", - strerror(error)); - } - error = pthread_create(&td, NULL, recv_thread, conn); - if (error != 0) { - g_gate_xlog("pthread_create(recv_thread): %s.", - strerror(error)); - } - for (i = 0; niothreads && i < niothreads - 1; i++) { - error = pthread_create(&td, NULL, disk_thread, conn); - if (error != 0) { - g_gate_xlog("pthread_create(disk_thread): %s.", - strerror(error)); - } - } - - disk_thread(conn); -} - -static void -sendfail(int sfd, int error, const char *fmt, ...) -{ - struct g_gate_sinit sinit; - va_list ap; - ssize_t data; - - memset(&sinit, 0, sizeof(sinit)); - sinit.gs_error = error; - g_gate_swap2n_sinit(&sinit); - data = g_gate_send(sfd, &sinit, sizeof(sinit), 0); - g_gate_swap2h_sinit(&sinit); - if (data != sizeof(sinit)) { - g_gate_log(LOG_WARNING, "Cannot send initial packet: %s.", - strerror(errno)); - return; - } - if (fmt != NULL) { - va_start(ap, fmt); - g_gate_vlog(LOG_WARNING, fmt, ap); - va_end(ap); - } -} - -static void * -malloc_waitok(size_t size) -{ - void *p; - - while ((p = malloc(size)) == NULL) { - g_gate_log(LOG_DEBUG, "Cannot allocate %zu bytes.", size); - sleep(1); - } - return (p); -} - -static void * -recv_thread(void *arg) -{ - struct ggd_connection *conn; - struct ggd_request *req; - ssize_t data; - int error, fd; - - conn = arg; - g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); - fd = conn->c_recvfd; - for (;;) { - /* - * Get header packet. - */ - req = malloc_waitok(sizeof(*req)); - data = g_gate_recv(fd, &req->r_hdr, sizeof(req->r_hdr), - MSG_WAITALL); - if (data == 0) { - g_gate_log(LOG_DEBUG, "Process %u exiting.", getpid()); - exit(EXIT_SUCCESS); - } else if (data == -1) { - g_gate_xlog("Error while receiving hdr packet: %s.", - strerror(errno)); - } else if (data != sizeof(req->r_hdr)) { - g_gate_xlog("Malformed hdr packet received."); - } - g_gate_log(LOG_DEBUG, "Received hdr packet."); - g_gate_swap2h_hdr(&req->r_hdr); - - g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, - (intmax_t)req->r_offset, (unsigned)req->r_length); - - /* - * Allocate memory for data. - */ - req->r_data = malloc_waitok(req->r_length); - - /* - * Receive data to write for WRITE request. - */ - if (req->r_cmd == GGATE_CMD_WRITE) { - g_gate_log(LOG_DEBUG, "Waiting for %u bytes of data...", - req->r_length); - data = g_gate_recv(fd, req->r_data, req->r_length, - MSG_WAITALL); - if (data == -1) { - g_gate_xlog("Error while receiving data: %s.", - strerror(errno)); - } - } - - /* - * Put the request onto the incoming queue. - */ - error = pthread_mutex_lock(&inqueue_mtx); - assert(error == 0); - TAILQ_INSERT_TAIL(&inqueue, req, r_next); - error = pthread_cond_signal(&inqueue_cond); - assert(error == 0); - error = pthread_mutex_unlock(&inqueue_mtx); - assert(error == 0); - } -} - -static void * -disk_thread(void *arg) -{ - struct ggd_connection *conn; - struct ggd_request *req; - ssize_t data; - int error, fd; - - conn = arg; - g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); - fd = conn->c_diskfd; - for (;;) { - /* - * Get a request from the incoming queue. - */ - error = pthread_mutex_lock(&inqueue_mtx); - assert(error == 0); - while ((req = TAILQ_FIRST(&inqueue)) == NULL) { - error = pthread_cond_wait(&inqueue_cond, &inqueue_mtx); - assert(error == 0); - } - TAILQ_REMOVE(&inqueue, req, r_next); - error = pthread_mutex_unlock(&inqueue_mtx); - assert(error == 0); - - /* - * Check the request. - */ - assert(req->r_cmd == GGATE_CMD_READ || req->r_cmd == GGATE_CMD_WRITE || req->r_cmd == GGATE_CMD_DELETE || req->r_cmd == GGATE_CMD_FLUSH); - assert(req->r_offset + req->r_length <= (uintmax_t)conn->c_mediasize); - assert((req->r_offset % conn->c_sectorsize) == 0); - assert((req->r_length % conn->c_sectorsize) == 0); - - g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, - (intmax_t)req->r_offset, (unsigned)req->r_length); - - /* - * Do the request. - */ - data = 0; - switch (req->r_cmd) { - case GGATE_CMD_READ: - data = pread(fd, req->r_data, req->r_length, - req->r_offset); - break; - case GGATE_CMD_WRITE: - data = pwrite(fd, req->r_data, req->r_length, - req->r_offset); - /* Free data memory here - better sooner. */ - free(req->r_data); - req->r_data = NULL; - break; - case GGATE_CMD_DELETE: - case GGATE_CMD_FLUSH: - /* do nothing for now */ - break; - } - if (data != (ssize_t)req->r_length) { - /* Report short reads/writes as I/O errors. */ - if (errno == 0) - errno = EIO; - g_gate_log(LOG_ERR, "Disk error: %s", strerror(errno)); - req->r_error = errno; - if (req->r_data != NULL) { - free(req->r_data); - req->r_data = NULL; - } - } - - /* - * Put the request onto the outgoing queue. - */ - error = pthread_mutex_lock(&outqueue_mtx); - assert(error == 0); - TAILQ_INSERT_TAIL(&outqueue, req, r_next); - error = pthread_cond_signal(&outqueue_cond); - assert(error == 0); - error = pthread_mutex_unlock(&outqueue_mtx); - assert(error == 0); - } - - /* NOTREACHED */ - return (NULL); -} - -static void * -send_thread(void *arg) -{ - struct ggd_connection *conn; - struct ggd_request *req; - ssize_t data; - int error, fd; - - conn = arg; - g_gate_log(LOG_NOTICE, "%s: started [%s]!", __func__, conn->c_path); - fd = conn->c_sendfd; - for (;;) { - /* - * Get a request from the outgoing queue. - */ - error = pthread_mutex_lock(&outqueue_mtx); - assert(error == 0); - while ((req = TAILQ_FIRST(&outqueue)) == NULL) { - error = pthread_cond_wait(&outqueue_cond, - &outqueue_mtx); - assert(error == 0); - } - TAILQ_REMOVE(&outqueue, req, r_next); - error = pthread_mutex_unlock(&outqueue_mtx); - assert(error == 0); - - g_gate_log(LOG_DEBUG, "%s: offset=%jd length=%u", __func__, - (intmax_t)req->r_offset, (unsigned)req->r_length); - - /* - * Send the request. - */ - g_gate_swap2n_hdr(&req->r_hdr); - if (g_gate_send(fd, &req->r_hdr, sizeof(req->r_hdr), 0) == -1) { - g_gate_xlog("Error while sending hdr packet: %s.", - strerror(errno)); - } - g_gate_log(LOG_DEBUG, "Sent hdr packet."); - g_gate_swap2h_hdr(&req->r_hdr); - if (req->r_data != NULL) { - data = g_gate_send(fd, req->r_data, req->r_length, 0); - if (data != (ssize_t)req->r_length) { - g_gate_xlog("Error while sending data: %s.", - strerror(errno)); - } - g_gate_log(LOG_DEBUG, - "Sent %zd bytes (offset=%ju, size=%zu).", data, - (uintmax_t)req->r_offset, (size_t)req->r_length); - free(req->r_data); - } - free(req); - } - - /* NOTREACHED */ - return (NULL); -} - -static void -log_connection(struct sockaddr *from) -{ - in_addr_t ip; - - ip = htonl(((struct sockaddr_in *)(void *)from)->sin_addr.s_addr); - g_gate_log(LOG_INFO, "Connection from: %s.", ip2str(ip)); -} - -static int -handshake(struct sockaddr *from, int sfd) -{ - struct g_gate_version ver; - struct g_gate_cinit cinit; - struct g_gate_sinit sinit; - struct ggd_connection *conn; - struct ggd_export *ex; - ssize_t data; - - log_connection(from); - /* - * Phase 1: Version verification. - */ - g_gate_log(LOG_DEBUG, "Receiving version packet."); - data = g_gate_recv(sfd, &ver, sizeof(ver), MSG_WAITALL); - g_gate_swap2h_version(&ver); - if (data != sizeof(ver)) { - g_gate_log(LOG_WARNING, "Malformed version packet."); - return (0); - } - g_gate_log(LOG_DEBUG, "Version packet received."); - if (memcmp(ver.gv_magic, GGATE_MAGIC, strlen(GGATE_MAGIC)) != 0) { - g_gate_log(LOG_WARNING, "Invalid magic field."); - return (0); - } - if (ver.gv_version != GGATE_VERSION) { - g_gate_log(LOG_WARNING, "Version %u is not supported.", - ver.gv_version); - return (0); - } - ver.gv_error = 0; - g_gate_swap2n_version(&ver); - data = g_gate_send(sfd, &ver, sizeof(ver), 0); - g_gate_swap2h_version(&ver); - if (data == -1) { - sendfail(sfd, errno, "Error while sending version packet: %s.", - strerror(errno)); - return (0); - } - - /* - * Phase 2: Request verification. - */ - g_gate_log(LOG_DEBUG, "Receiving initial packet."); - data = g_gate_recv(sfd, &cinit, sizeof(cinit), MSG_WAITALL); - g_gate_swap2h_cinit(&cinit); - if (data != sizeof(cinit)) { - g_gate_log(LOG_WARNING, "Malformed initial packet."); - return (0); - } - g_gate_log(LOG_DEBUG, "Initial packet received."); - conn = connection_find(&cinit); - if (conn != NULL) { - /* - * Connection should already exists. - */ - g_gate_log(LOG_DEBUG, "Found existing connection (token=%lu).", - (unsigned long)conn->c_token); - if (connection_add(conn, &cinit, from, sfd) == -1) { - connection_remove(conn); - return (0); - } - } else { - /* - * New connection, allocate space. - */ - conn = connection_new(&cinit, from, sfd); - if (conn == NULL) { - sendfail(sfd, ENOMEM, - "Cannot allocate new connection."); - return (0); - } - g_gate_log(LOG_DEBUG, "New connection created (token=%lu).", - (unsigned long)conn->c_token); - } - - ex = exports_find(from, &cinit, conn); - if (ex == NULL) { - sendfail(sfd, errno, NULL); - connection_remove(conn); - return (0); - } - if (conn->c_mediasize == 0) { - conn->c_mediasize = g_gate_mediasize(conn->c_diskfd); - conn->c_sectorsize = g_gate_sectorsize(conn->c_diskfd); - } - sinit.gs_mediasize = conn->c_mediasize; - sinit.gs_sectorsize = conn->c_sectorsize; - sinit.gs_error = 0; - - g_gate_log(LOG_DEBUG, "Sending initial packet."); - - g_gate_swap2n_sinit(&sinit); - data = g_gate_send(sfd, &sinit, sizeof(sinit), 0); - g_gate_swap2h_sinit(&sinit); - if (data == -1) { - sendfail(sfd, errno, "Error while sending initial packet: %s.", - strerror(errno)); - return (0); - } - - if (connection_ready(conn)) { - connection_launch(conn); - connection_remove(conn); - } - return (1); -} - -static void -huphandler(int sig __unused) -{ - - got_sighup = 1; -} - -/* - * If sockaddr is not NULL, bind to the Unix domain socket at sockname, - * otherwise bind to the INET addr:port - */ -static int -bindsocket(in_addr_t bindaddr, int port, char *sockname) -{ - struct sockaddr_un unixaddr; - struct sockaddr_in serv; - int sfd; - - g_gate_log(LOG_INFO, "binding socket"); - - if (sockname != NULL) { - if (strlen(sockname) + 1 > sizeof(unixaddr.sun_path)) - g_gate_xlog("Socket path is too long."); - - sfd = socket(AF_UNIX, SOCK_STREAM, 0); - if (sfd == -1) - g_gate_xlog("Cannot open stream socket: %s.", strerror(errno)); - unixaddr = (struct sockaddr_un) { - .sun_len = sizeof(unixaddr), - .sun_family = AF_UNIX, - }; - strncpy(unixaddr.sun_path, sockname, sizeof(unixaddr.sun_path)); - - if (bind(sfd, (struct sockaddr *)&unixaddr, sizeof(unixaddr)) == -1) - g_gate_xlog("bind(): %s.", strerror(errno)); - if (listen(sfd, 5) == -1) - g_gate_xlog("listen(): %s.", strerror(errno)); - - g_gate_log(LOG_INFO, "Listen on path: %s.", sockname); - } else { - sfd = socket(AF_INET, SOCK_STREAM, 0); - if (sfd == -1) - g_gate_xlog("Cannot open stream socket: %s.", strerror(errno)); - bzero(&serv, sizeof(serv)); - serv.sin_family = AF_INET; - serv.sin_addr.s_addr = bindaddr; - serv.sin_port = htons(port); - - g_gate_socket_settings(sfd); - - if (bind(sfd, (struct sockaddr *)&serv, sizeof(serv)) == -1) - g_gate_xlog("bind(): %s.", strerror(errno)); - if (listen(sfd, 5) == -1) - g_gate_xlog("listen(): %s.", strerror(errno)); - - g_gate_log(LOG_INFO, "Listen on port: %d.", port); - } - - return sfd; -} - -int -main(int argc, char *argv[]) -{ - const char *ggated_pidfile = _PATH_VARRUN "/ggated.pid"; - char *sockname = NULL; - struct pidfh *pfh; - struct sockaddr from; - socklen_t fromlen; - in_addr_t bindaddr; - pid_t otherpid; - int ch, sfd, tmpsfd; - unsigned port; - - bindaddr = htonl(INADDR_ANY); - port = G_GATE_PORT; - while ((ch = getopt(argc, argv, "a:hnp:q:F:R:S:s:v")) != -1) { - switch (ch) { - case 'a': - bindaddr = g_gate_str2ip(optarg); - if (bindaddr == INADDR_NONE) { - errx(EXIT_FAILURE, - "Invalid IP/host name to bind to."); - } - break; - case 'F': - ggated_pidfile = optarg; - break; - case 'n': - nagle = 0; - break; - case 'p': - errno = 0; - port = strtoul(optarg, NULL, 10); - if (port == 0 && errno != 0) - errx(EXIT_FAILURE, "Invalid port."); - break; - case 'q': - niothreads = strtol(optarg, NULL, 10); - if (niothreads <= 0) - errx(EXIT_FAILURE, "Invalid queue size."); - break; - case 'R': - errno = 0; - rcvbuf = strtoul(optarg, NULL, 10); - if (rcvbuf == 0 && errno != 0) - errx(EXIT_FAILURE, "Invalid rcvbuf."); - break; - case 'S': - errno = 0; - sndbuf = strtoul(optarg, NULL, 10); - if (sndbuf == 0 && errno != 0) - errx(EXIT_FAILURE, "Invalid sndbuf."); - break; - case 's': - sockname = strdup(optarg); - break; - case 'v': - g_gate_verbose++; - break; - case 'h': - default: - usage(); - } - } - argc -= optind; - argv += optind; - - if (argv[0] != NULL) - exports_file = argv[0]; - exports_get(); - - pfh = pidfile_open(ggated_pidfile, 0600, &otherpid); - if (pfh == NULL) { - if (errno == EEXIST) { - errx(EXIT_FAILURE, "Daemon already running, pid: %jd.", - (intmax_t)otherpid); - } - err(EXIT_FAILURE, "Cannot open/create pidfile"); - } - - signal(SIGCHLD, SIG_IGN); - - sfd = bindsocket(bindaddr, port, sockname); - - if (!g_gate_verbose) { - FILE *t = fopen("local.log", "w"); - /* Run in daemon mode. */ - if (daemon(0, 0) == -1) - g_gate_xlog("Cannot daemonize: %s", strerror(errno)); - - stderr = t; - } - - pidfile_write(pfh); - - signal(SIGHUP, huphandler); - - for (;;) { - fromlen = sizeof(from); - tmpsfd = accept(sfd, &from, &fromlen); - if (tmpsfd == -1) - g_gate_xlog("accept(): %s.", strerror(errno)); - - if (got_sighup) { - got_sighup = 0; - exports_get(); - } - - if (!handshake(&from, tmpsfd)) - close(tmpsfd); - } - close(sfd); - pidfile_remove(pfh); - exit(EXIT_SUCCESS); -} diff --git a/ggatel/Makefile b/ggatel/Makefile deleted file mode 100644 index 85bfb36..0000000 --- a/ggatel/Makefile +++ /dev/null @@ -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 diff --git a/ggatel/Makefile.depend b/ggatel/Makefile.depend deleted file mode 100644 index 1096cf5..0000000 --- a/ggatel/Makefile.depend +++ /dev/null @@ -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 - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/ggatel/ggatel.8 b/ggatel/ggatel.8 deleted file mode 100644 index 7facee0..0000000 --- a/ggatel/ggatel.8 +++ /dev/null @@ -1,164 +0,0 @@ -.\" Copyright (c) 2004 Pawel Jakub Dawidek -.\" 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 . diff --git a/ggatel/ggatel.c b/ggatel/ggatel.c deleted file mode 100644 index 4659cac..0000000 --- a/ggatel/ggatel.c +++ /dev/null @@ -1,330 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2004 Pawel Jakub Dawidek - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#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 ] " - "[-s sectorsize] [-t timeout] [-u unit] \n", getprogname()); - fprintf(stderr, " %s rescue [-v] [-o ] <-u unit> " - "\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); -}