Initial import of libiscsi

This commit is contained in:
Ronnie Sahlberg
2010-12-05 08:24:57 +11:00
commit 098bc5a9a7
23 changed files with 5742 additions and 0 deletions

127
lib/connect.c Normal file
View File

@@ -0,0 +1,127 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include "iscsi.h"
#include "iscsi-private.h"
#include "scsi-lowlevel.h"
struct connect_task {
iscsi_command_cb cb;
void *private_data;
int lun;
};
static void
iscsi_testunitready_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *private_data)
{
struct connect_task *ct = private_data;
if (status != 0) {
struct scsi_task *scsi = command_data;
if (scsi->sense.key == SCSI_SENSE_UNIT_ATTENTION
&& scsi->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) {
/* This is just the normal unitattention/busreset
* you always get just after a fresh login. Try
* again.
*/
if (iscsi_testunitready_async(iscsi, ct->lun,
iscsi_testunitready_cb,
ct) != 0) {
iscsi_set_error(iscsi, "iscsi_testunitready "
"failed.");
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL,
ct->private_data);
free(ct);
}
return;
}
}
ct->cb(iscsi, status?SCSI_STATUS_ERROR:SCSI_STATUS_GOOD, NULL,
ct->private_data);
free(ct);
}
static void
iscsi_login_cb(struct iscsi_context *iscsi, int status, void *command_data _U_,
void *private_data)
{
struct connect_task *ct = private_data;
if (status != 0) {
iscsi_set_error(iscsi, "Failed to login to iSCSI target. "
"%s", iscsi_get_error(iscsi));
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
free(ct);
return;
}
if (iscsi_testunitready_async(iscsi, ct->lun,
iscsi_testunitready_cb, ct) != 0) {
iscsi_set_error(iscsi, "iscsi_testunitready_async failed.");
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
free(ct);
}
}
static void
iscsi_connect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_,
void *private_data)
{
struct connect_task *ct = private_data;
if (status != 0) {
iscsi_set_error(iscsi, "Failed to connect to iSCSI socket. "
"%s", iscsi_get_error(iscsi));
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
free(ct);
return;
}
if (iscsi_login_async(iscsi, iscsi_login_cb, ct) != 0) {
iscsi_set_error(iscsi, "iscsi_login_async failed.");
ct->cb(iscsi, SCSI_STATUS_ERROR, NULL, ct->private_data);
free(ct);
}
}
int
iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal,
int lun, iscsi_command_cb cb, void *private_data)
{
struct connect_task *ct;
ct = malloc(sizeof(struct connect_task));
if (ct == NULL) {
iscsi_set_error(iscsi, "Out-of-memory. Failed to allocate "
"connect_task structure.");
return -ENOMEM;
}
ct->cb = cb;
ct->lun = lun;
ct->private_data = private_data;
if (iscsi_connect_async(iscsi, portal, iscsi_connect_cb, ct) != 0) {
free(ct);
return -ENOMEM;
}
return 0;
}

99
lib/crc32c.c Normal file
View File

@@ -0,0 +1,99 @@
#include "iscsi.h"
#include "iscsi-private.h"
/*****************************************************************/
/* */
/* CRC LOOKUP TABLE */
/* ================ */
/* The following CRC lookup table was generated automagically */
/* by the Rocksoft^tm Model CRC Algorithm Table Generation */
/* Program V1.0 using the following model parameters: */
/* */
/* Width : 4 bytes. */
/* Poly : 0x1EDC6F41L */
/* Reverse : TRUE. */
/* */
/* For more information on the Rocksoft^tm Model CRC Algorithm, */
/* see the document titled "A Painless Guide to CRC Error */
/* Detection Algorithms" by Ross Williams */
/* (ross@guest.adelaide.edu.au.). This document is likely to be */
/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft". */
/* */
/*****************************************************************/
static unsigned long crctable[256] = {
0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
};
unsigned long crc32c(char *buf, int len)
{
unsigned long crc = 0xffffffff;
while (len-- > 0) {
crc = (crc>>8) ^ crctable[(crc ^ (*buf++)) & 0xFF];
}
return crc^0xffffffff;
}

189
lib/discovery.c Normal file
View File

@@ -0,0 +1,189 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser 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 <arpa/inet.h>
#include "iscsi.h"
#include "iscsi-private.h"
int
iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
void *private_data)
{
struct iscsi_pdu *pdu;
char *str;
if (iscsi->session_type != ISCSI_SESSION_DISCOVERY) {
iscsi_set_error(iscsi, "Trying to do discovery on "
"non-discovery session.");
return -1;
}
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_TEXT_REQUEST,
ISCSI_PDU_TEXT_RESPONSE);
if (pdu == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
"text pdu.");
return -1;
}
/* immediate */
iscsi_pdu_set_immediate(pdu);
/* flags */
iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_TEXT_FINAL);
/* target transfer tag */
iscsi_pdu_set_ttt(pdu, 0xffffffff);
/* sendtargets */
str = (char *)"SendTargets=All";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
pdu->callback = cb;
pdu->private_data = private_data;
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
"text pdu.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
return 0;
}
static void
iscsi_free_discovery_addresses(struct iscsi_discovery_address *addresses)
{
while (addresses != NULL) {
struct iscsi_discovery_address *next = addresses->next;
free(discard_const(addresses->target_name));
addresses->target_name = NULL;
free(discard_const(addresses->target_address));
addresses->target_address = NULL;
addresses->next = NULL;
free(addresses);
addresses = next;
}
}
int
iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
const unsigned char *hdr, int size)
{
struct iscsi_discovery_address *targets = NULL;
/* verify the response looks sane */
if (hdr[1] != ISCSI_PDU_TEXT_FINAL) {
iscsi_set_error(iscsi, "unsupported flags in text "
"reply %02x", hdr[1]);
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
pdu->private_data);
return -1;
}
/* skip past the header */
hdr += ISCSI_HEADER_SIZE;
size -= ISCSI_HEADER_SIZE;
while (size > 0) {
int len;
len = strlen((char *)hdr);
if (len == 0) {
break;
}
if (len > size) {
iscsi_set_error(iscsi, "len > size when parsing "
"discovery data %d>%d", len, size);
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
pdu->private_data);
iscsi_free_discovery_addresses(targets);
return -1;
}
/* parse the strings */
if (!strncmp((char *)hdr, "TargetName=", 11)) {
struct iscsi_discovery_address *target;
target = malloc(sizeof(struct iscsi_discovery_address));
if (target == NULL) {
iscsi_set_error(iscsi, "Failed to allocate "
"data for new discovered "
"target");
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
pdu->private_data);
iscsi_free_discovery_addresses(targets);
return -1;
}
bzero(target, sizeof(struct iscsi_discovery_address));
target->target_name = strdup((char *)hdr+11);
if (target->target_name == NULL) {
iscsi_set_error(iscsi, "Failed to allocate "
"data for new discovered "
"target name");
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
pdu->private_data);
free(target);
target = NULL;
iscsi_free_discovery_addresses(targets);
return -1;
}
target->next = targets;
targets = target;
} else if (!strncmp((char *)hdr, "TargetAddress=", 14)) {
targets->target_address = strdup((char *)hdr+14);
if (targets->target_address == NULL) {
iscsi_set_error(iscsi, "Failed to allocate "
"data for new discovered "
"target address");
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
pdu->private_data);
iscsi_free_discovery_addresses(targets);
return -1;
}
} else {
iscsi_set_error(iscsi, "Dont know how to handle "
"discovery string : %s", hdr);
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
pdu->private_data);
iscsi_free_discovery_addresses(targets);
return -1;
}
hdr += len + 1;
size -= len + 1;
}
pdu->callback(iscsi, SCSI_STATUS_GOOD, targets, pdu->private_data);
iscsi_free_discovery_addresses(targets);
return 0;
}

207
lib/init.c Normal file
View File

