From patchwork Fri Dec 3 11:09:41 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: ronnie sahlberg X-Patchwork-Id: 74108 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [199.232.76.165]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 819C2B6F1E for ; Fri, 3 Dec 2010 22:14:09 +1100 (EST) Received: from localhost ([127.0.0.1]:33509 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1POTaY-0001dF-49 for incoming@patchwork.ozlabs.org; Fri, 03 Dec 2010 06:14:06 -0500 Received: from [140.186.70.92] (port=58607 helo=eggs.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1POTXJ-0001EJ-Uf for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:10:47 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1POTXI-0002Y4-9R for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:10:45 -0500 Received: from mail-iw0-f173.google.com ([209.85.214.173]:62308) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1POTXI-0002Vz-4U for qemu-devel@nongnu.org; Fri, 03 Dec 2010 06:10:44 -0500 Received: by mail-iw0-f173.google.com with SMTP id 38so3527589iwn.4 for ; Fri, 03 Dec 2010 03:10:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:received:from:to:cc:subject :date:message-id:x-mailer:in-reply-to:references; bh=zfz7WTvuXA/epxJHRWURqozo69QrbuKkhGHfenVRpMY=; b=IVhzYZlsn/wBhkeJT4D0mZWeRzGvcf60JV6/Ek7tMaQYQ0UqEEetXinAaSk/5xOmbA RQ3PzYBDfgAzlD89xGSptNPv6gf5lhLwY5KRN8uCRRV8sza9JZPq3wTs97DBMZeX0jKg tgtGGazNjOiKdGy9oCsplVTFYrq2Hs9mudwhk= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references; b=RZt/l76B1RCxJosTPTN5DYCpsPlGLQwAuo6cnxTPpN6m+tT8rMsiprcsAMqG4cwAaN sz92vZWMRKhXOwazPz3m6/guxMwPq6xopF8/NBYrM99FXTQ5MahWKbAeBvq1TBWDquXa b5prlJVmPL+ZEd2gl/Cf+kXcJWt6AZCPlvuHA= Received: by 10.231.35.11 with SMTP id n11mr1618029ibd.168.1291374643757; Fri, 03 Dec 2010 03:10:43 -0800 (PST) Received: from ronniesahlberg@gmail.com (CPE-121-216-183-74.lnse2.ken.bigpond.net.au [121.216.183.74]) by mx.google.com with ESMTPS id 8sm1483834iba.10.2010.12.03.03.10.39 (version=TLSv1/SSLv3 cipher=RC4-MD5); Fri, 03 Dec 2010 03:10:43 -0800 (PST) Received: by ronniesahlberg@gmail.com (sSMTP sendmail emulation); Fri, 03 Dec 2010 22:10:15 +1100 From: ronniesahlberg@gmail.com To: qemu-devel@nongnu.org Date: Fri, 3 Dec 2010 22:09:41 +1100 Message-Id: <1291374593-17448-3-git-send-email-ronniesahlberg@gmail.com> X-Mailer: git-send-email 1.7.3.1 In-Reply-To: <1291374593-17448-1-git-send-email-ronniesahlberg@gmail.com> References: <1291374593-17448-1-git-send-email-ronniesahlberg@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 2) Cc: Ronnie Sahlberg Subject: [Qemu-devel] [PATCH 02/14] ./block/iscsi/socket.c X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: qemu-devel.nongnu.org List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Ronnie Sahlberg 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 --- 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 + + 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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; +} +