summaryrefslogtreecommitdiffstats
path: root/usr.sbin/iscsid/control.c
diff options
context:
space:
mode:
authorclaudio <claudio@openbsd.org>2010-09-24 09:43:19 +0000
committerclaudio <claudio@openbsd.org>2010-09-24 09:43:19 +0000
commitbde1ae237b27fe974d1439632ef2c60652523f9c (patch)
treef82ac3859d73a643088d58b7ace6cfae900e1d30 /usr.sbin/iscsid/control.c
parentfor rdr-to and nat-to, mention in which direction they are usually used (diff)
downloadwireguard-openbsd-bde1ae237b27fe974d1439632ef2c60652523f9c.tar.xz
wireguard-openbsd-bde1ae237b27fe974d1439632ef2c60652523f9c.zip
iSCSI Initiatior daemon using vscsi(4).
Currently implements the absolute minimum of the protocol to make it work against targets. Many things still in flux but we're annoyed to work outside of the tree. Commited from a source tree on an iSCSI disk served via iscsid but it is not yet production ready. OK dlg@, matthew@, deraadt@
Diffstat (limited to 'usr.sbin/iscsid/control.c')
-rw-r--r--usr.sbin/iscsid/control.c327
1 files changed, 327 insertions, 0 deletions
diff --git a/usr.sbin/iscsid/control.c b/usr.sbin/iscsid/control.c
new file mode 100644
index 00000000000..e95b1a45e81
--- /dev/null
+++ b/usr.sbin/iscsid/control.c
@@ -0,0 +1,327 @@
+/* $OpenBSD: control.c,v 1.1 2010/09/24 09:43:19 claudio Exp $ */
+
+/*
+ * Copyright (c) 2010 Claudio Jeker <claudio@openbsd.org>
+ * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/types.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <errno.h>
+#include <event.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "iscsid.h"
+#include "log.h"
+
+struct control {
+ TAILQ_ENTRY(control) entry;
+ struct event ev;
+ struct pduq channel;
+ int fd;
+} *control_state;
+
+TAILQ_HEAD(, control) controls;
+
+#define CONTROL_BACKLOG 5
+
+void control_accept(int, short, void *);
+void control_close(struct control *);
+void control_dispatch(int, short, void *);
+struct pdu *control_getpdu(char *, size_t);
+
+int
+control_init(char *path)
+{
+ struct sockaddr_un sun;
+ int fd;
+ mode_t old_umask;
+
+ if ((control_state = calloc(1, sizeof(*control_state))) == NULL) {
+ log_warn("control_init: calloc");
+ return (-1);
+ }
+
+ if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
+ log_warn("control_init: socket");
+ return (-1);
+ }
+
+ bzero(&sun, sizeof(sun));
+ sun.sun_family = AF_UNIX;
+ strlcpy(sun.sun_path, path, sizeof(sun.sun_path));
+
+ if (unlink(path) == -1)
+ if (errno != ENOENT) {
+ log_warn("control_init: unlink %s", path);
+ close(fd);
+ return (-1);
+ }
+
+ old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
+ if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
+ log_warn("control_init: bind: %s", path);
+ close(fd);
+ umask(old_umask);
+ return (-1);
+ }
+ umask(old_umask);
+
+ if (chmod(path, S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) == -1) {
+ log_warn("control_init: chmod");
+ close(fd);
+ (void)unlink(path);
+ return (-1);
+ }
+
+ socket_setblockmode(fd, 1);
+ control_state->fd = fd;
+ TAILQ_INIT(&control_state->channel);
+ TAILQ_INIT(&controls);
+
+ return (0);
+}
+
+void
+control_cleanup(char *path)
+{
+ struct control *c;
+
+ if (path)
+ unlink(path);
+
+ while ((c = TAILQ_FIRST(&controls)) != NULL) {
+ TAILQ_REMOVE(&controls, c, entry);
+ control_close(c);
+ }
+ control_close(control_state);
+}
+
+int
+control_listen(void)
+{
+ if (listen(control_state->fd, CONTROL_BACKLOG) == -1) {
+ log_warn("control_listen: listen");
+ return (-1);
+ }
+
+ event_set(&control_state->ev, control_state->fd, EV_READ | EV_PERSIST,
+ control_accept, NULL);
+ event_add(&control_state->ev, NULL);
+
+ return (0);
+}
+
+/* ARGSUSED */
+void
+control_accept(int listenfd, short event, void *bula)
+{
+ int connfd;
+ socklen_t len;
+ struct sockaddr_un sun;
+ struct control *c;
+
+ len = sizeof(sun);
+ if ((connfd = accept(listenfd,
+ (struct sockaddr *)&sun, &len)) == -1) {
+ if (errno != EWOULDBLOCK && errno != EINTR)
+ log_warn("control_accept");
+ return;
+ }
+
+ if ((c = malloc(sizeof(struct control))) == NULL) {
+ log_warn("control_accept");
+ close(connfd);
+ return;
+ }
+
+ TAILQ_INIT(&c->channel);
+ c->fd = connfd;
+ event_set(&c->ev, connfd, EV_READ, control_dispatch, c);
+ event_add(&c->ev, NULL);
+}
+
+void
+control_close(struct control *c)
+{
+ close(c->fd);
+ event_del(&c->ev);
+ pdu_free_queue(&c->channel);
+ free(c);
+}
+
+static char cbuf[CONTROL_READ_SIZE];
+
+/* ARGSUSED */
+void
+control_dispatch(int fd, short event, void *bula)
+{
+ struct iovec iov[PDU_MAXIOV];
+ struct msghdr msg;
+ struct control *c = bula;
+ struct pdu *pdu;
+ ssize_t n;
+ unsigned int niov = 0;
+ short flags = EV_READ;
+
+ if (event & EV_TIMEOUT) {
+ log_debug("control connection (fd %d) timed out.", fd);
+ control_close(c);
+ return;
+ }
+ if (event & EV_READ) {
+ if ((n = recv(fd, cbuf, sizeof(cbuf), 0)) == -1 &&
+ !(errno == EAGAIN || errno == EINTR)) {
+ control_close(c);
+ return;
+ }
+ if (n == 0) {
+ control_close(c);
+ return;
+ }
+log_debug("control_dispatch: recv %zd bytes", n);
+ pdu = control_getpdu(cbuf, n);
+ if (!pdu) {
+ log_debug("control connection (fd %d) bad msg.", fd);
+ control_close(c);
+ return;
+ }
+ iscsid_ctrl_dispatch(c, pdu);
+ }
+ if (event & EV_WRITE) {
+ if ((pdu = TAILQ_FIRST(&c->channel)) != NULL) {
+ TAILQ_REMOVE(&c->channel, pdu, entry);
+
+ for (niov = 0; niov < PDU_MAXIOV; niov++) {
+ iov[niov].iov_base = pdu->iov[niov].iov_base;
+ iov[niov].iov_len = pdu->iov[niov].iov_len;
+ }
+ bzero(&msg, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = niov;
+log_debug("control_dispatch: send %d iov", niov);
+ if (sendmsg(fd, &msg, 0) == -1 &&
+ !(errno == EAGAIN || errno == ENOBUFS)) {
+ control_close(c);
+ return;
+ }
+ }
+ }
+ if (!TAILQ_EMPTY(&c->channel))
+ flags |= EV_WRITE;
+
+ event_del(&c->ev);
+ event_set(&c->ev, fd, flags, control_dispatch, c);
+ event_add(&c->ev, NULL);
+}
+
+struct pdu *
+control_getpdu(char *buf, size_t len)
+{
+ struct pdu *p;
+ struct ctrlmsghdr *cmh;
+ void *data;
+ size_t n;
+ int i;
+
+ if (len < sizeof(*cmh))
+ return NULL;
+
+ if (!(p = pdu_new()))
+ return NULL;
+
+ n = sizeof(*cmh);
+ cmh = pdu_alloc(n);
+ bcopy(buf, cmh, n);
+ buf += n;
+ len -= n;
+
+ if (pdu_addbuf(p, cmh, n, 0)) {
+ free(cmh);
+fail:
+ pdu_free(p);
+ return NULL;
+ }
+
+ for (i = 0; i < 3; i++) {
+ n = cmh->len[i];
+ if (n == 0)
+ continue;
+ if (PDU_LEN(n) > len)
+ goto fail;
+ if (!(data = pdu_alloc(n)))
+ goto fail;
+ bcopy(buf, data, n);
+ if (pdu_addbuf(p, data, n, i + 1)) {
+ free(data);
+ goto fail;
+ }
+ buf += PDU_LEN(n);
+ len -= PDU_LEN(n);
+ }
+
+ return p;
+}
+
+int
+control_queue(void *ch, struct pdu *pdu)
+{
+ struct control *c = ch;
+
+ TAILQ_INSERT_TAIL(&c->channel, pdu, entry);
+
+ event_del(&c->ev);
+ event_set(&c->ev, c->fd, EV_READ|EV_WRITE, control_dispatch, c);
+ event_add(&c->ev, NULL);
+
+ return 0;
+}
+
+int
+control_compose(void *ch, u_int16_t type, void *buf, size_t len)
+{
+ struct pdu *pdu;
+ struct ctrlmsghdr *cmh;
+ void *ptr;
+
+ if (PDU_LEN(len) > CONTROL_READ_SIZE - PDU_LEN(sizeof(*cmh)))
+ return -1;
+ if ((pdu = pdu_new()) == NULL)
+ return -1;
+ if ((cmh = pdu_alloc(sizeof(*cmh))) == NULL)
+ goto fail;
+ bzero(cmh, sizeof(*cmh));
+ cmh->type = type;
+ cmh->len[0] = len;
+ pdu_addbuf(pdu, cmh, sizeof(*cmh), 0);
+ if (len > 0) {
+ if ((ptr = pdu_alloc(len)) == NULL)
+ goto fail;
+ bcopy(buf, ptr, len);
+ pdu_addbuf(pdu, ptr, len, 1);
+ }
+
+ return control_queue(ch, pdu);
+fail:
+ pdu_free(pdu);
+ return -1;
+}