@@ -0,0 +1,207 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdarg.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include "iscsi.h"
#include "iscsi-private.h"
#include "slist.h"
struct iscsi_context *
iscsi_create_context(const char *initiator_name)
{
struct iscsi_context *iscsi;
iscsi = malloc(sizeof(struct iscsi_context));
if (iscsi == NULL) {
return NULL;
}
bzero(iscsi, sizeof(struct iscsi_context));
iscsi->initiator_name = strdup(initiator_name);
if (iscsi->initiator_name == NULL) {
free(iscsi);
return NULL;
}
iscsi->fd = -1;
/* initialize to a "random" isid */
iscsi_set_isid_random(iscsi, getpid() ^ time(NULL));
return iscsi;
}
int
iscsi_set_isid_random(struct iscsi_context *iscsi, int rnd)
{
iscsi->isid[0] = 0x80;
iscsi->isid[1] = rnd&0xff;
iscsi->isid[2] = rnd&0xff;
iscsi->isid[3] = rnd&0xff;
iscsi->isid[4] = 0;
iscsi->isid[5] = 0;
return 0;
}
int
iscsi_set_alias(struct iscsi_context *iscsi, const char *alias)
{
if (iscsi->is_loggedin != 0) {
iscsi_set_error(iscsi, "Already logged in when adding alias");
return -1;
}
free(discard_const(iscsi->alias));
iscsi->alias = strdup(alias);
if (iscsi->alias == NULL) {
iscsi_set_error(iscsi, "Failed to allocate alias name");
return -1;
}
return 0;
}
int
iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
{
if (iscsi->is_loggedin != 0) {
iscsi_set_error(iscsi, "Already logged in when adding "
"targetname");
return -1;
}
free(discard_const(iscsi->target_name));
iscsi->target_name = strdup(target_name);
if (iscsi->target_name == NULL) {
iscsi_set_error(iscsi, "Failed to allocate target name");
return -1;
}
return 0;
}
int
iscsi_destroy_context(struct iscsi_context *iscsi)
{
struct iscsi_pdu *pdu;
if (iscsi == NULL) {
return 0;
}
if (iscsi->fd != -1) {
iscsi_disconnect(iscsi);
}
while ((pdu = iscsi->outqueue)) {
SLIST_REMOVE(&iscsi->outqueue, pdu);
pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
pdu->private_data);
iscsi_free_pdu(iscsi, pdu);
}
while ((pdu = iscsi->waitpdu)) {
SLIST_REMOVE(&iscsi->waitpdu, pdu);
pdu->callback(iscsi, SCSI_STATUS_CANCELLED, NULL,
pdu->private_data);
iscsi_free_pdu(iscsi, pdu);
}
free(discard_const(iscsi->initiator_name));
iscsi->initiator_name = NULL;
free(discard_const(iscsi->target_name));
iscsi->target_name = NULL;
free(discard_const(iscsi->alias));
iscsi->alias = NULL;
if (iscsi->inbuf != NULL) {
free(iscsi->inbuf);
iscsi->inbuf = NULL;
iscsi->insize = 0;
iscsi->inpos = 0;
}
free(iscsi->error_string);
iscsi->error_string = NULL;
free(iscsi);
return 0;
}
void
iscsi_set_error(struct iscsi_context *iscsi, const char *error_string, ...)
{
va_list ap;
char *str;
va_start(ap, error_string);
if (vasprintf(&str, error_string, ap) < 0) {
/* not much we can do here */
str = NULL;
}
free(iscsi->error_string);
iscsi->error_string = str;
va_end(ap);
}
const char *
iscsi_get_error(struct iscsi_context *iscsi)
{
return iscsi->error_string;
}
int
iscsi_set_header_digest(struct iscsi_context *iscsi,
enum iscsi_header_digest header_digest)
{
if (iscsi->is_loggedin) {
iscsi_set_error(iscsi, "trying to set header digest while "
"logged in");
return -1;
}
iscsi->want_header_digest = header_digest;
return 0;
}
int
iscsi_is_logged_in(struct iscsi_context *iscsi)
{
return iscsi->is_loggedin;
}

378
lib/login.c Normal file
View File

@@ -0,0 +1,378 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include "iscsi.h"
#include "iscsi-private.h"
int
iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
void *private_data)
{
struct iscsi_pdu *pdu;
char *str;
if (iscsi->is_loggedin != 0) {
iscsi_set_error(iscsi, "Trying to login while already logged "
"in.");
return -1;
}
switch (iscsi->session_type) {
case ISCSI_SESSION_DISCOVERY:
case ISCSI_SESSION_NORMAL:
break;
default:
iscsi_set_error(iscsi, "trying to login without setting "
"session type.");
return -1;
}
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGIN_REQUEST,
ISCSI_PDU_LOGIN_RESPONSE);
if (pdu == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
"login pdu.");
return -1;
}
/* login request */
iscsi_pdu_set_immediate(pdu);
/* flags */
iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_LOGIN_TRANSIT
| ISCSI_PDU_LOGIN_CSG_OPNEG
| ISCSI_PDU_LOGIN_NSG_FF);
/* initiator name */
if (iscsi_pdu_add_data(iscsi, pdu,
(unsigned char *)"InitiatorName=",
14) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
"failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
if (iscsi_pdu_add_data(iscsi, pdu,
(unsigned char *)iscsi->initiator_name,
strlen(iscsi->initiator_name) +1) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
"failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
/* optional alias */
if (iscsi->alias) {
if (iscsi_pdu_add_data(iscsi, pdu,
(unsigned char *)"InitiatorAlias=",
15) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
"failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
if (iscsi_pdu_add_data(iscsi, pdu,
(unsigned char *)iscsi->alias,
strlen(iscsi->alias) +1) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
"failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
}
/* target name */
if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
if (iscsi->target_name == NULL) {
iscsi_set_error(iscsi, "Trying normal connect but "
"target name not set.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
if (iscsi_pdu_add_data(iscsi, pdu,
(unsigned char *)"TargetName=",
11) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
"failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
if (iscsi_pdu_add_data(iscsi, pdu,
(unsigned char *)iscsi->target_name,
strlen(iscsi->target_name) +1) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data "
"failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
}
/* session type */
switch (iscsi->session_type) {
case ISCSI_SESSION_DISCOVERY:
str = (char *)"SessionType=Discovery";
break;
case ISCSI_SESSION_NORMAL:
str = (char *)"SessionType=Normal";
break;
default:
iscsi_set_error(iscsi, "Can not handle sessions %d yet.",
iscsi->session_type);
return -1;
}
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
switch (iscsi->want_header_digest) {
case ISCSI_HEADER_DIGEST_NONE:
str = (char *)"HeaderDigest=None";
break;
case ISCSI_HEADER_DIGEST_NONE_CRC32C:
str = (char *)"HeaderDigest=None,CRC32C";
break;
case ISCSI_HEADER_DIGEST_CRC32C_NONE:
str = (char *)"HeaderDigest=CRC32C,None";
break;
case ISCSI_HEADER_DIGEST_CRC32C:
str = (char *)"HeaderDigest=CRC32C";
break;
}
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
str = (char *)"DataDigest=None";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
str = (char *)"InitialR2T=Yes";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
str = (char *)"ImmediateData=Yes";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
str = (char *)"MaxBurstLength=262144";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
str = (char *)"FirstBurstLength=262144";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
str = (char *)"MaxRecvDataSegmentLength=262144";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
str = (char *)"DataPDUInOrder=Yes";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
str = (char *)"DataSequenceInOrder=Yes";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: pdu add data failed.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
pdu->callback = cb;
pdu->private_data = private_data;
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
"pdu.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
return 0;
}
int
iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
const unsigned char *hdr, int size)
{
int status;
if (size < ISCSI_HEADER_SIZE) {
iscsi_set_error(iscsi, "dont have enough data to read status "
"from login reply");
return -1;
}
status = ntohs(*(uint16_t *)&hdr[36]);
if (status != 0) {
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
pdu->private_data);
return 0;
}
iscsi->statsn = ntohs(*(uint16_t *)&hdr[24]);
/* XXX here we should parse the data returned in case the target
* renegotiated some some parameters.
* we should also do proper handshaking if the target is not yet
* prepared to transition to the next stage
*/
/* skip past the header */
hdr += ISCSI_HEADER_SIZE;
size -= ISCSI_HEADER_SIZE;
while (size > 0) {
int len;
len = strlen((char *)hdr);
if (len == 0) {
break;
}
if (len > size) {
iscsi_set_error(iscsi, "len > size when parsing "
"login data %d>%d", len, size);
pdu->callback(iscsi, SCSI_STATUS_ERROR, NULL,
pdu->private_data);
return -1;
}
/* parse the strings */
if (!strncmp((char *)hdr, "HeaderDigest=", 13)) {
if (!strcmp((char *)hdr + 13, "CRC32C")) {
iscsi->header_digest
= ISCSI_HEADER_DIGEST_CRC32C;
} else {
iscsi->header_digest
= ISCSI_HEADER_DIGEST_NONE;
}
}
hdr += len + 1;
size -= len + 1;
}
iscsi->is_loggedin = 1;
pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
return 0;
}
int
iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
void *private_data)
{
struct iscsi_pdu *pdu;
if (iscsi->is_loggedin == 0) {
iscsi_set_error(iscsi, "Trying to logout while not logged in.");
return -1;
}
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGOUT_REQUEST,
ISCSI_PDU_LOGOUT_RESPONSE);
if (pdu == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to allocate "
"logout pdu.");
return -1;
}
/* logout request has the immediate flag set */
iscsi_pdu_set_immediate(pdu);
/* flags : close the session */
iscsi_pdu_set_pduflags(pdu, 0x80);
pdu->callback = cb;
pdu->private_data = private_data;
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
"logout pdu.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
return 0;
}
int
iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
const unsigned char *hdr _U_, int size _U_)
{
iscsi->is_loggedin = 0;
pdu->callback(iscsi, SCSI_STATUS_GOOD, NULL, pdu->private_data);
return 0;
}
int
iscsi_set_session_type(struct iscsi_context *iscsi,
enum iscsi_session_type session_type)
{
if (iscsi->is_loggedin) {
iscsi_set_error(iscsi, "trying to set session type while "
"logged in");
return -1;
}
iscsi->session_type = session_type;
return 0;
}

