/*
* Copyright (C) 2008-2012 Daisuke Aoyama <aoyama@peach.ne.jp>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "build.h"
#include <stdint.h>
#include <inttypes.h>
#include <stdarg.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <syslog.h>
#include <sys/types.h>
#include "istgt.h"
#include "istgt_ver.h"
#include "istgt_conf.h"
#include "istgt_sock.h"
#include "istgt_misc.h"
#include "istgt_md5.h"
#if !defined(__GNUC__)
#undef __attribute__
#define __attribute__(x)
#endif
//#define TRACE_UCTL
#define DEFAULT_UCTL_CONFIG BUILD_ETC_ISTGT "/istgtcontrol.conf"
#define DEFAULT_UCTL_TIMEOUT 60
#define DEFAULT_UCTL_PORT 3261
#define DEFAULT_UCTL_HOST "localhost"
#define DEFAULT_UCTL_LUN 0
#define DEFAULT_UCTL_MTYPE "-"
#define DEFAULT_UCTL_MFLAGS "ro"
#define DEFAULT_UCTL_MSIZE "auto"
#define MAX_LINEBUF 4096
#define UCTL_CHAP_CHALLENGE_LEN 1024
typedef struct istgt_uctl_auth_t {
char *user;
char *secret;
char *muser;
char *msecret;
uint8_t chap_id[1];
uint8_t chap_mid[1];
int chap_challenge_len;
uint8_t chap_challenge[UCTL_CHAP_CHALLENGE_LEN];
int chap_mchallenge_len;
uint8_t chap_mchallenge[UCTL_CHAP_CHALLENGE_LEN];
} UCTL_AUTH;
typedef struct istgt_uctl_t {
CONFIG *config;
char *host;
int port;
int sock;
char *iqn;
int lun;
char *mflags;
char *mfile;
char *msize;
char *mtype;
int family;
char caddr[MAX_ADDRBUF];
char saddr[MAX_ADDRBUF];
UCTL_AUTH auth;
int timeout;
int req_auth_auto;
int req_auth;
int req_auth_mutual;
int recvtmpsize;
int recvtmpcnt;
int recvtmpidx;
int recvbufsize;
int sendbufsize;
int worksize;
char recvtmp[MAX_LINEBUF];
char recvbuf[MAX_LINEBUF];
char sendbuf[MAX_LINEBUF];
char work[MAX_LINEBUF];
char *cmd;
char *arg;
} UCTL;
typedef UCTL *UCTL_Ptr;
static void fatal(const char *format, ...) __attribute__((__noreturn__, __format__(__printf__, 1, 2)));
static void
fatal(const char *format, ...)
{
va_list ap;
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
typedef enum {
UCTL_CMD_OK = 0,
UCTL_CMD_ERR = 1,
UCTL_CMD_EOF = 2,
UCTL_CMD_QUIT = 3,
UCTL_CMD_DISCON = 4,
UCTL_CMD_REQAUTH = 5,
UCTL_CMD_CHAPSEQ = 6,
} UCTL_CMD_STATUS;
//#define ARGS_DELIM " \t\r\n"
#define ARGS_DELIM " \t"
static int
uctl_readline(UCTL_Ptr uctl)
{
ssize_t total;
total = istgt_readline_socket(uctl->sock, uctl->recvbuf, uctl->recvbufsize,
uctl->recvtmp, uctl->recvtmpsize,
&uctl->recvtmpidx, &uctl->recvtmpcnt,
uctl->timeout);
if (total < 0) {
return UCTL_CMD_DISCON;
}
if (total == 0) {
return UCTL_CMD_EOF;
}
return UCTL_CMD_OK;
}
static int
uctl_writeline(UCTL_Ptr uctl)
{
ssize_t total;
ssize_t expect;
expect = strlen(uctl->sendbuf);
total = istgt_writeline_socket(uctl->sock, uctl->sendbuf, uctl->timeout);
if (total < 0) {
return UCTL_CMD_DISCON;
}
if (total != expect) {
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int uctl_snprintf(UCTL_Ptr uctl, const char *format, ...) __attribute__((__format__(__printf__, 2, 3)));
static int
uctl_snprintf(UCTL_Ptr uctl, const char *format, ...)
{
va_list ap;
int rc;
va_start(ap, format);
rc = vsnprintf(uctl->sendbuf, uctl->sendbufsize, format, ap);
va_end(ap);
return rc;
}
static char *
get_banner(UCTL_Ptr uctl)
{
char *banner;
int rc;
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return NULL;
}
banner = xstrdup(trim_string(uctl->recvbuf));
return banner;
}
static int
is_err_req_auth(UCTL_Ptr uctl __attribute__((__unused__)), char *s)
{
const char *req_auth_string = "auth required";
#ifdef TRACE_UCTL
printf("S=%s, Q=%s\n", s, req_auth_string);
#endif /* TRCAE_UCTL */
if (strncasecmp(s, req_auth_string, strlen(req_auth_string)) == 0)
return 1;
return 0;
}
static int
is_err_chap_seq(UCTL_Ptr uctl __attribute__((__unused__)), char *s)
{
const char *chap_seq_string = "CHAP sequence error";
#ifdef TRACE_UCTL
printf("S=%s, Q=%s\n", s, chap_seq_string);
#endif /* TRCAE_UCTL */
if (strncasecmp(s, chap_seq_string, strlen(chap_seq_string)) == 0)
return 1;
return 0;
}
static int
exec_quit(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
int rc;
/* send command */
uctl_snprintf(uctl, "QUIT\n");
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int
exec_noop(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
int rc;
/* send command */
uctl_snprintf(uctl, "NOOP\n");
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int
exec_version(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
char *version;
char *extver;
int rc;
/* send command */
uctl_snprintf(uctl, "VERSION\n");
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
while (1) {
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, uctl->cmd) != 0) {
break;
}
version = strsepq(&arg, delim);
extver = strsepq(&arg, delim);
printf("target version %s %s\n", version, extver);
}
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int
exec_unload(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
int rc;
/* send command */
if (uctl->iqn == NULL || uctl->lun < 0) {
return UCTL_CMD_ERR;
}
uctl_snprintf(uctl, "UNLOAD \"%s\" %d\n",
uctl->iqn, uctl->lun);
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int
exec_load(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
int rc;
/* send command */
if (uctl->iqn == NULL || uctl->lun < 0) {
return UCTL_CMD_ERR;
}
uctl_snprintf(uctl, "LOAD \"%s\" %d\n",
uctl->iqn, uctl->lun);
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int
exec_list(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
char *target;
int rc;
/* send command */
if (uctl->iqn != NULL) {
uctl_snprintf(uctl, "LIST \"%s\"\n", uctl->iqn);
} else {
uctl_snprintf(uctl, "LIST\n");
}
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
while (1) {
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, uctl->cmd) != 0)
break;
if (uctl->iqn != NULL) {
printf("%s\n", arg);
} else {
target = strsepq(&arg, delim);
printf("%s\n", target);
}
}
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int
exec_change(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
int rc;
/* send command */
if (uctl->iqn == NULL || uctl->mfile == NULL || uctl->mtype == NULL
|| uctl->mflags == NULL || uctl->msize == NULL) {
return UCTL_CMD_ERR;
}
uctl_snprintf(uctl, "CHANGE \"%s\" %d \"%s\" "
"\"%s\" \"%s\" \"%s\"\n",
uctl->iqn, uctl->lun, uctl->mtype,
uctl->mflags, uctl->mfile, uctl->msize);
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int
exec_reset(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
int rc;
/* send command */
if (uctl->iqn == NULL || uctl->lun < 0) {
return UCTL_CMD_ERR;
}
uctl_snprintf(uctl, "RESET \"%s\" %d\n",
uctl->iqn, uctl->lun);
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static int
exec_info(UCTL_Ptr uctl)
{
const char *delim = ARGS_DELIM;
char *arg;
char *result;
int rc;
/* send command */
if (uctl->iqn != NULL) {
uctl_snprintf(uctl, "INFO \"%s\"\n", uctl->iqn);
} else {
uctl_snprintf(uctl, "INFO\n");
}
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive result */
while (1) {
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, uctl->cmd) != 0)
break;
if (uctl->iqn != NULL) {
printf("%s\n", arg);
} else {
printf("%s\n", arg);
}
}
if (strcmp(result, "OK") != 0) {
if (is_err_req_auth(uctl, arg))
return UCTL_CMD_REQAUTH;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
typedef struct exec_table_t
{
const char *name;
int (*func) (UCTL_Ptr uctl);
int req_argc;
int req_target;
} EXEC_TABLE;
static EXEC_TABLE exec_table[] =
{
{ "QUIT", exec_quit, 0, 0 },
{ "NOOP", exec_noop, 0, 0 },
{ "VERSION", exec_version, 0, 0 },
{ "LIST", exec_list, 0, 0 },
{ "UNLOAD", exec_unload, 0, 1 },
{ "LOAD", exec_load, 0, 1 },
{ "CHANGE", exec_change, 1, 1 },
{ "RESET", exec_reset, 0, 1 },
{ "INFO", exec_info, 0, 0 },
{ NULL, NULL, 0, 0 },
};
static int
do_auth(UCTL_Ptr uctl)
{
uint8_t uctlmd5[ISTGT_MD5DIGEST_LEN];
uint8_t resmd5[ISTGT_MD5DIGEST_LEN];
ISTGT_MD5CTX md5ctx;
const char *delim = ARGS_DELIM;
char *arg;
char *result;
char *label;
char *chap_i;
char *chap_c;
char *chap_n;
char *chap_r;
char *hexmd5;
char *hexchallenge;
char *workp;
int worksize;
int algorithm = 5; /* CHAP with MD5 */
int rc;
#ifdef TRACE_UCTL
printf("do_auth: user=%s, secret=%s, muser=%s, msecret=%s\n",
uctl->auth.user,
uctl->auth.secret,
uctl->auth.muser,
uctl->auth.msecret);
#endif /* TRACE_UCTL */
/* send algorithm CHAP_A */
uctl_snprintf(uctl, "AUTH CHAP_A %d\n",
algorithm);
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive CHAP_IC */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "AUTH") != 0) {
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
label = strsepq(&arg, delim);
chap_i = strsepq(&arg, delim);
chap_c = strsepq(&arg, delim);
if (label == NULL || chap_i == NULL || chap_c == NULL) {
fprintf(stderr, "CHAP sequence error\n");
return UCTL_CMD_ERR;
}
if (strcasecmp(label, "CHAP_IC") != 0) {
fprintf(stderr, "CHAP sequence error\n");
return UCTL_CMD_ERR;
}
/* Identifier */
uctl->auth.chap_id[0] = (uint8_t) strtol(chap_i, NULL, 10);
/* Challenge Value */
rc = istgt_hex2bin(uctl->auth.chap_challenge,
UCTL_CHAP_CHALLENGE_LEN,
chap_c);
if (rc < 0) {
fprintf(stderr, "challenge format error\n");
return UCTL_CMD_ERR;
}
uctl->auth.chap_challenge_len = rc;
if (uctl->auth.user == NULL || uctl->auth.secret == NULL) {
fprintf(stderr, "ERROR auth user or secret is missing\n");
return UCTL_CMD_ERR;
}
istgt_md5init(&md5ctx);
/* Identifier */
istgt_md5update(&md5ctx, uctl->auth.chap_id, 1);
/* followed by secret */
istgt_md5update(&md5ctx, uctl->auth.secret,
strlen(uctl->auth.secret));
/* followed by Challenge Value */
istgt_md5update(&md5ctx, uctl->auth.chap_challenge,
uctl->auth.chap_challenge_len);
/* uctlmd5 is Response Value */
istgt_md5final(uctlmd5, &md5ctx);
workp = uctl->work;
worksize = uctl->worksize;
istgt_bin2hex(workp, worksize,
uctlmd5, ISTGT_MD5DIGEST_LEN);
hexmd5 = workp;
worksize -= strlen(hexmd5) + 1;
workp += strlen(hexmd5) + 1;
/* mutual CHAP? */
if (uctl->req_auth_mutual) {
/* Identifier is one octet */
istgt_gen_random(uctl->auth.chap_mid, 1);
/* Challenge Value is a variable stream of octets */
/* (binary length MUST not exceed 1024 bytes) */
uctl->auth.chap_mchallenge_len = UCTL_CHAP_CHALLENGE_LEN;
istgt_gen_random(uctl->auth.chap_mchallenge,
uctl->auth.chap_mchallenge_len);
istgt_bin2hex(workp, worksize,
uctl->auth.chap_mchallenge,
uctl->auth.chap_mchallenge_len);
hexchallenge = workp;
worksize -= strlen(hexchallenge) + 1;
workp += strlen(hexchallenge) + 1;
/* send CHAP_NR with CHAP_IC */
uctl_snprintf(uctl, "AUTH CHAP_NR %s %s %d %s\n",
uctl->auth.user, hexmd5,
(int) uctl->auth.chap_mid[0], hexchallenge);
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
/* receive CHAP_NR */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "AUTH") != 0) {
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
label = strsepq(&arg, delim);
chap_n = strsepq(&arg, delim);
chap_r = strsepq(&arg, delim);
if (label == NULL || chap_n == NULL || chap_r == NULL) {
fprintf(stderr, "CHAP sequence error\n");
return UCTL_CMD_ERR;
}
if (strcasecmp(label, "CHAP_NR") != 0) {
fprintf(stderr, "CHAP sequence error\n");
return UCTL_CMD_ERR;
}
rc = istgt_hex2bin(resmd5, ISTGT_MD5DIGEST_LEN, chap_r);
if (rc < 0 || rc != ISTGT_MD5DIGEST_LEN) {
fprintf(stderr, "response format error\n");
return UCTL_CMD_ERR;
}
if (uctl->auth.muser == NULL || uctl->auth.msecret == NULL) {
fprintf(stderr, "ERROR auth user or secret is missing\n");
return UCTL_CMD_ERR;
}
istgt_md5init(&md5ctx);
/* Identifier */
istgt_md5update(&md5ctx, uctl->auth.chap_mid, 1);
/* followed by secret */
istgt_md5update(&md5ctx, uctl->auth.msecret,
strlen(uctl->auth.msecret));
/* followed by Challenge Value */
istgt_md5update(&md5ctx, uctl->auth.chap_mchallenge,
uctl->auth.chap_mchallenge_len);
/* uctlmd5 is expecting Response Value */
istgt_md5final(uctlmd5, &md5ctx);
/* compare MD5 digest */
if (memcmp(uctlmd5, resmd5, ISTGT_MD5DIGEST_LEN) != 0) {
/* not match */
fprintf(stderr, "ERROR auth user or secret is missing\n");
/* discard result line */
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "OK") != 0) {
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
/* final with ERR */
return UCTL_CMD_ERR;
}
} else {
/* not mutual */
/* send CHAP_NR */
uctl_snprintf(uctl, "AUTH CHAP_NR %s %s\n",
uctl->auth.user, hexmd5);
rc = uctl_writeline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
}
/* receive result */
rc = uctl_readline(uctl);
if (rc != UCTL_CMD_OK) {
return rc;
}
arg = trim_string(uctl->recvbuf);
result = strsepq(&arg, delim);
strupr(result);
if (strcmp(result, "OK") != 0) {
if (is_err_chap_seq(uctl, arg))
return UCTL_CMD_CHAPSEQ;
fprintf(stderr, "ERROR %s\n", arg);
return UCTL_CMD_ERR;
}
return UCTL_CMD_OK;
}
static char *
uctl_get_nmval(CF_SECTION *sp, const char *key, int idx1, int idx2)
{
CF_ITEM *ip;
CF_VALUE *vp;
int i;
ip = istgt_find_cf_nitem(sp, key, idx1);
if (ip == NULL)
return NULL;
vp = ip->val;
if (vp == NULL)
return NULL;
for (i = 0; vp != NULL; vp = vp->next) {
if (i == idx2)
return vp->value;
i++;
}
return NULL;
}
static char *
uctl_get_nval(CF_SECTION *sp, const char *key, int idx)
{
CF_ITEM *ip;
CF_VALUE *vp;
ip = istgt_find_cf_nitem(sp, key, idx);
if (ip == NULL)
return NULL;
vp = ip->val;
if (vp == NULL)
return NULL;
return vp->value;
}
static char *
uctl_get_val(CF_SECTION *sp, const char *key)
{
return uctl_get_nval(sp, key, 0);
}
static int
uctl_get_nintval(CF_SECTION *sp, const char *key, int idx)
{
const char *v;
int value;
v = uctl_get_nval(sp, key, idx);
if (v == NULL)
return -1;
value = (int)strtol(v, NULL, 10);
return value;
}
static int
uctl_get_intval(CF_SECTION *sp, const char *key)
{
return uctl_get_nintval(sp, key, 0);
}
static int
uctl_init(UCTL_Ptr uctl)
{
CF_SECTION *sp;
const char *val;
const char *user, *muser;
const char *secret, *msecret;
int timeout;
int port;
int lun;
int i;
sp = istgt_find_cf_section(uctl->config, "Global");
if (sp == NULL) {
fprintf(stderr, "find_cf_section failed()\n");
return -1;
}
val = uctl_get_val(sp, "Comment");
if (val != NULL) {
/* nothing */
#ifdef TRACE_UCTL
printf("Comment %s\n", val);
#endif /* TRACE_UCTL */
}
val = uctl_get_val(sp, "Host");
if (val == NULL) {
val = DEFAULT_UCTL_HOST;
}
uctl->host = xstrdup(val);
#ifdef TRACE_UCTL
printf("Host %s\n", uctl->host);
#endif /* TRACE_UCTL */
port = uctl_get_intval(sp, "Port");
if (port < 0) {
port = DEFAULT_UCTL_PORT;
}
uctl->port = port;
#ifdef TRACE_UCTL
printf("Port %d\n", uctl->port);
#endif /* TRACE_UCTL */
val = uctl_get_val(sp, "TargetName");
if (val == NULL) {
val = NULL;
}
uctl->iqn = xstrdup(val);
#ifdef TRACE_UCTL
printf("TargetName %s\n", uctl->iqn);
#endif /* TRACE_UCTL */
lun = uctl_get_intval(sp, "Lun");
if (lun < 0) {
lun = DEFAULT_UCTL_LUN;
}
uctl->lun = lun;
#ifdef TRACE_UCTL
printf("Lun %d\n", uctl->lun);
#endif /* TRACE_UCTL */
val = uctl_get_val(sp, "Flags");
if (val == NULL) {
val = DEFAULT_UCTL_MFLAGS;
}
uctl->mflags = xstrdup(val);
#ifdef TRACE_UCTL
printf("Flags %s\n", uctl->mflags);
#endif /* TRACE_UCTL */
val = uctl_get_val(sp, "Size");
if (val == NULL) {
val = DEFAULT_UCTL_MSIZE;
}
uctl->msize = xstrdup(val);
#ifdef TRACE_UCTL
printf("Size %s\n", uctl->msize);
#endif /* TRACE_UCTL */
timeout = uctl_get_intval(sp, "Timeout");
if (timeout < 0) {
timeout = DEFAULT_UCTL_TIMEOUT;
}
uctl->timeout = timeout;
#ifdef TRACE_UCTL
printf("Timeout %d\n", uctl->timeout);
#endif /* TRACE_UCTL */
val = uctl_get_val(sp, "AuthMethod");
if (val == NULL) {
uctl->req_auth_auto = 0;
uctl->req_auth = 0;
} else {
uctl->req_auth_auto = 0;
for (i = 0; ; i++) {
val = uctl_get_nmval(sp, "AuthMethod", 0, i);
if (val == NULL)
break;
if (strcasecmp(val, "CHAP") == 0) {
uctl->req_auth = 1;
} else if (strcasecmp(val, "Mutual") == 0) {
uctl->req_auth_mutual = 1;
} else if (strcasecmp(val, "Auto") == 0) {
uctl->req_auth_auto = 1;
uctl->req_auth = 0;
uctl->req_auth_mutual = 0;
} else if (strcasecmp(val, "None") == 0) {
uctl->req_auth = 0;
uctl->req_auth_mutual = 0;
} else {
fprintf(stderr, "unknown auth\n");
return -1;
}
}
if (uctl->req_auth_mutual && !uctl->req_auth) {
fprintf(stderr, "Mutual but not CHAP\n");
return -1;
}
}
#ifdef TRACE_UCTL
if (uctl->req_auth == 0) {
printf("AuthMethod Auto\n");
} else {
printf("AuthMethod %s %s\n",
uctl->req_auth ? "CHAP" : "",
uctl->req_auth_mutual ? "Mutual" : "");
}
#endif /* TRACE_UCTL */
val = uctl_get_nval(sp, "Auth", 0);
if (val == NULL) {
user = secret = muser = msecret = NULL;
} else {
user = uctl_get_nmval(sp, "Auth", 0, 0);
secret = uctl_get_nmval(sp, "Auth", 0, 1);
muser = uctl_get_nmval(sp, "Auth", 0, 2);
msecret = uctl_get_nmval(sp, "Auth", 0, 3);
}
uctl->auth.user = xstrdup(user);
uctl->auth.secret = xstrdup(secret);
uctl->auth.muser = xstrdup(muser);
uctl->auth.msecret = xstrdup(msecret);
#ifdef TRACE_UCTL
printf("user=%s, secret=%s, muser=%s, msecret=%s\n",
user, secret, muser, msecret);
#endif /* TRACE_UCTL */
return 0;
}
static void
usage(void)
{
printf("istgtcotrol [options] <command> [<file>]\n");
printf("options:\n");
printf("default may be changed by configuration file\n");
printf(" -c config config file (default %s)\n", DEFAULT_UCTL_CONFIG);
printf(" -h host target host name or IP (default %s)\n", DEFAULT_UCTL_HOST);
printf(" -p port port number (default %d)\n", DEFAULT_UCTL_PORT);
printf(" -t target target iqn\n");
printf(" -l lun target lun (default %d)\n", DEFAULT_UCTL_LUN);
printf(" -f flags media flags (default %s)\n", DEFAULT_UCTL_MFLAGS);
printf(" -s size media size (default %s)\n", DEFAULT_UCTL_MSIZE);
printf(" -q quiet mode\n");
printf(" -v verbose mode\n");
printf(" -A method authentication method (CHAP/Mutual CHAP/Auto)\n");
printf(" -U user auth user\n");
printf(" -S secret auth secret\n");
printf(" -M muser mutual auth user\n");
printf(" -R msecret mutual auth secret\n");
printf(" -H show this usage\n");
printf(" -V show version\n");
printf("command:\n");
printf(" noop no operation\n");
printf(" version show target version\n");
printf(" list list all or specified target\n");
printf(" load load media to specified unit\n");
printf(" unload unload media from specified unit\n");
printf(" change change media with <file> at specified unit\n");
printf(" reset reset specified lun of target\n");
printf(" info show connections of target\n");
}
int
main(int argc, char *argv[])
{
const char *config_file = DEFAULT_UCTL_CONFIG;
CONFIG *config;
UCTL xuctl, *uctl;
struct sigaction sigact, sigoldact;
int (*func) (UCTL_Ptr);
int port = -1;
int lun = -1;
const char *host = NULL;
const char *mflags = NULL;
const char *mfile = NULL;
const char *msize = NULL;
const char *mtype = DEFAULT_UCTL_MTYPE;
char *target = NULL;
char *user = NULL;
char *secret = NULL;
char *muser = NULL;
char *msecret = NULL;
char *cmd;
char *banner;
long l;
int exec_result;
int req_argc;
int req_target;
int quiet = 0;
int verbose = 0;
int req_auth = -1;
int ch;
int sock;
int rc;
int i;
#ifdef HAVE_SETPROCTITLE
setproctitle("version %s (%s)",
ISTGT_VERSION, ISTGT_EXTRA_VERSION);
#endif
memset(&xuctl, 0, sizeof xuctl);
uctl = &xuctl;
while ((ch = getopt(argc, argv, "c:h:p:t:l:f:s:qvA:U:S:M:R:VH")) != -1) {
switch (ch) {
case 'c':
config_file = optarg;
break;
case 'h':
host = optarg;
break;
case 'p':
l = strtol(optarg, NULL, 10);
if (l < 0 || l > 65535) {
fatal("invalid port %s\n", optarg);
}
port = (int) l;
break;
case 't':
target = optarg;
break;
case 'l':
l = strtol(optarg, NULL, 10);
if (l < 0 || l > 0x3fff) {
fatal("invalid lun %s\n", optarg);
}
lun = (int) l;
break;
case 'f':
mflags = optarg;
break;
case 's':
msize = optarg;
break;
case 'q':
quiet = 1;
break;
case 'v':
verbose = 1;
break;
case 'A':
if (strcasecmp(optarg, "CHAP") == 0) {
req_auth = 1;
} else if (strcasecmp(optarg, "Mutual") == 0
|| strcasecmp(optarg, "Mutual CHAP") == 0
|| strcasecmp(optarg, "CHAP Mutual") == 0) {
req_auth = 2;
} else if (strcasecmp(optarg, "Auto") == 0) {
req_auth = 0;
} else {
usage();
exit(EXIT_SUCCESS);
}
break;
case 'U':
user = optarg;
break;
case 'S':
secret = optarg;
#ifndef HAVE_SETPROCTITLE
secret = xstrdup(optarg);
memset(optarg, 'x', strlen(optarg));
#endif
break;
case 'M':
muser = optarg;
break;
case 'R':
msecret = optarg;
#ifndef HAVE_SETPROCTITLE
msecret = xstrdup(optarg);
memset(optarg, 'x', strlen(optarg));
#endif
break;
case 'V':
printf("istgtcontrol version %s (%s)\n",
ISTGT_VERSION, ISTGT_EXTRA_VERSION);
exit(EXIT_SUCCESS);
case 'H':
default:
usage();
exit(EXIT_SUCCESS);
}
}
argc -= optind;
argv += optind;
/* read config files */
config = istgt_allocate_config();
rc = istgt_read_config(config, config_file);
if (rc < 0) {
fprintf(stderr, "config error\n");
exit(EXIT_FAILURE);
}
if (config->section == NULL) {
fprintf(stderr, "empty config\n");
istgt_free_config(config);
exit(EXIT_FAILURE);
}
uctl->config = config;
//istgt_print_config(config);
istgtcontrol_open_log();
/* take specified command */
if (argc < 1) {
error_usage_return:
istgt_free_config(config);
usage();
exit(EXIT_FAILURE);
}
cmd = strupr(xstrdup(argv[0]));
argc--;
argv++;
/* get function pointer and parameters for specified command */
func = NULL;
req_argc = -1;
req_target = -1;
for (i = 0; exec_table[i].name != NULL; i++) {
if (cmd[0] == exec_table[i].name[0]
&& strcmp(cmd, exec_table[i].name) == 0) {
func = exec_table[i].func;
req_argc = exec_table[i].req_argc;
req_target = exec_table[i].req_target;
break;
}
}
if (func == NULL) {
istgt_free_config(config);
fatal("unknown command %s\n", cmd);
}
/* patrameter check */
if (argc < req_argc) {
goto error_usage_return;
}
#if 0
if (req_target) {
if (target == NULL) {
goto error_usage_return;
}
}
#endif
/* take args */
if (strcmp(cmd, "CHANGE") == 0) {
/* change require file */
mfile = argv[0];
}
/* build parameters */
rc = uctl_init(uctl);
if (rc < 0) {
fprintf(stderr, "uctl_init() failed\n");
istgt_free_config(config);
exit(EXIT_FAILURE);
}
uctl->recvtmpcnt = 0;
uctl->recvtmpidx = 0;
uctl->recvtmpsize = sizeof uctl->recvtmp;
uctl->recvbufsize = sizeof uctl->recvbuf;
uctl->sendbufsize = sizeof uctl->sendbuf;
uctl->worksize = sizeof uctl->work;
/* override by command line */
if (user != NULL) {
xfree(uctl->auth.user);
uctl->auth.user = xstrdup(user);
}
if (secret != NULL) {
xfree(uctl->auth.secret);
uctl->auth.secret = xstrdup(secret);
}
if (muser != NULL) {
xfree(uctl->auth.muser);
uctl->auth.muser = xstrdup(muser);
}
if (msecret != NULL) {
xfree(uctl->auth.msecret);
uctl->auth.msecret = xstrdup(msecret);
}
if (req_target) {
if (uctl->iqn == NULL
&& target == NULL) {
goto error_usage_return;
}
}
if (req_auth >= 0) {
uctl->req_auth_auto = 1;
uctl->req_auth = 0;
uctl->req_auth_mutual = 0;
if (req_auth > 1) {
uctl->req_auth_auto = 0;
uctl->req_auth = 1;
uctl->req_auth_mutual = 1;
} else if (req_auth > 0) {
uctl->req_auth_auto = 0;
uctl->req_auth = 1;
}
}
#ifdef TRACE_UCTL
printf("auto=%d, auth=%d, mutual=%d\n",
uctl->req_auth_auto, uctl->req_auth, uctl->req_auth_mutual);
#endif /* TRACE_UCTL */
if (host != NULL) {
xfree(uctl->host);
uctl->host = xstrdup(host);
}
if (port >= 0) {
uctl->port = port;
}
if (target != NULL) {
xfree(uctl->iqn);
if (strcasecmp(target, "ALL") == 0) {
uctl->iqn = NULL;
} else {
uctl->iqn = escape_string(target);
}
}
if (lun >= 0) {
uctl->lun = lun;
}
if (mflags != NULL) {
xfree(uctl->mflags);
uctl->mflags = escape_string(mflags);
}
uctl->mfile = escape_string(mfile);
if (msize != NULL) {
xfree(uctl->msize);
uctl->msize = escape_string(msize);
}
uctl->mtype = escape_string(mtype);
uctl->cmd = escape_string(cmd);
/* show setting */
#define NULLP(S) ((S) == NULL ? "NULL" : (S))
if (verbose) {
printf("iqn=%s, lun=%d\n", NULLP(uctl->iqn), uctl->lun);
printf("media file=%s, flags=%s, size=%s\n",
NULLP(uctl->mfile), NULLP(uctl->mflags), NULLP(uctl->msize));
}
/* set signals */
memset(&sigact, 0, sizeof sigact);
memset(&sigoldact, 0, sizeof sigoldact);
sigact.sa_handler = SIG_IGN;
sigemptyset(&sigact.sa_mask);
if (sigaction(SIGPIPE, &sigact, &sigoldact) != 0) {
istgt_free_config(config);
fatal("sigaction() failed");
}
/* connect to target */
if (verbose) {
printf("connect to %s:%d\n", uctl->host, uctl->port);
}
sock = istgt_connect(uctl->host, uctl->port);
if (sock < 0) {
istgt_free_config(config);
fatal("istgt_connect(%s:%d) failed\n", uctl->host, uctl->port);
}
uctl->sock = sock;
/* get target banner (ready to send) */
banner = get_banner(uctl);
if (banner == NULL) {
close(uctl->sock);
istgt_free_config(config);
fatal("get_banner() failed\n");
}
if (verbose) {
printf("target banner \"%s\"\n", banner);
}
/* authentication */
retry_auth:
if (uctl->req_auth) {
rc = do_auth(uctl);
if (rc != UCTL_CMD_OK) {
if (rc == UCTL_CMD_REQAUTH
|| rc == UCTL_CMD_CHAPSEQ) {
retry_auth_auto:
/* Auth negotiation */
if (uctl->req_auth == 0) {
#ifdef TRCAE_UCTL
printf("Auto negotiation CHAP\n");
#endif /* TRCAE_UCTL */
uctl->req_auth = 1;
goto retry_auth;
} else if (uctl->req_auth_mutual == 0) {
#ifdef TRCAE_UCTL
printf("Auto negotiation Mutual CHAP\n");
#endif /* TRCAE_UCTL */
uctl->req_auth_mutual = 1;
goto retry_auth;
}
}
if (!quiet) {
printf("AUTH failed\n");
}
exec_result = rc;
goto disconnect;
}
}
/* send specified command */
rc = func(uctl);
exec_result = rc;
if (rc != UCTL_CMD_OK) {
if (rc == UCTL_CMD_REQAUTH
|| rc == UCTL_CMD_CHAPSEQ) {
goto retry_auth_auto;
}
if (!quiet) {
printf("ABORT %s command\n", uctl->cmd);
}
} else {
if (!quiet) {
printf("DONE %s command\n", uctl->cmd);
}
}
/* disconnect from target */
disconnect:
rc = exec_quit(uctl);
if (rc != UCTL_CMD_OK) {
fprintf(stderr, "QUIT failed\n");
/* error but continue */
}
/* cleanup */
close(sock);
xfree(uctl->host);
xfree(uctl->iqn);
xfree(uctl->mflags);
xfree(uctl->mfile);
xfree(uctl->msize);
xfree(uctl->mtype);
xfree(uctl->cmd);
xfree(banner);
xfree(cmd);
istgt_free_config(config);
istgtcontrol_close_log();
/* return value as execution result */
if (exec_result != UCTL_CMD_OK) {
exit(EXIT_FAILURE);
}
return EXIT_SUCCESS;
}
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>