嵌入式上 iscsi实现
前言
去年公司设备(haisi3516)上需要提供iscsi的功能,于是花了几天时间探究了下。linux内核(2.6.xx)支持iscsi,只是我发现当时我们设备的内核编译时没有选上,于是重新编译了内核,以模块的形式加入即可。主要的驱动如下(加载的顺序很重要有依赖关系的):
iscsi整理
下面是我收集总结的东西,希望对其他人有帮助(建议先在非嵌入式系统上做测试后...,那样简单些):
相关命令:
配置iscsi存储
[root@xifenfei ~]# iscsiadm -m discovery -t sendtargets -p 192.168.1.254:3260
[root@xifenfei ~]# iscsiadm -m node –T iqn.2006-01.com.openfiler:tsn.32b32087937b -p 192.168.1.254:3260 -l
[root@xifenfei ~]# iscsiadm -m node –T iqn.2006-01.com.openfiler:tsn.32b32087937b -p 192.168.1.254:3260
>--op update -n node.startup -v automatic
卸载iscsi存储
iscsiadm -m node --logoutall=all
iscsiadm -m node --op delete --targetname iqn.2006-01.com.openfiler:tsn.32b32087937b
增加iscsi存储
发现iscsi存储:iscsiadm -m discovery -t st -p ISCSI_IP
查看iscsi发现记录:iscsiadm -m node
登录iscsi存储:iscsiadm -m node -T LUN_NAME -p ISCSI_IP -l
开机自动: iscsiadm -m node –T LUN_NAME -p ISCSI_IP --op update -n node.startup -v automatic
删除iscsi存储
登出iscsi存储 iscsiadm -m node -T LUN_NAME -p ISCSI_IP -u
对出iscsi所有登录 iscsiadm -m node --logoutall=all
删除iscsi发现记录:iscsiadm -m node -o delete -T LUN_NAME -p ISCSI_IP
登入需验证码的节点
- 开启认证,*.使用-o同--op
iscsiadm -m node -T LUN_NAME -o update --name node.session.auth.authmethod --value=CHAP
- 添加用户
iscsiadm -m node -T LUN_NAME --op update --name node.session.auth.username --value=[用户名]
3. 添加密码
iscsiadm –m node –T LUN_NAME –op update –name node.session.auth.password –value=[密码]
参考: http://www.orasos.com/date/2012/06
查看当前会话
iscsiadm -m session
下面是我写的一个简单的demo:
头文件:
#ifndef __JISCSI_H_
#define __JISCSI_H_
typedef struct JISCSI_S {
char ip[32];
short port;
char target_name[512];
int bchap;
char username[512];
char userpass[64];
char discname[512];
char discpass[64];
}JISCSI_T;
int jiscsi_init(char *name, int timeout);
int jiscsi_uninit(void);
int jiscsi_attach_target(JISCSI_T *stpJiscsi, int timeout);
int jiscsi_detach_target(JISCSI_T *stpJiscsi, int timeout);
#endif /*__JISCSI_H_*/
c文件:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#include "jiscsi.h"
#define ISCSI_LOGIN "iscsiadm -m node -T %s -p %s:%d -l 2>&1"
#define ISCSI_LOGOUT "iscsiadm -m node -T %s -p %s:%d -u 2>&1"
#define ISCSI_EN_CHAP "iscsiadm -m node -T %s --op update --name node.session.auth.authmethod --value=CHAP 2>&1"
#define ISCSI_ADD_USER "iscsiadm -m node -T %s --op update --name node.session.auth.username --value=%s 2>&1"
#define ISCSI_ADD_PASS "iscsiadm -m node -T %s --op update --name node.session.auth.password --value=%s 2>&1"
#define ISCSI_DISCOVERY_EX "iscsiadm -m discovery -t st -p %s:%d 2>&1 | cut -d ' ' -f 2"
#define ISCSI_DISCOVERY "iscsiadm -m discovery -t st -p %s:%d 2>&1"
#define ISCSI_SESSION_STATUS "iscsiadm -m session"
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
typedef struct {
char *cmd;
char *buf;
}ARG_T;
enum JISCSI_RET {
SYSERR = -4,
TIMEOUT,
ARGV_INVALID,
FAILED,
SUCCESSED,
};
static void *do_work(void *arg)
{
ARG_T *stpArg = arg;
char *buf = stpArg->buf;
char *cmd = stpArg->cmd;
pthread_detach(pthread_self());
/* pthread_mutex_lock(&mutex);*/
FILE *fp = popen(cmd, "r");
if (NULL == fp) {
perror("popen");
return NULL;
}
fread(buf, 1, 1024, fp);
pclose(fp);
pthread_mutex_lock(&mutex);
pthread_cond_signal(&cond);
pthread_mutex_unlock(&mutex);
pthread_exit(NULL);
}
static int exec_cmd(char *cmd, char *findstr, int time)
{
if (NULL == cmd || NULL == findstr)
return ARGV_INVALID;
char buf[1024];
ARG_T stArg = {.buf = buf, .cmd = cmd};
pthread_t tid;
pthread_mutex_lock(&mutex);
if (pthread_create(&tid, NULL, do_work, &stArg) < 0) {
perror("pthread_create");
return SYSERR;
}
struct timeval now;
struct timespec timeout;
gettimeofday(&now, NULL);
timeout.tv_sec = now.tv_sec+time;
timeout.tv_nsec= now.tv_usec;
int ret = pthread_cond_timedwait(&cond, &mutex, &timeout);
if(ret == ETIMEDOUT) {
pthread_mutex_unlock(&mutex);
printf("timeout!!!\n");
return TIMEOUT;
}
pthread_mutex_unlock(&mutex);
if (NULL == strstr(buf, findstr))
return FAILED;
memset(buf, 0, 1024); //?
return SUCCESSED;
}
static int jsystem(char *cmd)
{
int ret = -1;
int status = system(cmd);
if (-1 == status) {
ret = -1;
printf("error!!maybe fork failed...\n");
} else if (WIFEXITED(status)) {
ret = WEXITSTATUS(status);
printf("normal termination, exit status = %d\n", ret);
} else if (WIFSIGNALED(status)) {
ret = WEXITSTATUS(status);
printf("abnormal termination,signal number =%d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
ret = WEXITSTATUS(status);
printf("process stopped, signal number =%d\n", WSTOPSIG(status));
}
return ret;
}
#define J_EXEC_CMD(cmd) do {int ret; if ((ret = jsystem(cmd)) != 0) return ret;}while(0)
/*void process_cfg_file()*/
/*{*/
/* J_EXEC_CMD("iscsiadm -m node -L all 2>&1"); */
/*}*/
static int jiscsi_set_own_name(char *name)
{
char cmd[1024];
sprintf(cmd, "sed -i 's/\(InitiatorName=\).*/\1%s/g' /etc/iscsi/initiatorname.iscsi", name);
return jsystem(cmd);
}
int jiscsi_init(char *name, int timeout)
{
system("insmod crc32c.ko; insmod scsi_mod.ko; insmod scsi_transport_iscsi.ko; \
insmod libiscsi.ko; insmod libiscsi_tcp.ko; insmod iscsi_tcp.ko; insmod sd_mod.ko ");
system("mkdir /etc/iscsi 2>&1");
system("mkdir -p /var/lock/iscsi 2>&1");
system("cp iscsid.conf initiatorname.iscsi /etc/iscsi/ 2>&1");
system("ln -s `pwd`/iscsid /sbin/iscsid ");
system("ln -s `pwd`/iscsiadm /sbin/iscsiadm ");
if (NULL != name)
jiscsi_set_own_name(name);
system("iscsid &");
return exec_cmd("ps | grep iscsid | grep -v grep", "iscsid", timeout);
/* int ret = exec_cmd("ps | grep iscsid | grep -v iscsid", "iscsid");*/
/* //read configuration file, and execute it */
/* process_cfg_file();*/
/* return ret;*/
}
int jiscsi_uninit(void)
{
J_EXEC_CMD("iscsiadm -m node -U all 2>&1");
return SUCCESSED;
}
static int get_target_names(char *ip, short port, char name_buf[], int buf_size)
{
char cmd[1024];
sprintf(cmd, ISCSI_DISCOVERY_EX, ip, port);
FILE *fp = popen(cmd, "r");
if (NULL == fp) {
perror("popen");
return FAILED;
}
fread(name_buf, 1, buf_size>2048?2048:buf_size, fp);
/* printf("buf:%s\n", name_buf);*/
pclose(fp);
return SUCCESSED;
}
static int check_target_is_valid(char *buf_names, char *target_name)
{
return strstr(buf_names, target_name) == NULL ? SUCCESSED:FAILED;
}
int jiscsi_attach_target(JISCSI_T *stpJiscsi, int timeout)
{
if (NULL == stpJiscsi)
return ARGV_INVALID;
char cmd[1024];
char buf_names[2048];
if (get_target_names(stpJiscsi->ip, stpJiscsi->port, buf_names, 2048) < 0)
return FAILED;
if (!check_target_is_valid(buf_names, stpJiscsi->target_name))
return FAILED;
if (stpJiscsi->bchap) {
sprintf(cmd, ISCSI_EN_CHAP, stpJiscsi->target_name);
J_EXEC_CMD(cmd);
sprintf(cmd, ISCSI_ADD_USER, stpJiscsi->target_name, stpJiscsi->username);
J_EXEC_CMD(cmd);
sprintf(cmd, ISCSI_ADD_PASS, stpJiscsi->target_name, stpJiscsi->userpass);
J_EXEC_CMD(cmd);
}
sprintf(cmd, ISCSI_LOGIN, stpJiscsi->target_name, stpJiscsi->ip, stpJiscsi->port);
/* printf("cmd:%s\n", cmd);*/
return exec_cmd(cmd, "success", timeout);
}
int jiscsi_detach_target(JISCSI_T *stpJiscsi, int timeout)
{
if (NULL == stpJiscsi)
return ARGV_INVALID;
char cmd[1024];
sprintf(cmd, ISCSI_LOGOUT, stpJiscsi->target_name, stpJiscsi->ip, stpJiscsi->port);
return exec_cmd(cmd, "success", timeout);
}
//for test
void help(void)
{
printf( "-d: means discovery mode, depend -a [-p]\n" \
"-l: means login mode, depend -a [-p] [-T] -t\n" \
"-u: means logout mode, depend -a [-p] [-T] -t\n" \
"-i: means init iscsi\n" \
"-I: means uninit iscsi\n" \
"-a: means set ipaddr, need ip argument\n" \
"-p: means set port, need port argument, default is 3260\n" \
"-t: means set target name, need target name argument\n" \
"-T: means set timeout time(s), need timeout argument, default is 2s\n"\
"-c: means use chap, need username*password argument, eg -c jxj*123456\n"\
"-s: means display current iscsi status\n");
printf( "eg: init iscsi ./jiscsi -i\n"\
"eg: disp status ./jiscsi -s\n"\
"eg: discovery ./jiscsi -d -a xxx.xxx.xxx.xxx it will take back target_name\n"\
"target_name like: iqn.skysan.cn.com.bwstor:1343989129\n"\
"eg: login ./jiscsi -l -a xxx.xxx.xxx.xxx -t target_name\n"\
"eg: login ./jiscsi -u -a xxx.xxx.xxx.xxx -t target_name\n");
}
int main(int argc, char *argv[])
{
int c;
int ret = -1;
int mode = 0, timeout = 2;
JISCSI_T stJiscsi = {.port = 3260};
//handle input
/* opterr = 0;*/
while(-1 != (c = getopt(argc, argv, ":dlup:a:t:T:hsc:iI"))) {
switch(c) {
case 'd':
mode = 1;
break;
case 'l':
mode = 2;
break;
case 'u':
mode = 3;
break;
case 'a':
printf("ipaddr:%s\n", optarg);
strcpy(stJiscsi.ip, optarg);
break;
case 'c':
printf("username*password:%s\n", optarg);
if (sscanf(optarg, "%[^\*]*%s", stJiscsi.username, stJiscsi.userpass) != 2) {
printf("argument is invalid!!!\n");
printf("-c: means use chap, need username*password argument, eg -c jxj*123456\n");
return -1;
}
/* printf("username%s password:%s\n", stJiscsi.username, stJiscsi.userpass);*/
stJiscsi.bchap = 1;
break;
case 'p':
printf("port:%s\n", optarg);
stJiscsi.port = atoi(optarg);
break;
case 't':
printf("target name:%s\n", optarg);
strcpy(stJiscsi.target_name, optarg);
break;
case 'T':
printf("time:%s\n", optarg);
timeout = atoi(optarg);
break;
case 's':
mode = 4;
break;
case 'i':
ret = jiscsi_init(NULL, 2);
printf("jiscsi_init ret: %d\n", ret);
return 0;
case 'I':
ret = jiscsi_uninit();
printf("jiscsi_uninit ret: %d\n", ret);
return 0;
case 'h':
help();
return 0;
case '?':
printf("invalid option \'%c\'\n", optopt);
return -1;
case ':':
printf(": option \'%c\' need argument\n", optopt);
return -1;
default:
printf("default invalid option \'%c\' ret:%d\n", optopt, c);
return -1;
}
}
//execute
char cmd[1024];
switch(mode) {
case 1:
sprintf(cmd, ISCSI_DISCOVERY, stJiscsi.ip, stJiscsi.port);
J_EXEC_CMD(cmd);
break;
case 2:
ret = jiscsi_attach_target(&stJiscsi, timeout);
printf("jiscsi_attach_target ret:%d\n", ret);
break;
case 3:
ret = jiscsi_detach_target(&stJiscsi, timeout);
printf("jiscsi_detach_target ret:%d\n", ret);
break;
case 4:
J_EXEC_CMD(ISCSI_SESSION_STATUS);
break;
default:
printf("argument is invalid\n");
help();
return -1;
}
return 0;
/* JISCSI_T stJiscsi1 = {.ip = "192.168.1.251", .port = 3260, */
/* .target_name = "iqn.2012-11.com.example:storage.sdb1", .bchap = 1, */
/* .username = "rongp", .userpass = "123456abcdef"}; */
/**/
/* JISCSI_T stJiscsi2 = {.ip = "192.168.1.251", .port = 3260, */
/* .target_name = "iqn.2012-11.com.example:storage.sdb2", .bchap = 0, */
/* .username = "", .userpass = ""}; */
/**/
/* JISCSI_T stJiscsi3 = {.ip = "192.168.1.251", .port = 3260, */
/* .target_name = "iqn.2012-11.com.example:storage.sdb3", .bchap = 0, */
/* .username = "", .userpass = ""}; */
/*#if 0*/
/* int ret = jiscsi_attach_target(&stJiscsi1, 1);*/
/* printf("ret :%d\n", ret);*/
/* ret = jiscsi_attach_target(&stJiscsi2, 1);*/
/* printf("ret :%d\n", ret);*/
/* ret = jiscsi_attach_target(&stJiscsi3, 1);*/
/* printf("ret :%d\n", ret);*/
/*#else */
/* jiscsi_detach_target(&stJiscsi1, 1);*/
/* jiscsi_detach_target(&stJiscsi2, 1);*/
/* jiscsi_detach_target(&stJiscsi3, 1);*/
/*#endif*/
}
完!
2013年8月
毕业那两年在做嵌入式应用开发,主要是单片机和arm linux上的应用开发,后来又做了两年arm linux驱动开发,15年到现在在做pc端及嵌入式端开发,包括服务器系统裁剪、底层框架实现、硬件加速等。喜欢技术分享、交流!联系方式: 907882971@qq.com、rongpmcu@gmail.com