90
lib/nop.c Normal file
View File

@@ -0,0 +1,90 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "iscsi.h"
#include "iscsi-private.h"
int
iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
unsigned char *data, int len, void *private_data)
{
struct iscsi_pdu *pdu;
if (iscsi->is_loggedin == 0) {
iscsi_set_error(iscsi, "trying send nop-out while not logged "
"in");
return -1;
}
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NOP_IN);
if (pdu == NULL) {
iscsi_set_error(iscsi, "Failed to allocate nop-out pdu");
return -1;
}
/* immediate flag */
iscsi_pdu_set_immediate(pdu);
/* flags */
iscsi_pdu_set_pduflags(pdu, 0x80);
/* ttt */
iscsi_pdu_set_ttt(pdu, 0xffffffff);
/* lun */
iscsi_pdu_set_lun(pdu, 2);
/* cmdsn is not increased if Immediate delivery*/
iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
pdu->cmdsn = iscsi->cmdsn;
pdu->callback = cb;
pdu->private_data = private_data;
if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) {
iscsi_set_error(iscsi, "Failed to add outdata to nop-out");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
iscsi_set_error(iscsi, "failed to queue iscsi nop-out pdu");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
return 0;
}
int
iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
const unsigned char *hdr, int size)
{
struct iscsi_data data;
data.data = NULL;
data.size = 0;
if (size > ISCSI_HEADER_SIZE) {
data.data = discard_const(&hdr[ISCSI_HEADER_SIZE]);
data.size = size - ISCSI_HEADER_SIZE;
}
pdu->callback(iscsi, SCSI_STATUS_GOOD, &data, pdu->private_data);
return 0;
}

336
lib/pdu.c Normal file
View File

@@ -0,0 +1,336 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <arpa/inet.h>
#include "iscsi.h"
#include "iscsi-private.h"
#include "scsi-lowlevel.h"
#include "slist.h"
struct iscsi_pdu *
iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode,
enum iscsi_opcode response_opcode)
{
struct iscsi_pdu *pdu;
pdu = malloc(sizeof(struct iscsi_pdu));
if (pdu == NULL) {
iscsi_set_error(iscsi, "failed to allocate pdu");
return NULL;
}
bzero(pdu, sizeof(struct iscsi_pdu));
pdu->outdata.size = ISCSI_HEADER_SIZE;
pdu->outdata.data = malloc(pdu->outdata.size);
if (pdu->outdata.data == NULL) {
iscsi_set_error(iscsi, "failed to allocate pdu header");
free(pdu);
return NULL;
}
bzero(pdu->outdata.data, pdu->outdata.size);
/* opcode */
pdu->outdata.data[0] = opcode;
pdu->response_opcode = response_opcode;
/* isid */
if (opcode == ISCSI_PDU_LOGIN_REQUEST) {
memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6);
}
/* itt */
*(uint32_t *)&pdu->outdata.data[16] = htonl(iscsi->itt);
pdu->itt = iscsi->itt;
iscsi->itt++;
return pdu;
}
void
iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
if (pdu == NULL) {
iscsi_set_error(iscsi, "trying to free NULL pdu");
return;
}
free(pdu->outdata.data);
pdu->outdata.data = NULL;
free(pdu->indata.data);
pdu->indata.data = NULL;
if (pdu->scsi_cbdata) {
iscsi_free_scsi_cbdata(pdu->scsi_cbdata);
pdu->scsi_cbdata = NULL;
}
free(pdu);
}
int
iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data,
unsigned char *dptr, int dsize, int pdualignment)
{
int len, aligned;
unsigned char *buf;
if (dsize == 0) {
iscsi_set_error(iscsi, "Trying to append zero size data to "
"iscsi_data");
return -1;
}
len = data->size + dsize;
aligned = len;
if (pdualignment) {
aligned = (aligned+3)&0xfffffffc;
}
buf = malloc(aligned);
if (buf == NULL) {
iscsi_set_error(iscsi, "failed to allocate buffer for %d "
"bytes", len);
return -1;
}
if (data->size > 0) {
memcpy(buf, data->data, data->size);
}
memcpy(buf + data->size, dptr, dsize);
if (len != aligned) {
/* zero out any padding at the end */
bzero(buf+len, aligned-len);
}
free(data->data);
data->data = buf;
data->size = len;
return 0;
}
int
iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
unsigned char *dptr, int dsize)
{
if (pdu == NULL) {
iscsi_set_error(iscsi, "trying to add data to NULL pdu");
return -1;
}
if (dsize == 0) {
iscsi_set_error(iscsi, "Trying to append zero size data to "
"pdu");
return -1;
}
if (iscsi_add_data(iscsi, &pdu->outdata, dptr, dsize, 1) != 0) {
iscsi_set_error(iscsi, "failed to add data to pdu buffer");
return -1;
}
/* update data segment length */
*(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size
- ISCSI_HEADER_SIZE);
return 0;
}
int
iscsi_get_pdu_size(struct iscsi_context *iscsi, const unsigned char *hdr)
{
int size;
size = (ntohl(*(uint32_t *)&hdr[4])&0x00ffffff) + ISCSI_HEADER_SIZE;
size = (size+3)&0xfffffffc;
return size;
}
int
iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr,
int size)
{
uint32_t itt;
enum iscsi_opcode opcode;
struct iscsi_pdu *pdu;
uint8_t ahslen;
opcode = hdr[0] & 0x3f;
ahslen = hdr[4];
itt = ntohl(*(uint32_t *)&hdr[16]);
if (ahslen != 0) {
iscsi_set_error(iscsi, "cant handle expanded headers yet");
return -1;
}
for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
enum iscsi_opcode expected_response = pdu->response_opcode;
int is_finished = 1;
if (pdu->itt != itt) {
continue;
}
/* we have a special case with scsi-command opcodes,
* they are replied to by either a scsi-response
* or a data-in, or a combination of both.
*/
if (opcode == ISCSI_PDU_DATA_IN
&& expected_response == ISCSI_PDU_SCSI_RESPONSE) {
expected_response = ISCSI_PDU_DATA_IN;
}
if (opcode != expected_response) {
iscsi_set_error(iscsi, "Got wrong opcode back for "
"itt:%d got:%d expected %d",
itt, opcode, pdu->response_opcode);
return -1;
}
switch (opcode) {
case ISCSI_PDU_LOGIN_RESPONSE:
if (iscsi_process_login_reply(iscsi, pdu, hdr, size)
!= 0) {
SLIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi login reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_TEXT_RESPONSE:
if (iscsi_process_text_reply(iscsi, pdu, hdr, size)
!= 0) {
SLIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi text reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_LOGOUT_RESPONSE:
if (iscsi_process_logout_reply(iscsi, pdu, hdr, size)
!= 0) {
SLIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi logout reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_SCSI_RESPONSE:
if (iscsi_process_scsi_reply(iscsi, pdu, hdr, size)
!= 0) {
SLIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi response reply "
"failed");
return -1;
}
break;
case ISCSI_PDU_DATA_IN:
if (iscsi_process_scsi_data_in(iscsi, pdu, hdr, size,
&is_finished) != 0) {
SLIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi data in "
"failed");
return -1;
}
break;
case ISCSI_PDU_NOP_IN:
if (iscsi_process_nop_out_reply(iscsi, pdu, hdr, size)
!= 0) {
SLIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
iscsi_set_error(iscsi, "iscsi nop-in failed");
return -1;
}
break;
default:
iscsi_set_error(iscsi, "Dont know how to handle "
"opcode %d", opcode);
return -1;
}
if (is_finished) {
SLIST_REMOVE(&iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
}
return 0;
}
return 0;
}
void
iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags)
{
pdu->outdata.data[1] = flags;
}
void
iscsi_pdu_set_immediate(struct iscsi_pdu *pdu)
{
pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE;
}
void
iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt)
{
*(uint32_t *)&pdu->outdata.data[20] = htonl(ttt);
}
void
iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn)
{
*(uint32_t *)&pdu->outdata.data[24] = htonl(cmdsn);
}
void
iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn)
{
*(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn);
}
void
iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task)
{
bzero(&pdu->outdata.data[32], 16);
memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size);
}
void
iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun)
{
pdu->outdata.data[9] = lun;
}
void
iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen)
{
*(uint32_t *)&pdu->outdata.data[20] = htonl(expxferlen);
}

