File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / istgt / src / istgt_lu_ctl.c
Revision 1.1.1.1 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Tue Feb 21 16:42:02 2012 UTC (12 years, 5 months ago) by misho
Branches: istgt, MAIN
CVS tags: v20111008, HEAD
istgt

    1: /*
    2:  * Copyright (C) 2008-2011 Daisuke Aoyama <aoyama@peach.ne.jp>.
    3:  * All rights reserved.
    4:  *
    5:  * Redistribution and use in source and binary forms, with or without
    6:  * modification, are permitted provided that the following conditions
    7:  * are met:
    8:  * 1. Redistributions of source code must retain the above copyright
    9:  *    notice, this list of conditions and the following disclaimer.
   10:  * 2. Redistributions in binary form must reproduce the above copyright
   11:  *    notice, this list of conditions and the following disclaimer in the
   12:  *    documentation and/or other materials provided with the distribution.
   13:  *
   14:  * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
   15:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   16:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   17:  * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
   18:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   19:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   20:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   21:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   22:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   23:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   24:  * SUCH DAMAGE.
   25:  *
   26:  */
   27: 
   28: #ifdef HAVE_CONFIG_H
   29: #include "config.h"
   30: #endif
   31: 
   32: #include <inttypes.h>
   33: #include <stdint.h>
   34: 
   35: #include <stdarg.h>
   36: #include <stdio.h>
   37: #include <stdlib.h>
   38: #include <string.h>
   39: #include <pthread.h>
   40: #ifdef HAVE_PTHREAD_NP_H
   41: #include <pthread_np.h>
   42: #endif
   43: #include <unistd.h>
   44: #include <sys/param.h>
   45: 
   46: #include "istgt.h"
   47: #include "istgt_ver.h"
   48: #include "istgt_log.h"
   49: #include "istgt_sock.h"
   50: #include "istgt_misc.h"
   51: #include "istgt_md5.h"
   52: #include "istgt_lu.h"
   53: #include "istgt_iscsi.h"
   54: #include "istgt_proto.h"
   55: 
   56: #define TIMEOUT_RW 60
   57: #define MAX_LINEBUF 4096
   58: 
   59: typedef struct istgt_uctl_t {
   60: 	int id;
   61: 
   62: 	ISTGT_Ptr istgt;
   63: 	PORTAL portal;
   64: 	int sock;
   65: 	pthread_t thread;
   66: 
   67: 	int family;
   68: 	char caddr[MAX_ADDRBUF];
   69: 	char saddr[MAX_ADDRBUF];
   70: 
   71: 	ISTGT_CHAP_AUTH auth;
   72: 	int authenticated;
   73: 
   74: 	int timeout;
   75: 	int auth_group;
   76: 	int no_auth;
   77: 	int req_auth;
   78: 	int req_mutual;
   79: 
   80: 	char *mediadirectory;
   81: 
   82: 	int recvtmpsize;
   83: 	int recvtmpcnt;
   84: 	int recvtmpidx;
   85: 	int recvbufsize;
   86: 	int sendbufsize;
   87: 	int worksize;
   88: 	char recvtmp[MAX_LINEBUF];
   89: 	char recvbuf[MAX_LINEBUF];
   90: 	char sendbuf[MAX_LINEBUF];
   91: 	char work[MAX_LINEBUF];
   92: 	char *cmd;
   93: 	char *arg;
   94: } UCTL;
   95: typedef UCTL *UCTL_Ptr;
   96: 
   97: typedef enum {
   98: 	UCTL_CMD_OK = 0,
   99: 	UCTL_CMD_ERR = 1,
  100: 	UCTL_CMD_EOF = 2,
  101: 	UCTL_CMD_QUIT = 3,
  102: 	UCTL_CMD_DISCON = 4,
  103: } UCTL_CMD_STATUS;
  104: 
  105: #define ARGS_DELIM " \t"
  106: 
  107: static int
  108: istgt_uctl_readline(UCTL_Ptr uctl)
  109: {
  110: 	ssize_t total;
  111: 
  112: 	total = istgt_readline_socket(uctl->sock, uctl->recvbuf, uctl->recvbufsize,
  113: 	    uctl->recvtmp, uctl->recvtmpsize,
  114: 	    &uctl->recvtmpidx, &uctl->recvtmpcnt,
  115: 	    uctl->timeout);
  116: 	if (total < 0) {
  117: 		return UCTL_CMD_DISCON;
  118: 	}
  119: 	if (total == 0) {
  120: 		return UCTL_CMD_EOF;
  121: 	}
  122: 	return UCTL_CMD_OK;
  123: }
  124: 
  125: static int
  126: istgt_uctl_writeline(UCTL_Ptr uctl)
  127: {
  128: 	ssize_t total;
  129: 	ssize_t expect;
  130: 
  131: 	expect = strlen(uctl->sendbuf);
  132: 	total = istgt_writeline_socket(uctl->sock, uctl->sendbuf, uctl->timeout);
  133: 	if (total < 0) {
  134: 		return UCTL_CMD_DISCON;
  135: 	}
  136: 	if (total != expect) {
  137: 		return UCTL_CMD_ERR;
  138: 	}
  139: 	return UCTL_CMD_OK;
  140: }
  141: 
  142: static int
  143: istgt_uctl_snprintf(UCTL_Ptr uctl, const char *format, ...)
  144: {
  145: 	va_list ap;
  146: 	int rc;
  147: 
  148: 	va_start(ap, format);
  149: 	rc = vsnprintf(uctl->sendbuf, uctl->sendbufsize, format, ap);
  150: 	va_end(ap);
  151: 	return rc;
  152: }
  153: 
  154: static int
  155: istgt_uctl_get_media_present(ISTGT_LU_Ptr lu, int lun)
  156: {
  157: 	int rc;
  158: 
  159: 	switch (lu->type) {
  160: 	case ISTGT_LU_TYPE_DVD:
  161: 		MTX_LOCK(&lu->mutex);
  162: 		rc = istgt_lu_dvd_media_present(lu->lun[lun].spec);
  163: 		MTX_UNLOCK(&lu->mutex);
  164: 		break;
  165: 	case ISTGT_LU_TYPE_TAPE:
  166: 		MTX_LOCK(&lu->mutex);
  167: 		rc = istgt_lu_tape_media_present(lu->lun[lun].spec);
  168: 		MTX_UNLOCK(&lu->mutex);
  169: 		break;
  170: 	default:
  171: 		rc = 0;
  172: 	}
  173: 	return rc;
  174: }
  175: 
  176: static int
  177: istgt_uctl_get_media_lock(ISTGT_LU_Ptr lu, int lun)
  178: {
  179: 	int rc;
  180: 
  181: 	switch (lu->type) {
  182: 	case ISTGT_LU_TYPE_DVD:
  183: 		MTX_LOCK(&lu->mutex);
  184: 		rc = istgt_lu_dvd_media_lock(lu->lun[lun].spec);
  185: 		MTX_UNLOCK(&lu->mutex);
  186: 		break;
  187: 	case ISTGT_LU_TYPE_TAPE:
  188: 		MTX_LOCK(&lu->mutex);
  189: 		rc = istgt_lu_tape_media_lock(lu->lun[lun].spec);
  190: 		MTX_UNLOCK(&lu->mutex);
  191: 		break;
  192: 	default:
  193: 		rc = 0;
  194: 	}
  195: 	return rc;
  196: }
  197: 
  198: static int
  199: istgt_uctl_get_authinfo(UCTL_Ptr uctl, const char *authuser)
  200: {
  201: 	char *authfile = NULL;
  202: 	int ag_tag;
  203: 	int rc;
  204: 
  205: 	ag_tag = uctl->auth_group;
  206: 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ag_tag=%d\n", ag_tag);
  207: 
  208: 	MTX_LOCK(&uctl->istgt->mutex);
  209: 	authfile = xstrdup(uctl->istgt->authfile);
  210: 	MTX_UNLOCK(&uctl->istgt->mutex);
  211: 
  212: 	rc = istgt_chap_get_authinfo(&uctl->auth, authfile, authuser, ag_tag);
  213: 	if (rc < 0) {
  214: 		ISTGT_ERRLOG("chap_get_authinfo() failed\n");
  215: 		xfree(authfile);
  216: 		return -1;
  217: 	}
  218: 	xfree(authfile);
  219: 	return 0;
  220: }
  221: 
  222: static int
  223: istgt_uctl_cmd_auth(UCTL_Ptr uctl)
  224: {
  225: 	const char *delim = ARGS_DELIM;
  226: 	char *arg;
  227: 	char *label;
  228: 	char *chap_a;
  229: 	char *chap_i;
  230: 	char *chap_c;
  231: 	char *chap_n;
  232: 	char *chap_r;
  233: 	int rc;
  234: 
  235: 	arg = uctl->arg;
  236: 	label = strsepq(&arg, delim);
  237: 
  238: 	if (label == NULL) {
  239: 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
  240: 		rc = istgt_uctl_writeline(uctl);
  241: 		if (rc != UCTL_CMD_OK) {
  242: 			return rc;
  243: 		}
  244: 		return UCTL_CMD_ERR;
  245: 	}
  246: 
  247: 	if (strcasecmp(label, "CHAP_A") == 0) {
  248: 		if (uctl->auth.chap_phase != ISTGT_CHAP_PHASE_WAIT_A) {
  249: 			istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n");
  250: 		error_return:
  251: 			uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A;
  252: 			rc = istgt_uctl_writeline(uctl);
  253: 			if (rc != UCTL_CMD_OK) {
  254: 				return rc;
  255: 			}
  256: 			return UCTL_CMD_ERR;
  257: 		}
  258: 
  259: 		chap_a = strsepq(&arg, delim);
  260: 		if (chap_a == NULL  || strcasecmp(chap_a, "5") != 0) {
  261: 			istgt_uctl_snprintf(uctl, "ERR invalid algorithm\n");
  262: 			goto error_return;
  263: 		}
  264: 
  265: 		/* Identifier is one octet */
  266: 		istgt_gen_random(uctl->auth.chap_id, 1);
  267: 		/* Challenge Value is a variable stream of octets */
  268: 		/* (binary length MUST not exceed 1024 bytes) */
  269: 		uctl->auth.chap_challenge_len = ISTGT_CHAP_CHALLENGE_LEN;
  270: 		istgt_gen_random(uctl->auth.chap_challenge,
  271: 		    uctl->auth.chap_challenge_len);
  272: 
  273: 		istgt_bin2hex(uctl->work, uctl->worksize,
  274: 		    uctl->auth.chap_challenge,
  275: 		    uctl->auth.chap_challenge_len);
  276: 
  277: 		istgt_uctl_snprintf(uctl, "%s CHAP_IC %d %s\n",
  278: 		    uctl->cmd, (int) uctl->auth.chap_id[0],
  279: 		    uctl->work);
  280: 		
  281: 		rc = istgt_uctl_writeline(uctl);
  282: 		if (rc != UCTL_CMD_OK) {
  283: 			return rc;
  284: 		}
  285: 		uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_NR;
  286: 		/* 3-way handshake */
  287: 		return UCTL_CMD_OK;
  288: 	} else if (strcasecmp(label, "CHAP_NR") == 0) {
  289: 		uint8_t resmd5[ISTGT_MD5DIGEST_LEN];
  290: 		uint8_t tgtmd5[ISTGT_MD5DIGEST_LEN];
  291: 		ISTGT_MD5CTX md5ctx;
  292: 
  293: 		if (uctl->auth.chap_phase != ISTGT_CHAP_PHASE_WAIT_NR) {
  294: 			istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n");
  295: 			goto error_return;
  296: 		}
  297: 
  298: 		chap_n = strsepq(&arg, delim);
  299: 		chap_r = strsepq(&arg, delim);
  300: 		if (chap_n == NULL || chap_r == NULL) {
  301: 			istgt_uctl_snprintf(uctl, "ERR no response\n");
  302: 			goto error_return;
  303: 		}
  304: 		//ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "N=%s, R=%s\n", chap_n, chap_r);
  305: 
  306: 		rc = istgt_hex2bin(resmd5, ISTGT_MD5DIGEST_LEN, chap_r);
  307: 		if (rc < 0 || rc != ISTGT_MD5DIGEST_LEN) {
  308: 			istgt_uctl_snprintf(uctl, "ERR response format error\n");
  309: 			goto error_return;
  310: 		}
  311: 
  312: 		rc = istgt_uctl_get_authinfo(uctl, chap_n);
  313: 		if (rc < 0) {
  314: 			ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n);
  315: 			istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n");
  316: 			goto error_return;
  317: 		}
  318: 		if (uctl->auth.user == NULL || uctl->auth.secret == NULL) {
  319: 			ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n);
  320: 			istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n");
  321: 			goto error_return;
  322: 		}
  323: 
  324: 		istgt_md5init(&md5ctx);
  325: 		/* Identifier */
  326: 		istgt_md5update(&md5ctx, uctl->auth.chap_id, 1);
  327: 		/* followed by secret */
  328: 		istgt_md5update(&md5ctx, uctl->auth.secret,
  329: 		    strlen(uctl->auth.secret));
  330: 		/* followed by Challenge Value */
  331: 		istgt_md5update(&md5ctx, uctl->auth.chap_challenge,
  332: 		    uctl->auth.chap_challenge_len);
  333: 		/* tgtmd5 is expecting Response Value */
  334: 		istgt_md5final(tgtmd5, &md5ctx);
  335: 
  336: 		/* compare MD5 digest */
  337: 		if (memcmp(tgtmd5, resmd5, ISTGT_MD5DIGEST_LEN) != 0) {
  338: 			/* not match */
  339: 			ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n);
  340: 			istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n");
  341: 			goto error_return;
  342: 		}
  343: 		/* OK client's secret */
  344: 		uctl->authenticated = 1;
  345: 
  346: 		/* mutual CHAP? */
  347: 		chap_i = strsepq(&arg, delim);
  348: 		chap_c = strsepq(&arg, delim);
  349: 		if (chap_i != NULL && chap_c != NULL) {
  350: 			/* Identifier */
  351: 			uctl->auth.chap_mid[0] = (uint8_t) strtol(chap_i, NULL, 10);
  352: 			/* Challenge Value */
  353: 			rc = istgt_hex2bin(uctl->auth.chap_mchallenge,
  354: 			    ISTGT_CHAP_CHALLENGE_LEN, chap_c);
  355: 			if (rc < 0) {
  356: 				istgt_uctl_snprintf(uctl, "ERR challenge format error\n");
  357: 				goto error_return;
  358: 			}
  359: 			uctl->auth.chap_mchallenge_len = rc;
  360: 
  361: 			if (uctl->auth.muser == NULL || uctl->auth.msecret == NULL) {
  362: 				ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n);
  363: 				istgt_uctl_snprintf(uctl,
  364: 				    "ERR auth user or secret is missing\n");
  365: 				goto error_return;
  366: 			}
  367: 
  368: 			istgt_md5init(&md5ctx);
  369: 			/* Identifier */
  370: 			istgt_md5update(&md5ctx, uctl->auth.chap_mid, 1);
  371: 			/* followed by secret */
  372: 			istgt_md5update(&md5ctx, uctl->auth.msecret,
  373: 			    strlen(uctl->auth.msecret));
  374: 			/* followed by Challenge Value */
  375: 			istgt_md5update(&md5ctx, uctl->auth.chap_mchallenge,
  376: 			    uctl->auth.chap_mchallenge_len);
  377: 			/* tgtmd5 is Response Value */
  378: 			istgt_md5final(tgtmd5, &md5ctx);
  379: 
  380: 			istgt_bin2hex(uctl->work, uctl->worksize,
  381: 			    tgtmd5, ISTGT_MD5DIGEST_LEN);
  382: 
  383: 			/* send NR for mutual CHAP */
  384: 			istgt_uctl_snprintf(uctl, "%s CHAP_NR \"%s\" %s\n",
  385: 			    uctl->cmd,
  386: 			    uctl->auth.muser,
  387: 			    uctl->work);
  388: 			rc = istgt_uctl_writeline(uctl);
  389: 			if (rc != UCTL_CMD_OK) {
  390: 				return rc;
  391: 			}
  392: 		} else {
  393: 			/* not mutual */
  394: 			if (uctl->req_mutual) {
  395: 				ISTGT_ERRLOG("required mutual CHAP\n");
  396: 				istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n");
  397: 				goto error_return;
  398: 			}
  399: 		}
  400: 
  401: 		uctl->auth.chap_phase = ISTGT_CHAP_PHASE_END;
  402: 	} else {
  403: 		istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n");
  404: 		goto error_return;
  405: 	}
  406: 
  407: 	/* auth succeeded (but mutual may fail) */
  408: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
  409: 	rc = istgt_uctl_writeline(uctl);
  410: 	if (rc != UCTL_CMD_OK) {
  411: 		return rc;
  412: 	}
  413: 	return UCTL_CMD_OK;
  414: }
  415: 
  416: static int
  417: istgt_uctl_cmd_quit(UCTL_Ptr uctl)
  418: {
  419: 	int rc;
  420: 
  421: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
  422: 	rc = istgt_uctl_writeline(uctl);
  423: 	if (rc != UCTL_CMD_OK) {
  424: 		return rc;
  425: 	}
  426: 	return UCTL_CMD_QUIT;
  427: }
  428: 
  429: static int
  430: istgt_uctl_cmd_noop(UCTL_Ptr uctl)
  431: {
  432: 	int rc;
  433: 
  434: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
  435: 	rc = istgt_uctl_writeline(uctl);
  436: 	if (rc != UCTL_CMD_OK) {
  437: 		return rc;
  438: 	}
  439: 	return UCTL_CMD_OK;
  440: }
  441: 
  442: static int
  443: istgt_uctl_cmd_version(UCTL_Ptr uctl)
  444: {
  445: 	int rc;
  446: 
  447: 	istgt_uctl_snprintf(uctl, "%s %s (%s)\n", uctl->cmd,
  448: 	    ISTGT_VERSION, ISTGT_EXTRA_VERSION);
  449: 	rc = istgt_uctl_writeline(uctl);
  450: 	if (rc != UCTL_CMD_OK) {
  451: 		return rc;
  452: 	}
  453: 
  454: 	/* version succeeded */
  455: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
  456: 	rc = istgt_uctl_writeline(uctl);
  457: 	if (rc != UCTL_CMD_OK) {
  458: 		return rc;
  459: 	}
  460: 	return UCTL_CMD_OK;
  461: }
  462: 
  463: static int
  464: istgt_uctl_cmd_list(UCTL_Ptr uctl)
  465: {
  466: 	ISTGT_LU_Ptr lu;
  467: 	ISTGT_LU_LUN_Ptr llp;
  468: 	const char *delim = ARGS_DELIM;
  469: 	char *arg;
  470: 	char *iqn;
  471: 	char *lun;
  472: 	char *mflags;
  473: 	char *mfile;
  474: 	char *msize;
  475: 	char *mtype;
  476: 	char *workp;
  477: 	int lun_i;
  478: 	int worksize;
  479: 	int present;
  480: 	int lock;
  481: 	int rc;
  482: 	int i;
  483: 
  484: 	arg = uctl->arg;
  485: 	iqn = strsepq(&arg, delim);
  486: 	lun = strsepq(&arg, delim);
  487: 
  488: 	if (arg != NULL) {
  489: 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
  490: 		rc = istgt_uctl_writeline(uctl);
  491: 		if (rc != UCTL_CMD_OK) {
  492: 			return rc;
  493: 		}
  494: 		return UCTL_CMD_ERR;
  495: 	}
  496: 
  497: 	if (iqn == NULL) {
  498: 		/* all targets */
  499: 		MTX_LOCK(&uctl->istgt->mutex);
  500: 		for (i = 0; i < MAX_LOGICAL_UNIT; i++) {
  501: 			lu = uctl->istgt->logical_unit[i];
  502: 			if (lu == NULL)
  503: 				continue;
  504: 			istgt_uctl_snprintf(uctl, "%s %s\n", uctl->cmd, lu->name);
  505: 			rc = istgt_uctl_writeline(uctl);
  506: 			if (rc != UCTL_CMD_OK) {
  507: 				MTX_UNLOCK(&uctl->istgt->mutex);
  508: 				return rc;
  509: 			}
  510: 		}
  511: 		MTX_UNLOCK(&uctl->istgt->mutex);
  512: 	} else {
  513: 		/* specified target */
  514: 		MTX_LOCK(&uctl->istgt->mutex);
  515: 		if (lun == NULL) {
  516: 			lun_i = 0;
  517: 		} else {
  518: 			lun_i = (int) strtol(lun, NULL, 10);
  519: 		}
  520: 		lu = istgt_lu_find_target(uctl->istgt, iqn);
  521: 		if (lu == NULL) {
  522: 			MTX_UNLOCK(&uctl->istgt->mutex);
  523: 			istgt_uctl_snprintf(uctl, "ERR no target\n");
  524: 		error_return:
  525: 			rc = istgt_uctl_writeline(uctl);
  526: 			if (rc != UCTL_CMD_OK) {
  527: 				return rc;
  528: 			}
  529: 			return UCTL_CMD_ERR;
  530: 		}
  531: 		if (lun_i < 0 || lun_i >= lu->maxlun) {
  532: 			MTX_UNLOCK(&uctl->istgt->mutex);
  533: 			istgt_uctl_snprintf(uctl, "ERR no target\n");
  534: 			goto error_return;
  535: 		}
  536: 		llp = &lu->lun[lun_i];
  537: 
  538: 		worksize = uctl->worksize;
  539: 		workp = uctl->work;
  540: 
  541: 		switch (llp->type) {
  542: 		case ISTGT_LU_LUN_TYPE_REMOVABLE:
  543: 			mflags = istgt_lu_get_media_flags_string(llp->u.removable.flags,
  544: 			    workp, worksize);
  545: 			worksize -= strlen(mflags) + 1;
  546: 			workp += strlen(mflags) + 1;
  547: 			present = istgt_uctl_get_media_present(lu, lun_i);
  548: 			lock = istgt_uctl_get_media_lock(lu, lun_i);
  549: 			mfile = llp->u.removable.file;
  550: 			if (llp->u.removable.flags & ISTGT_LU_FLAG_MEDIA_AUTOSIZE) {
  551: 				snprintf(workp, worksize, "auto");
  552: 			} else {
  553: 				snprintf(workp, worksize, "%"PRIu64,
  554: 				    llp->u.removable.size);
  555: 			}
  556: 			msize = workp;
  557: 			worksize -= strlen(msize) + 1;
  558: 			workp += strlen(msize) + 1;
  559: 			snprintf(workp, worksize, "-");
  560: 			mtype = workp;
  561: 			worksize -= strlen(msize) + 1;
  562: 			workp += strlen(msize) + 1;
  563: 
  564: 			istgt_uctl_snprintf(uctl, "%s lun%u %s %s %s %s %s \"%s\" %s\n",
  565: 			    uctl->cmd, lun_i,
  566: 			    "removable",
  567: 			    (present ? "present" : "absent"),
  568: 			    (lock ? "lock" : "unlock"),
  569: 			    mtype, mflags, mfile, msize);
  570: 			rc = istgt_uctl_writeline(uctl);
  571: 			break;
  572: 		case ISTGT_LU_LUN_TYPE_STORAGE:
  573: 			mfile = llp->u.storage.file;
  574: 			snprintf(workp, worksize, "%"PRIu64,
  575: 			    llp->u.storage.size);
  576: 			msize = workp;
  577: 			worksize -= strlen(msize) + 1;
  578: 			workp += strlen(msize) + 1;
  579: 
  580: 			istgt_uctl_snprintf(uctl, "%s lun%u %s \"%s\" %s\n",
  581: 			    uctl->cmd, lun_i,
  582: 			    "storage",
  583: 			    mfile, msize);
  584: 			rc = istgt_uctl_writeline(uctl);
  585: 			break;
  586: 		case ISTGT_LU_LUN_TYPE_DEVICE:
  587: 			mfile = llp->u.device.file;
  588: 
  589: 			istgt_uctl_snprintf(uctl, "%s lun%u %s \"%s\"\n",
  590: 			    uctl->cmd, lun_i,
  591: 			    "device",
  592: 			    mfile);
  593: 			rc = istgt_uctl_writeline(uctl);
  594: 			break;
  595: 		case ISTGT_LU_LUN_TYPE_SLOT:
  596: 		default:
  597: 			MTX_UNLOCK(&uctl->istgt->mutex);
  598: 			istgt_uctl_snprintf(uctl, "ERR unsupport LUN type\n");
  599: 			goto error_return;
  600: 		}
  601: 
  602: 		if (rc != UCTL_CMD_OK) {
  603: 			MTX_UNLOCK(&uctl->istgt->mutex);
  604: 			return rc;
  605: 		}
  606: 		MTX_UNLOCK(&uctl->istgt->mutex);
  607: 	}
  608: 
  609: 	/* list succeeded */
  610: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
  611: 	rc = istgt_uctl_writeline(uctl);
  612: 	if (rc != UCTL_CMD_OK) {
  613: 		return rc;
  614: 	}
  615: 	return UCTL_CMD_OK;
  616: }
  617: 
  618: static int
  619: istgt_uctl_cmd_unload(UCTL_Ptr uctl)
  620: {
  621: 	ISTGT_LU_Ptr lu;
  622: 	ISTGT_LU_LUN_Ptr llp;
  623: 	const char *delim = ARGS_DELIM;
  624: 	char *arg;
  625: 	char *iqn;
  626: 	char *lun;
  627: 	int lun_i;
  628: 	int rc;
  629: 
  630: 	arg = uctl->arg;
  631: 	iqn = strsepq(&arg, delim);
  632: 	lun = strsepq(&arg, delim);
  633: 
  634: 	if (iqn == NULL || arg != NULL) {
  635: 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
  636: 		rc = istgt_uctl_writeline(uctl);
  637: 		if (rc != UCTL_CMD_OK) {
  638: 			return rc;
  639: 		}
  640: 		return UCTL_CMD_ERR;
  641: 	}
  642: 
  643: 	if (lun == NULL) {
  644: 		lun_i = 0;
  645: 	} else {
  646: 		lun_i = (int) strtol(lun, NULL, 10);
  647: 	}
  648: 	lu = istgt_lu_find_target(uctl->istgt, iqn);
  649: 	if (lu == NULL) {
  650: 		istgt_uctl_snprintf(uctl, "ERR no target\n");
  651: 	error_return:
  652: 		rc = istgt_uctl_writeline(uctl);
  653: 		if (rc != UCTL_CMD_OK) {
  654: 			return rc;
  655: 		}
  656: 		return UCTL_CMD_ERR;
  657: 	}
  658: 	if (lun_i < 0 || lun_i >= lu->maxlun) {
  659: 		istgt_uctl_snprintf(uctl, "ERR no target\n");
  660: 		goto error_return;
  661: 	}
  662: 	llp = &lu->lun[lun_i];
  663: 	if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
  664: 		istgt_uctl_snprintf(uctl, "ERR not removable\n");
  665: 		goto error_return;
  666: 	}
  667: 
  668: 	/* unload media from lun */
  669: 	switch (lu->type) {
  670: 	case ISTGT_LU_TYPE_DVD:
  671: 		MTX_LOCK(&lu->mutex);
  672: 		rc = istgt_lu_dvd_unload_media(lu->lun[lun_i].spec);
  673: 		MTX_UNLOCK(&lu->mutex);
  674: 		break;
  675: 	case ISTGT_LU_TYPE_TAPE:
  676: 		MTX_LOCK(&lu->mutex);
  677: 		rc = istgt_lu_tape_unload_media(lu->lun[lun_i].spec);
  678: 		MTX_UNLOCK(&lu->mutex);
  679: 		break;
  680: 	default:
  681: 		rc = -1;
  682: 	}
  683: 
  684: 	if (rc < 0) {
  685: 		istgt_uctl_snprintf(uctl, "ERR unload\n");
  686: 		rc = istgt_uctl_writeline(uctl);
  687: 		if (rc != UCTL_CMD_OK) {
  688: 			return rc;
  689: 		}
  690: 		return UCTL_CMD_ERR;
  691: 	}
  692: 
  693: 	/* logging event */
  694: 	ISTGT_NOTICELOG("Media Unload %s lun%d from %s\n",
  695: 	    iqn, lun_i, uctl->caddr);
  696: 
  697: 	/* unload succeeded */
  698: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
  699: 	rc = istgt_uctl_writeline(uctl);
  700: 	if (rc != UCTL_CMD_OK) {
  701: 		return rc;
  702: 	}
  703: 	return UCTL_CMD_OK;
  704: }
  705: 
  706: static int
  707: istgt_uctl_cmd_load(UCTL_Ptr uctl)
  708: {
  709: 	ISTGT_LU_Ptr lu;
  710: 	ISTGT_LU_LUN_Ptr llp;
  711: 	const char *delim = ARGS_DELIM;
  712: 	char *arg;
  713: 	char *iqn;
  714: 	char *lun;
  715: 	int lun_i;
  716: 	int rc;
  717: 
  718: 	arg = uctl->arg;
  719: 	iqn = strsepq(&arg, delim);
  720: 	lun = strsepq(&arg, delim);
  721: 
  722: 	if (iqn == NULL || arg != NULL) {
  723: 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
  724: 		rc = istgt_uctl_writeline(uctl);
  725: 		if (rc != UCTL_CMD_OK) {
  726: 			return rc;
  727: 		}
  728: 		return UCTL_CMD_ERR;
  729: 	}
  730: 
  731: 	if (lun == NULL) {
  732: 		lun_i = 0;
  733: 	} else {
  734: 		lun_i = (int) strtol(lun, NULL, 10);
  735: 	}
  736: 	lu = istgt_lu_find_target(uctl->istgt, iqn);
  737: 	if (lu == NULL) {
  738: 		istgt_uctl_snprintf(uctl, "ERR no target\n");
  739: 	error_return:
  740: 		rc = istgt_uctl_writeline(uctl);
  741: 		if (rc != UCTL_CMD_OK) {
  742: 			return rc;
  743: 		}
  744: 		return UCTL_CMD_ERR;
  745: 	}
  746: 	if (lun_i < 0 || lun_i >= lu->maxlun) {
  747: 		istgt_uctl_snprintf(uctl, "ERR no target\n");
  748: 		goto error_return;
  749: 	}
  750: 	llp = &lu->lun[lun_i];
  751: 	if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
  752: 		istgt_uctl_snprintf(uctl, "ERR not removable\n");
  753: 		goto error_return;
  754: 	}
  755: 
  756: 	/* load media to lun */
  757: 	switch (lu->type) {
  758: 	case ISTGT_LU_TYPE_DVD:
  759: 		MTX_LOCK(&lu->mutex);
  760: 		rc = istgt_lu_dvd_load_media(lu->lun[lun_i].spec);
  761: 		MTX_UNLOCK(&lu->mutex);
  762: 		break;
  763: 	case ISTGT_LU_TYPE_TAPE:
  764: 		MTX_LOCK(&lu->mutex);
  765: 		rc = istgt_lu_tape_load_media(lu->lun[lun_i].spec);
  766: 		MTX_UNLOCK(&lu->mutex);
  767: 		break;
  768: 	default:
  769: 		rc = -1;
  770: 	}
  771: 
  772: 	if (rc < 0) {
  773: 		istgt_uctl_snprintf(uctl, "ERR load\n");
  774: 		rc = istgt_uctl_writeline(uctl);
  775: 		if (rc != UCTL_CMD_OK) {
  776: 			return rc;
  777: 		}
  778: 		return UCTL_CMD_ERR;
  779: 	}
  780: 
  781: 	/* logging event */
  782: 	ISTGT_NOTICELOG("Media Load %s lun%d from %s\n",
  783: 	    iqn, lun_i, uctl->caddr);
  784: 
  785: 	/* load succeeded */
  786: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
  787: 	rc = istgt_uctl_writeline(uctl);
  788: 	if (rc != UCTL_CMD_OK) {
  789: 		return rc;
  790: 	}
  791: 	return UCTL_CMD_OK;
  792: }
  793: 
  794: static int
  795: istgt_uctl_cmd_change(UCTL_Ptr uctl)
  796: {
  797: 	ISTGT_LU_Ptr lu;
  798: 	ISTGT_LU_LUN_Ptr llp;
  799: 	const char *delim = ARGS_DELIM;
  800: 	char empty_flags[] = "ro";
  801: 	char empty_size[] = "0";
  802: 	char *arg;
  803: 	char *iqn;
  804: 	char *lun;
  805: 	char *type;
  806: 	char *flags;
  807: 	char *file;
  808: 	char *size;
  809: 	char *safedir;
  810: 	char *fullpath;
  811: 	char *abspath;
  812: 	int lun_i;
  813: 	int len;
  814: 	int rc;
  815: 
  816: 	arg = uctl->arg;
  817: 	iqn = strsepq(&arg, delim);
  818: 	lun = strsepq(&arg, delim);
  819: 
  820: 	type = strsepq(&arg, delim);
  821: 	flags = strsepq(&arg, delim);
  822: 	file = strsepq(&arg, delim);
  823: 	size = strsepq(&arg, delim);
  824: 
  825: 	if (iqn == NULL || lun == NULL || type == NULL || flags == NULL
  826: 	    || file == NULL || size == NULL || arg != NULL) {
  827: 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
  828: 		rc = istgt_uctl_writeline(uctl);
  829: 		if (rc != UCTL_CMD_OK) {
  830: 			return rc;
  831: 		}
  832: 		return UCTL_CMD_ERR;
  833: 	}
  834: 
  835: 	if (lun == NULL) {
  836: 		lun_i = 0;
  837: 	} else {
  838: 		lun_i = (int) strtol(lun, NULL, 10);
  839: 	}
  840: 	lu = istgt_lu_find_target(uctl->istgt, iqn);
  841: 	if (lu == NULL) {
  842: 		istgt_uctl_snprintf(uctl, "ERR no target\n");
  843: 	error_return:
  844: 		rc = istgt_uctl_writeline(uctl);
  845: 		if (rc != UCTL_CMD_OK) {
  846: 			return rc;
  847: 		}
  848: 		return UCTL_CMD_ERR;
  849: 	}
  850: 	if (lun_i < 0 || lun_i >= lu->maxlun) {
  851: 		istgt_uctl_snprintf(uctl, "ERR no target\n");
  852: 		goto error_return;
  853: 	}
  854: 	llp = &lu->lun[lun_i];
  855: 	if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) {
  856: 		istgt_uctl_snprintf(uctl, "ERR not removable\n");
  857: 		goto error_return;
  858: 	}
  859: 
  860: 	/* make safe directory (start '/', end '/') */
  861: 	len = 1 + strlen(uctl->mediadirectory) + 1 + 1;
  862: 	safedir = xmalloc(len);
  863: 	if (uctl->mediadirectory[0] != '/') {
  864: 		ISTGT_WARNLOG("MediaDirectory is not starting with '/'\n");
  865: 		snprintf(safedir, len, "/%s", uctl->mediadirectory);
  866: 	} else {
  867: 		snprintf(safedir, len, "%s", uctl->mediadirectory);
  868: 	}
  869: 	if (strlen(safedir) > 1 && safedir[strlen(safedir) - 1] != '/') {
  870: 		safedir[strlen(safedir) + 1] = '\0';
  871: 		safedir[strlen(safedir)] = '/';
  872: 	}
  873: 
  874: 	/* check abspath in mediadirectory? */
  875: 	len = strlen(safedir) + strlen(file) + 1;
  876: 	fullpath = xmalloc(len);
  877: 	if (file[0] != '/') {
  878: 		snprintf(fullpath, len, "%s%s", safedir, file);
  879: 	} else {
  880: 		snprintf(fullpath, len, "%s", file);
  881: 	}
  882: #ifdef PATH_MAX
  883: 	abspath = xmalloc(len + PATH_MAX);
  884: 	file = realpath(fullpath, abspath);
  885: #else
  886: /*
  887: 	{
  888: 		long path_max;
  889: 		path_max = pathconf(fullpath, _PC_PATH_MAX);
  890: 		if (path_max != -1L) {
  891: 			abspath = xmalloc(path_max);
  892: 			file = realpath(fullpath, abspath);
  893: 		}
  894: 	}
  895: */
  896: 	file = abspath = realpath(fullpath, NULL);
  897: #endif /* PATH_MAX */
  898: 	if (file == NULL) {
  899: 		ISTGT_ERRLOG("realpath(%s) failed\n", fullpath);
  900: 	internal_error:
  901: 		xfree(safedir);
  902: 		xfree(fullpath);
  903: 		xfree(abspath);
  904: 		istgt_uctl_snprintf(uctl, "ERR %s internal error\n", uctl->cmd);
  905: 		rc = istgt_uctl_writeline(uctl);
  906: 		if (rc != UCTL_CMD_OK) {
  907: 			return rc;
  908: 		}
  909: 		return UCTL_CMD_ERR;
  910: 	}
  911: 	if (strcasecmp(file, "/dev/null") == 0) {
  912: 		/* OK, empty slot */
  913: 		flags = empty_flags;
  914: 		size = empty_size;
  915: 	} else if (strncasecmp(file, safedir, strlen(safedir)) != 0) {
  916: 		ISTGT_ERRLOG("Realpath(%s) is not within MediaDirectory(%s)\n",
  917: 		    file, safedir);
  918: 		goto internal_error;
  919: 	}
  920: 
  921: 	/* unload and load media from lun */
  922: 	switch (lu->type) {
  923: 	case ISTGT_LU_TYPE_DVD:
  924: 		MTX_LOCK(&lu->mutex);
  925: 		rc = istgt_lu_dvd_change_media(lu->lun[lun_i].spec,
  926: 		    type, flags, file, size);
  927: 		MTX_UNLOCK(&lu->mutex);
  928: 		break;
  929: 	case ISTGT_LU_TYPE_TAPE:
  930: 		MTX_LOCK(&lu->mutex);
  931: 		rc = istgt_lu_tape_change_media(lu->lun[lun_i].spec,
  932: 		    type, flags, file, size);
  933: 		MTX_UNLOCK(&lu->mutex);
  934: 		break;
  935: 	default:
  936: 		rc = -1;
  937: 	}
  938: 
  939: 	if (rc < 0) {
  940: 		xfree(safedir);
  941: 		xfree(fullpath);
  942: 		xfree(abspath);
  943: 		istgt_uctl_snprintf(uctl, "ERR change\n");
  944: 		rc = istgt_uctl_writeline(uctl);
  945: 		if (rc != UCTL_CMD_OK) {
  946: 			return rc;
  947: 		}
  948: 		return UCTL_CMD_ERR;
  949: 	}
  950: 
  951: 	/* logging event */
  952: 	ISTGT_NOTICELOG("Media Change \"%s %s %s %s\" on %s lun%d from %s\n",
  953: 	    type, flags, file, size, iqn, lun_i, uctl->caddr);
  954: 
  955: 	xfree(safedir);
  956: 	xfree(fullpath);
  957: 	xfree(abspath);
  958: 
  959: 	/* change succeeded */
  960: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
  961: 	rc = istgt_uctl_writeline(uctl);
  962: 	if (rc != UCTL_CMD_OK) {
  963: 		return rc;
  964: 	}
  965: 	return UCTL_CMD_OK;
  966: }
  967: 
  968: static int
  969: istgt_uctl_cmd_reset(UCTL_Ptr uctl)
  970: {
  971: 	ISTGT_LU_Ptr lu;
  972: 	ISTGT_LU_LUN_Ptr llp;
  973: 	const char *delim = ARGS_DELIM;
  974: 	char *arg;
  975: 	char *iqn;
  976: 	char *lun;
  977: 	int lun_i;
  978: 	int rc;
  979: 
  980: 	arg = uctl->arg;
  981: 	iqn = strsepq(&arg, delim);
  982: 	lun = strsepq(&arg, delim);
  983: 
  984: 	if (iqn == NULL || arg != NULL) {
  985: 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
  986: 		rc = istgt_uctl_writeline(uctl);
  987: 		if (rc != UCTL_CMD_OK) {
  988: 			return rc;
  989: 		}
  990: 		return UCTL_CMD_ERR;
  991: 	}
  992: 
  993: 	if (lun == NULL) {
  994: 		lun_i = 0;
  995: 	} else {
  996: 		lun_i = (int) strtol(lun, NULL, 10);
  997: 	}
  998: 	lu = istgt_lu_find_target(uctl->istgt, iqn);
  999: 	if (lu == NULL) {
 1000: 		istgt_uctl_snprintf(uctl, "ERR no target\n");
 1001: 	error_return:
 1002: 		rc = istgt_uctl_writeline(uctl);
 1003: 		if (rc != UCTL_CMD_OK) {
 1004: 			return rc;
 1005: 		}
 1006: 		return UCTL_CMD_ERR;
 1007: 	}
 1008: 	if (lun_i < 0 || lun_i >= lu->maxlun) {
 1009: 		istgt_uctl_snprintf(uctl, "ERR no target\n");
 1010: 		goto error_return;
 1011: 	}
 1012: 	llp = &lu->lun[lun_i];
 1013: 
 1014: 	/* reset lun */
 1015: 	switch (lu->type) {
 1016: 	case ISTGT_LU_TYPE_DISK:
 1017: 		MTX_LOCK(&lu->mutex);
 1018: 		rc = istgt_lu_disk_reset(lu, lun_i);
 1019: 		MTX_UNLOCK(&lu->mutex);
 1020: 		break;
 1021: 	case ISTGT_LU_TYPE_DVD:
 1022: 		MTX_LOCK(&lu->mutex);
 1023: 		rc = istgt_lu_dvd_reset(lu, lun_i);
 1024: 		MTX_UNLOCK(&lu->mutex);
 1025: 		break;
 1026: 	case ISTGT_LU_TYPE_TAPE:
 1027: 		MTX_LOCK(&lu->mutex);
 1028: 		rc = istgt_lu_tape_reset(lu, lun_i);
 1029: 		MTX_UNLOCK(&lu->mutex);
 1030: 		break;
 1031: 	case ISTGT_LU_TYPE_NONE:
 1032: 	case ISTGT_LU_TYPE_PASS:
 1033: 		rc = -1;
 1034: 		break;
 1035: 	default:
 1036: 		rc = -1;
 1037: 	}
 1038: 
 1039: 	if (rc < 0) {
 1040: 		istgt_uctl_snprintf(uctl, "ERR reset\n");
 1041: 		rc = istgt_uctl_writeline(uctl);
 1042: 		if (rc != UCTL_CMD_OK) {
 1043: 			return rc;
 1044: 		}
 1045: 		return UCTL_CMD_ERR;
 1046: 	}
 1047: 
 1048: 	/* logging event */
 1049: 	ISTGT_NOTICELOG("Unit Reset %s lun%d from %s\n",
 1050: 	    iqn, lun_i, uctl->caddr);
 1051: 
 1052: 	/* reset succeeded */
 1053: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
 1054: 	rc = istgt_uctl_writeline(uctl);
 1055: 	if (rc != UCTL_CMD_OK) {
 1056: 		return rc;
 1057: 	}
 1058: 	return UCTL_CMD_OK;
 1059: }
 1060: 
 1061: static int
 1062: istgt_uctl_cmd_info(UCTL_Ptr uctl)
 1063: {
 1064: 	ISTGT_LU_Ptr lu;
 1065: 	CONN_Ptr conn;
 1066: 	SESS_Ptr sess;
 1067: 	const char *delim = ARGS_DELIM;
 1068: 	char *arg;
 1069: 	char *iqn;
 1070: 	int ncount;
 1071: 	int rc;
 1072: 	int i, j, k;
 1073: 
 1074: 	arg = uctl->arg;
 1075: 	iqn = strsepq(&arg, delim);
 1076: 
 1077: 	if (arg != NULL) {
 1078: 		istgt_uctl_snprintf(uctl, "ERR invalid parameters\n");
 1079: 		rc = istgt_uctl_writeline(uctl);
 1080: 		if (rc != UCTL_CMD_OK) {
 1081: 			return rc;
 1082: 		}
 1083: 		return UCTL_CMD_ERR;
 1084: 	}
 1085: 
 1086: 	ncount = 0;
 1087: 	MTX_LOCK(&uctl->istgt->mutex);
 1088: 	for (i = 0; i < MAX_LOGICAL_UNIT; i++) {
 1089: 		lu = uctl->istgt->logical_unit[i];
 1090: 		if (lu == NULL)
 1091: 			continue;
 1092: 		if (iqn != NULL && strcasecmp(iqn, lu->name) != 0)
 1093: 			continue;
 1094: 
 1095: 		istgt_lock_gconns();
 1096: 		MTX_LOCK(&lu->mutex);
 1097: 		for (j = 1; j < MAX_LU_TSIH; j++) {
 1098: 			if (lu->tsih[j].initiator_port != NULL
 1099: 				&& lu->tsih[j].tsih != 0) {
 1100: 				conn = istgt_find_conn(lu->tsih[j].initiator_port,
 1101: 				    lu->name, lu->tsih[j].tsih);
 1102: 				if (conn == NULL || conn->sess == NULL)
 1103: 					continue;
 1104: 
 1105: 				sess = conn->sess;
 1106: 				MTX_LOCK(&sess->mutex);
 1107: 				for (k = 0; k < sess->connections; k++) {
 1108: 					conn = sess->conns[k];
 1109: 					if (conn == NULL)
 1110: 						continue;
 1111: 
 1112: 					istgt_uctl_snprintf(uctl, "%s Login from %s (%s) on %s LU%d"
 1113: 					    " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u,"
 1114: 					    " CID=%u, HeaderDigest=%s, DataDigest=%s,"
 1115: 					    " MaxConnections=%u,"
 1116: 					    " FirstBurstLength=%u, MaxBurstLength=%u,"
 1117: 					    " MaxRecvDataSegmentLength=%u,"
 1118: 					    " InitialR2T=%s, ImmediateData=%s\n",
 1119: 					    uctl->cmd,
 1120: 					    conn->initiator_name,
 1121: 					    conn->initiator_addr,
 1122: 					    conn->target_name, lu->num,
 1123: 					    conn->portal.host, conn->portal.port,
 1124: 					    conn->portal.tag,
 1125: 					    conn->sess->isid, conn->sess->tsih,
 1126: 					    conn->cid,
 1127: 					    (conn->header_digest ? "on" : "off"),
 1128: 					    (conn->data_digest ? "on" : "off"),
 1129: 					    conn->sess->MaxConnections,
 1130: 					    conn->sess->FirstBurstLength,
 1131: 					    conn->sess->MaxBurstLength,
 1132: 					    conn->MaxRecvDataSegmentLength,
 1133: 					    (conn->sess->initial_r2t ? "Yes" : "No"),
 1134: 					    (conn->sess->immediate_data ? "Yes" : "No"));
 1135: 					rc = istgt_uctl_writeline(uctl);
 1136: 					if (rc != UCTL_CMD_OK) {
 1137: 						MTX_UNLOCK(&sess->mutex);
 1138: 						MTX_UNLOCK(&lu->mutex);
 1139: 						istgt_unlock_gconns();
 1140: 						MTX_UNLOCK(&uctl->istgt->mutex);
 1141: 						return rc;
 1142: 					}
 1143: 					ncount++;
 1144: 				}
 1145: 				MTX_UNLOCK(&sess->mutex);
 1146: 			}
 1147: 		}
 1148: 		MTX_UNLOCK(&lu->mutex);
 1149: 		istgt_unlock_gconns();
 1150: 	}
 1151: 	MTX_UNLOCK(&uctl->istgt->mutex);
 1152: 	if (ncount == 0) {
 1153: 		istgt_uctl_snprintf(uctl, "%s no login\n", uctl->cmd);
 1154: 		rc = istgt_uctl_writeline(uctl);
 1155: 		if (rc != UCTL_CMD_OK) {
 1156: 			return rc;
 1157: 		}
 1158: 	}
 1159: 
 1160: 	/* info succeeded */
 1161: 	istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd);
 1162: 	rc = istgt_uctl_writeline(uctl);
 1163: 	if (rc != UCTL_CMD_OK) {
 1164: 		return rc;
 1165: 	}
 1166: 	return UCTL_CMD_OK;
 1167: }
 1168: 
 1169: 
 1170: typedef struct istgt_uctl_cmd_table_t
 1171: {
 1172: 	const char *name;
 1173: 	int (*func) (UCTL_Ptr uctl);
 1174: } ISTGT_UCTL_CMD_TABLE;
 1175: 
 1176: static ISTGT_UCTL_CMD_TABLE istgt_uctl_cmd_table[] = 
 1177: {
 1178: 	{ "AUTH",    istgt_uctl_cmd_auth },
 1179: 	{ "QUIT",    istgt_uctl_cmd_quit },
 1180: 	{ "NOOP",    istgt_uctl_cmd_noop },
 1181: 	{ "VERSION", istgt_uctl_cmd_version },
 1182: 	{ "LIST",    istgt_uctl_cmd_list },
 1183: 	{ "UNLOAD",  istgt_uctl_cmd_unload },
 1184: 	{ "LOAD",    istgt_uctl_cmd_load },
 1185: 	{ "CHANGE",  istgt_uctl_cmd_change },
 1186: 	{ "RESET",   istgt_uctl_cmd_reset },
 1187: 	{ "INFO",    istgt_uctl_cmd_info },
 1188: 	{ NULL,      NULL },
 1189: };
 1190: 
 1191: static int
 1192: istgt_uctl_cmd_execute(UCTL_Ptr uctl)
 1193: {
 1194: 	int (*func) (UCTL_Ptr);
 1195: 	const char *delim = ARGS_DELIM;
 1196: 	char *arg;
 1197: 	char *cmd;
 1198: 	int rc;
 1199: 	int i;
 1200: 
 1201: 	arg = trim_string(uctl->recvbuf);
 1202: 	cmd = strsepq(&arg, delim);
 1203: 	uctl->arg = arg;
 1204: 	uctl->cmd = strupr(cmd);
 1205: 
 1206: 	func = NULL;
 1207: 	for (i = 0; istgt_uctl_cmd_table[i].name != NULL; i++) {
 1208: 		if (cmd[0] == istgt_uctl_cmd_table[i].name[0]
 1209: 		    && strcmp(cmd, istgt_uctl_cmd_table[i].name) == 0) {
 1210: 			func = istgt_uctl_cmd_table[i].func;
 1211: 			break;
 1212: 		}
 1213: 	}
 1214: 	if (func == NULL) {
 1215: 		istgt_uctl_snprintf(uctl, "ERR unknown command\n");
 1216: 		rc = istgt_uctl_writeline(uctl);
 1217: 		if (rc != UCTL_CMD_OK) {
 1218: 			return UCTL_CMD_DISCON;
 1219: 		}
 1220: 		return UCTL_CMD_ERR;
 1221: 	}
 1222: 
 1223: 	if (uctl->no_auth
 1224: 	    && (strcasecmp(cmd, "AUTH") == 0)) {
 1225: 		istgt_uctl_snprintf(uctl, "ERR auth not required\n");
 1226: 		rc = istgt_uctl_writeline(uctl);
 1227: 		if (rc != UCTL_CMD_OK) {
 1228: 			return UCTL_CMD_DISCON;
 1229: 		}
 1230: 		return UCTL_CMD_ERR;
 1231: 	}
 1232: 	if (uctl->req_auth && uctl->authenticated == 0
 1233: 	    && !(strcasecmp(cmd, "QUIT") == 0
 1234: 		|| strcasecmp(cmd, "AUTH") == 0)) {
 1235: 		istgt_uctl_snprintf(uctl, "ERR auth required\n");
 1236: 		rc = istgt_uctl_writeline(uctl);
 1237: 		if (rc != UCTL_CMD_OK) {
 1238: 			return UCTL_CMD_DISCON;
 1239: 		}
 1240: 		return UCTL_CMD_ERR;
 1241: 	}
 1242: 
 1243: 	rc = func(uctl);
 1244: 	return rc;
 1245: }
 1246: 
 1247: static void istgt_free_uctl(UCTL_Ptr uctl);
 1248: 
 1249: static void *
 1250: uctlworker(void *arg)
 1251: {
 1252: 	UCTL_Ptr uctl = (UCTL_Ptr) arg;
 1253: 	int rc;
 1254: 
 1255: 	ISTGT_TRACELOG(ISTGT_TRACE_NET, "connect to %s:%s,%d\n",
 1256: 	    uctl->portal.host, uctl->portal.port, uctl->portal.tag);
 1257: 
 1258: 	istgt_uctl_snprintf(uctl, "iSCSI Target Controller version %s (%s)"
 1259: 	    " on %s from %s\n",
 1260: 	    ISTGT_VERSION, ISTGT_EXTRA_VERSION,
 1261: 	    uctl->saddr, uctl->caddr);
 1262: 	rc = istgt_uctl_writeline(uctl);
 1263: 	if (rc != UCTL_CMD_OK) {
 1264: 		ISTGT_ERRLOG("uctl_writeline() failed\n");
 1265: 		return NULL;
 1266: 	}
 1267: 
 1268: 	while (1) {
 1269: 		if (istgt_get_state(uctl->istgt) != ISTGT_STATE_RUNNING) {
 1270: 			break;
 1271: 		}
 1272: 
 1273: 		/* read from socket */
 1274: 		rc = istgt_uctl_readline(uctl);
 1275: 		if (rc == UCTL_CMD_EOF) {
 1276: 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "uctl_readline() EOF\n");
 1277: 			break;
 1278: 		}
 1279: 		if (rc != UCTL_CMD_OK) {
 1280: 			ISTGT_ERRLOG("uctl_readline() failed\n");
 1281: 			break;
 1282: 		}
 1283: 		/* execute command */
 1284: 		rc = istgt_uctl_cmd_execute(uctl);
 1285: 		if (rc == UCTL_CMD_QUIT) {
 1286: 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "receive QUIT\n");
 1287: 			break;
 1288: 		}
 1289: 		if (rc == UCTL_CMD_DISCON) {
 1290: 			ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request disconnect\n");
 1291: 			break;
 1292: 		}
 1293: 	}
 1294: 
 1295: 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "exiting ctlworker\n");
 1296: 
 1297: 	close(uctl->sock);
 1298: 	uctl->sock = -1;
 1299: 	istgt_free_uctl(uctl);
 1300: 	return NULL;
 1301: }
 1302: 
 1303: static void
 1304: istgt_free_uctl(UCTL_Ptr uctl)
 1305: {
 1306: 	if (uctl == NULL)
 1307: 		return;
 1308: 	xfree(uctl->mediadirectory);
 1309: 	xfree(uctl->portal.label);
 1310: 	xfree(uctl->portal.host);
 1311: 	xfree(uctl->portal.port);
 1312: 	xfree(uctl->auth.user);
 1313: 	xfree(uctl->auth.secret);
 1314: 	xfree(uctl->auth.muser);
 1315: 	xfree(uctl->auth.msecret);
 1316: 	xfree(uctl);
 1317: }
 1318: 
 1319: int
 1320: istgt_create_uctl(ISTGT_Ptr istgt, PORTAL_Ptr portal, int sock, struct sockaddr *sa, socklen_t salen)
 1321: {
 1322: 	char buf[MAX_TMPBUF];
 1323: 	UCTL_Ptr uctl;
 1324: 	int rc;
 1325: 	int i;
 1326: 
 1327: 	uctl = xmalloc(sizeof *uctl);
 1328: 	memset(uctl, 0, sizeof *uctl);
 1329: 
 1330: 	uctl->istgt = istgt;
 1331: 	MTX_LOCK(&istgt->mutex);
 1332: 	uctl->auth_group = istgt->uctl_auth_group;
 1333: 	uctl->no_auth = istgt->no_uctl_auth;
 1334: 	uctl->req_auth = istgt->req_uctl_auth;
 1335: 	uctl->req_mutual = istgt->req_uctl_auth_mutual;
 1336: 	uctl->mediadirectory = xstrdup(istgt->mediadirectory);
 1337: 	MTX_UNLOCK(&istgt->mutex);
 1338: 
 1339: 	uctl->portal.label = xstrdup(portal->label);
 1340: 	uctl->portal.host = xstrdup(portal->host);
 1341: 	uctl->portal.port = xstrdup(portal->port);
 1342: 	uctl->portal.tag = portal->tag;
 1343: 	uctl->portal.sock = -1;
 1344: 	uctl->sock = sock;
 1345: 
 1346: 	uctl->timeout = TIMEOUT_RW;
 1347: 	uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A;
 1348: 	uctl->auth.user = NULL;
 1349: 	uctl->auth.secret = NULL;
 1350: 	uctl->auth.muser = NULL;
 1351: 	uctl->auth.msecret = NULL;
 1352: 	uctl->authenticated = 0;
 1353: 
 1354: 	uctl->recvtmpcnt = 0;
 1355: 	uctl->recvtmpidx = 0;
 1356: 	uctl->recvtmpsize = sizeof uctl->recvtmp;
 1357: 	uctl->recvbufsize = sizeof uctl->recvbuf;
 1358: 	uctl->sendbufsize = sizeof uctl->sendbuf;
 1359: 	uctl->worksize = sizeof uctl->work;
 1360: 
 1361: 	memset(uctl->caddr, 0, sizeof uctl->caddr);
 1362: 	memset(uctl->saddr, 0, sizeof uctl->saddr);
 1363: 
 1364: 	switch (sa->sa_family) {
 1365: 	case AF_INET6:
 1366: 		uctl->family = AF_INET6;
 1367: 		rc = istgt_getaddr(sock, uctl->saddr, sizeof uctl->saddr,
 1368: 		    uctl->caddr, sizeof uctl->caddr);
 1369: 		if (rc < 0) {
 1370: 			ISTGT_ERRLOG("istgt_getaddr() failed\n");
 1371: 			goto error_return;
 1372: 		}
 1373: 		break;
 1374: 	case AF_INET:
 1375: 		uctl->family = AF_INET;
 1376: 		rc = istgt_getaddr(sock, uctl->saddr, sizeof uctl->saddr,
 1377: 		    uctl->caddr, sizeof uctl->caddr);
 1378: 		if (rc < 0) {
 1379: 			ISTGT_ERRLOG("istgt_getaddr() failed\n");
 1380: 			goto error_return;
 1381: 		}
 1382: 		break;
 1383: 	default:
 1384: 		ISTGT_ERRLOG("unsupported family\n");
 1385: 		goto error_return;
 1386: 	}
 1387: 
 1388: 	if (istgt->nuctl_netmasks != 0) {
 1389: 		rc = -1;
 1390: 		for (i = 0; i < istgt->nuctl_netmasks; i++) {
 1391: 			rc = istgt_lu_allow_netmask(istgt->uctl_netmasks[i], uctl->caddr);
 1392: 			if (rc > 0) {
 1393: 				/* OK netmask */
 1394: 				break;
 1395: 			}
 1396: 		}
 1397: 		if (rc <= 0) {
 1398: 			ISTGT_WARNLOG("UCTL access denied from %s to (%s:%s)\n",
 1399: 			    uctl->caddr, uctl->portal.host, uctl->portal.port);
 1400: 			goto error_return;
 1401: 		}
 1402: 	}
 1403: 
 1404: 	printf("sock=%d, addr=%s, peer=%s\n",
 1405: 	    sock, uctl->saddr,
 1406: 	    uctl->caddr);
 1407: 
 1408: 	/* wildcard? */
 1409: 	if (strcasecmp(uctl->portal.host, "[::]") == 0
 1410: 	    || strcasecmp(uctl->portal.host, "[*]") == 0) {
 1411: 		if (uctl->family != AF_INET6) {
 1412: 			ISTGT_ERRLOG("address family error\n");
 1413: 			goto error_return;
 1414: 		}
 1415: 		snprintf(buf, sizeof buf, "[%s]", uctl->caddr);
 1416: 		xfree(uctl->portal.host);
 1417: 		uctl->portal.host = xstrdup(buf);
 1418: 	} else if (strcasecmp(uctl->portal.host, "0.0.0.0") == 0
 1419: 	    || strcasecmp(uctl->portal.host, "*") == 0) {
 1420: 		if (uctl->family != AF_INET) {
 1421: 			ISTGT_ERRLOG("address family error\n");
 1422: 			goto error_return;
 1423: 		}
 1424: 		snprintf(buf, sizeof buf, "%s", uctl->caddr);
 1425: 		xfree(uctl->portal.host);
 1426: 		uctl->portal.host = xstrdup(buf);
 1427: 	}
 1428: 
 1429: 	/* set timeout msec. */
 1430: 	rc = istgt_set_recvtimeout(uctl->sock, uctl->timeout * 1000);
 1431: 	if (rc != 0) {
 1432: 		ISTGT_ERRLOG("istgt_set_recvtimeo() failed\n");
 1433: 		goto error_return;
 1434: 	}
 1435: 	rc = istgt_set_sendtimeout(uctl->sock, uctl->timeout * 1000);
 1436: 	if (rc != 0) {
 1437: 		ISTGT_ERRLOG("istgt_set_sendtimeo() failed\n");
 1438: 		goto error_return;
 1439: 	}
 1440: 
 1441: 	/* create new thread */
 1442: #ifdef ISTGT_STACKSIZE
 1443: 	rc = pthread_create(&uctl->thread, &istgt->attr, &uctlworker, (void *)uctl);
 1444: #else
 1445: 	rc = pthread_create(&uctl->thread, NULL, &uctlworker, (void *)uctl);
 1446: #endif
 1447: 	if (rc != 0) {
 1448: 		ISTGT_ERRLOG("pthread_create() failed\n");
 1449: 	error_return:
 1450: 		xfree(uctl->portal.label);
 1451: 		xfree(uctl->portal.host);
 1452: 		xfree(uctl->portal.port);
 1453: 		xfree(uctl);
 1454: 		return -1;
 1455: 	}
 1456: 	rc = pthread_detach(uctl->thread);
 1457: 	if (rc != 0) {
 1458: 		ISTGT_ERRLOG("pthread_detach() failed\n");
 1459: 		goto error_return;
 1460: 	}
 1461: #ifdef HAVE_PTHREAD_SET_NAME_NP
 1462: 	pthread_set_name_np(uctl->thread, "uctlthread");
 1463: #endif
 1464: 
 1465: 	return 0;
 1466: }
 1467: 
 1468: int
 1469: istgt_init_uctl(ISTGT_Ptr istgt)
 1470: {
 1471: 	CF_SECTION *sp;
 1472: 	const char *val;
 1473: 	const char *ag_tag;
 1474: 	int alloc_len;
 1475: 	int ag_tag_i;
 1476: 	int masks;
 1477: 	int i;
 1478: 
 1479: 	ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_init_uctl_section\n");
 1480: 
 1481: 	sp = istgt_find_cf_section(istgt->config, "UnitControl");
 1482: 	if (sp == NULL) {
 1483: 		ISTGT_ERRLOG("find_cf_section failed()\n");
 1484: 		return -1;
 1485: 	}
 1486: 
 1487: 	val = istgt_get_val(sp, "Comment");
 1488: 	if (val != NULL) {
 1489: 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val);
 1490: 	}
 1491: 
 1492: 	for (i = 0; ; i++) {
 1493: 		val = istgt_get_nval(sp, "Netmask", i);
 1494: 		if (val == NULL)
 1495: 			break;
 1496: 	}
 1497: 	masks = i;
 1498: 	if (masks > MAX_NETMASK) {
 1499: 		ISTGT_ERRLOG("%d > MAX_NETMASK\n", masks);
 1500: 		return -1;
 1501: 	}
 1502: 	istgt->nuctl_netmasks = masks;
 1503: 	alloc_len = sizeof (char *) * masks;
 1504: 	istgt->uctl_netmasks = xmalloc(alloc_len);
 1505: 	for (i = 0; i < masks; i++) {
 1506: 		val = istgt_get_nval(sp, "Netmask", i);
 1507: 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Netmask %s\n", val);
 1508: 		istgt->uctl_netmasks[i] = xstrdup(val);
 1509: 	}
 1510: 
 1511: 	val = istgt_get_val(sp, "AuthMethod");
 1512: 	if (val == NULL) {
 1513: 		istgt->no_uctl_auth = 0;
 1514: 		istgt->req_uctl_auth = 0;
 1515: 	} else {
 1516: 		istgt->no_uctl_auth = 0;
 1517: 		for (i = 0; ; i++) {
 1518: 			val = istgt_get_nmval(sp, "AuthMethod", 0, i);
 1519: 			if (val == NULL)
 1520: 				break;
 1521: 			if (strcasecmp(val, "CHAP") == 0) {
 1522: 				istgt->req_uctl_auth = 1;
 1523: 			} else if (strcasecmp(val, "Mutual") == 0) {
 1524: 				istgt->req_uctl_auth_mutual = 1;
 1525: 			} else if (strcasecmp(val, "Auto") == 0) {
 1526: 				istgt->req_uctl_auth = 0;
 1527: 				istgt->req_uctl_auth_mutual = 0;
 1528: 			} else if (strcasecmp(val, "None") == 0) {
 1529: 				istgt->no_uctl_auth = 1;
 1530: 				istgt->req_uctl_auth = 0;
 1531: 				istgt->req_uctl_auth_mutual = 0;
 1532: 			} else {
 1533: 				ISTGT_ERRLOG("unknown auth\n");
 1534: 				return -1;
 1535: 			}
 1536: 		}
 1537: 	}
 1538: 	if (istgt->no_uctl_auth == 0) {
 1539: 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod None\n");
 1540: 	} else if (istgt->req_uctl_auth == 0) {
 1541: 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod Auto\n");
 1542: 	} else {
 1543: 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod %s %s\n",
 1544: 		    istgt->req_uctl_auth ? "CHAP" : "",
 1545: 		    istgt->req_uctl_auth_mutual ? "Mutual" : "");
 1546: 	}
 1547: 
 1548: 	val = istgt_get_val(sp, "AuthGroup");
 1549: 	if (val == NULL) {
 1550: 		istgt->uctl_auth_group = 0;
 1551: 	} else {
 1552: 		ag_tag = val;
 1553: 		if (strcasecmp(ag_tag, "None") == 0) {
 1554: 			ag_tag_i = 0;
 1555: 		} else {
 1556: 			if (strncasecmp(ag_tag, "AuthGroup",
 1557: 				strlen("AuthGroup")) != 0
 1558: 			    || sscanf(ag_tag, "%*[^0-9]%d", &ag_tag_i) != 1) {
 1559: 				ISTGT_ERRLOG("auth group error\n");
 1560: 				return -1;
 1561: 			}
 1562: 			if (ag_tag_i == 0) {
 1563: 				ISTGT_ERRLOG("invalid auth group %d\n", ag_tag_i);
 1564: 				return -1;
 1565: 			}
 1566: 		}
 1567: 		istgt->uctl_auth_group = ag_tag_i;
 1568: 	}
 1569: 	if (istgt->uctl_auth_group == 0) {
 1570: 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthGroup None\n");
 1571: 	} else {
 1572: 		ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthGroup AuthGroup%d\n",
 1573: 		    istgt->uctl_auth_group);
 1574: 	}
 1575: 
 1576: 	return 0;
 1577: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>