Initial import of libiscsi
This commit is contained in:
13
INSTALL
Normal file
13
INSTALL
Normal file
@@ -0,0 +1,13 @@
|
||||
This installs a new shared library under /usr/lib[64]
|
||||
and executables under /usr/bin
|
||||
|
||||
|
||||
From source:
|
||||
============
|
||||
$ make
|
||||
# make install
|
||||
|
||||
Building RPM:
|
||||
=============
|
||||
./packaging/RPM/makerpms.sh
|
||||
|
||||
56
Makefile
Normal file
56
Makefile
Normal file
@@ -0,0 +1,56 @@
|
||||
LIBS="-lpopt"
|
||||
CC=gcc
|
||||
CFLAGS=-g -O0 -fPIC -Wall -W -I./include "-D_U_=__attribute__((unused))"
|
||||
LIBISCSI_OBJ = lib/connect.o lib/crc32c.o lib/discovery.o lib/init.o lib/login.o lib/nop.o lib/pdu.o lib/scsi-command.o lib/scsi-lowlevel.o lib/socket.o lib/sync.o
|
||||
INSTALLCMD = /usr/bin/install -c
|
||||
|
||||
LIBISCSI_SO_NAME=libiscsi.so.1
|
||||
VERSION=1.0.0
|
||||
LIBISCSI_SO=libiscsi.so.$(VERSION)
|
||||
|
||||
all: bin/iscsi-inq bin/iscsi-ls lib/$(LIBISCSI_SO)
|
||||
|
||||
bin/iscsi-ls: src/iscsi-ls.c lib/libiscsi.a
|
||||
mkdir -p bin
|
||||
$(CC) $(CFLAGS) -o $@ src/iscsi-ls.c lib/libiscsi.a $(LIBS)
|
||||
|
||||
bin/iscsi-inq: src/iscsi-inq.c lib/libiscsi.a
|
||||
mkdir -p bin
|
||||
$(CC) $(CFLAGS) -o $@ src/iscsi-inq.c lib/libiscsi.a $(LIBS)
|
||||
|
||||
lib/$(LIBISCSI_SO): $(LIBISCSI_OBJ)
|
||||
@echo Creating shared library $@
|
||||
$(CC) -shared -Wl,-soname=$(LIBISCSI_SO_NAME) -o $@ $(LIBISCSI_OBJ)
|
||||
|
||||
lib/libiscsi.a: $(LIBISCSI_OBJ)
|
||||
@echo Creating library $@
|
||||
ar r lib/libiscsi.a $(LIBISCSI_OBJ)
|
||||
ranlib lib/libiscsi.a
|
||||
|
||||
examples: bin/iscsiclient
|
||||
|
||||
bin/iscsiclient: examples/iscsiclient.c lib/libiscsi.a
|
||||
mkdir -p bin
|
||||
$(CC) $(CFLAGS) -o $@ examples/iscsiclient.c lib/libiscsi.a $(LIBS)
|
||||
|
||||
install: lib/libiscsi.a lib/$(LIBISCSI_SO) bin/iscsi-ls bin/iscsi-inq
|
||||
ifeq ("$(LIBDIR)x","x")
|
||||
$(INSTALLCMD) -m 755 lib/$(LIBISCSI_SO) /usr/lib
|
||||
$(INSTALLCMD) -m 755 lib/libiscsi.a /usr/lib
|
||||
ldconfig
|
||||
else
|
||||
$(INSTALLCMD) -m 755 lib/$(LIBISCSI_SO) $(LIBDIR)
|
||||
$(INSTALLCMD) -m 755 lib/libiscsi.a $(LIBDIR)
|
||||
endif
|
||||
$(INSTALLCMD) -m 755 bin/iscsi-ls $(DESTDIR)/usr/bin
|
||||
$(INSTALLCMD) -m 755 bin/iscsi-inq $(DESTDIR)/usr/bin
|
||||
mkdir -p $(DESTDIR)/usr/include/iscsi
|
||||
$(INSTALLCMD) -m 644 include/iscsi.h $(DESTDIR)/usr/include/iscsi
|
||||
$(INSTALLCMD) -m 644 include/scsi-lowlevel.h $(DESTDIR)/usr/include/iscsi
|
||||
|
||||
clean:
|
||||
rm -f lib/*.o src/*.o examples/*.o
|
||||
rm -f bin/*
|
||||
rm -f lib/libiscsi.so*
|
||||
rm -f lib/libiscsi.a
|
||||
rm -f iscsi-inq iscsi-ls
|
||||
12
README
Normal file
12
README
Normal file
@@ -0,0 +1,12 @@
|
||||
Libiscsi is a work in progress.
|
||||
It aims to become a full async library for iscsi functionality,
|
||||
including all features required to establish and maintain a iscsi
|
||||
session, as well as a low level scsi library to create scsi cdb's
|
||||
and parse/unmarshall data-in structures.
|
||||
|
||||
|
||||
Installation
|
||||
============
|
||||
make
|
||||
make install
|
||||
|
||||
459
examples/iscsiclient.c
Normal file
459
examples/iscsiclient.c
Normal file
@@ -0,0 +1,459 @@
|
||||
/* This is an example of using libiscsi.
|
||||
* It basically logs in to the the target and performs a discovery.
|
||||
* It then selects the last target in the returned list and
|
||||
* starts a normal login to that target.
|
||||
* Once logged in it issues a REPORTLUNS call and selects the last returned lun in the list.
|
||||
* This LUN is then used to send INQUIRY, READCAPACITY10 and READ10 test calls to.
|
||||
*/
|
||||
/* The reason why we have to specify an allocation length and sometimes probe, starting with a small value, probing how big the buffer
|
||||
* should be, and asking again with a bigger buffer.
|
||||
* Why not just always ask with a buffer that is big enough?
|
||||
* The reason is that a lot of scsi targets are "sensitive" and ""buggy""
|
||||
* many targets will just fail the operation completely if they thing alloc len is unreasonably big.
|
||||
*/
|
||||
|
||||
/* This is the host/port we connect to.*/
|
||||
#define TARGET "127.0.0.1:3260"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include "iscsi.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
struct client_state {
|
||||
int finished;
|
||||
char *message;
|
||||
int has_discovered_target;
|
||||
char *target_name;
|
||||
char *target_address;
|
||||
int lun;
|
||||
int block_size;
|
||||
};
|
||||
|
||||
void synccache10_cb(struct iscsi_context *iscsi _U_, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
struct client_state *clnt = (struct client_state *)private_data;
|
||||
|
||||
printf("SYNCCACHE10 status:%d\n", status);
|
||||
clnt->finished = 1;
|
||||
}
|
||||
|
||||
void nop_out_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct iscsi_data *data = command_data;
|
||||
|
||||
printf("NOP-IN status:%d\n", status);
|
||||
if (data->size > 0) {
|
||||
printf("NOP-IN data:%s\n", data->data);
|
||||
}
|
||||
printf("Send SYNCHRONIZECACHE10\n");
|
||||
if (iscsi_synchronizecache10_async(iscsi, 2, 0, 0, 0, 0, synccache10_cb, private_data) != 0) {
|
||||
printf("failed to send sync cache10\n");
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void write10_cb(struct iscsi_context *iscsi _U_, int status, void *command_data, void *private_data _U_)
|
||||
{
|
||||
struct scsi_task *task = command_data;
|
||||
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
|
||||
printf("Write10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("Write successful\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
|
||||
void read10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct scsi_task *task = command_data;
|
||||
int i;
|
||||
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
printf("Read10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("READ10 successful. Block content:\n");
|
||||
for (i=0;i<task->datain.size;i++) {
|
||||
printf("%02x ", task->datain.data[i]);
|
||||
if (i%16==15)
|
||||
printf("\n");
|
||||
if (i==69)
|
||||
break;
|
||||
}
|
||||
printf("...\n");
|
||||
|
||||
printf("Finished, wont try to write data since that will likely destroy your LUN :-(\n");
|
||||
printf("Send NOP-OUT\n");
|
||||
if (iscsi_nop_out_async(iscsi, nop_out_cb, (unsigned char *)"Ping!", 6, private_data) != 0) {
|
||||
printf("failed to send nop-out\n");
|
||||
exit(10);
|
||||
}
|
||||
// printf("write the block back\n");
|
||||
// if (iscsi_write10_async(iscsi, clnt->lun, task->data.datain, task->datain.size, 0, 0, 0, clnt->block_size, write10_cb, private_data) != 0) {
|
||||
// printf("failed to send write10 command\n");
|
||||
// exit(10);
|
||||
// }
|
||||
}
|
||||
|
||||
void readcapacity10_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct client_state *clnt = (struct client_state *)private_data;
|
||||
struct scsi_task *task = command_data;
|
||||
struct scsi_readcapacity10 *rc10;
|
||||
int full_size;
|
||||
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
printf("Readcapacity10 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
full_size = scsi_datain_getfullsize(task);
|
||||
if (full_size < task->datain.size) {
|
||||
printf("not enough data for full size readcapacity10\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
rc10 = scsi_datain_unmarshall(task);
|
||||
if (rc10 == NULL) {
|
||||
printf("failed to unmarshall readcapacity10 data\n");
|
||||
exit(10);
|
||||
}
|
||||
clnt->block_size = rc10->block_size;
|
||||
printf("READCAPACITY10 successful. Size:%d blocks blocksize:%d. Read first block\n", rc10->lba, rc10->block_size);
|
||||
|
||||
if (iscsi_read10_async(iscsi, clnt->lun, 0, clnt->block_size, clnt->block_size, read10_cb, private_data) != 0) {
|
||||
printf("failed to send read10 command\n");
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
void modesense6_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct client_state *clnt = (struct client_state *)private_data;
|
||||
struct scsi_task *task = command_data;
|
||||
int full_size;
|
||||
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
printf("Modesense6 failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
|
||||
} else {
|
||||
full_size = scsi_datain_getfullsize(task);
|
||||
if (full_size > task->datain.size) {
|
||||
printf("did not get enough data for mode sense, sening modesense again asking for bigger buffer\n");
|
||||
if (iscsi_modesense6_async(iscsi, clnt->lun, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES, 0, full_size, modesense6_cb, private_data) != 0) {
|
||||
printf("failed to send modesense6 command\n");
|
||||
exit(10);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printf("MODESENSE6 successful.\n");
|
||||
}
|
||||
|
||||
printf("Send READCAPACITY10\n");
|
||||
if (iscsi_readcapacity10_async(iscsi, clnt->lun, 0, 0, readcapacity10_cb, private_data) != 0) {
|
||||
printf("failed to send readcapacity command\n");
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
void inquiry_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct client_state *clnt = (struct client_state *)private_data;
|
||||
struct scsi_task *task = command_data;
|
||||
struct scsi_inquiry_standard *inq;
|
||||
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
printf("Inquiry failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("INQUIRY successful for standard data.\n");
|
||||
inq = scsi_datain_unmarshall(task);
|
||||
if (inq == NULL) {
|
||||
printf("failed to unmarshall inquiry datain blob\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("Device Type is %d. VendorId:%s ProductId:%s\n", inq->periperal_device_type, inq->vendor_identification, inq->product_identification);
|
||||
printf("Send MODESENSE6\n");
|
||||
if (iscsi_modesense6_async(iscsi, clnt->lun, 0, SCSI_MODESENSE_PC_CURRENT, SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES, 0, 4, modesense6_cb, private_data) != 0) {
|
||||
printf("failed to send modesense6 command\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void testunitready_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct client_state *clnt = (struct client_state *)private_data;
|
||||
struct scsi_task *task = command_data;
|
||||
|
||||
if (status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
printf("First testunitready failed with sense key:%d ascq:%04x\n", task->sense.key, task->sense.ascq);
|
||||
if (task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) {
|
||||
printf("target device just came online, try again\n");
|
||||
|
||||
if (iscsi_testunitready_async(iscsi, clnt->lun, testunitready_cb, private_data) != 0) {
|
||||
printf("failed to send testunitready command\n");
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
printf("TESTUNITREADY successful, do an inquiry on lun:%d\n", clnt->lun);
|
||||
if (iscsi_inquiry_async(iscsi, clnt->lun, 0, 0, 64, inquiry_cb, private_data) != 0) {
|
||||
printf("failed to send inquiry command : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void reportluns_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct client_state *clnt = (struct client_state *)private_data;
|
||||
struct scsi_task *task = command_data;
|
||||
struct scsi_reportluns_list *list;
|
||||
int full_report_size;
|
||||
int i;
|
||||
|
||||
if (status != SCSI_STATUS_GOOD) {
|
||||
printf("Reportluns failed with : %s\n", iscsi_get_error(iscsi));
|
||||
return;
|
||||
}
|
||||
|
||||
full_report_size = scsi_datain_getfullsize(task);
|
||||
|
||||
printf("REPORTLUNS status:%d data size:%d, full reports luns data size:%d\n", status, task->datain.size, full_report_size);
|
||||
if (full_report_size > task->datain.size) {
|
||||
printf("We did not get all the data we need in reportluns, ask again\n");
|
||||
if (iscsi_reportluns_async(iscsi, 0, full_report_size, reportluns_cb, private_data) != 0) {
|
||||
printf("failed to send reportluns command\n");
|
||||
exit(10);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
list = scsi_datain_unmarshall(task);
|
||||
if (list == NULL) {
|
||||
printf("failed to unmarshall reportluns datain blob\n");
|
||||
exit(10);
|
||||
}
|
||||
for (i=0; i < (int)list->num; i++) {
|
||||
printf("LUN:%d found\n", list->luns[i]);
|
||||
clnt->lun = list->luns[i];
|
||||
}
|
||||
|
||||
printf("Will use LUN:%d\n", clnt->lun);
|
||||
printf("Send testunitready to lun %d\n", clnt->lun);
|
||||
if (iscsi_testunitready_async(iscsi, clnt->lun, testunitready_cb, private_data) != 0) {
|
||||
printf("failed to send testunitready command : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void normallogin_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
if (status != 0) {
|
||||
printf("Failed to log in to target : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("Logged in normal session, send reportluns\n");
|
||||
if (iscsi_reportluns_async(iscsi, 0, 16, reportluns_cb, private_data) != 0) {
|
||||
printf("failed to send reportluns command : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void normalconnect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
printf("Connected to iscsi socket\n");
|
||||
|
||||
if (status != 0) {
|
||||
printf("normalconnect_cb: connection failed status:%d\n", status);
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("connected, send login command\n");
|
||||
iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_CRC32C_NONE);
|
||||
if (iscsi_login_async(iscsi, normallogin_cb, private_data) != 0) {
|
||||
printf("iscsi_login_async failed\n");
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void discoverylogout_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
struct client_state *clnt = (struct client_state *)private_data;
|
||||
|
||||
printf("discovery session logged out, Message from main() was:[%s]\n", clnt->message);
|
||||
|
||||
if (status != 0) {
|
||||
printf("Failed to logout from target. : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("disconnect socket\n");
|
||||
if (iscsi_disconnect(iscsi) != 0) {
|
||||
printf("Failed to disconnect old socket\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("reconnect with normal login to [%s]\n", clnt->target_address);
|
||||
printf("Use targetname [%s] when connecting\n", clnt->target_name);
|
||||
if (iscsi_set_targetname(iscsi, clnt->target_name)) {
|
||||
printf("Failed to set target name\n");
|
||||
exit(10);
|
||||
}
|
||||
if (iscsi_set_alias(iscsi, "ronnie") != 0) {
|
||||
printf("Failed to add alias\n");
|
||||
exit(10);
|
||||
}
|
||||
if (iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL) != 0) {
|
||||
printf("Failed to set settion type to normal\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (iscsi_connect_async(iscsi, clnt->target_address, normalconnect_cb, clnt) != 0) {
|
||||
printf("iscsi_connect failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct client_state *clnt = (struct client_state *)private_data;
|
||||
struct iscsi_discovery_address *addr;
|
||||
|
||||
printf("discovery callback status:%04x\n", status);
|
||||
|
||||
if (status != 0) {
|
||||
printf("Failed to do discovery on target. : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
for(addr=command_data; addr; addr=addr->next) {
|
||||
printf("Target:%s Address:%s\n", addr->target_name, addr->target_address);
|
||||
}
|
||||
|
||||
addr=command_data;
|
||||
clnt->has_discovered_target = 1;
|
||||
clnt->target_name = strdup(addr->target_name);
|
||||
clnt->target_address = strdup(addr->target_address);
|
||||
|
||||
|
||||
printf("discovery complete, send logout command\n");
|
||||
|
||||
if (iscsi_logout_async(iscsi, discoverylogout_cb, private_data) != 0) {
|
||||
printf("iscsi_logout_async failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void discoverylogin_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
if (status != 0) {
|
||||
printf("Failed to log in to target. : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("Logged in to target, send discovery command\n");
|
||||
if (iscsi_discovery_async(iscsi, discovery_cb, private_data) != 0) {
|
||||
printf("failed to send discovery command : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void discoveryconnect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
printf("Connected to iscsi socket status:0x%08x\n", status);
|
||||
|
||||
if (status != 0) {
|
||||
printf("discoveryconnect_cb: connection failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
printf("connected, send login command\n");
|
||||
iscsi_set_session_type(iscsi, ISCSI_SESSION_DISCOVERY);
|
||||
if (iscsi_login_async(iscsi, discoverylogin_cb, private_data) != 0) {
|
||||
printf("iscsi_login_async failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc _U_, char *argv[] _U_)
|
||||
{
|
||||
struct iscsi_context *iscsi;
|
||||
struct pollfd pfd;
|
||||
struct client_state clnt;
|
||||
|
||||
printf("iscsi client\n");
|
||||
|
||||
bzero(&clnt, sizeof(clnt));
|
||||
|
||||
iscsi = iscsi_create_context("iqn.2002-10.com.ronnie:client");
|
||||
if (iscsi == NULL) {
|
||||
printf("Failed to create context\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (iscsi_set_alias(iscsi, "ronnie") != 0) {
|
||||
printf("Failed to add alias\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
clnt.message = "Hello iSCSI";
|
||||
clnt.has_discovered_target = 0;
|
||||
if (iscsi_connect_async(iscsi, TARGET, discoveryconnect_cb, &clnt) != 0) {
|
||||
printf("iscsi_connect failed. %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
while (clnt.finished == 0) {
|
||||
pfd.fd = iscsi_get_fd(iscsi);
|
||||
pfd.events = iscsi_which_events(iscsi);
|
||||
|
||||
if (poll(&pfd, 1, -1) < 0) {
|
||||
printf("Poll failed");
|
||||
exit(10);
|
||||
}
|
||||
if (iscsi_service(iscsi, pfd.revents) < 0) {
|
||||
printf("iscsi_service failed with : %s\n", iscsi_get_error(iscsi));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
iscsi_destroy_context(iscsi);
|
||||
|
||||
if (clnt.target_name != NULL) {
|
||||
free(clnt.target_name);
|
||||
}
|
||||
if (clnt.target_address != NULL) {
|
||||
free(clnt.target_address);
|
||||
}
|
||||
|
||||
printf("ok\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
171
include/iscsi-private.h
Normal file
171
include/iscsi-private.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
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 <stdint.h>
|
||||
|
||||
#ifndef discard_const
|
||||
#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
|
||||
#endif
|
||||
|
||||
struct iscsi_context {
|
||||
const char *initiator_name;
|
||||
const char *target_name;
|
||||
const char *alias;
|
||||
enum iscsi_session_type session_type;
|
||||
unsigned char isid[6];
|
||||
uint32_t itt;
|
||||
uint32_t cmdsn;
|
||||
uint32_t statsn;
|
||||
enum iscsi_header_digest want_header_digest;
|
||||
enum iscsi_header_digest header_digest;
|
||||
|
||||
char *error_string;
|
||||
|
||||
int fd;
|
||||
int is_connected;
|
||||
int is_loggedin;
|
||||
|
||||
iscsi_command_cb socket_status_cb;
|
||||
void *connect_data;
|
||||
|
||||
struct iscsi_pdu *outqueue;
|
||||
struct iscsi_pdu *waitpdu;
|
||||
|
||||
int insize;
|
||||
int inpos;
|
||||
unsigned char *inbuf;
|
||||
};
|
||||
|
||||
#define ISCSI_RAW_HEADER_SIZE 48
|
||||
|
||||
#define ISCSI_HEADER_SIZE (ISCSI_RAW_HEADER_SIZE \
|
||||
+ (iscsi->header_digest == ISCSI_HEADER_DIGEST_NONE?0:4))
|
||||
|
||||
#define ISCSI_PDU_IMMEDIATE 0x40
|
||||
|
||||
#define ISCSI_PDU_TEXT_FINAL 0x80
|
||||
#define ISCSI_PDU_TEXT_CONTINUE 0x40
|
||||
|
||||
#define ISCSI_PDU_LOGIN_TRANSIT 0x80
|
||||
#define ISCSI_PDU_LOGIN_CONTINUE 0x40
|
||||
#define ISCSI_PDU_LOGIN_CSG_SECNEG 0x00
|
||||
#define ISCSI_PDU_LOGIN_CSG_OPNEG 0x04
|
||||
#define ISCSI_PDU_LOGIN_CSG_FF 0x0c
|
||||
#define ISCSI_PDU_LOGIN_NSG_SECNEG 0x00
|
||||
#define ISCSI_PDU_LOGIN_NSG_OPNEG 0x01
|
||||
#define ISCSI_PDU_LOGIN_NSG_FF 0x03
|
||||
|
||||
#define ISCSI_PDU_SCSI_FINAL 0x80
|
||||
#define ISCSI_PDU_SCSI_READ 0x40
|
||||
#define ISCSI_PDU_SCSI_WRITE 0x20
|
||||
#define ISCSI_PDU_SCSI_ATTR_UNTAGGED 0x00
|
||||
#define ISCSI_PDU_SCSI_ATTR_SIMPLE 0x01
|
||||
#define ISCSI_PDU_SCSI_ATTR_ORDERED 0x02
|
||||
#define ISCSI_PDU_SCSI_ATTR_HEADOFQUEUE 0x03
|
||||
#define ISCSI_PDU_SCSI_ATTR_ACA 0x04
|
||||
|
||||
#define ISCSI_PDU_DATA_FINAL 0x80
|
||||
#define ISCSI_PDU_DATA_ACK_REQUESTED 0x40
|
||||
#define ISCSI_PDU_DATA_BIDIR_OVERFLOW 0x10
|
||||
#define ISCSI_PDU_DATA_BIDIR_UNDERFLOW 0x08
|
||||
#define ISCSI_PDU_DATA_RESIDUAL_OVERFLOW 0x04
|
||||
#define ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW 0x02
|
||||
#define ISCSI_PDU_DATA_CONTAINS_STATUS 0x01
|
||||
|
||||
enum iscsi_opcode {
|
||||
ISCSI_PDU_NOP_OUT = 0x00,
|
||||
ISCSI_PDU_SCSI_REQUEST = 0x01,
|
||||
ISCSI_PDU_LOGIN_REQUEST = 0x03,
|
||||
ISCSI_PDU_TEXT_REQUEST = 0x04,
|
||||
ISCSI_PDU_LOGOUT_REQUEST = 0x06,
|
||||
ISCSI_PDU_NOP_IN = 0x20,
|
||||
ISCSI_PDU_SCSI_RESPONSE = 0x21,
|
||||
ISCSI_PDU_LOGIN_RESPONSE = 0x23,
|
||||
ISCSI_PDU_TEXT_RESPONSE = 0x24,
|
||||
ISCSI_PDU_DATA_IN = 0x25,
|
||||
ISCSI_PDU_LOGOUT_RESPONSE = 0x26
|
||||
};
|
||||
|
||||
struct iscsi_pdu {
|
||||
struct iscsi_pdu *next;
|
||||
|
||||
uint32_t itt;
|
||||
uint32_t cmdsn;
|
||||
enum iscsi_opcode response_opcode;
|
||||
|
||||
iscsi_command_cb callback;
|
||||
void *private_data;
|
||||
|
||||
int written;
|
||||
struct iscsi_data outdata;
|
||||
struct iscsi_data indata;
|
||||
|
||||
struct iscsi_scsi_cbdata *scsi_cbdata;
|
||||
};
|
||||
|
||||
void iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata);
|
||||
|
||||
struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi,
|
||||
enum iscsi_opcode opcode,
|
||||
enum iscsi_opcode response_opcode);
|
||||
void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
|
||||
void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags);
|
||||
void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu);
|
||||
void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt);
|
||||
void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn);
|
||||
void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun);
|
||||
void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn);
|
||||
void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen);
|
||||
int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu,
|
||||
unsigned char *dptr, int dsize);
|
||||
int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
|
||||
int iscsi_add_data(struct iscsi_context *iscsi, struct iscsi_data *data,
|
||||
unsigned char *dptr, int dsize, int pdualignment);
|
||||
|
||||
struct scsi_task;
|
||||
void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task);
|
||||
|
||||
int iscsi_get_pdu_size(struct iscsi_context *iscsi, const unsigned char *hdr);
|
||||
int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr,
|
||||
int size);
|
||||
|
||||
int iscsi_process_login_reply(struct iscsi_context *iscsi,
|
||||
struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size);
|
||||
int iscsi_process_text_reply(struct iscsi_context *iscsi,
|
||||
struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size);
|
||||
int iscsi_process_logout_reply(struct iscsi_context *iscsi,
|
||||
struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size);
|
||||
int iscsi_process_scsi_reply(struct iscsi_context *iscsi,
|
||||
struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size);
|
||||
int iscsi_process_scsi_data_in(struct iscsi_context *iscsi,
|
||||
struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size,
|
||||
int *is_finished);
|
||||
int iscsi_process_nop_out_reply(struct iscsi_context *iscsi,
|
||||
struct iscsi_pdu *pdu,
|
||||
const unsigned char *hdr, int size);
|
||||
|
||||
void iscsi_set_error(struct iscsi_context *iscsi, const char *error_string,
|
||||
...);
|
||||
|
||||
unsigned long crc32c(char *buf, int len);
|
||||
|
||||
void iscsi_cbdata_steal_scsi_task(struct scsi_task *task);
|
||||
436
include/iscsi.h
Normal file
436
include/iscsi.h
Normal file
@@ -0,0 +1,436 @@
|
||||
/*
|
||||
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/>.
|
||||
*/
|
||||
|
||||
struct iscsi_context;
|
||||
struct sockaddr;
|
||||
|
||||
|
||||
const char *iscsi_get_error(struct iscsi_context *iscsi);
|
||||
|
||||
/*
|
||||
* Returns the file descriptor that libiscsi uses.
|
||||
*/
|
||||
int iscsi_get_fd(struct iscsi_context *iscsi);
|
||||
|
||||
/*
|
||||
* Returns which events that we need to poll for for the iscsi file descriptor.
|
||||
*/
|
||||
int iscsi_which_events(struct iscsi_context *iscsi);
|
||||
|
||||
/*
|
||||
* Called to process the events when events become available for the iscsi
|
||||
* file descriptor.
|
||||
*/
|
||||
int iscsi_service(struct iscsi_context *iscsi, int revents);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Create a context for an ISCSI session.
|
||||
* Initiator_name is the iqn name we want to identify to the target as.
|
||||
*
|
||||
* Returns:
|
||||
* 0: success
|
||||
* <0: error
|
||||
*/
|
||||
struct iscsi_context *iscsi_create_context(const char *initiator_name);
|
||||
|
||||
/*
|
||||
* Destroy an existing ISCSI context and tear down any existing connection.
|
||||
* Callbacks for any command in flight will be invoked with
|
||||
* ISCSI_STATUS_CANCELLED.
|
||||
*
|
||||
* Returns:
|
||||
* 0: success
|
||||
* <0: error
|
||||
*/
|
||||
int iscsi_destroy_context(struct iscsi_context *iscsi);
|
||||
|
||||
/*
|
||||
* Set an optional alias name to identify with when connecting to the target
|
||||
*
|
||||
* Returns:
|
||||
* 0: success
|
||||
* <0: error
|
||||
*/
|
||||
int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias);
|
||||
|
||||
/*
|
||||
* Set the iqn name of the taqget to login to.
|
||||
* The target name must be set before a normal-login can be initiated.
|
||||
* Only discovery-logins are possible without setting the target iqn name.
|
||||
*
|
||||
* Returns:
|
||||
* 0: success
|
||||
* <0: error
|
||||
*/
|
||||
int iscsi_set_targetname(struct iscsi_context *iscsi, const char *targetname);
|
||||
|
||||
|
||||
/* Types of icsi sessions. Discovery sessions are used to query for what
|
||||
* targets exist behin the portal connected to. Normal sessions are used to
|
||||
* log in and do I/O to the SCSI LUNs
|
||||
*/
|
||||
enum iscsi_session_type {
|
||||
ISCSI_SESSION_DISCOVERY = 1,
|
||||
ISCSI_SESSION_NORMAL = 2
|
||||
};
|
||||
|
||||
/*
|
||||
* Set the session type for a scsi context.
|
||||
* Session type can only be set/changed while the iscsi context is not
|
||||
* logged in to a target.
|
||||
*
|
||||
* Returns:
|
||||
* 0: success
|
||||
* <0: error
|
||||
*/
|
||||
int iscsi_set_session_type(struct iscsi_context *iscsi,
|
||||
enum iscsi_session_type session_type);
|
||||
|
||||
|
||||
/*
|
||||
* Types of header digest we support. Default is NONE
|
||||
*/
|
||||
enum iscsi_header_digest {
|
||||
ISCSI_HEADER_DIGEST_NONE = 0,
|
||||
ISCSI_HEADER_DIGEST_NONE_CRC32C = 1,
|
||||
ISCSI_HEADER_DIGEST_CRC32C_NONE = 2,
|
||||
ISCSI_HEADER_DIGEST_CRC32C = 3
|
||||
};
|
||||
|
||||
/*
|
||||
* Set the desired header digest for a scsi context.
|
||||
* Header digest can only be set/changed while the iscsi context is not
|
||||
* logged in to a target.
|
||||
*
|
||||
* Returns:
|
||||
* 0: success
|
||||
* <0: error
|
||||
*/
|
||||
int iscsi_set_header_digest(struct iscsi_context *iscsi,
|
||||
enum iscsi_header_digest header_digest);
|
||||
|
||||
|
||||
/*
|
||||
* check if the context is logged in or not
|
||||
*/
|
||||
int iscsi_is_logged_in(struct iscsi_context *iscsi);
|
||||
|
||||
|
||||
enum scsi_status {
|
||||
SCSI_STATUS_GOOD = 0,
|
||||
SCSI_STATUS_CHECK_CONDITION = 2,
|
||||
SCSI_STATUS_CANCELLED = 0x0f000000,
|
||||
SCSI_STATUS_ERROR = 0x0f000001
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Generic callback for completion of iscsi_*_async().
|
||||
* command_data depends on status.
|
||||
*/
|
||||
typedef void (*iscsi_command_cb)(struct iscsi_context *iscsi, int status,
|
||||
void *command_data, void *private_data);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Asynchronous call to connect a TCP connection to the target-host/port
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the call was initiated and a connection will be attempted. Result of
|
||||
* the connection will be reported through the callback function.
|
||||
* <0 if there was an error. The callback function will not be invoked.
|
||||
*
|
||||
* This command is unique in that the callback can be invoked twice.
|
||||
*
|
||||
* Callback parameters :
|
||||
* status can be either of :
|
||||
* ISCSI_STATUS_GOOD : Connection was successful. Command_data is NULL.
|
||||
* In this case the callback will be invoked a
|
||||
* second time once the connection is torn down.
|
||||
*
|
||||
* ISCSI_STATUS_ERROR : Either failed to establish the connection, or
|
||||
* an already established connection has failed
|
||||
* with an error.
|
||||
*
|
||||
* The callback will NOT be invoked if the session is explicitely torn down
|
||||
* through a call to iscsi_disconnect() or iscsi_destroy_context().
|
||||
*/
|
||||
int iscsi_connect_async(struct iscsi_context *iscsi, const char *portal,
|
||||
iscsi_command_cb cb, void *private_data);
|
||||
|
||||
/*
|
||||
* Synchronous call to connect a TCP connection to the target-host/port
|
||||
*
|
||||
* Returns:
|
||||
* 0 if connected successfully.
|
||||
* <0 if there was an error.
|
||||
*
|
||||
*/
|
||||
int iscsi_connect_sync(struct iscsi_context *iscsi, const char *portal);
|
||||
|
||||
|
||||
/*
|
||||
* Asynchronous call to connect a lun
|
||||
* This function will connect to the portal, login, and verify that the lun
|
||||
* is available.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the call was initiated and a connection will be attempted. Result
|
||||
* of the connection will be reported through the callback function.
|
||||
* <0 if there was an error. The callback function will not be invoked.
|
||||
*
|
||||
* This command is unique in that the callback can be invoked twice.
|
||||
*
|
||||
* Callback parameters :
|
||||
* status can be either of :
|
||||
* ISCSI_STATUS_GOOD : Connection was successful. Command_data is NULL.
|
||||
* In this case the callback will be invoked a
|
||||
* second time once the connection is torn down.
|
||||
*
|
||||
* ISCSI_STATUS_ERROR : Either failed to establish the connection, or
|
||||
* an already established connection has failed
|
||||
* with an error.
|
||||
*
|
||||
* The callback will NOT be invoked if the session is explicitely torn down
|
||||
* through a call to iscsi_disconnect() or iscsi_destroy_context().
|
||||
*/
|
||||
int iscsi_full_connect_async(struct iscsi_context *iscsi, const char *portal,
|
||||
int lun, iscsi_command_cb cb, void *private_data);
|
||||
|
||||
/*
|
||||
* Synchronous call to connect a lun
|
||||
* This function will connect to the portal, login, and verify that the lun
|
||||
* is available.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the cconnect was successful.
|
||||
* <0 if there was an error.
|
||||
*/
|
||||
int iscsi_full_connect_sync(struct iscsi_context *iscsi, const char *portal,
|
||||
int lun);
|
||||
|
||||
/*
|
||||
* Disconnect a connection to a target.
|
||||
* You can not disconnect while being logged in to a target.
|
||||
*
|
||||
* Returns:
|
||||
* 0 disconnect was successful
|
||||
* <0 error
|
||||
*/
|
||||
int iscsi_disconnect(struct iscsi_context *iscsi);
|
||||
|
||||
/*
|
||||
* Asynchronous call to perform an ISCSI login.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the call was initiated and a login will be attempted. Result of the
|
||||
* login will be reported through the callback function.
|
||||
* <0 if there was an error. The callback function will not be invoked.
|
||||
*
|
||||
* Callback parameters :
|
||||
* status can be either of :
|
||||
* ISCSI_STATUS_GOOD : login was successful. Command_data is always
|
||||
* NULL.
|
||||
* ISCSI_STATUS_CANCELLED: login was aborted. Command_data is NULL.
|
||||
* ISCSI_STATUS_ERROR : login failed. Command_data is NULL.
|
||||
*/
|
||||
int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
||||
void *private_data);
|
||||
|
||||
/*
|
||||
* Synchronous call to perform an ISCSI login.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the login was successful
|
||||
* <0 if there was an error.
|
||||
*/
|
||||
int iscsi_login_sync(struct iscsi_context *iscsi);
|
||||
|
||||
|
||||
/*
|
||||
* Asynchronous call to perform an ISCSI logout.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the call was initiated and a logout will be attempted. Result of the
|
||||
* logout will be reported through the callback function.
|
||||
* <0 if there was an error. The callback function will not be invoked.
|
||||
*
|
||||
* Callback parameters :
|
||||
* status can be either of :
|
||||
* ISCSI_STATUS_GOOD : logout was successful. Command_data is always
|
||||
* NULL.
|
||||
* ISCSI_STATUS_CANCELLED: logout was aborted. Command_data is NULL.
|
||||
*/
|
||||
int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
||||
void *private_data);
|
||||
|
||||
/*
|
||||
* Synchronous call to perform an ISCSI logout.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the logout was successful
|
||||
* <0 if there was an error.
|
||||
*/
|
||||
int iscsi_logout_sync(struct iscsi_context *iscsi);
|
||||
|
||||
|
||||
/*
|
||||
* Asynchronous call to perform an ISCSI discovery.
|
||||
*
|
||||
* discoveries can only be done on connected and logged in discovery sessions.
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the call was initiated and a discovery will be attempted. Result
|
||||
* of the logout will be reported through the callback function.
|
||||
* <0 if there was an error. The callback function will not be invoked.
|
||||
*
|
||||
* Callback parameters :
|
||||
* status can be either of :
|
||||
* ISCSI_STATUS_GOOD : Discovery was successful. Command_data is a
|
||||
* pointer to a iscsi_discovery_address list of
|
||||
* structures.
|
||||
* This list of structures is only valid for the
|
||||
* duration of the callback and all data will be
|
||||
* freed once the callback returns.
|
||||
* ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
|
||||
*/
|
||||
int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
||||
void *private_data);
|
||||
|
||||
struct iscsi_discovery_address {
|
||||
struct iscsi_discovery_address *next;
|
||||
const char *target_name;
|
||||
const char *target_address;
|
||||
};
|
||||
|
||||
/*
|
||||
* Asynchronous call to perform an ISCSI NOP-OUT call
|
||||
*
|
||||
* Returns:
|
||||
* 0 if the call was initiated and a nop-out will be attempted. Result will
|
||||
* be reported through the callback function.
|
||||
* <0 if there was an error. The callback function will not be invoked.
|
||||
*
|
||||
* Callback parameters :
|
||||
* status can be either of :
|
||||
* ISCSI_STATUS_GOOD : NOP-OUT was successful and the server responded
|
||||
* with a NOP-IN callback_data is a iscsi_data
|
||||
* structure containing the data returned from
|
||||
* the server.
|
||||
* ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
|
||||
*/
|
||||
int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb,
|
||||
unsigned char *data, int len, void *private_data);
|
||||
|
||||
|
||||
/* These are the possible status values for the callbacks for scsi commands.
|
||||
* The content of command_data depends on the status type.
|
||||
*
|
||||
* status :
|
||||
* ISCSI_STATUS_GOOD the scsi command completed successfullt on the target.
|
||||
* If this scsi command returns DATA-IN, that data is stored in an scsi_task
|
||||
* structure returned in the command_data parameter. This buffer will be
|
||||
* automatically freed once the callback returns.
|
||||
*
|
||||
* ISCSI_STATUS_CHECK_CONDITION the scsi command failed with a scsi sense.
|
||||
* Command_data contains a struct scsi_task. When the callback returns,
|
||||
* this buffer will automatically become freed.
|
||||
*
|
||||
* ISCSI_STATUS_CANCELLED the scsi command was aborted. Command_data is
|
||||
* NULL.
|
||||
*
|
||||
* ISCSI_STATUS_ERROR the command failed. Command_data is NULL.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
struct iscsi_data {
|
||||
int size;
|
||||
unsigned char *data;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Async commands for SCSI
|
||||
*/
|
||||
struct scsi_task;
|
||||
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);
|
||||
|
||||
int iscsi_reportluns_async(struct iscsi_context *iscsi, int report_type,
|
||||
int alloc_len, iscsi_command_cb cb,
|
||||
void *private_data);
|
||||
int iscsi_testunitready_async(struct iscsi_context *iscsi, int lun,
|
||||
iscsi_command_cb cb, void *private_data);
|
||||
int iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, int evpd,
|
||||
int page_code, int maxsize, iscsi_command_cb cb,
|
||||
void *private_data);
|
||||
int iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int pmi, iscsi_command_cb cb,
|
||||
void *private_data);
|
||||
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);
|
||||
|
||||
int iscsi_read10_async(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int datalen, int blocksize, iscsi_command_cb cb,
|
||||
void *private_data);
|
||||
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);
|
||||
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);
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Sync commands for SCSI
|
||||
*/
|
||||
struct scsi_task *
|
||||
iscsi_scsi_command_sync(struct iscsi_context *iscsi, int lun,
|
||||
struct scsi_task *task, struct iscsi_data *data);
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_reportluns_sync(struct iscsi_context *iscsi, int report_type,
|
||||
int alloc_len);
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_testunitready_sync(struct iscsi_context *iscsi, int lun);
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_inquiry_sync(struct iscsi_context *iscsi, int lun, int evpd,
|
||||
int page_code, int maxsize);
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_readcapacity10_sync(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int pmi);
|
||||
|
||||
struct scsi_task *
|
||||
iscsi_synchronizecache10_sync(struct iscsi_context *iscsi, int lun, int lba,
|
||||
int num_blocks, int syncnv, int immed);
|
||||
|
||||
int
|
||||
iscsi_set_isid_random(struct iscsi_context *iscsi, int rnd);
|
||||
369
include/scsi-lowlevel.h
Normal file
369
include/scsi-lowlevel.h
Normal file
@@ -0,0 +1,369 @@
|
||||
/*
|
||||
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 SCSI_CDB_MAX_SIZE 16
|
||||
|
||||
enum scsi_opcode {
|
||||
SCSI_OPCODE_TESTUNITREADY = 0x00,
|
||||
SCSI_OPCODE_INQUIRY = 0x12,
|
||||
SCSI_OPCODE_MODESENSE6 = 0x1a,
|
||||
SCSI_OPCODE_READCAPACITY10 = 0x25,
|
||||
SCSI_OPCODE_READ10 = 0x28,
|
||||
SCSI_OPCODE_WRITE10 = 0x2A,
|
||||
SCSI_OPCODE_SYNCHRONIZECACHE10 = 0x35,
|
||||
SCSI_OPCODE_REPORTLUNS = 0xA0
|
||||
};
|
||||
|
||||
/* sense keys */
|
||||
enum scsi_sense_key {
|
||||
SCSI_SENSE_NO_SENSE = 0x00,
|
||||
SCSI_SENSE_RECOVERED_ERROR = 0x01,
|
||||
SCSI_SENSE_NOT_READY = 0x02,
|
||||
SCSI_SENSE_MEDIUM_ERROR = 0x03,
|
||||
SCSI_SENSE_HARDWARE_ERROR = 0x04,
|
||||
SCSI_SENSE_ILLEGAL_REQUEST = 0x05,
|
||||
SCSI_SENSE_UNIT_ATTENTION = 0x06,
|
||||
SCSI_SENSE_DATA_PROTECTION = 0x07,
|
||||
SCSI_SENSE_BLANK_CHECK = 0x08,
|
||||
SCSI_SENSE_VENDOR_SPECIFIC = 0x09,
|
||||
SCSI_SENSE_COPY_ABORTED = 0x0a,
|
||||
SCSI_SENSE_COMMAND_ABORTED = 0x0b,
|
||||
SCSI_SENSE_OBSOLETE_ERROR_CODE = 0x0c,
|
||||
SCSI_SENSE_OVERFLOW_COMMAND = 0x0d,
|
||||
SCSI_SENSE_MISCOMPARE = 0x0e
|
||||
};
|
||||
|
||||
const char *scsi_sense_key_str(int key);
|
||||
|
||||
/* ascq */
|
||||
#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB 0x2400
|
||||
#define SCSI_SENSE_ASCQ_LOGICAL_UNIT_NOT_SUPPORTED 0x2500
|
||||
#define SCSI_SENSE_ASCQ_BUS_RESET 0x2900
|
||||
|
||||
const char *scsi_sense_ascq_str(int ascq);
|
||||
|
||||
|
||||
enum scsi_xfer_dir {
|
||||
SCSI_XFER_NONE = 0,
|
||||
SCSI_XFER_READ = 1,
|
||||
SCSI_XFER_WRITE = 2
|
||||
};
|
||||
|
||||
struct scsi_reportluns_params {
|
||||
int report_type;
|
||||
};
|
||||
struct scsi_readcapacity10_params {
|
||||
int lba;
|
||||
int pmi;
|
||||
};
|
||||
struct scsi_inquiry_params {
|
||||
int evpd;
|
||||
int page_code;
|
||||
};
|
||||
struct scsi_modesense6_params {
|
||||
int dbd;
|
||||
int pc;
|
||||
int page_code;
|
||||
int sub_page_code;
|
||||
};
|
||||
|
||||
struct scsi_sense {
|
||||
unsigned char error_type;
|
||||
enum scsi_sense_key key;
|
||||
int ascq;
|
||||
};
|
||||
|
||||
struct scsi_data {
|
||||
int size;
|
||||
unsigned char *data;
|
||||
};
|
||||
|
||||
struct scsi_allocated_memory {
|
||||
struct scsi_allocated_memory *next;
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
struct scsi_task {
|
||||
int status;
|
||||
|
||||
int cdb_size;
|
||||
int xfer_dir;
|
||||
int expxferlen;
|
||||
unsigned char cdb[SCSI_CDB_MAX_SIZE];
|
||||
union {
|
||||
struct scsi_readcapacity10_params readcapacity10;
|
||||
struct scsi_reportluns_params reportluns;
|
||||
struct scsi_inquiry_params inquiry;
|
||||
struct scsi_modesense6_params modesense6;
|
||||
} params;
|
||||
|
||||
struct scsi_sense sense;
|
||||
struct scsi_data datain;
|
||||
struct scsi_allocated_memory *mem;
|
||||
|
||||
void *ptr;
|
||||
};
|
||||
|
||||
void scsi_free_scsi_task(struct scsi_task *task);
|
||||
void scsi_set_task_private_ptr(struct scsi_task *task, void *ptr);
|
||||
void *scsi_get_task_private_ptr(struct scsi_task *task);
|
||||
|
||||
/*
|
||||
* TESTUNITREADY
|
||||
*/
|
||||
struct scsi_task *scsi_cdb_testunitready(void);
|
||||
|
||||
|
||||
/*
|
||||
* REPORTLUNS
|
||||
*/
|
||||
#define SCSI_REPORTLUNS_REPORT_ALL_LUNS 0x00
|
||||
#define SCSI_REPORTLUNS_REPORT_WELL_KNOWN_ONLY 0x01
|
||||
#define SCSI_REPORTLUNS_REPORT_AVAILABLE_LUNS_ONLY 0x02
|
||||
|
||||
struct scsi_reportluns_list {
|
||||
uint32_t num;
|
||||
uint16_t luns[0];
|
||||
};
|
||||
|
||||
struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len);
|
||||
|
||||
/*
|
||||
* READCAPACITY10
|
||||
*/
|
||||
struct scsi_readcapacity10 {
|
||||
uint32_t lba;
|
||||
uint32_t block_size;
|
||||
};
|
||||
struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi);
|
||||
|
||||
|
||||
/*
|
||||
* INQUIRY
|
||||
*/
|
||||
enum scsi_inquiry_peripheral_qualifier {
|
||||
SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED = 0x00,
|
||||
SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED = 0x01,
|
||||
SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED = 0x03
|
||||
};
|
||||
|
||||
const char *scsi_devqualifier_to_str(
|
||||
enum scsi_inquiry_peripheral_qualifier qualifier);
|
||||
|
||||
enum scsi_inquiry_peripheral_device_type {
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS = 0x00,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS = 0x01,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER = 0x02,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR = 0x03,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE = 0x04,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC = 0x05,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER = 0x06,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY = 0x07,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER = 0x08,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS = 0x09,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER = 0x0c,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES = 0x0d,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS = 0x0e,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER = 0x0f,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER = 0x10,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD = 0x11,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION = 0x12,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER = 0x13,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN = 0x1e,
|
||||
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN = 0x1f
|
||||
};
|
||||
|
||||
const char *scsi_devtype_to_str(enum scsi_inquiry_peripheral_device_type type);
|
||||
|
||||
enum scsi_version {
|
||||
SCSI_VERSION_SPC = 0x03,
|
||||
SCSI_VERSION_SPC2 = 0x04,
|
||||
SCSI_VERSION_SPC3 = 0x05
|
||||
};
|
||||
|
||||
const char *scsi_version_to_str(enum scsi_version version);
|
||||
|
||||
enum scsi_inquiry_tpgs {
|
||||
SCSI_INQUIRY_TPGS_NO_SUPPORT = 0x00,
|
||||
SCSI_INQUIRY_TPGS_IMPLICIT = 0x01,
|
||||
SCSI_INQUIRY_TPGS_EXPLICIT = 0x02,
|
||||
SCSI_INQUIRY_TPGS_IMPLICIT_AND_EXPLICIT = 0x03
|
||||
};
|
||||
|
||||
struct scsi_inquiry_standard {
|
||||
enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
|
||||
enum scsi_inquiry_peripheral_device_type periperal_device_type;
|
||||
int rmb;
|
||||
int version;
|
||||
int normaca;
|
||||
int hisup;
|
||||
int response_data_format;
|
||||
|
||||
int sccs;
|
||||
int acc;
|
||||
int tpgs;
|
||||
int threepc;
|
||||
int protect;
|
||||
|
||||
int encserv;
|
||||
int multip;
|
||||
int addr16;
|
||||
int wbus16;
|
||||
int sync;
|
||||
int cmdque;
|
||||
|
||||
int clocking;
|
||||
int qas;
|
||||
int ius;
|
||||
|
||||
char vendor_identification[8+1];
|
||||
char product_identification[16+1];
|
||||
char product_revision_level[4+1];
|
||||
};
|
||||
|
||||
enum scsi_inquiry_pagecode {
|
||||
SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES = 0x00,
|
||||
SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER = 0x80,
|
||||
SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION = 0x83,
|
||||
SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS = 0xB1
|
||||
};
|
||||
|
||||
const char *scsi_inquiry_pagecode_to_str(int pagecode);
|
||||
|
||||
struct scsi_inquiry_supported_pages {
|
||||
enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
|
||||
enum scsi_inquiry_peripheral_device_type periperal_device_type;
|
||||
enum scsi_inquiry_pagecode pagecode;
|
||||
|
||||
int num_pages;
|
||||
unsigned char *pages;
|
||||
};
|
||||
|
||||
struct scsi_inquiry_block_device_characteristics {
|
||||
enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
|
||||
enum scsi_inquiry_peripheral_device_type periperal_device_type;
|
||||
enum scsi_inquiry_pagecode pagecode;
|
||||
|
||||
int medium_rotation_rate;
|
||||
};
|
||||
|
||||
struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len);
|
||||
|
||||
struct scsi_inquiry_unit_serial_number {
|
||||
enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
|
||||
enum scsi_inquiry_peripheral_device_type periperal_device_type;
|
||||
enum scsi_inquiry_pagecode pagecode;
|
||||
|
||||
char *usn;
|
||||
};
|
||||
|
||||
enum scsi_protocol_identifier {
|
||||
SCSI_PROTOCOL_IDENTIFIER_FIBRE_CHANNEL = 0x00,
|
||||
SCSI_PROTOCOL_IDENTIFIER_PARALLEL_SCSI = 0x01,
|
||||
SCSI_PROTOCOL_IDENTIFIER_SSA = 0x02,
|
||||
SCSI_PROTOCOL_IDENTIFIER_IEEE_1394 = 0x03,
|
||||
SCSI_PROTOCOL_IDENTIFIER_RDMA = 0x04,
|
||||
SCSI_PROTOCOL_IDENTIFIER_ISCSI = 0x05,
|
||||
SCSI_PROTOCOL_IDENTIFIER_SAS = 0x06,
|
||||
SCSI_PROTOCOL_IDENTIFIER_ADT = 0x07,
|
||||
SCSI_PROTOCOL_IDENTIFIER_ATA = 0x08
|
||||
};
|
||||
|
||||
const char *scsi_protocol_identifier_to_str(int identifier);
|
||||
|
||||
enum scsi_codeset {
|
||||
SCSI_CODESET_BINARY = 0x01,
|
||||
SCSI_CODESET_ASCII = 0x02,
|
||||
SCSI_CODESET_UTF8 = 0x03
|
||||
};
|
||||
|
||||
const char *scsi_codeset_to_str(int codeset);
|
||||
|
||||
enum scsi_association {
|
||||
SCSI_ASSOCIATION_LOGICAL_UNIT = 0x00,
|
||||
SCSI_ASSOCIATION_TARGET_PORT = 0x01,
|
||||
SCSI_ASSOCIATION_TARGET_DEVICE = 0x02
|
||||
};
|
||||
|
||||
const char *scsi_association_to_str(int association);
|
||||
|
||||
enum scsi_designator_type {
|
||||
SCSI_DESIGNATOR_TYPE_VENDOR_SPECIFIC = 0x00,
|
||||
SCSI_DESIGNATOR_TYPE_T10_VENDORT_ID = 0x01,
|
||||
SCSI_DESIGNATOR_TYPE_EUI_64 = 0x02,
|
||||
SCSI_DESIGNATOR_TYPE_NAA = 0x03,
|
||||
SCSI_DESIGNATOR_TYPE_RELATIVE_TARGET_PORT = 0x04,
|
||||
SCSI_DESIGNATOR_TYPE_TARGET_PORT_GROUP = 0x05,
|
||||
SCSI_DESIGNATOR_TYPE_LOGICAL_UNIT_GROUP = 0x06,
|
||||
SCSI_DESIGNATOR_TYPE_MD5_LOGICAL_UNIT_IDENTIFIER = 0x07,
|
||||
SCSI_DESIGNATOR_TYPE_SCSI_NAME_STRING = 0x08
|
||||
};
|
||||
|
||||
const char *scsi_designator_type_to_str(int association);
|
||||
|
||||
struct scsi_inquiry_device_designator {
|
||||
struct scsi_inquiry_device_designator *next;
|
||||
|
||||
enum scsi_protocol_identifier protocol_identifier;
|
||||
enum scsi_codeset code_set;
|
||||
int piv;
|
||||
enum scsi_association association;
|
||||
enum scsi_designator_type designator_type;
|
||||
int designator_length;
|
||||
char *designator;
|
||||
};
|
||||
|
||||
struct scsi_inquiry_device_identification {
|
||||
enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
|
||||
enum scsi_inquiry_peripheral_device_type periperal_device_type;
|
||||
enum scsi_inquiry_pagecode pagecode;
|
||||
|
||||
struct scsi_inquiry_device_designator *designators;
|
||||
};
|
||||
|
||||
/*
|
||||
* MODESENSE6
|
||||
*/
|
||||
enum scsi_modesense_page_control {
|
||||
SCSI_MODESENSE_PC_CURRENT = 0x00,
|
||||
SCSI_MODESENSE_PC_CHANGEABLE = 0x01,
|
||||
SCSI_MODESENSE_PC_DEFAULT = 0x02,
|
||||
SCSI_MODESENSE_PC_SAVED = 0x03
|
||||
};
|
||||
|
||||
enum scsi_modesense_page_code {
|
||||
SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES = 0x3f
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
|
||||
|
||||
|
||||
int scsi_datain_getfullsize(struct scsi_task *task);
|
||||
void *scsi_datain_unmarshall(struct scsi_task *task);
|
||||
|
||||
struct scsi_task *scsi_cdb_read10(int lba, int xferlen, int blocksize);
|
||||
struct scsi_task *scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv,
|
||||
int blocksize);
|
||||
|
||||
struct scsi_task *scsi_cdb_synchronizecache10(int lba, int num_blocks,
|
||||
int syncnv, int immed);
|
||||
51
include/slist.h
Normal file
51
include/slist.h
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
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 SLIST_ADD(list, item) \
|
||||
do { \
|
||||
(item)->next = (*list); \
|
||||
(*list) = (item); \
|
||||
} while (0);
|
||||
|
||||
#define SLIST_ADD_END(list, item) \
|
||||
if ((*list) == NULL) { \
|
||||
SLIST_ADD((list), (item)); \
|
||||
} else { \
|
||||
void *head = (*list); \
|
||||
while ((*list)->next) \
|
||||
(*list) = (*list)->next; \
|
||||
(*list)->next = (item); \
|
||||
(item)->next = NULL; \
|
||||
(*list) = head; \
|
||||
}
|
||||
|
||||
#define SLIST_REMOVE(list, item) \
|
||||
if ((*list) == (item)) { \
|
||||
(*list) = (item)->next; \
|
||||
} else { \
|
||||
void *head = (*list); \
|
||||
while ((*list)->next && (*list)->next != (item)) \
|
||||
(*list) = (*list)->next; \
|
||||
if ((*list)->next != NULL) { \
|
||||
(*list)->next = (*list)->next->next; \
|
||||
} \
|
||||
(*list) = head; \
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
127
lib/connect.c
Normal file
127
lib/connect.c
Normal 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
99
lib/crc32c.c
Normal 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
189
lib/discovery.c
Normal 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
207
lib/init.c
Normal 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
378
lib/login.c
Normal 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
90
lib/nop.c
Normal 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
336
lib/pdu.c
Normal 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
528
lib/scsi-command.c
Normal 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
876
lib/scsi-lowlevel.c
Normal 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
351
lib/socket.c
Normal 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
282
lib/sync.c
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
87
packaging/RPM/libiscsi.spec.in
Normal file
87
packaging/RPM/libiscsi.spec.in
Normal file
@@ -0,0 +1,87 @@
|
||||
Name: libiscsi
|
||||
Summary: iSCSI client library
|
||||
Vendor: Ronnie Sahlberg
|
||||
Packager: Ronnie Sahlberg <ronniesahlberg@gmail.com>
|
||||
Version: 1.0.0
|
||||
Release: 1GITHASH
|
||||
Epoch: 0
|
||||
License: GNU LGPL version 3
|
||||
Group: Libraries
|
||||
URL: http://github.something/
|
||||
|
||||
Source: libiscsi-%{version}.tar.gz
|
||||
|
||||
Prereq: fileutils
|
||||
|
||||
Provides: libiscsi = %{version}
|
||||
|
||||
Prefix: /usr
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-root
|
||||
|
||||
%description
|
||||
libiscsi is a client library for attaching to iscsi resources across
|
||||
a network and a set of assorted useful utilities
|
||||
|
||||
|
||||
#######################################################################
|
||||
|
||||
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
# setup the init script and sysconfig file
|
||||
%setup -T -D -n libiscsi-%{version} -q
|
||||
|
||||
%build
|
||||
|
||||
CC="gcc"
|
||||
|
||||
export CC
|
||||
|
||||
%install
|
||||
# Clean up in case there is trash left from a previous build
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
# Create the target build directory hierarchy
|
||||
mkdir -p $RPM_BUILD_ROOT%{_bindir}
|
||||
mkdir -p $RPM_BUILD_ROOT%{_libdir}
|
||||
mkdir -p $RPM_BUILD_ROOT%{_includedir}
|
||||
mkdir -p $RPM_BUILD_ROOT%{_includedir}/iscsi
|
||||
|
||||
#
|
||||
make DESTDIR=$RPM_BUILD_ROOT LIBDIR=$RPM_BUILD_ROOT%{_libdir} install
|
||||
|
||||
# Remove "*.old" files
|
||||
find $RPM_BUILD_ROOT -name "*.old" -exec rm -f {} \;
|
||||
|
||||
%clean
|
||||
rm -rf $RPM_BUILD_ROOT
|
||||
|
||||
|
||||
#######################################################################
|
||||
## Files section ##
|
||||
#######################################################################
|
||||
|
||||
%files
|
||||
%defattr(-,root,root)
|
||||
|
||||
%{_bindir}/iscsi-ls
|
||||
%{_bindir}/iscsi-inq
|
||||
|
||||
%package devel
|
||||
Summary: iSCSI client development libraries
|
||||
Group: Development
|
||||
|
||||
%description devel
|
||||
development libraries for iSCSI
|
||||
|
||||
%files devel
|
||||
%defattr(-,root,root)
|
||||
%{_includedir}/iscsi/iscsi.h
|
||||
%{_includedir}/iscsi/scsi-lowlevel.h
|
||||
%{_libdir}/libiscsi.a
|
||||
%{_libdir}/libiscsi.so.1.0.0
|
||||
|
||||
%changelog
|
||||
* Sat Dec 4 2010 : Version 1.0.0
|
||||
- Initial version
|
||||
92
packaging/RPM/makerpms.sh
Executable file
92
packaging/RPM/makerpms.sh
Executable file
@@ -0,0 +1,92 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# makerpms.sh - build RPM packages from the git sources
|
||||
#
|
||||
# Copyright (C) John H Terpstra 1998-2002
|
||||
# Copyright (C) Gerald (Jerry) Carter 2003
|
||||
# Copyright (C) Jim McDonough 2007
|
||||
# Copyright (C) Andrew Tridgell 2007
|
||||
# Copyright (C) Michael Adam 2008-2009
|
||||
#
|
||||
# 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 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 General Public License along with
|
||||
# this program; if not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
#
|
||||
# The following allows environment variables to override the target directories
|
||||
# the alternative is to have a file in your home directory calles .rpmmacros
|
||||
# containing the following:
|
||||
# %_topdir /home/mylogin/redhat
|
||||
#
|
||||
# Note: Under this directory rpm expects to find the same directories that are under the
|
||||
# /usr/src/redhat directory
|
||||
#
|
||||
|
||||
EXTRA_OPTIONS="$1"
|
||||
|
||||
DIRNAME=$(dirname $0)
|
||||
TOPDIR=${DIRNAME}/../..
|
||||
|
||||
SPECDIR=`rpm --eval %_specdir`
|
||||
SRCDIR=`rpm --eval %_sourcedir`
|
||||
|
||||
SPECFILE="libiscsi.spec"
|
||||
SPECFILE_IN="libiscsi.spec.in"
|
||||
RPMBUILD="rpmbuild"
|
||||
|
||||
GITHASH=".$(git log --pretty=format:%h -1)"
|
||||
|
||||
if test "x$USE_GITHASH" = "xno" ; then
|
||||
GITHASH=""
|
||||
fi
|
||||
|
||||
sed -e s/GITHASH/${GITHASH}/g \
|
||||
< ${DIRNAME}/${SPECFILE_IN} \
|
||||
> ${DIRNAME}/${SPECFILE}
|
||||
|
||||
VERSION=$(grep ^Version ${DIRNAME}/${SPECFILE} | sed -e 's/^Version:\ \+//')
|
||||
RELEASE=$(grep ^Release ${DIRNAME}/${SPECFILE} | sed -e 's/^Release:\ \+//')
|
||||
|
||||
if echo | gzip -c --rsyncable - > /dev/null 2>&1 ; then
|
||||
GZIP="gzip -9 --rsyncable"
|
||||
else
|
||||
GZIP="gzip -9"
|
||||
fi
|
||||
|
||||
pushd ${TOPDIR}
|
||||
echo -n "Creating libiscsi-${VERSION}.tar.gz ... "
|
||||
git archive --prefix=libiscsi-${VERSION}/ HEAD | ${GZIP} > ${SRCDIR}/libiscsi-${VERSION}.tar.gz
|
||||
RC=$?
|
||||
popd
|
||||
echo "Done."
|
||||
if [ $RC -ne 0 ]; then
|
||||
echo "Build failed!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# At this point the SPECDIR and SRCDIR vaiables must have a value!
|
||||
|
||||
##
|
||||
## copy additional source files
|
||||
##
|
||||
cp -p ${DIRNAME}/${SPECFILE} ${SPECDIR}
|
||||
|
||||
##
|
||||
## Build
|
||||
##
|
||||
echo "$(basename $0): Getting Ready to build release package"
|
||||
${RPMBUILD} -ba --clean --rmsource ${EXTRA_OPTIONS} ${SPECDIR}/${SPECFILE} || exit 1
|
||||
|
||||
echo "$(basename $0): Done."
|
||||
|
||||
exit 0
|
||||
218
src/iscsi-inq.c
Normal file
218
src/iscsi-inq.c
Normal file
@@ -0,0 +1,218 @@
|
||||
/*
|
||||
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 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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <popt.h>
|
||||
#include "iscsi.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
char *initiator = "iqn.2010-11.ronnie:iscsi-inq";
|
||||
|
||||
|
||||
void inquiry_block_device_characteristics(struct scsi_inquiry_block_device_characteristics *inq)
|
||||
{
|
||||
printf("Medium Rotation Rate:%dRPM\n", inq->medium_rotation_rate);
|
||||
}
|
||||
|
||||
void inquiry_device_identification(struct scsi_inquiry_device_identification *inq)
|
||||
{
|
||||
struct scsi_inquiry_device_designator *dev;
|
||||
int i;
|
||||
|
||||
printf("Peripheral Qualifier:%s\n",
|
||||
scsi_devqualifier_to_str(inq->periperal_qualifier));
|
||||
printf("Peripheral Device Type:%s\n",
|
||||
scsi_devtype_to_str(inq->periperal_device_type));
|
||||
printf("Page Code:(0x%02x) %s\n",
|
||||
inq->pagecode, scsi_inquiry_pagecode_to_str(inq->pagecode));
|
||||
|
||||
for (i=0, dev = inq->designators; dev; i++, dev = dev->next) {
|
||||
printf("DEVICE DESIGNATOR #%d\n", i);
|
||||
if (dev->piv != 0) {
|
||||
printf("Device Protocol Identifier:(%d) %s\n", dev->protocol_identifier, scsi_protocol_identifier_to_str(dev->protocol_identifier));
|
||||
}
|
||||
printf("Code Set:(%d) %s\n", dev->code_set, scsi_codeset_to_str(dev->code_set));
|
||||
printf("PIV:%d\n", dev->piv);
|
||||
printf("Association:(%d) %s\n", dev->association, scsi_association_to_str(dev->association));
|
||||
printf("Designator Type:(%d) %s\n", dev->designator_type, scsi_designator_type_to_str(dev->designator_type));
|
||||
printf("Designator:[%s]\n", dev->designator);
|
||||
}
|
||||
}
|
||||
|
||||
void inquiry_unit_serial_number(struct scsi_inquiry_unit_serial_number *inq)
|
||||
{
|
||||
printf("Unit Serial Number:[%s]\n", inq->usn);
|
||||
}
|
||||
|
||||
void inquiry_supported_pages(struct scsi_inquiry_supported_pages *inq)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < inq->num_pages; i++) {
|
||||
printf("Page:0x%02x %s\n", inq->pages[i], scsi_inquiry_pagecode_to_str(inq->pages[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void inquiry_standard(struct scsi_inquiry_standard *inq)
|
||||
{
|
||||
printf("Peripheral Qualifier:%s\n",
|
||||
scsi_devqualifier_to_str(inq->periperal_qualifier));
|
||||
printf("Peripheral Device Type:%s\n",
|
||||
scsi_devtype_to_str(inq->periperal_device_type));
|
||||
printf("Removable:%d\n", inq->rmb);
|
||||
printf("Version:%d %s\n", inq->version, scsi_version_to_str(inq->version));
|
||||
printf("NormACA:%d\n", inq->normaca);
|
||||
printf("HiSup:%d\n", inq->hisup);
|
||||
printf("ReponseDataFormat:%d\n", inq->response_data_format);
|
||||
printf("SCCS:%d\n", inq->sccs);
|
||||
printf("ACC:%d\n", inq->acc);
|
||||
printf("TPGS:%d\n", inq->tpgs);
|
||||
printf("3PC:%d\n", inq->threepc);
|
||||
printf("Protect:%d\n", inq->protect);
|
||||
printf("EncServ:%d\n", inq->encserv);
|
||||
printf("MultiP:%d\n", inq->multip);
|
||||
printf("SYNC:%d\n", inq->sync);
|
||||
printf("CmdQue:%d\n", inq->cmdque);
|
||||
printf("Vendor:%s\n", inq->vendor_identification);
|
||||
printf("Product:%s\n", inq->product_identification);
|
||||
printf("Revision:%s\n", inq->product_revision_level);
|
||||
}
|
||||
|
||||
void do_inquiry(struct iscsi_context *iscsi, int lun, int evpd, int pc)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
int full_size;
|
||||
void *inq;
|
||||
|
||||
/* See how big this inquiry data is */
|
||||
task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, 64);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "Inquiry command failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
full_size = scsi_datain_getfullsize(task);
|
||||
if (full_size > task->datain.size) {
|
||||
scsi_free_scsi_task(task);
|
||||
|
||||
/* we need more data for the full list */
|
||||
if ((task = iscsi_inquiry_sync(iscsi, lun, evpd, pc, full_size)) == NULL) {
|
||||
fprintf(stderr, "Inquiry command failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
inq = scsi_datain_unmarshall(task);
|
||||
if (inq == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall inquiry datain blob\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (evpd == 0) {
|
||||
inquiry_standard(inq);
|
||||
} else {
|
||||
switch (pc) {
|
||||
case SCSI_INQUIRY_PAGECODE_SUPPORTED_VPD_PAGES:
|
||||
inquiry_supported_pages(inq);
|
||||
break;
|
||||
case SCSI_INQUIRY_PAGECODE_UNIT_SERIAL_NUMBER:
|
||||
inquiry_unit_serial_number(inq);
|
||||
break;
|
||||
case SCSI_INQUIRY_PAGECODE_DEVICE_IDENTIFICATION:
|
||||
inquiry_device_identification(inq);
|
||||
break;
|
||||
case SCSI_INQUIRY_PAGECODE_BLOCK_DEVICE_CHARACTERISTICS:
|
||||
inquiry_block_device_characteristics(inq);
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "Usupported pagecode:0x%02x\n", pc);
|
||||
}
|
||||
}
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
poptContext pc;
|
||||
struct iscsi_context *iscsi;
|
||||
char *portal = NULL;
|
||||
char *target = NULL;
|
||||
int lun = -1, evpd = 0, pagecode = 0;
|
||||
int res;
|
||||
|
||||
struct poptOption popt_options[] = {
|
||||
POPT_AUTOHELP
|
||||
{ "initiator-name", 'i', POPT_ARG_STRING, &initiator, 0, "Initiatorname to use", "iqn-name" },
|
||||
{ "portal", 'p', POPT_ARG_STRING, &portal, 0, "Target portal", "address[:port]" },
|
||||
{ "target", 't', POPT_ARG_STRING, &target, 0, "Target name", "iqn-name" },
|
||||
{ "lun", 'l', POPT_ARG_INT, &lun, 0, "LUN", "integer" },
|
||||
{ "evpd", 'e', POPT_ARG_INT, &evpd, 0, "evpd", "integer" },
|
||||
{ "pagecode", 'c', POPT_ARG_INT, &pagecode, 0, "page code", "integer" },
|
||||
POPT_TABLEEND
|
||||
};
|
||||
|
||||
pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_POSIXMEHARDER);
|
||||
if ((res = poptGetNextOpt(pc)) < -1) {
|
||||
fprintf(stderr, "Failed to parse option : %s %s\n",
|
||||
poptBadOption(pc, 0), poptStrerror(res));
|
||||
exit(10);
|
||||
}
|
||||
poptFreeContext(pc);
|
||||
|
||||
if (portal == NULL) {
|
||||
fprintf(stderr, "You must specify target portal\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (target == NULL) {
|
||||
fprintf(stderr, "You must specify target name\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (lun == -1) {
|
||||
fprintf(stderr, "You must specify LUN\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
iscsi = iscsi_create_context(initiator);
|
||||
if (iscsi == NULL) {
|
||||
fprintf(stderr, "Failed to create context\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
iscsi_set_targetname(iscsi, target);
|
||||
iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||
|
||||
if (iscsi_full_connect_sync(iscsi, portal, lun) != 0) {
|
||||
fprintf(stderr, "Failed to log in to target %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
do_inquiry(iscsi, lun, evpd, pagecode);
|
||||
|
||||
iscsi_logout_sync(iscsi);
|
||||
iscsi_destroy_context(iscsi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
315
src/iscsi-ls.c
Normal file
315
src/iscsi-ls.c
Normal file
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
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 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 <stdint.h>
|
||||
#include <string.h>
|
||||
#include <poll.h>
|
||||
#include <popt.h>
|
||||
#include "iscsi.h"
|
||||
#include "scsi-lowlevel.h"
|
||||
|
||||
int showluns;
|
||||
char *initiator = "iqn.2010-11.ronnie:iscsi-ls";
|
||||
|
||||
struct client_state {
|
||||
int finished;
|
||||
int status;
|
||||
int lun;
|
||||
int type;
|
||||
};
|
||||
|
||||
|
||||
void event_loop(struct iscsi_context *iscsi, struct client_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) {
|
||||
fprintf(stderr, "Poll failed");
|
||||
exit(10);
|
||||
}
|
||||
if (iscsi_service(iscsi, pfd.revents) < 0) {
|
||||
fprintf(stderr, "iscsi_service failed with : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void show_lun(struct iscsi_context *iscsi, int lun)
|
||||
{
|
||||
struct scsi_task *task;
|
||||
struct scsi_inquiry_standard *inq;
|
||||
int type;
|
||||
long long size = 0;
|
||||
int size_pf = 0;
|
||||
static const char sf[] = {' ', 'k', 'M', 'G', 'T' };
|
||||
|
||||
/* check we can talk to the lun */
|
||||
tur_try_again:
|
||||
if ((task = iscsi_testunitready_sync(iscsi, lun)) == NULL) {
|
||||
fprintf(stderr, "testunitready failed\n");
|
||||
exit(10);
|
||||
}
|
||||
if (task->status == SCSI_STATUS_CHECK_CONDITION) {
|
||||
if (task->sense.key == SCSI_SENSE_UNIT_ATTENTION && task->sense.ascq == SCSI_SENSE_ASCQ_BUS_RESET) {
|
||||
scsi_free_scsi_task(task);
|
||||
goto tur_try_again;
|
||||
}
|
||||
}
|
||||
if (task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "TESTUNITREADY failed with %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
scsi_free_scsi_task(task);
|
||||
|
||||
|
||||
|
||||
/* check what type of lun we have */
|
||||
task = iscsi_inquiry_sync(iscsi, lun, 0, 0, 64);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "failed to send inquiry command : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
inq = scsi_datain_unmarshall(task);
|
||||
if (inq == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall inquiry datain blob\n");
|
||||
exit(10);
|
||||
}
|
||||
type = inq->periperal_device_type;
|
||||
scsi_free_scsi_task(task);
|
||||
|
||||
|
||||
|
||||
if (type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) {
|
||||
struct scsi_readcapacity10 *rc10;
|
||||
|
||||
task = iscsi_readcapacity10_sync(iscsi, lun, 0, 0);
|
||||
if (task == NULL || task->status != SCSI_STATUS_GOOD) {
|
||||
fprintf(stderr, "failed to send readcapacity command\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
rc10 = scsi_datain_unmarshall(task);
|
||||
if (rc10 == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall readcapacity10 data\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
size = rc10->block_size;
|
||||
size *= rc10->lba;
|
||||
|
||||
for (size_pf=0; size_pf<4 && size > 1024; size_pf++) {
|
||||
size /= 1024;
|
||||
}
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
}
|
||||
|
||||
|
||||
printf("Lun:%-4d Type:%s", lun, scsi_devtype_to_str(type));
|
||||
if (type == SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS) {
|
||||
printf(" (Size:%lld%c)", size, sf[size_pf]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void list_luns(const char *target, const char *portal)
|
||||
{
|
||||
struct iscsi_context *iscsi;
|
||||
struct scsi_task *task;
|
||||
struct scsi_reportluns_list *list;
|
||||
int full_report_size;
|
||||
int i;
|
||||
|
||||
iscsi = iscsi_create_context(initiator);
|
||||
if (iscsi == NULL) {
|
||||
printf("Failed to create context\n");
|
||||
exit(10);
|
||||
}
|
||||
if (iscsi_set_targetname(iscsi, target)) {
|
||||
fprintf(stderr, "Failed to set target name\n");
|
||||
exit(10);
|
||||
}
|
||||
iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL);
|
||||
iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C);
|
||||
if (iscsi_connect_sync(iscsi, portal) != 0) {
|
||||
printf("iscsi_connect failed. %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (iscsi_login_sync(iscsi) != 0) {
|
||||
fprintf(stderr, "login failed :%s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
|
||||
/* get initial reportluns data, all targets can report 16 bytes but some
|
||||
* fail if we ask for too much.
|
||||
*/
|
||||
if ((task = iscsi_reportluns_sync(iscsi, 0, 16)) == NULL) {
|
||||
fprintf(stderr, "reportluns failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
full_report_size = scsi_datain_getfullsize(task);
|
||||
if (full_report_size > task->datain.size) {
|
||||
scsi_free_scsi_task(task);
|
||||
|
||||
/* we need more data for the full list */
|
||||
if ((task = iscsi_reportluns_sync(iscsi, 0, full_report_size)) == NULL) {
|
||||
fprintf(stderr, "reportluns failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
list = scsi_datain_unmarshall(task);
|
||||
if (list == NULL) {
|
||||
fprintf(stderr, "failed to unmarshall reportluns datain blob\n");
|
||||
exit(10);
|
||||
}
|
||||
for (i=0; i < (int)list->num; i++) {
|
||||
show_lun(iscsi, list->luns[i]);
|
||||
}
|
||||
|
||||
scsi_free_scsi_task(task);
|
||||
iscsi_destroy_context(iscsi);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void discoverylogout_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
struct client_state *state = (struct client_state *)private_data;
|
||||
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "Failed to logout from target. : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (iscsi_disconnect(iscsi) != 0) {
|
||||
fprintf(stderr, "Failed to disconnect old socket\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
state->finished = 1;
|
||||
}
|
||||
|
||||
void discovery_cb(struct iscsi_context *iscsi, int status, void *command_data, void *private_data)
|
||||
{
|
||||
struct iscsi_discovery_address *addr;
|
||||
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "Failed to do discovery on target. : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
for(addr=command_data; addr; addr=addr->next) {
|
||||
printf("Target:%s Portal:%s\n", addr->target_name, addr->target_address);
|
||||
if (showluns != 0) {
|
||||
list_luns(addr->target_name, addr->target_address);
|
||||
}
|
||||
}
|
||||
|
||||
if (iscsi_logout_async(iscsi, discoverylogout_cb, private_data) != 0) {
|
||||
fprintf(stderr, "iscsi_logout_async failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void discoverylogin_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "Failed to log in to target. : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (iscsi_discovery_async(iscsi, discovery_cb, private_data) != 0) {
|
||||
fprintf(stderr, "failed to send discovery command : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
void discoveryconnect_cb(struct iscsi_context *iscsi, int status, void *command_data _U_, void *private_data)
|
||||
{
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "discoveryconnect_cb: connection failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
if (iscsi_login_async(iscsi, discoverylogin_cb, private_data) != 0) {
|
||||
fprintf(stderr, "iscsi_login_async failed : %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, const char *argv[])
|
||||
{
|
||||
struct iscsi_context *iscsi;
|
||||
struct client_state state;
|
||||
char *portal = NULL;
|
||||
poptContext pc;
|
||||
int res;
|
||||
|
||||
struct poptOption popt_options[] = {
|
||||
POPT_AUTOHELP
|
||||
{ "initiator-name", 'i', POPT_ARG_STRING, &initiator, 0, "Initiatorname to use", "iqn-name" },
|
||||
{ "portal", 'p', POPT_ARG_STRING, &portal, 0, "Target portal", "address[:port]" },
|
||||
{ "show-luns", 's', POPT_ARG_NONE, &showluns, 0, "Show the luns for each target", NULL },
|
||||
POPT_TABLEEND
|
||||
};
|
||||
|
||||
bzero(&state, sizeof(state));
|
||||
|
||||
pc = poptGetContext(argv[0], argc, argv, popt_options, POPT_CONTEXT_POSIXMEHARDER);
|
||||
if ((res = poptGetNextOpt(pc)) < -1) {
|
||||
fprintf(stderr, "Failed to parse option : %s %s\n",
|
||||
poptBadOption(pc, 0), poptStrerror(res));
|
||||
exit(10);
|
||||
}
|
||||
poptFreeContext(pc);
|
||||
|
||||
if (portal == NULL) {
|
||||
fprintf(stderr, "You must specify target portal\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
iscsi = iscsi_create_context(initiator);
|
||||
if (iscsi == NULL) {
|
||||
printf("Failed to create context\n");
|
||||
exit(10);
|
||||
}
|
||||
|
||||
iscsi_set_session_type(iscsi, ISCSI_SESSION_DISCOVERY);
|
||||
|
||||
if (iscsi_connect_async(iscsi, portal, discoveryconnect_cb, &state) != 0) {
|
||||
fprintf(stderr, "iscsi_connect failed. %s\n", iscsi_get_error(iscsi));
|
||||
exit(10);
|
||||
}
|
||||
|
||||
event_loop(iscsi, &state);
|
||||
|
||||
iscsi_destroy_context(iscsi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user