528
lib/scsi-command.c Normal file
View File

@@ -0,0 +1,528 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser 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 <arpa/inet.h>
#include "iscsi.h"
#include "iscsi-private.h"
#include "scsi-lowlevel.h"
struct iscsi_scsi_cbdata {
struct iscsi_scsi_cbdata *prev, *next;
iscsi_command_cb callback;
void *private_data;
struct scsi_task *task;
};
void
iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata)
{
if (scsi_cbdata == NULL) {
return;
}
if (scsi_cbdata->task != NULL) {
scsi_free_scsi_task(scsi_cbdata->task);
scsi_cbdata->task = NULL;
}
free(scsi_cbdata);
}
void
iscsi_cbdata_steal_scsi_task(struct scsi_task *task)
{
struct iscsi_scsi_cbdata *scsi_cbdata =
scsi_get_task_private_ptr(task);
if (scsi_cbdata != NULL) {
scsi_cbdata->task = NULL;
}
}
static void
iscsi_scsi_response_cb(struct iscsi_context *iscsi, int status,
void *command_data, void *private_data)
{
struct iscsi_scsi_cbdata *scsi_cbdata =
(struct iscsi_scsi_cbdata *)private_data;
struct scsi_task *task = command_data;
switch (status) {
case SCSI_STATUS_GOOD:
scsi_cbdata->callback(iscsi, SCSI_STATUS_GOOD, task,
scsi_cbdata->private_data);
return;
case SCSI_STATUS_CHECK_CONDITION:
scsi_cbdata->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task,
scsi_cbdata->private_data);
return;
default:
iscsi_set_error(iscsi, "Cant handle scsi status %d yet.",
status);
scsi_cbdata->callback(iscsi, SCSI_STATUS_ERROR, task,
scsi_cbdata->private_data);
}
}
int
iscsi_scsi_command_async(struct iscsi_context *iscsi, int lun,
struct scsi_task *task, iscsi_command_cb cb,
struct iscsi_data *data, void *private_data)
{
struct iscsi_pdu *pdu;
struct iscsi_scsi_cbdata *scsi_cbdata;
int flags;
if (iscsi->session_type != ISCSI_SESSION_NORMAL) {
iscsi_set_error(iscsi, "Trying to send command on "
"discovery session.");
scsi_free_scsi_task(task);
return -1;
}
if (iscsi->is_loggedin == 0) {
iscsi_set_error(iscsi, "Trying to send command while "
"not logged in.");
scsi_free_scsi_task(task);
return -1;
}
scsi_cbdata = malloc(sizeof(struct iscsi_scsi_cbdata));
if (scsi_cbdata == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: failed to allocate "
"scsi cbdata.");
scsi_free_scsi_task(task);
return -1;
}
bzero(scsi_cbdata, sizeof(struct iscsi_scsi_cbdata));
scsi_cbdata->task = task;
scsi_cbdata->callback = cb;
scsi_cbdata->private_data = private_data;
scsi_set_task_private_ptr(task, scsi_cbdata);
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_SCSI_REQUEST,
ISCSI_PDU_SCSI_RESPONSE);
if (pdu == NULL) {
iscsi_set_error(iscsi, "Out-of-memory, Failed to allocate "
"scsi pdu.");
iscsi_free_scsi_cbdata(scsi_cbdata);
return -1;
}
pdu->scsi_cbdata = scsi_cbdata;
/* flags */
flags = ISCSI_PDU_SCSI_FINAL|ISCSI_PDU_SCSI_ATTR_SIMPLE;
switch (task->xfer_dir) {
case SCSI_XFER_NONE:
break;
case SCSI_XFER_READ:
flags |= ISCSI_PDU_SCSI_READ;
break;
case SCSI_XFER_WRITE:
flags |= ISCSI_PDU_SCSI_WRITE;
if (data == NULL) {
iscsi_set_error(iscsi, "DATA-OUT command but data "
"== NULL.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
if (data->size != task->expxferlen) {
iscsi_set_error(iscsi, "Data size:%d is not same as "
"expected data transfer "
"length:%d.", data->size,
task->expxferlen);
iscsi_free_pdu(iscsi, pdu);
return -1;
}
if (iscsi_pdu_add_data(iscsi, pdu, data->data, data->size)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to "
"add outdata to the pdu.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
break;
}
iscsi_pdu_set_pduflags(pdu, flags);
/* lun */
iscsi_pdu_set_lun(pdu, lun);
/* expxferlen */
iscsi_pdu_set_expxferlen(pdu, task->expxferlen);
/* cmdsn */
iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
pdu->cmdsn = iscsi->cmdsn;
iscsi->cmdsn++;
/* exp statsn */
iscsi_pdu_set_expstatsn(pdu, iscsi->statsn+1);
/* cdb */
iscsi_pdu_set_cdb(pdu, task);
pdu->callback = iscsi_scsi_response_cb;
pdu->private_data = scsi_cbdata;
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
iscsi_set_error(iscsi, "Out-of-memory: failed to queue iscsi "
"scsi pdu.");
iscsi_free_pdu(iscsi, pdu);
return -1;
}
return 0;
}
int
iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
const unsigned char *hdr, int size)
{
int statsn, flags, response, status;
struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
struct scsi_task *task = scsi_cbdata->task;
statsn = ntohl(*(uint32_t *)&hdr[24]);
if (statsn > (int)iscsi->statsn) {
iscsi->statsn = statsn;
}
flags = hdr[1];
if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
iscsi_set_error(iscsi, "scsi response pdu but Final bit is "
"not set: 0x%02x.", flags);
pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
pdu->private_data);
return -1;
}
if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
iscsi_set_error(iscsi, "scsi response asked for ACK "
"0x%02x.", flags);
pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
pdu->private_data);
return -1;
}
response = hdr[2];
status = hdr[3];
switch (status) {
case SCSI_STATUS_GOOD:
task->datain.data = pdu->indata.data;
task->datain.size = pdu->indata.size;
pdu->indata.data = NULL;
pdu->indata.size = 0;
pdu->callback(iscsi, SCSI_STATUS_GOOD, task,
pdu->private_data);
break;
case SCSI_STATUS_CHECK_CONDITION:
task->datain.size = size - ISCSI_HEADER_SIZE;
task->datain.data = malloc(task->datain.size);
if (task->datain.data == NULL) {
iscsi_set_error(iscsi, "failed to allocate blob for "
"sense data");
}
memcpy(task->datain.data, hdr + ISCSI_HEADER_SIZE,
task->datain.size);
task->sense.error_type = task->datain.data[2] & 0x7f;
task->sense.key = task->datain.data[4] & 0x0f;
task->sense.ascq = ntohs(*(uint16_t *)
&(task->datain.data[14]));
iscsi_set_error(iscsi, "SENSE KEY:%s(%d) ASCQ:%s(0x%04x)",
scsi_sense_key_str(task->sense.key),
task->sense.key,
scsi_sense_ascq_str(task->sense.ascq),
task->sense.ascq);
pdu->callback(iscsi, SCSI_STATUS_CHECK_CONDITION, task,
pdu->private_data);
break;
default:
iscsi_set_error(iscsi, "Unknown SCSI status :%d.", status);
pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
pdu->private_data);
return -1;
}
return 0;
}
int
iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
const unsigned char *hdr, int size _U_, int *is_finished)
{
int statsn, flags, status;
struct iscsi_scsi_cbdata *scsi_cbdata = pdu->scsi_cbdata;
struct scsi_task *task = scsi_cbdata->task;
int dsl;
statsn = ntohl(*(uint32_t *)&hdr[24]);
if (statsn > (int)iscsi->statsn) {
iscsi->statsn = statsn;
}
flags = hdr[1];
if ((flags&ISCSI_PDU_DATA_ACK_REQUESTED) != 0) {
iscsi_set_error(iscsi, "scsi response asked for ACK "
"0x%02x.", flags);
pdu->callback(iscsi, SCSI_STATUS_ERROR, task,
pdu->private_data);
return -1;
}
dsl = ntohl(*(uint32_t *)&hdr[4])&0x00ffffff;
if (iscsi_add_data(iscsi, &pdu->indata,
discard_const(hdr + ISCSI_HEADER_SIZE), dsl, 0)
!= 0) {
iscsi_set_error(iscsi, "Out-of-memory: failed to add data "
"to pdu in buffer.");
return -1;
}
if ((flags&ISCSI_PDU_DATA_FINAL) == 0) {
*is_finished = 0;
}
if ((flags&ISCSI_PDU_DATA_CONTAINS_STATUS) == 0) {
*is_finished = 0;
}
if (*is_finished == 0) {
return 0;
}
/* this was the final data-in packet in the sequence and it has
* the s-bit set, so invoke the callback.
*/
status = hdr[3];
task->datain.data = pdu->indata.data;
task->datain.size = pdu->indata.size;
pdu->indata.data = NULL;
pdu->indata.size = 0;
pdu->callback(iscsi, status, task, pdu->private_data);
return 0;
}
/*
* SCSI commands
*/
int
iscsi_testunitready_async(struct iscsi_context *iscsi, int lun,
iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
int ret;
task = scsi_cdb_testunitready();
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"testunitready cdb.");
return -1;
}
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
private_data);
return ret;
}
int
iscsi_reportluns_async(struct iscsi_context *iscsi, int report_type,
int alloc_len, iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
int ret;
if (alloc_len < 16) {
iscsi_set_error(iscsi, "Minimum allowed alloc len for "
"reportluns is 16. You specified %d.",
alloc_len);
return -1;
}
task = scsi_reportluns_cdb(report_type, alloc_len);
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"reportluns cdb.");
return -1;
}
/* report luns are always sent to lun 0 */
ret = iscsi_scsi_command_async(iscsi, 0, task, cb, NULL,
private_data);
return ret;
}
int
iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, int evpd,
int page_code, int maxsize,
iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
int ret;
task = scsi_cdb_inquiry(evpd, page_code, maxsize);
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"inquiry cdb.");
return -1;
}
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
private_data);
return ret;
}
int
iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, int lba,
int pmi, iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
int ret;
task = scsi_cdb_readcapacity10(lba, pmi);
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"readcapacity10 cdb.");
return -1;
}
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
private_data);
return ret;
}
int
iscsi_read10_async(struct iscsi_context *iscsi, int lun, int lba,
int datalen, int blocksize,
iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
int ret;
if (datalen % blocksize != 0) {
iscsi_set_error(iscsi, "Datalen:%d is not a multiple of "
"the blocksize:%d.", datalen, blocksize);
return -1;
}
task = scsi_cdb_read10(lba, datalen, blocksize);
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"read10 cdb.");
return -1;
}
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
private_data);
return ret;
}
int
iscsi_write10_async(struct iscsi_context *iscsi, int lun, unsigned char *data,
int datalen, int lba, int fua, int fuanv, int blocksize,
iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
struct iscsi_data outdata;
int ret;
if (datalen % blocksize != 0) {
iscsi_set_error(iscsi, "Datalen:%d is not a multiple of the "
"blocksize:%d.", datalen, blocksize);
return -1;
}
task = scsi_cdb_write10(lba, datalen, fua, fuanv, blocksize);
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"read10 cdb.");
return -1;
}
outdata.data = data;
outdata.size = datalen;
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, &outdata,
private_data);
return ret;
}
int
iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, int dbd, int pc,
int page_code, int sub_page_code,
unsigned char alloc_len,
iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
int ret;
task = scsi_cdb_modesense6(dbd, pc, page_code, sub_page_code,
alloc_len);
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"modesense6 cdb.");
return -1;
}
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
private_data);
return ret;
}
int
iscsi_synchronizecache10_async(struct iscsi_context *iscsi, int lun, int lba,
int num_blocks, int syncnv, int immed,
iscsi_command_cb cb, void *private_data)
{
struct scsi_task *task;
int ret;
task = scsi_cdb_synchronizecache10(lba, num_blocks, syncnv,
immed);
if (task == NULL) {
iscsi_set_error(iscsi, "Out-of-memory: Failed to create "
"synchronizecache10 cdb.");
return -1;
}
ret = iscsi_scsi_command_async(iscsi, lun, task, cb, NULL,
private_data);
return ret;
}

