[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c
From: |
ronniesahlberg |
Subject: |
[Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c |
Date: |
Fri, 3 Dec 2010 22:09:41 +1100 |
From: Ronnie Sahlberg <address@hidden>
iscsi client library : socket.c
This file contains functions for basic manipulation of the socket
used to talk to a iscsi target.
This includes, connect, disconnect, basic primitives for interfacing
with an external eventsystem, reading and writing to the socket.
The socket layer is fully nonblocking and will read/write data based
on when the socket becomes readable/writeable through the eventsystem.
...
./block/iscsi/ contains a copy of a general purpose iscsi client
library which is aimed at providing a clientside api for iscsi
for both qemu/kvm as well as otther scsi related utilities.
As such, there is need to make merging across various consumers,
qemu/kvm being one of many here, as easy as possible when features
are added to the library.
As such, no consumer/qemu specific code is used in this library as well
as coding guidelined might not be adhered to 100%
It is the intention that this library will be useful for many
and that iscsi use spawned from this will flourish.
Signed-off-by: Ronnie Sahlberg <address@hidden>
---
block/iscsi/socket.c | 344 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 344 insertions(+), 0 deletions(-)
create mode 100644 block/iscsi/socket.c
diff --git a/block/iscsi/socket.c b/block/iscsi/socket.c
new file mode 100644
index 0000000..072668f
--- /dev/null
+++ b/block/iscsi/socket.c
@@ -0,0 +1,344 @@
+/*
+ Copyright (C) 2010 by Ronnie Sahlberg <address@hidden>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/ioctl.h>
+#include <arpa/inet.h>
+#include "iscsi.h"
+#include "iscsi-private.h"
+#include "slist.h"
+
+static void set_nonblocking(int fd)
+{
+ unsigned v;
+ v = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, v | O_NONBLOCK);
+}
+
+int
+iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
+ iscsi_command_cb cb, void *private_data)
+{
+ int tpgt = -1;
+ int port = 3260;
+ char *str;
+ char *addr;
+ struct sockaddr_storage s;
+ struct sockaddr_in *sin = (struct sockaddr_in *)&s;
+ int socksize;
+
+ if (iscsi->fd != -1) {
+ iscsi_set_error(iscsi,
+ "Trying to connect but already connected.");
+ return -1;
+ }
+
+ addr = strdup(portal);
+ if (addr == NULL) {
+ iscsi_set_error(iscsi, "Out-of-memory: "
+ "Failed to strdup portal address.");
+ return -2;
+ }
+
+ /* check if we have a target portal group tag */
+ str = rindex(addr, ',');
+ if (str != NULL) {
+ tpgt = atoi(str+1);
+ str[0] = 0;
+ }
+
+ /* XXX need handling for {ipv6 addresses} */
+ /* for now, assume all is ipv4 */
+ str = rindex(addr, ':');
+ if (str != NULL) {
+ port = atoi(str+1);
+ str[0] = 0;
+ }
+
+ sin->sin_family = AF_INET;
+ sin->sin_port = htons(port);
+ if (inet_pton(AF_INET, addr, &sin->sin_addr) != 1) {
+ iscsi_set_error(iscsi, "Invalid target:%s "
+ "Failed to convert to ip address.", addr);
+ free(addr);
+ return -3;
+ }
+ free(addr);
+
+ switch (s.ss_family) {
+ case AF_INET:
+ iscsi->fd = socket(AF_INET, SOCK_STREAM, 0);
+ socksize = sizeof(struct sockaddr_in);
+ break;
+ default:
+ iscsi_set_error(iscsi, "Unknown address family :%d. "
+ "Only IPv4 supported so far.", s.ss_family);
+ return -4;
+
+ }
+
+ if (iscsi->fd == -1) {
+ iscsi_set_error(iscsi, "Failed to open iscsi socket. "
+ "Errno:%s(%d).", strerror(errno), errno);
+ return -5;
+
+ }
+
+ iscsi->connect_cb = cb;
+ iscsi->connect_data = private_data;
+
+ set_nonblocking(iscsi->fd);
+
+ if (connect(iscsi->fd, (struct sockaddr *)&s, socksize) != 0
+ && errno != EINPROGRESS) {
+ iscsi_set_error(iscsi, "Connect failed with errno : "
+ "%s(%d)\n", strerror(errno), errno);
+ return -6;
+ }
+
+ return 0;
+}
+
+int
+iscsi_disconnect(struct iscsi_context *iscsi)
+{
+ if (iscsi->fd == -1) {
+ iscsi_set_error(iscsi, "Trying to disconnect "
+ "but not connected");
+ return -1;
+ }
+
+ close(iscsi->fd);
+ iscsi->fd = -1;
+ iscsi->is_connected = 0;
+
+ return 0;
+}
+
+int
+iscsi_get_fd(struct iscsi_context *iscsi)
+{
+ return iscsi->fd;
+}
+
+int
+iscsi_which_events(struct iscsi_context *iscsi)
+{
+ int events = POLLIN;
+
+ if (iscsi->is_connected == 0) {
+ events |= POLLOUT;
+ }
+
+ if (iscsi->outqueue) {
+ events |= POLLOUT;
+ }
+ return events;
+}
+
+static int
+iscsi_read_from_socket(struct iscsi_context *iscsi)
+{
+ int available;
+ int size;
+ unsigned char *buf;
+ ssize_t count;
+
+ if (ioctl(iscsi->fd, FIONREAD, &available) != 0) {
+ iscsi_set_error(iscsi, "ioctl FIONREAD returned error : "
+ "%d\n", errno);
+ return -1;
+ }
+ if (available == 0) {
+ iscsi_set_error(iscsi, "no data readable in socket, "
+ "socket is closed\n");
+ return -2;
+ }
+ size = iscsi->insize - iscsi->inpos + available;
+ buf = malloc(size);
+ if (buf == NULL) {
+ iscsi_set_error(iscsi, "failed to allocate %d bytes for "
+ "input buffer\n", size);
+ return -3;
+ }
+ if (iscsi->insize > iscsi->inpos) {
+ memcpy(buf, iscsi->inbuf + iscsi->inpos,
+ iscsi->insize - iscsi->inpos);
+ iscsi->insize -= iscsi->inpos;
+ iscsi->inpos = 0;
+ }
+
+ count = read(iscsi->fd, buf + iscsi->insize, available);
+ if (count == -1) {
+ if (errno == EINTR) {
+ free(buf);
+ buf = NULL;
+ return 0;
+ }
+ iscsi_set_error(iscsi, "read from socket failed, "
+ "errno:%d\n", errno);
+ free(buf);
+ buf = NULL;
+ return -4;
+ }
+
+ if (iscsi->inbuf != NULL) {
+ free(iscsi->inbuf);
+ }
+ iscsi->inbuf = buf;
+ iscsi->insize += count;
+
+ while (1) {
+ if (iscsi->insize - iscsi->inpos < 48) {
+ return 0;
+ }
+ count = iscsi_get_pdu_size(iscsi,
+ iscsi->inbuf + iscsi->inpos);
+ if (iscsi->insize + iscsi->inpos < count) {
+ return 0;
+ }
+ if (iscsi_process_pdu(iscsi, iscsi->inbuf + iscsi->inpos,
+ count) != 0) {
+ free(buf);
+ return -7;
+ }
+ iscsi->inpos += count;
+ if (iscsi->inpos == iscsi->insize) {
+ free(iscsi->inbuf);
+ iscsi->inbuf = NULL;
+ iscsi->insize = 0;
+ iscsi->inpos = 0;
+ }
+ if (iscsi->inpos > iscsi->insize) {
+ iscsi_set_error(iscsi, "inpos > insize. bug!\n");
+ return -6;
+ }
+ }
+
+ return 0;
+}
+
+static int
+iscsi_write_to_socket(struct iscsi_context *iscsi)
+{
+ ssize_t count;
+
+ if (iscsi->fd == -1) {
+ iscsi_set_error(iscsi, "trying to write but not connected\n");
+ return -2;
+ }
+
+ while (iscsi->outqueue != NULL) {
+ ssize_t total;
+
+ total = iscsi->outqueue->outdata.size;
+ total = (total + 3) & 0xfffffffc;
+
+ count = write(iscsi->fd,
+ iscsi->outqueue->outdata.data
+ + iscsi->outqueue->written,
+ total - iscsi->outqueue->written);
+ if (count == -1) {
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ return 0;
+ }
+ iscsi_set_error(iscsi, "Error when writing to "
+ "socket :%d\n", errno);
+ return -3;
+ }
+
+ iscsi->outqueue->written += count;
+ if (iscsi->outqueue->written == total) {
+ struct iscsi_pdu *pdu = iscsi->outqueue;
+
+ SLIST_REMOVE(&iscsi->outqueue, pdu);
+ SLIST_ADD_END(&iscsi->waitpdu, pdu);
+ }
+ }
+ return 0;
+}
+
+int
+iscsi_service(struct iscsi_context *iscsi, int revents)
+{
+ if (revents & POLLERR) {
+ iscsi_set_error(iscsi, "iscsi_service: POLLERR, "
+ "socket error.");
+ iscsi->connect_cb(iscsi, SCSI_STATUS_ERROR, NULL,
+ iscsi->connect_data);
+ return -1;
+ }
+ if (revents & POLLHUP) {
+ iscsi_set_error(iscsi, "iscsi_service: POLLHUP, "
+ "socket error.");
+ iscsi->connect_cb(iscsi, SCSI_STATUS_ERROR, NULL,
+ iscsi->connect_data);
+ return -2;
+ }
+
+ if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
+ iscsi->is_connected = 1;
+ iscsi->connect_cb(iscsi, SCSI_STATUS_GOOD, NULL,
+ iscsi->connect_data);
+ return 0;
+ }
+
+ if (revents & POLLOUT && iscsi->outqueue != NULL) {
+ if (iscsi_write_to_socket(iscsi) != 0) {
+ return -3;
+ }
+ }
+ if (revents & POLLIN) {
+ if (iscsi_read_from_socket(iscsi) != 0)
+ return -4;
+ }
+
+ return 0;
+}
+
+int
+iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
+{
+ if (pdu == NULL) {
+ iscsi_set_error(iscsi, "trying to queue NULL pdu");
+ return -2;
+ }
+
+ if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) {
+ unsigned long crc;
+
+ crc = crc32c((char *)pdu->outdata.data, ISCSI_RAW_HEADER_SIZE);
+
+ pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+3] = (crc >> 24)&0xff;
+ pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+2] = (crc >> 16)&0xff;
+ pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+1] = (crc >> 8)&0xff;
+ pdu->outdata.data[ISCSI_RAW_HEADER_SIZE+0] = (crc) &0xff;
+ }
+
+ SLIST_ADD_END(&iscsi->outqueue, pdu);
+
+ return 0;
+}
+
--
1.7.3.1
- [Qemu-devel] [Patch 0/14] builtin iscsi support, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c,
ronniesahlberg <=
- [Qemu-devel] [PATCH 03/14] ./block/iscsi/login.c, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 04/14] ./block/iscsi/discovery.c, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 05/14] ./block/iscsi/crc32c.c, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 06/14] ./block/iscsi/nop.c, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 07/14] ./block/iscsi/pdu.c, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 08/14] ./block/iscsi/sscsi-command.c, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 09/14] ./block/iscsi/scsi-lowlevel.c, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 10/14] ./block/iscsi/sync.c, ronniesahlberg, 2010/12/03
- [Qemu-devel] [PATCH 11/14] ./block/iscsi/connect.c, ronniesahlberg, 2010/12/03