876
lib/scsi-lowlevel.c Normal file
View File

@@ -0,0 +1,876 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
/*
* would be nice if this could grow into a full blown library for scsi to
* 1, build a CDB
* 2, check how big a complete data-in structure needs to be
* 3, unmarshall data-in into a real structure
* 4, marshall a real structure into a data-out blob
*/
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <strings.h>
#include <stdint.h>
#include <arpa/inet.h>
#include "scsi-lowlevel.h"
#include "slist.h"
void
scsi_free_scsi_task(struct scsi_task *task)
{
struct scsi_allocated_memory *mem;
while ((mem = task->mem)) {
SLIST_REMOVE(&task->mem, mem);
free(mem->ptr);
free(mem);
}
free(task->datain.data);
free(task);
}
static void *
scsi_malloc(struct scsi_task *task, size_t size)
{
struct scsi_allocated_memory *mem;
mem = malloc(sizeof(struct scsi_allocated_memory));
if (mem == NULL) {
return NULL;
}
bzero(mem, sizeof(struct scsi_allocated_memory));
mem->ptr = malloc(size);
if (mem->ptr == NULL) {
free(mem);
return NULL;
}
bzero(mem->ptr, size);
SLIST_ADD(&task->mem, mem);
return mem->ptr;
}
struct value_string {
int value;
const char *string;
};
static const char *
value_string_find(struct value_string *values, int value)
{
for (; values->value; values++) {
if (value == values->value) {
return values->string;
}
}
return NULL;
}
const char *
scsi_sense_key_str(int key)
{
struct value_string keys[] = {
{SCSI_SENSE_ILLEGAL_REQUEST,
"ILLEGAL_REQUEST"},
{SCSI_SENSE_UNIT_ATTENTION,
"UNIT_ATTENTION"},
{0, NULL}
};
return value_string_find(keys, key);
}
const char *
scsi_sense_ascq_str(int ascq)
{
struct value_string ascqs[] = {
{SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB,
"INVALID_FIELD_IN_CDB"},
{SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED,
"LOGICAL_UNIT_NOT_SUPPORTED"},
{SCSI_SENSE_ASCQ_BUS_RESET,
"BUS_RESET"},
{0, NULL}
};
return value_string_find(ascqs, ascq);
}
/*
* TESTUNITREADY
*/
struct scsi_task *
scsi_cdb_testunitready(void)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
bzero(task, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_TESTUNITREADY;
task->cdb_size = 6;
task->xfer_dir = SCSI_XFER_NONE;
task->expxferlen = 0;
return task;
}
/*
* REPORTLUNS
*/
struct scsi_task *
scsi_reportluns_cdb(int report_type, int alloc_len)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
bzero(task, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_REPORTLUNS;
task->cdb[2] = report_type;
*(uint32_t *)&task->cdb[6] = htonl(alloc_len);
task->cdb_size = 12;
task->xfer_dir = SCSI_XFER_READ;
task->expxferlen = alloc_len;
task->params.reportluns.report_type = report_type;
return task;
}
/*
* parse the data in blob and calcualte the size of a full report luns
* datain structure
*/
static int
scsi_reportluns_datain_getfullsize(struct scsi_task *task)
{
uint32_t list_size;
list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
return list_size;
}
/*
* unmarshall the data in blob for reportluns into a structure
*/
static struct scsi_reportluns_list *
scsi_reportluns_datain_unmarshall(struct scsi_task *task)
{
struct scsi_reportluns_list *list;
int list_size;
int i, num_luns;
if (task->datain.size < 4) {
return NULL;
}
list_size = htonl(*(uint32_t *)&(task->datain.data[0])) + 8;
if (list_size < task->datain.size) {
return NULL;
}
num_luns = list_size / 8 - 1;
list = scsi_malloc(task, offsetof(struct scsi_reportluns_list, luns)
+ sizeof(uint16_t) * num_luns);
if (list == NULL) {
return NULL;
}
list->num = num_luns;
for (i = 0; i < num_luns; i++) {
list->luns[i] = htons(*(uint16_t *)
&(task->datain.data[i*8+8]));
}
return list;
}
/*
* READCAPACITY10
*/
struct scsi_task *
scsi_cdb_readcapacity10(int lba, int pmi)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
bzero(task, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_READCAPACITY10;
*(uint32_t *)&task->cdb[2] = htonl(lba);
if (pmi) {
task->cdb[8] |= 0x01;
}
task->cdb_size = 10;
task->xfer_dir = SCSI_XFER_READ;
task->expxferlen = 8;
task->params.readcapacity10.lba = lba;
task->params.readcapacity10.pmi = pmi;
return task;
}
/*
* parse the data in blob and calcualte the size of a full
* readcapacity10 datain structure
*/
static int
scsi_readcapacity10_datain_getfullsize(struct scsi_task *task _U_)
{
return 8;
}
/*
* unmarshall the data in blob for readcapacity10 into a structure
*/
static struct scsi_readcapacity10 *
scsi_readcapacity10_datain_unmarshall(struct scsi_task *task)
{
struct scsi_readcapacity10 *rc10;
if (task->datain.size < 8) {
return NULL;
}
rc10 = scsi_malloc(task, sizeof(struct scsi_readcapacity10));
if (rc10 == NULL) {
return NULL;
}
rc10->lba = htonl(*(uint32_t *)&(task->datain.data[0]));
rc10->block_size = htonl(*(uint32_t *)&(task->datain.data[4]));
return rc10;
}
/*
* INQUIRY
*/
struct scsi_task *
scsi_cdb_inquiry(int evpd, int page_code, int alloc_len)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
bzero(task, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_INQUIRY;
if (evpd) {
task->cdb[1] |= 0x01;
}
task->cdb[2] = page_code;
*(uint16_t *)&task->cdb[3] = htons(alloc_len);
task->cdb_size = 6;
task->xfer_dir = SCSI_XFER_READ;
task->expxferlen = alloc_len;
task->params.inquiry.evpd = evpd;
task->params.inquiry.page_code = page_code;
return task;
}
/*
* parse the data in blob and calcualte the size of a full
* inquiry datain structure
*/
static int
scsi_inquiry_datain_getfullsize(struct scsi_task *task)
{
if (task->params.inquiry.evpd == 0) {
return task->datain.data[4] + 3;
}
switch (task->params.inquiry.page_code) {
case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
return task->datain.data[3] + 4;
case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
return task->datain.data[3] + 4;
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
return ntohs(*(uint16_t *)&task->datain.data[2]) + 4;
case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
return task->datain.data[3] + 4;
default:
return -1;
}
}
/*
* unmarshall the data in blob for inquiry into a structure
*/
static void *
scsi_inquiry_datain_unmarshall(struct scsi_task *task)
{
if (task->params.inquiry.evpd == 0) {
struct scsi_inquiry_standard *inq;
/* standard inquiry */
inq = scsi_malloc(task, sizeof(struct scsi_inquiry_standard));
if (inq == NULL) {
return NULL;
}
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
inq->periperal_device_type = task->datain.data[0]&0x1f;
inq->rmb = !!(task->datain.data[1]&0x80);
inq->version = task->datain.data[2];
inq->normaca = !!(task->datain.data[3]&0x20);
inq->hisup = !!(task->datain.data[3]&0x10);
inq->response_data_format = task->datain.data[3]&0x0f;
inq->sccs = !!(task->datain.data[5]&0x80);
inq->acc = !!(task->datain.data[5]&0x40);
inq->tpgs = (task->datain.data[5]>>4)&0x03;
inq->threepc = !!(task->datain.data[5]&0x08);
inq->protect = !!(task->datain.data[5]&0x01);
inq->encserv = !!(task->datain.data[6]&0x40);
inq->multip = !!(task->datain.data[6]&0x10);
inq->addr16 = !!(task->datain.data[6]&0x01);
inq->wbus16 = !!(task->datain.data[7]&0x20);
inq->sync = !!(task->datain.data[7]&0x10);
inq->cmdque = !!(task->datain.data[7]&0x02);
memcpy(&inq->vendor_identification[0],
&task->datain.data[8], 8);
memcpy(&inq->product_identification[0],
&task->datain.data[16], 16);
memcpy(&inq->product_revision_level[0],
&task->datain.data[32], 4);
inq->clocking = (task->datain.data[56]>>2)&0x03;
inq->qas = !!(task->datain.data[56]&0x02);
inq->ius = !!(task->datain.data[56]&0x01);
return inq;
}
if (task->params.inquiry.page_code
== SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES) {
struct scsi_inquiry_supported_pages *inq;
inq = scsi_malloc(task,
sizeof(struct scsi_inquiry_supported_pages));
if (inq == NULL) {
return NULL;
}
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
inq->periperal_device_type = task->datain.data[0]&0x1f;
inq->pagecode = task->datain.data[1];
inq->num_pages = task->datain.data[3];
inq->pages = scsi_malloc(task, inq->num_pages);
if (inq->pages == NULL) {
return NULL;
}
memcpy(inq->pages, &task->datain.data[4], inq->num_pages);
return inq;
} else if (task->params.inquiry.page_code
== SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER) {
struct scsi_inquiry_unit_serial_number *inq;
inq = scsi_malloc(task,
sizeof(struct scsi_inquiry_unit_serial_number));
if (inq == NULL) {
return NULL;
}
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
inq->periperal_device_type = task->datain.data[0]&0x1f;
inq->pagecode = task->datain.data[1];
inq->usn = scsi_malloc(task, task->datain.data[3]+1);
if (inq->usn == NULL) {
return NULL;
}
memcpy(inq->usn, &task->datain.data[4], task->datain.data[3]);
inq->usn[task->datain.data[3]] = 0;
return inq;
} else if (task->params.inquiry.page_code
== SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION) {
struct scsi_inquiry_device_identification *inq;
int remaining = ntohs(*(uint16_t *)&task->datain.data[2]);
unsigned char *dptr;
inq = scsi_malloc(task,
sizeof(struct scsi_inquiry_device_identification));
if (inq == NULL) {
return NULL;
}
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
inq->periperal_device_type = task->datain.data[0]&0x1f;
inq->pagecode = task->datain.data[1];
dptr = &task->datain.data[4];
while (remaining > 0) {
struct scsi_inquiry_device_designator *dev;
dev = scsi_malloc(task,
sizeof(struct scsi_inquiry_device_designator));
if (dev == NULL) {
return NULL;
}
dev->next = inq->designators;
inq->designators = dev;
dev->protocol_identifier = (dptr[0]>>4) & 0x0f;
dev->code_set = dptr[0] & 0x0f;
dev->piv = !!(dptr[1]&0x80);
dev->association = (dptr[1]>>4)&0x03;
dev->designator_type = dptr[1]&0x0f;
dev->designator_length = dptr[3];
dev->designator = scsi_malloc(task,
dev->designator_length+1);
if (dev->designator == NULL) {
return NULL;
}
dev->designator[dev->designator_length] = 0;
memcpy(dev->designator, &dptr[4],
dev->designator_length);
remaining -= 4;
remaining -= dev->designator_length;
dptr += dev->designator_length + 4;
}
return inq;
} else if (task->params.inquiry.page_code
== SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS) {
struct scsi_inquiry_block_device_characteristics *inq;
inq = scsi_malloc(task,
sizeof(struct scsi_inquiry_block_device_characteristics));
if (inq == NULL) {
return NULL;
}
inq->periperal_qualifier = (task->datain.data[0]>>5)&0x07;
inq->periperal_device_type = task->datain.data[0]&0x1f;
inq->pagecode = task->datain.data[1];
inq->medium_rotation_rate = ntohs(*(uint16_t *)
&task->datain.data[4]);
return inq;
}
return NULL;
}
/*
* READ10
*/
struct scsi_task *
scsi_cdb_read10(int lba, int xferlen, int blocksize)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
bzero(task, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_READ10;
*(uint32_t *)&task->cdb[2] = htonl(lba);
*(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
task->cdb_size = 10;
task->xfer_dir = SCSI_XFER_READ;
task->expxferlen = xferlen;
return task;
}
/*
* WRITE10
*/
struct scsi_task *
scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
bzero(task, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_WRITE10;
if (fua) {
task->cdb[1] |= 0x08;
}
if (fuanv) {
task->cdb[1] |= 0x02;
}
*(uint32_t *)&task->cdb[2] = htonl(lba);
*(uint16_t *)&task->cdb[7] = htons(xferlen/blocksize);
task->cdb_size = 10;
task->xfer_dir = SCSI_XFER_WRITE;
task->expxferlen = xferlen;
return task;
}
/*
* MODESENSE6
*/
struct scsi_task *
scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc,
enum scsi_modesense_page_code page_code,
int sub_page_code, unsigned char alloc_len)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
bzero(task, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_MODESENSE6;
if (dbd) {
task->cdb[1] |= 0x08;
}
task->cdb[2] = pc<<6 | page_code;
task->cdb[3] = sub_page_code;
task->cdb[4] = alloc_len;
task->cdb_size = 6;
task->xfer_dir = SCSI_XFER_READ;
task->expxferlen = alloc_len;
task->params.modesense6.dbd = dbd;
task->params.modesense6.pc = pc;
task->params.modesense6.page_code = page_code;
task->params.modesense6.sub_page_code = sub_page_code;
return task;
}
/*
* parse the data in blob and calcualte the size of a full
* modesense6 datain structure
*/
static int
scsi_modesense6_datain_getfullsize(struct scsi_task *task)
{
int len;
len = task->datain.data[0] + 1;
return len;
}
/*
* SYNCHRONIZECACHE10
*/
struct scsi_task *
scsi_cdb_synchronizecache10(int lba, int num_blocks, int syncnv, int immed)
{
struct scsi_task *task;
task = malloc(sizeof(struct scsi_task));
if (task == NULL) {
return NULL;
}
bzero(task, sizeof(struct scsi_task));
task->cdb[0] = SCSI_OPCODE_SYNCHRONIZECACHE10;
if (syncnv) {
task->cdb[1] |= 0x04;
}
if (immed) {
task->cdb[1] |= 0x02;
}
*(uint32_t *)&task->cdb[2] = htonl(lba);
*(uint16_t *)&task->cdb[7] = htons(num_blocks);
task->cdb_size = 10;
task->xfer_dir = SCSI_XFER_NONE;
task->expxferlen = 0;
return task;
}
int
scsi_datain_getfullsize(struct scsi_task *task)
{
switch (task->cdb[0]) {
case SCSI_OPCODE_TESTUNITREADY:
return 0;
case SCSI_OPCODE_INQUIRY:
return scsi_inquiry_datain_getfullsize(task);
case SCSI_OPCODE_MODESENSE6:
return scsi_modesense6_datain_getfullsize(task);
case SCSI_OPCODE_READCAPACITY10:
return scsi_readcapacity10_datain_getfullsize(task);
case SCSI_OPCODE_SYNCHRONIZECACHE10:
return 0;
case SCSI_OPCODE_REPORTLUNS:
return scsi_reportluns_datain_getfullsize(task);
}
return -1;
}
void *
scsi_datain_unmarshall(struct scsi_task *task)
{
switch (task->cdb[0]) {
case SCSI_OPCODE_TESTUNITREADY:
return NULL;
case SCSI_OPCODE_INQUIRY:
return scsi_inquiry_datain_unmarshall(task);
case SCSI_OPCODE_READCAPACITY10:
return scsi_readcapacity10_datain_unmarshall(task);
case SCSI_OPCODE_SYNCHRONIZECACHE10:
return NULL;
case SCSI_OPCODE_REPORTLUNS:
return scsi_reportluns_datain_unmarshall(task);
}
return NULL;
}
const char *
scsi_devtype_to_str(enum scsi_inquiry_peripheral_device_type type)
{
switch (type) {
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS:
return "DIRECT_ACCESS";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS:
return "SEQUENTIAL_ACCESS";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER:
return "PRINTER";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR:
return "PROCESSOR";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE:
return "WRITE_ONCE";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC:
return "MMC";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER:
return "SCANNER";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY:
return "OPTICAL_MEMORY";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER:
return "MEDIA_CHANGER";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS:
return "COMMUNICATIONS";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER:
return "STORAGE_ARRAY_CONTROLLER";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES:
return "ENCLOSURE_SERVICES";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS:
return "SIMPLIFIED_DIRECT_ACCESS";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER:
return "OPTICAL_CARD_READER";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER:
return "BRIDGE_CONTROLLER";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD:
return "OSD";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION:
return "AUTOMATION";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER:
return "SEQURITY_MANAGER";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN:
return "WELL_KNOWN_LUN";
case SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN:
return "UNKNOWN";
}
return "unknown";
}
const char *
scsi_devqualifier_to_str(enum scsi_inquiry_peripheral_qualifier qualifier)
{
switch (qualifier) {
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED:
return "CONNECTED";
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED:
return "DISCONNECTED";
case SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED:
return "NOT_SUPPORTED";
}
return "unknown";
}
const char *
scsi_version_to_str(enum scsi_version version)
{
switch (version) {
case SCSI_VERSION_SPC:
return "ANSI INCITS 301-1997 (SPC)";
case SCSI_VERSION_SPC2:
return "ANSI INCITS 351-2001 (SPC-2)";
case SCSI_VERSION_SPC3:
return "ANSI INCITS 408-2005 (SPC-3)";
}
return "unknown";
}
const char *
scsi_inquiry_pagecode_to_str(int pagecode)
{
switch (pagecode) {
case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
return "SUPPORTED_VPD_PAGES";
case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
return "UNIT_SERIAL_NUMBER";
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
return "DEVICE_IDENTIFICATION";
case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
return "BLOCK_DEVICE_CHARACTERISTICS";
}
return "unknown";
}
const char *
scsi_protocol_identifier_to_str(int identifier)
{
switch (identifier) {
case SCSI_PROTOCOL_IDENTIFIER_FIBRE_CHANNEL:
return "FIBRE_CHANNEL";
case SCSI_PROTOCOL_IDENTIFIER_PARALLEL_SCSI:
return "PARALLEL_SCSI";
case SCSI_PROTOCOL_IDENTIFIER_SSA:
return "SSA";
case SCSI_PROTOCOL_IDENTIFIER_IEEE_1394:
return "IEEE_1394";
case SCSI_PROTOCOL_IDENTIFIER_RDMA:
return "RDMA";
case SCSI_PROTOCOL_IDENTIFIER_ISCSI:
return "ISCSI";
case SCSI_PROTOCOL_IDENTIFIER_SAS:
return "SAS";
case SCSI_PROTOCOL_IDENTIFIER_ADT:
return "ADT";
case SCSI_PROTOCOL_IDENTIFIER_ATA:
return "ATA";
}
return "unknown";
}
const char *
scsi_codeset_to_str(int codeset)
{
switch (codeset) {
case SCSI_CODESET_BINARY:
return "BINARY";
case SCSI_CODESET_ASCII:
return "ASCII";
case SCSI_CODESET_UTF8:
return "UTF8";
}
return "unknown";
}
const char *
scsi_association_to_str(int association)
{
switch (association) {
case SCSI_ASSOCIATION_LOGICAL_UNIT:
return "LOGICAL_UNIT";
case SCSI_ASSOCIATION_TARGET_PORT:
return "TARGET_PORT";
case SCSI_ASSOCIATION_TARGET_DEVICE:
return "TARGET_DEVICE";
}
return "unknown";
}
const char *
scsi_designator_type_to_str(int type)
{
switch (type) {
case SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC:
return "VENDOR_SPECIFIC";
case SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID:
return "T10_VENDORT_ID";
case SCSI_DESIGNATOR_TYPE_EUI_64:
return "EUI_64";
case SCSI_DESIGNATOR_TYPE_NAA:
return "NAA";
case SCSI_DESIGNATOR_TYPE_RELATIVE_TARGET_PORT:
return "RELATIVE_TARGET_PORT";
case SCSI_DESIGNATOR_TYPE_TARGET_PORT_GROUP:
return "TARGET_PORT_GROUP";
case SCSI_DESIGNATOR_TYPE_LOGICAL_UNIT_GROUP:
return "LOGICAL_UNIT_GROUP";
case SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER:
return "MD5_LOGICAL_UNIT_IDENTIFIER";
case SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING:
return "SCSI_NAME_STRING";
}
return "unknown";
}
void
scsi_set_task_private_ptr(struct scsi_task *task, void *ptr)
{
task->ptr = ptr;
}
void *
scsi_get_task_private_ptr(struct scsi_task *task)
{
return task->ptr;
}

351
lib/socket.c Normal file
View File

@@ -0,0 +1,351 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser 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 -1;
}
/* 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 -1;
}
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 -1;
}
if (iscsi->fd == -1) {
iscsi_set_error(iscsi, "Failed to open iscsi socket. "
"Errno:%s(%d).", strerror(errno), errno);
return -1;
}
iscsi->socket_status_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)", strerror(errno), errno);
close(iscsi->fd);
iscsi->fd = -1;
return -1;
}
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", errno);
return -1;
}
if (available == 0) {
iscsi_set_error(iscsi, "no data readable in socket, "
"socket is closed");
return -1;
}
size = iscsi->insize - iscsi->inpos + available;
buf = malloc(size);
if (buf == NULL) {
iscsi_set_error(iscsi, "failed to allocate %d bytes for "
"input buffer", size);
return -1;
}
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", errno);
free(buf);
buf = NULL;
return -1;
}
free(iscsi->inbuf);
iscsi->inbuf = buf;
iscsi->insize += count;
while (1) {
if (iscsi->insize - iscsi->inpos < ISCSI_RAW_HEADER_SIZE) {
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) {
iscsi->inpos += count;
return -1;
}
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!");
return -1;
}
}
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");
return -1;
}
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", errno);
return -1;
}
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->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL,
iscsi->connect_data);
return -1;
}
if (revents & POLLHUP) {
iscsi_set_error(iscsi, "iscsi_service: POLLHUP, "
"socket error.");
iscsi->socket_status_cb(iscsi, SCSI_STATUS_ERROR, NULL,
iscsi->connect_data);
return -1;
}
if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
iscsi->is_connected = 1;
iscsi->socket_status_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 -1;
}
}
if (revents & POLLIN) {
if (iscsi_read_from_socket(iscsi) != 0)
return -1;
}
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 -1;
}
if (iscsi->header_digest != ISCSI_HEADER_DIGEST_NONE) {
unsigned long crc;
if (pdu->outdata.size < ISCSI_RAW_HEADER_SIZE + 4) {
iscsi_set_error(iscsi, "PDU too small (%d) to contain header digest",
pdu->outdata.size);
return -1;
}
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;
}

282
lib/sync.c Normal file
View File

@@ -0,0 +1,282 @@
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 3 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 Lesser 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 <poll.h>
#include "iscsi.h"
#include "iscsi-private.h"
#include "scsi-lowlevel.h"
struct scsi_sync_state {
int finished;
struct scsi_task *task;
};
struct iscsi_sync_state {
int finished;
int status;
};
static void
event_loop(struct iscsi_context *iscsi, struct scsi_sync_state *state)
{
struct pollfd pfd;
while (state->finished == 0) {
pfd.fd = iscsi_get_fd(iscsi);
pfd.events = iscsi_which_events(iscsi);
if (poll(&pfd, 1, -1) < 0) {
iscsi_set_error(iscsi, "Poll failed");
return;
}
if (iscsi_service(iscsi, pfd.revents) < 0) {
iscsi_set_error(iscsi,
"iscsi_service failed with : %s",
iscsi_get_error(iscsi));
return;
}
}
}
/*
* Synchronous iSCSI commands
*/
static void
iscsi_sync_cb(struct iscsi_context *iscsi _U_, int status,
void *command_data _U_, void *private_data)
{
struct iscsi_sync_state *state = private_data;
state->status = status;
state->finished = 1;
}
int
iscsi_connect_sync(struct iscsi_context *iscsi, const char *portal)
{
struct iscsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_connect_async(iscsi, portal,
iscsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi,
"Failed to start connect() %s",
iscsi_get_error(iscsi));
return -1;
}
event_loop(iscsi, (struct scsi_sync_state *)&state);
return state.status;
}
int
iscsi_full_connect_sync(struct iscsi_context *iscsi,
const char *portal, int lun)
{
struct iscsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_full_connect_async(iscsi, portal, lun,
iscsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi,
"Failed to start full connect %s",
iscsi_get_error(iscsi));
return -1;
}
event_loop(iscsi, (struct scsi_sync_state *)&state);
return state.status;
}
int iscsi_login_sync(struct iscsi_context *iscsi)
{
struct iscsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_login_async(iscsi, iscsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi, "Failed to login. %s",
iscsi_get_error(iscsi));
return -1;
}
event_loop(iscsi, (struct scsi_sync_state *)&state);
return state.status;
}
int iscsi_logout_sync(struct iscsi_context *iscsi)
{
struct iscsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_logout_async(iscsi, iscsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi, "Failed to start logout() %s",
iscsi_get_error(iscsi));
return -1;
}
event_loop(iscsi, (struct scsi_sync_state *)&state);
return state.status;
}
/*
* Synchronous SCSI commands
*/
static void
scsi_sync_cb(struct iscsi_context *iscsi _U_, int status, void *command_data,
void *private_data)
{
struct scsi_task *task = command_data;
struct scsi_sync_state *state = private_data;
task->status = status;
state->finished = 1;
state->task = task;
iscsi_cbdata_steal_scsi_task(task);
}
struct scsi_task *
iscsi_reportluns_sync(struct iscsi_context *iscsi, int report_type,
int alloc_len)
{
struct scsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_reportluns_async(iscsi, report_type, alloc_len,
scsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi, "Failed to send ReportLuns command");
return NULL;
}
event_loop(iscsi, &state);
return state.task;
}
struct scsi_task *
iscsi_testunitready_sync(struct iscsi_context *iscsi, int lun)
{
struct scsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_testunitready_async(iscsi, lun,
scsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi,
"Failed to send TestUnitReady command");
return NULL;
}
event_loop(iscsi, &state);
return state.task;
}
struct scsi_task *
iscsi_inquiry_sync(struct iscsi_context *iscsi, int lun, int evpd,
int page_code, int maxsize)
{
struct scsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_inquiry_async(iscsi, lun, evpd, page_code, maxsize,
scsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi, "Failed to send Inquiry command");
return NULL;
}
event_loop(iscsi, &state);
return state.task;
}
struct scsi_task *
iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba,
int pmi)
{
struct scsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_readcapacity10_async(iscsi, lun, lba, pmi,
scsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi,
"Failed to send ReadCapacity10 command");
return NULL;
}
event_loop(iscsi, &state);
return state.task;
}
struct scsi_task *
iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba,
int num_blocks, int syncnv, int immed)
{
struct scsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_synchronizecache10_async(iscsi, lun, lba, num_blocks,
syncnv, immed,
scsi_sync_cb, &state) != 0) {
iscsi_set_error(iscsi,
"Failed to send SynchronizeCache10 command");
return NULL;
}
event_loop(iscsi, &state);
return state.task;
}
struct scsi_task *
iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun,
struct scsi_task *task, struct iscsi_data *data)
{
struct scsi_sync_state state;
bzero(&state, sizeof(state));
if (iscsi_scsi_command_async(iscsi, lun, task,
scsi_sync_cb, data, &state) != 0) {
iscsi_set_error(iscsi, "Failed to send SCSI command");
return NULL;
}
event_loop(iscsi, &state);
return state.task;
}