Annotation of embedaddon/istgt/src/istgtcontrol.c, revision 1.1.1.1
1.1 misho 1: /*
2: * Copyright (C) 2008-2010 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 "build.h"
33:
34: #include <stdint.h>
35: #include <inttypes.h>
36:
37: #include <stdarg.h>
38: #include <signal.h>
39: #include <stdio.h>
40: #include <stdlib.h>
41: #include <string.h>
42: #include <unistd.h>
43: #include <sys/types.h>
44:
45: #include "istgt.h"
46: #include "istgt_ver.h"
47: #include "istgt_conf.h"
48: #include "istgt_sock.h"
49: #include "istgt_misc.h"
50: #include "istgt_md5.h"
51:
52: //#define TRACE_UCTL
53:
54: #define DEFAULT_UCTL_CONFIG BUILD_ETC_ISTGT "/istgtcontrol.conf"
55: #define DEFAULT_UCTL_TIMEOUT 60
56: #define DEFAULT_UCTL_PORT 3261
57: #define DEFAULT_UCTL_HOST "localhost"
58: #define DEFAULT_UCTL_LUN 0
59: #define DEFAULT_UCTL_MTYPE "-"
60: #define DEFAULT_UCTL_MFLAGS "ro"
61: #define DEFAULT_UCTL_MSIZE "auto"
62:
63: #define MAX_LINEBUF 4096
64: #define UCTL_CHAP_CHALLENGE_LEN 1024
65:
66: typedef struct istgt_uctl_auth_t {
67: char *user;
68: char *secret;
69: char *muser;
70: char *msecret;
71:
72: uint8_t chap_id[1];
73: uint8_t chap_mid[1];
74: int chap_challenge_len;
75: uint8_t chap_challenge[UCTL_CHAP_CHALLENGE_LEN];
76: int chap_mchallenge_len;
77: uint8_t chap_mchallenge[UCTL_CHAP_CHALLENGE_LEN];
78: } UCTL_AUTH;
79:
80: typedef struct istgt_uctl_t {
81: CONFIG *config;
82:
83: char *host;
84: int port;
85:
86: int sock;
87: char *iqn;
88: int lun;
89: char *mflags;
90: char *mfile;
91: char *msize;
92: char *mtype;
93:
94: int family;
95: char caddr[MAX_ADDRBUF];
96: char saddr[MAX_ADDRBUF];
97:
98: UCTL_AUTH auth;
99:
100: int timeout;
101: int req_auth_auto;
102: int req_auth;
103: int req_auth_mutual;
104:
105: int recvtmpsize;
106: int recvtmpcnt;
107: int recvtmpidx;
108: int recvbufsize;
109: int sendbufsize;
110: int worksize;
111: char recvtmp[MAX_LINEBUF];
112: char recvbuf[MAX_LINEBUF];
113: char sendbuf[MAX_LINEBUF];
114: char work[MAX_LINEBUF];
115: char *cmd;
116: char *arg;
117: } UCTL;
118: typedef UCTL *UCTL_Ptr;
119:
120:
121: static void
122: fatal(const char *format, ...)
123: {
124: va_list ap;
125:
126: va_start(ap, format);
127: vfprintf(stderr, format, ap);
128: va_end(ap);
129: exit(EXIT_FAILURE);
130: }
131:
132: typedef enum {
133: UCTL_CMD_OK = 0,
134: UCTL_CMD_ERR = 1,
135: UCTL_CMD_EOF = 2,
136: UCTL_CMD_QUIT = 3,
137: UCTL_CMD_DISCON = 4,
138: UCTL_CMD_REQAUTH = 5,
139: UCTL_CMD_CHAPSEQ = 6,
140: } UCTL_CMD_STATUS;
141:
142: //#define ARGS_DELIM " \t\r\n"
143: #define ARGS_DELIM " \t"
144:
145: static int
146: uctl_readline(UCTL_Ptr uctl)
147: {
148: ssize_t total;
149:
150: total = istgt_readline_socket(uctl->sock, uctl->recvbuf, uctl->recvbufsize,
151: uctl->recvtmp, uctl->recvtmpsize,
152: &uctl->recvtmpidx, &uctl->recvtmpcnt,
153: uctl->timeout);
154: if (total < 0) {
155: return UCTL_CMD_DISCON;
156: }
157: if (total == 0) {
158: return UCTL_CMD_EOF;
159: }
160: return UCTL_CMD_OK;
161: }
162:
163: static int
164: uctl_writeline(UCTL_Ptr uctl)
165: {
166: ssize_t total;
167: ssize_t expect;
168:
169: expect = strlen(uctl->sendbuf);
170: total = istgt_writeline_socket(uctl->sock, uctl->sendbuf, uctl->timeout);
171: if (total < 0) {
172: return UCTL_CMD_DISCON;
173: }
174: if (total != expect) {
175: return UCTL_CMD_ERR;
176: }
177: return UCTL_CMD_OK;
178: }
179:
180: static int
181: uctl_snprintf(UCTL_Ptr uctl, const char *format, ...)
182: {
183: va_list ap;
184: int rc;
185:
186: va_start(ap, format);
187: rc = vsnprintf(uctl->sendbuf, uctl->sendbufsize, format, ap);
188: va_end(ap);
189: return rc;
190: }
191:
192: static char *
193: get_banner(UCTL_Ptr uctl)
194: {
195: char *banner;
196: int rc;
197:
198: rc = uctl_readline(uctl);
199: if (rc != UCTL_CMD_OK) {
200: return NULL;
201: }
202: banner = xstrdup(trim_string(uctl->recvbuf));
203: return banner;
204: }
205:
206: static int
207: is_err_req_auth(UCTL_Ptr uctl, char *s)
208: {
209: const char *req_auth_string = "auth required";
210:
211: #ifdef TRACE_UCTL
212: printf("S=%s, Q=%s\n", s, req_auth_string);
213: #endif /* TRCAE_UCTL */
214: if (strncasecmp(s, req_auth_string, strlen(req_auth_string)) == 0)
215: return 1;
216: return 0;
217: }
218:
219: static int
220: is_err_chap_seq(UCTL_Ptr uctl, char *s)
221: {
222: const char *chap_seq_string = "CHAP sequence error";
223:
224: #ifdef TRACE_UCTL
225: printf("S=%s, Q=%s\n", s, chap_seq_string);
226: #endif /* TRCAE_UCTL */
227: if (strncasecmp(s, chap_seq_string, strlen(chap_seq_string)) == 0)
228: return 1;
229: return 0;
230: }
231:
232: static int
233: exec_quit(UCTL_Ptr uctl)
234: {
235: const char *delim = ARGS_DELIM;
236: char *arg;
237: char *result;
238: int rc;
239:
240: /* send command */
241: uctl_snprintf(uctl, "QUIT\n");
242: rc = uctl_writeline(uctl);
243: if (rc != UCTL_CMD_OK) {
244: return rc;
245: }
246:
247: /* receive result */
248: rc = uctl_readline(uctl);
249: if (rc != UCTL_CMD_OK) {
250: return rc;
251: }
252: arg = trim_string(uctl->recvbuf);
253: result = strsepq(&arg, delim);
254: strupr(result);
255: if (strcmp(result, "OK") != 0) {
256: if (is_err_req_auth(uctl, arg))
257: return UCTL_CMD_REQAUTH;
258: fprintf(stderr, "ERROR %s\n", arg);
259: return UCTL_CMD_ERR;
260: }
261: return UCTL_CMD_OK;
262: }
263:
264: static int
265: exec_noop(UCTL_Ptr uctl)
266: {
267: const char *delim = ARGS_DELIM;
268: char *arg;
269: char *result;
270: int rc;
271:
272: /* send command */
273: uctl_snprintf(uctl, "NOOP\n");
274: rc = uctl_writeline(uctl);
275: if (rc != UCTL_CMD_OK) {
276: return rc;
277: }
278:
279: /* receive result */
280: rc = uctl_readline(uctl);
281: if (rc != UCTL_CMD_OK) {
282: return rc;
283: }
284: arg = trim_string(uctl->recvbuf);
285: result = strsepq(&arg, delim);
286: strupr(result);
287: if (strcmp(result, "OK") != 0) {
288: if (is_err_req_auth(uctl, arg))
289: return UCTL_CMD_REQAUTH;
290: fprintf(stderr, "ERROR %s\n", arg);
291: return UCTL_CMD_ERR;
292: }
293: return UCTL_CMD_OK;
294: }
295:
296: static int
297: exec_version(UCTL_Ptr uctl)
298: {
299: const char *delim = ARGS_DELIM;
300: char *arg;
301: char *result;
302: char *version;
303: char *extver;
304: int rc;
305:
306: /* send command */
307: uctl_snprintf(uctl, "VERSION\n");
308: rc = uctl_writeline(uctl);
309: if (rc != UCTL_CMD_OK) {
310: return rc;
311: }
312:
313: /* receive result */
314: while (1) {
315: rc = uctl_readline(uctl);
316: if (rc != UCTL_CMD_OK) {
317: return rc;
318: }
319: arg = trim_string(uctl->recvbuf);
320: result = strsepq(&arg, delim);
321: strupr(result);
322: if (strcmp(result, uctl->cmd) != 0) {
323: break;
324: }
325: version = strsepq(&arg, delim);
326: extver = strsepq(&arg, delim);
327: printf("target version %s %s\n", version, extver);
328: }
329: if (strcmp(result, "OK") != 0) {
330: if (is_err_req_auth(uctl, arg))
331: return UCTL_CMD_REQAUTH;
332: fprintf(stderr, "ERROR %s\n", arg);
333: return UCTL_CMD_ERR;
334: }
335: return UCTL_CMD_OK;
336: }
337:
338: static int
339: exec_unload(UCTL_Ptr uctl)
340: {
341: const char *delim = ARGS_DELIM;
342: char *arg;
343: char *result;
344: int rc;
345:
346: /* send command */
347: if (uctl->iqn == NULL || uctl->lun < 0) {
348: return UCTL_CMD_ERR;
349: }
350: uctl_snprintf(uctl, "UNLOAD \"%s\" %d\n",
351: uctl->iqn, uctl->lun);
352: rc = uctl_writeline(uctl);
353: if (rc != UCTL_CMD_OK) {
354: return rc;
355: }
356:
357: /* receive result */
358: rc = uctl_readline(uctl);
359: if (rc != UCTL_CMD_OK) {
360: return rc;
361: }
362: arg = trim_string(uctl->recvbuf);
363: result = strsepq(&arg, delim);
364: strupr(result);
365: if (strcmp(result, "OK") != 0) {
366: if (is_err_req_auth(uctl, arg))
367: return UCTL_CMD_REQAUTH;
368: fprintf(stderr, "ERROR %s\n", arg);
369: return UCTL_CMD_ERR;
370: }
371: return UCTL_CMD_OK;
372: }
373:
374: static int
375: exec_load(UCTL_Ptr uctl)
376: {
377: const char *delim = ARGS_DELIM;
378: char *arg;
379: char *result;
380: int rc;
381:
382: /* send command */
383: if (uctl->iqn == NULL || uctl->lun < 0) {
384: return UCTL_CMD_ERR;
385: }
386: uctl_snprintf(uctl, "LOAD \"%s\" %d\n",
387: uctl->iqn, uctl->lun);
388: rc = uctl_writeline(uctl);
389: if (rc != UCTL_CMD_OK) {
390: return rc;
391: }
392:
393: /* receive result */
394: rc = uctl_readline(uctl);
395: if (rc != UCTL_CMD_OK) {
396: return rc;
397: }
398: arg = trim_string(uctl->recvbuf);
399: result = strsepq(&arg, delim);
400: strupr(result);
401: if (strcmp(result, "OK") != 0) {
402: if (is_err_req_auth(uctl, arg))
403: return UCTL_CMD_REQAUTH;
404: fprintf(stderr, "ERROR %s\n", arg);
405: return UCTL_CMD_ERR;
406: }
407: return UCTL_CMD_OK;
408: }
409:
410: static int
411: exec_list(UCTL_Ptr uctl)
412: {
413: const char *delim = ARGS_DELIM;
414: char *arg;
415: char *result;
416: char *target;
417: int rc;
418:
419: /* send command */
420: if (uctl->iqn != NULL) {
421: uctl_snprintf(uctl, "LIST \"%s\"\n", uctl->iqn);
422: } else {
423: uctl_snprintf(uctl, "LIST\n");
424: }
425: rc = uctl_writeline(uctl);
426: if (rc != UCTL_CMD_OK) {
427: return rc;
428: }
429:
430: /* receive result */
431: while (1) {
432: rc = uctl_readline(uctl);
433: if (rc != UCTL_CMD_OK) {
434: return rc;
435: }
436: arg = trim_string(uctl->recvbuf);
437: result = strsepq(&arg, delim);
438: strupr(result);
439: if (strcmp(result, uctl->cmd) != 0)
440: break;
441: if (uctl->iqn != NULL) {
442: printf("%s\n", arg);
443: } else {
444: target = strsepq(&arg, delim);
445: printf("%s\n", target);
446: }
447: }
448: if (strcmp(result, "OK") != 0) {
449: if (is_err_req_auth(uctl, arg))
450: return UCTL_CMD_REQAUTH;
451: fprintf(stderr, "ERROR %s\n", arg);
452: return UCTL_CMD_ERR;
453: }
454: return UCTL_CMD_OK;
455: }
456:
457: static int
458: exec_change(UCTL_Ptr uctl)
459: {
460: const char *delim = ARGS_DELIM;
461: char *arg;
462: char *result;
463: int rc;
464:
465: /* send command */
466: if (uctl->iqn == NULL || uctl->mfile == NULL || uctl->mtype == NULL
467: || uctl->mflags == NULL || uctl->msize == NULL) {
468: return UCTL_CMD_ERR;
469: }
470: uctl_snprintf(uctl, "CHANGE \"%s\" %d \"%s\" "
471: "\"%s\" \"%s\" \"%s\"\n",
472: uctl->iqn, uctl->lun, uctl->mtype,
473: uctl->mflags, uctl->mfile, uctl->msize);
474: rc = uctl_writeline(uctl);
475: if (rc != UCTL_CMD_OK) {
476: return rc;
477: }
478:
479: /* receive result */
480: rc = uctl_readline(uctl);
481: if (rc != UCTL_CMD_OK) {
482: return rc;
483: }
484: arg = trim_string(uctl->recvbuf);
485: result = strsepq(&arg, delim);
486: strupr(result);
487: if (strcmp(result, "OK") != 0) {
488: if (is_err_req_auth(uctl, arg))
489: return UCTL_CMD_REQAUTH;
490: fprintf(stderr, "ERROR %s\n", arg);
491: return UCTL_CMD_ERR;
492: }
493: return UCTL_CMD_OK;
494: }
495:
496: static int
497: exec_reset(UCTL_Ptr uctl)
498: {
499: const char *delim = ARGS_DELIM;
500: char *arg;
501: char *result;
502: int rc;
503:
504: /* send command */
505: if (uctl->iqn == NULL || uctl->lun < 0) {
506: return UCTL_CMD_ERR;
507: }
508: uctl_snprintf(uctl, "RESET \"%s\" %d\n",
509: uctl->iqn, uctl->lun);
510: rc = uctl_writeline(uctl);
511: if (rc != UCTL_CMD_OK) {
512: return rc;
513: }
514:
515: /* receive result */
516: rc = uctl_readline(uctl);
517: if (rc != UCTL_CMD_OK) {
518: return rc;
519: }
520: arg = trim_string(uctl->recvbuf);
521: result = strsepq(&arg, delim);
522: strupr(result);
523: if (strcmp(result, "OK") != 0) {
524: if (is_err_req_auth(uctl, arg))
525: return UCTL_CMD_REQAUTH;
526: fprintf(stderr, "ERROR %s\n", arg);
527: return UCTL_CMD_ERR;
528: }
529: return UCTL_CMD_OK;
530: }
531:
532: static int
533: exec_info(UCTL_Ptr uctl)
534: {
535: const char *delim = ARGS_DELIM;
536: char *arg;
537: char *result;
538: int rc;
539:
540: /* send command */
541: if (uctl->iqn != NULL) {
542: uctl_snprintf(uctl, "INFO \"%s\"\n", uctl->iqn);
543: } else {
544: uctl_snprintf(uctl, "INFO\n");
545: }
546: rc = uctl_writeline(uctl);
547: if (rc != UCTL_CMD_OK) {
548: return rc;
549: }
550:
551: /* receive result */
552: while (1) {
553: rc = uctl_readline(uctl);
554: if (rc != UCTL_CMD_OK) {
555: return rc;
556: }
557: arg = trim_string(uctl->recvbuf);
558: result = strsepq(&arg, delim);
559: strupr(result);
560: if (strcmp(result, uctl->cmd) != 0)
561: break;
562: if (uctl->iqn != NULL) {
563: printf("%s\n", arg);
564: } else {
565: printf("%s\n", arg);
566: }
567: }
568: if (strcmp(result, "OK") != 0) {
569: if (is_err_req_auth(uctl, arg))
570: return UCTL_CMD_REQAUTH;
571: fprintf(stderr, "ERROR %s\n", arg);
572: return UCTL_CMD_ERR;
573: }
574: return UCTL_CMD_OK;
575: }
576:
577: typedef struct exec_table_t
578: {
579: const char *name;
580: int (*func) (UCTL_Ptr uctl);
581: int req_argc;
582: int req_target;
583: } EXEC_TABLE;
584:
585: static EXEC_TABLE exec_table[] =
586: {
587: { "QUIT", exec_quit, 0, 0 },
588: { "NOOP", exec_noop, 0, 0 },
589: { "VERSION", exec_version, 0, 0 },
590: { "LIST", exec_list, 0, 0 },
591: { "UNLOAD", exec_unload, 0, 1 },
592: { "LOAD", exec_load, 0, 1 },
593: { "CHANGE", exec_change, 1, 1 },
594: { "RESET", exec_reset, 0, 1 },
595: { "INFO", exec_info, 0, 0 },
596: { NULL, NULL, 0, 0 },
597: };
598:
599: static int
600: do_auth(UCTL_Ptr uctl)
601: {
602: uint8_t uctlmd5[ISTGT_MD5DIGEST_LEN];
603: uint8_t resmd5[ISTGT_MD5DIGEST_LEN];
604: ISTGT_MD5CTX md5ctx;
605: const char *delim = ARGS_DELIM;
606: char *arg;
607: char *result;
608: char *label;
609: char *chap_i;
610: char *chap_c;
611: char *chap_n;
612: char *chap_r;
613: char *hexmd5;
614: char *hexchallenge;
615: char *workp;
616: int worksize;
617: int algorithm = 5; /* CHAP with MD5 */
618: int rc;
619:
620: #ifdef TRACE_UCTL
621: printf("do_auth: user=%s, secret=%s, muser=%s, msecret=%s\n",
622: uctl->auth.user,
623: uctl->auth.secret,
624: uctl->auth.muser,
625: uctl->auth.msecret);
626: #endif /* TRACE_UCTL */
627:
628: /* send algorithm CHAP_A */
629: uctl_snprintf(uctl, "AUTH CHAP_A %d\n",
630: algorithm);
631: rc = uctl_writeline(uctl);
632: if (rc != UCTL_CMD_OK) {
633: return rc;
634: }
635:
636: /* receive CHAP_IC */
637: rc = uctl_readline(uctl);
638: if (rc != UCTL_CMD_OK) {
639: return rc;
640: }
641: arg = trim_string(uctl->recvbuf);
642: result = strsepq(&arg, delim);
643: strupr(result);
644: if (strcmp(result, "AUTH") != 0) {
645: fprintf(stderr, "ERROR %s\n", arg);
646: return UCTL_CMD_ERR;
647: }
648:
649: label = strsepq(&arg, delim);
650: chap_i = strsepq(&arg, delim);
651: chap_c = strsepq(&arg, delim);
652: if (label == NULL || chap_i == NULL || chap_c == NULL) {
653: fprintf(stderr, "CHAP sequence error\n");
654: return UCTL_CMD_ERR;
655: }
656: if (strcasecmp(label, "CHAP_IC") != 0) {
657: fprintf(stderr, "CHAP sequence error\n");
658: return UCTL_CMD_ERR;
659: }
660:
661: /* Identifier */
662: uctl->auth.chap_id[0] = (uint8_t) strtol(chap_i, NULL, 10);
663: /* Challenge Value */
664: rc = istgt_hex2bin(uctl->auth.chap_challenge,
665: UCTL_CHAP_CHALLENGE_LEN,
666: chap_c);
667: if (rc < 0) {
668: fprintf(stderr, "challenge format error\n");
669: return UCTL_CMD_ERR;
670: }
671: uctl->auth.chap_challenge_len = rc;
672:
673: if (uctl->auth.user == NULL || uctl->auth.secret == NULL) {
674: fprintf(stderr, "ERROR auth user or secret is missing\n");
675: return UCTL_CMD_ERR;
676: }
677:
678: istgt_md5init(&md5ctx);
679: /* Identifier */
680: istgt_md5update(&md5ctx, uctl->auth.chap_id, 1);
681: /* followed by secret */
682: istgt_md5update(&md5ctx, uctl->auth.secret,
683: strlen(uctl->auth.secret));
684: /* followed by Challenge Value */
685: istgt_md5update(&md5ctx, uctl->auth.chap_challenge,
686: uctl->auth.chap_challenge_len);
687: /* uctlmd5 is Response Value */
688: istgt_md5final(uctlmd5, &md5ctx);
689:
690: workp = uctl->work;
691: worksize = uctl->worksize;
692:
693: istgt_bin2hex(workp, worksize,
694: uctlmd5, ISTGT_MD5DIGEST_LEN);
695: hexmd5 = workp;
696: worksize -= strlen(hexmd5) + 1;
697: workp += strlen(hexmd5) + 1;
698:
699: /* mutual CHAP? */
700: if (uctl->req_auth_mutual) {
701: /* Identifier is one octet */
702: istgt_gen_random(uctl->auth.chap_mid, 1);
703: /* Challenge Value is a variable stream of octets */
704: /* (binary length MUST not exceed 1024 bytes) */
705: uctl->auth.chap_mchallenge_len = UCTL_CHAP_CHALLENGE_LEN;
706: istgt_gen_random(uctl->auth.chap_mchallenge,
707: uctl->auth.chap_mchallenge_len);
708:
709: istgt_bin2hex(workp, worksize,
710: uctl->auth.chap_mchallenge,
711: uctl->auth.chap_mchallenge_len);
712: hexchallenge = workp;
713: worksize -= strlen(hexchallenge) + 1;
714: workp += strlen(hexchallenge) + 1;
715:
716: /* send CHAP_NR with CHAP_IC */
717: uctl_snprintf(uctl, "AUTH CHAP_NR %s %s %d %s\n",
718: uctl->auth.user, hexmd5,
719: (int) uctl->auth.chap_mid[0], hexchallenge);
720: rc = uctl_writeline(uctl);
721: if (rc != UCTL_CMD_OK) {
722: return rc;
723: }
724:
725: /* receive CHAP_NR */
726: rc = uctl_readline(uctl);
727: if (rc != UCTL_CMD_OK) {
728: return rc;
729: }
730: arg = trim_string(uctl->recvbuf);
731: result = strsepq(&arg, delim);
732: strupr(result);
733: if (strcmp(result, "AUTH") != 0) {
734: fprintf(stderr, "ERROR %s\n", arg);
735: return UCTL_CMD_ERR;
736: }
737:
738: label = strsepq(&arg, delim);
739: chap_n = strsepq(&arg, delim);
740: chap_r = strsepq(&arg, delim);
741: if (label == NULL || chap_n == NULL || chap_r == NULL) {
742: fprintf(stderr, "CHAP sequence error\n");
743: return UCTL_CMD_ERR;
744: }
745: if (strcasecmp(label, "CHAP_NR") != 0) {
746: fprintf(stderr, "CHAP sequence error\n");
747: return UCTL_CMD_ERR;
748: }
749:
750: rc = istgt_hex2bin(resmd5, ISTGT_MD5DIGEST_LEN, chap_r);
751: if (rc < 0 || rc != ISTGT_MD5DIGEST_LEN) {
752: fprintf(stderr, "response format error\n");
753: return UCTL_CMD_ERR;
754: }
755:
756: if (uctl->auth.muser == NULL || uctl->auth.msecret == NULL) {
757: fprintf(stderr, "ERROR auth user or secret is missing\n");
758: return UCTL_CMD_ERR;
759: }
760:
761: istgt_md5init(&md5ctx);
762: /* Identifier */
763: istgt_md5update(&md5ctx, uctl->auth.chap_mid, 1);
764: /* followed by secret */
765: istgt_md5update(&md5ctx, uctl->auth.msecret,
766: strlen(uctl->auth.msecret));
767: /* followed by Challenge Value */
768: istgt_md5update(&md5ctx, uctl->auth.chap_mchallenge,
769: uctl->auth.chap_mchallenge_len);
770: /* uctlmd5 is expecting Response Value */
771: istgt_md5final(uctlmd5, &md5ctx);
772:
773: /* compare MD5 digest */
774: if (memcmp(uctlmd5, resmd5, ISTGT_MD5DIGEST_LEN) != 0) {
775: /* not match */
776: fprintf(stderr, "ERROR auth user or secret is missing\n");
777: /* discard result line */
778: if (rc != UCTL_CMD_OK) {
779: return rc;
780: }
781: arg = trim_string(uctl->recvbuf);
782: result = strsepq(&arg, delim);
783: strupr(result);
784: if (strcmp(result, "OK") != 0) {
785: fprintf(stderr, "ERROR %s\n", arg);
786: return UCTL_CMD_ERR;
787: }
788: /* final with ERR */
789: return UCTL_CMD_ERR;
790: }
791: } else {
792: /* not mutual */
793: /* send CHAP_NR */
794: uctl_snprintf(uctl, "AUTH CHAP_NR %s %s\n",
795: uctl->auth.user, hexmd5);
796: rc = uctl_writeline(uctl);
797: if (rc != UCTL_CMD_OK) {
798: return rc;
799: }
800: }
801:
802: /* receive result */
803: rc = uctl_readline(uctl);
804: if (rc != UCTL_CMD_OK) {
805: return rc;
806: }
807: arg = trim_string(uctl->recvbuf);
808: result = strsepq(&arg, delim);
809: strupr(result);
810: if (strcmp(result, "OK") != 0) {
811: if (is_err_chap_seq(uctl, arg))
812: return UCTL_CMD_CHAPSEQ;
813: fprintf(stderr, "ERROR %s\n", arg);
814: return UCTL_CMD_ERR;
815: }
816: return UCTL_CMD_OK;
817: }
818:
819: static char *
820: uctl_get_nmval(CF_SECTION *sp, const char *key, int idx1, int idx2)
821: {
822: CF_ITEM *ip;
823: CF_VALUE *vp;
824: int i;
825:
826: ip = istgt_find_cf_nitem(sp, key, idx1);
827: if (ip == NULL)
828: return NULL;
829: vp = ip->val;
830: if (vp == NULL)
831: return NULL;
832: for (i = 0; vp != NULL; vp = vp->next) {
833: if (i == idx2)
834: return vp->value;
835: i++;
836: }
837: return NULL;
838: }
839:
840: static char *
841: uctl_get_nval(CF_SECTION *sp, const char *key, int idx)
842: {
843: CF_ITEM *ip;
844: CF_VALUE *vp;
845:
846: ip = istgt_find_cf_nitem(sp, key, idx);
847: if (ip == NULL)
848: return NULL;
849: vp = ip->val;
850: if (vp == NULL)
851: return NULL;
852: return vp->value;
853: }
854:
855: static char *
856: uctl_get_val(CF_SECTION *sp, const char *key)
857: {
858: return uctl_get_nval(sp, key, 0);
859: }
860:
861: static int
862: uctl_get_nintval(CF_SECTION *sp, const char *key, int idx)
863: {
864: const char *v;
865: int value;
866:
867: v = uctl_get_nval(sp, key, idx);
868: if (v == NULL)
869: return -1;
870: value = (int)strtol(v, NULL, 10);
871: return value;
872: }
873:
874: static int
875: uctl_get_intval(CF_SECTION *sp, const char *key)
876: {
877: return uctl_get_nintval(sp, key, 0);
878: }
879:
880: static int
881: uctl_init(UCTL_Ptr uctl)
882: {
883: CF_SECTION *sp;
884: const char *val;
885: const char *user, *muser;
886: const char *secret, *msecret;
887: int timeout;
888: int port;
889: int lun;
890: int i;
891:
892: sp = istgt_find_cf_section(uctl->config, "Global");
893: if (sp == NULL) {
894: fprintf(stderr, "find_cf_section failed()\n");
895: return -1;
896: }
897:
898: val = uctl_get_val(sp, "Comment");
899: if (val != NULL) {
900: /* nothing */
901: #ifdef TRACE_UCTL
902: printf("Comment %s\n", val);
903: #endif /* TRACE_UCTL */
904: }
905:
906: val = uctl_get_val(sp, "Host");
907: if (val == NULL) {
908: val = DEFAULT_UCTL_HOST;
909: }
910: uctl->host = xstrdup(val);
911: #ifdef TRACE_UCTL
912: printf("Host %s\n", uctl->host);
913: #endif /* TRACE_UCTL */
914:
915: port = uctl_get_intval(sp, "Port");
916: if (port < 0) {
917: port = DEFAULT_UCTL_PORT;
918: }
919: uctl->port = port;
920: #ifdef TRACE_UCTL
921: printf("Port %d\n", uctl->port);
922: #endif /* TRACE_UCTL */
923:
924: val = uctl_get_val(sp, "TargetName");
925: if (val == NULL) {
926: val = NULL;
927: }
928: uctl->iqn = xstrdup(val);
929: #ifdef TRACE_UCTL
930: printf("TargetName %s\n", uctl->iqn);
931: #endif /* TRACE_UCTL */
932:
933: lun = uctl_get_intval(sp, "Lun");
934: if (lun < 0) {
935: lun = DEFAULT_UCTL_LUN;
936: }
937: uctl->lun = lun;
938: #ifdef TRACE_UCTL
939: printf("Lun %d\n", uctl->lun);
940: #endif /* TRACE_UCTL */
941:
942: val = uctl_get_val(sp, "Flags");
943: if (val == NULL) {
944: val = DEFAULT_UCTL_MFLAGS;
945: }
946: uctl->mflags = xstrdup(val);
947: #ifdef TRACE_UCTL
948: printf("Flags %s\n", uctl->mflags);
949: #endif /* TRACE_UCTL */
950:
951: val = uctl_get_val(sp, "Size");
952: if (val == NULL) {
953: val = DEFAULT_UCTL_MSIZE;
954: }
955: uctl->msize = xstrdup(val);
956: #ifdef TRACE_UCTL
957: printf("Size %s\n", uctl->msize);
958: #endif /* TRACE_UCTL */
959:
960: timeout = uctl_get_intval(sp, "Timeout");
961: if (timeout < 0) {
962: timeout = DEFAULT_UCTL_TIMEOUT;
963: }
964: uctl->timeout = timeout;
965: #ifdef TRACE_UCTL
966: printf("Timeout %d\n", uctl->timeout);
967: #endif /* TRACE_UCTL */
968:
969: val = uctl_get_val(sp, "AuthMethod");
970: if (val == NULL) {
971: uctl->req_auth_auto = 0;
972: uctl->req_auth = 0;
973: } else {
974: uctl->req_auth_auto = 0;
975: for (i = 0; ; i++) {
976: val = uctl_get_nmval(sp, "AuthMethod", 0, i);
977: if (val == NULL)
978: break;
979: if (strcasecmp(val, "CHAP") == 0) {
980: uctl->req_auth = 1;
981: } else if (strcasecmp(val, "Mutual") == 0) {
982: uctl->req_auth_mutual = 1;
983: } else if (strcasecmp(val, "Auto") == 0) {
984: uctl->req_auth_auto = 1;
985: uctl->req_auth = 0;
986: uctl->req_auth_mutual = 0;
987: } else {
988: fprintf(stderr, "unknown auth\n");
989: return -1;
990: }
991: }
992: if (uctl->req_auth_mutual && !uctl->req_auth) {
993: fprintf(stderr, "Mutual but not CHAP\n");
994: return -1;
995: }
996: }
997: #ifdef TRACE_UCTL
998: if (uctl->req_auth == 0) {
999: printf("AuthMethod Auto\n");
1000: } else {
1001: printf("AuthMethod %s %s\n",
1002: uctl->req_auth ? "CHAP" : "",
1003: uctl->req_auth_mutual ? "Mutual" : "");
1004: }
1005: #endif /* TRACE_UCTL */
1006:
1007: val = uctl_get_nval(sp, "Auth", 0);
1008: if (val == NULL) {
1009: user = secret = muser = msecret = NULL;
1010: } else {
1011: user = uctl_get_nmval(sp, "Auth", 0, 0);
1012: secret = uctl_get_nmval(sp, "Auth", 0, 1);
1013: muser = uctl_get_nmval(sp, "Auth", 0, 2);
1014: msecret = uctl_get_nmval(sp, "Auth", 0, 3);
1015: }
1016: uctl->auth.user = xstrdup(user);
1017: uctl->auth.secret = xstrdup(secret);
1018: uctl->auth.muser = xstrdup(muser);
1019: uctl->auth.msecret = xstrdup(msecret);
1020: #ifdef TRACE_UCTL
1021: printf("user=%s, secret=%s, muser=%s, msecret=%s\n",
1022: user, secret, muser, msecret);
1023: #endif /* TRACE_UCTL */
1024:
1025: return 0;
1026: }
1027:
1028: static void
1029: usage(void)
1030: {
1031: printf("istgtcotrol [options] <command> [<file>]\n");
1032: printf("options:\n");
1033: printf("default may be changed by configuration file\n");
1034: printf(" -c config config file (default %s)\n", DEFAULT_UCTL_CONFIG);
1035: printf(" -h host target host name or IP (default %s)\n", DEFAULT_UCTL_HOST);
1036: printf(" -p port port number (default %d)\n", DEFAULT_UCTL_PORT);
1037: printf(" -t target target iqn\n");
1038: printf(" -l lun target lun (default %d)\n", DEFAULT_UCTL_LUN);
1039: printf(" -f flags media flags (default %s)\n", DEFAULT_UCTL_MFLAGS);
1040: printf(" -s size media size (default %s)\n", DEFAULT_UCTL_MSIZE);
1041: printf(" -q quiet mode\n");
1042: printf(" -v verbose mode\n");
1043: printf(" -A method authentication method (CHAP/Mutual CHAP/Auto)\n");
1044: printf(" -U user auth user\n");
1045: printf(" -S secret auth secret\n");
1046: printf(" -M muser mutual auth user\n");
1047: printf(" -R msecret mutual auth secret\n");
1048: printf(" -H show this usage\n");
1049: printf(" -V show version\n");
1050: printf("command:\n");
1051: printf(" noop no operation\n");
1052: printf(" version show target version\n");
1053: printf(" list list all or specified target\n");
1054: printf(" load load media to specified unit\n");
1055: printf(" unload unload media from specified unit\n");
1056: printf(" change change media with <file> at specified unit\n");
1057: printf(" reset reset specified lun of target\n");
1058: printf(" info show connections of target\n");
1059: }
1060:
1061: int
1062: main(int argc, char *argv[])
1063: {
1064: const char *config_file = DEFAULT_UCTL_CONFIG;
1065: CONFIG *config;
1066: UCTL xuctl, *uctl;
1067: struct sigaction sigact, sigoldact;
1068: int (*func) (UCTL_Ptr);
1069: int port = -1;
1070: int lun = -1;
1071: const char *host = NULL;
1072: const char *mflags = NULL;
1073: const char *mfile = NULL;
1074: const char *msize = NULL;
1075: const char *mtype = DEFAULT_UCTL_MTYPE;
1076: char *target = NULL;
1077: char *user = NULL;
1078: char *secret = NULL;
1079: char *muser = NULL;
1080: char *msecret = NULL;
1081: char *cmd;
1082: char *banner;
1083: long l;
1084: int exec_result;
1085: int req_argc;
1086: int req_target;
1087: int quiet = 0;
1088: int verbose = 0;
1089: int req_auth = -1;
1090: int ch;
1091: int sock;
1092: int rc;
1093: int i;
1094:
1095: #ifdef HAVE_SETPROCTITLE
1096: setproctitle("version %s (%s)",
1097: ISTGT_VERSION, ISTGT_EXTRA_VERSION);
1098: #endif
1099:
1100: memset(&xuctl, 0, sizeof xuctl);
1101: uctl = &xuctl;
1102:
1103: while ((ch = getopt(argc, argv, "c:h:p:t:l:f:s:qvA:U:S:M:R:VH")) != -1) {
1104: switch (ch) {
1105: case 'c':
1106: config_file = optarg;
1107: break;
1108: case 'h':
1109: host = optarg;
1110: break;
1111: case 'p':
1112: l = strtol(optarg, NULL, 10);
1113: if (l < 0 || l > 65535) {
1114: fatal("invalid port %s\n", optarg);
1115: }
1116: port = (int) l;
1117: break;
1118: case 't':
1119: target = optarg;
1120: break;
1121: case 'l':
1122: l = strtol(optarg, NULL, 10);
1123: if (l < 0 || l > 0x3fffU) {
1124: fatal("invalid lun %s\n", optarg);
1125: }
1126: lun = (int) l;
1127: break;
1128: case 'f':
1129: mflags = optarg;
1130: break;
1131: case 's':
1132: msize = optarg;
1133: break;
1134: case 'q':
1135: quiet = 1;
1136: break;
1137: case 'v':
1138: verbose = 1;
1139: break;
1140: case 'A':
1141: if (strcasecmp(optarg, "CHAP") == 0) {
1142: req_auth = 1;
1143: } else if (strcasecmp(optarg, "Mutual") == 0
1144: || strcasecmp(optarg, "Mutual CHAP") == 0
1145: || strcasecmp(optarg, "CHAP Mutual") == 0) {
1146: req_auth = 2;
1147: } else if (strcasecmp(optarg, "Auto") == 0) {
1148: req_auth = 0;
1149: } else {
1150: usage();
1151: exit(EXIT_SUCCESS);
1152: }
1153: break;
1154: case 'U':
1155: user = optarg;
1156: break;
1157: case 'S':
1158: secret = optarg;
1159: #ifndef HAVE_SETPROCTITLE
1160: secret = xstrdup(optarg);
1161: memset(optarg, 'x', strlen(optarg));
1162: #endif
1163: break;
1164: case 'M':
1165: muser = optarg;
1166: break;
1167: case 'R':
1168: msecret = optarg;
1169: #ifndef HAVE_SETPROCTITLE
1170: msecret = xstrdup(optarg);
1171: memset(optarg, 'x', strlen(optarg));
1172: #endif
1173: break;
1174: case 'V':
1175: printf("istgtcontrol version %s (%s)\n",
1176: ISTGT_VERSION, ISTGT_EXTRA_VERSION);
1177: exit(EXIT_SUCCESS);
1178: case 'H':
1179: default:
1180: usage();
1181: exit(EXIT_SUCCESS);
1182: }
1183: }
1184: argc -= optind;
1185: argv += optind;
1186:
1187: /* read config files */
1188: config = istgt_allocate_config();
1189: rc = istgt_read_config(config, config_file);
1190: if (rc < 0) {
1191: fprintf(stderr, "config error\n");
1192: exit(EXIT_FAILURE);
1193: }
1194: if (config->section == NULL) {
1195: fprintf(stderr, "empty config\n");
1196: istgt_free_config(config);
1197: exit(EXIT_FAILURE);
1198: }
1199: uctl->config = config;
1200: //istgt_print_config(config);
1201:
1202: /* take specified command */
1203: if (argc < 1) {
1204: error_usage_return:
1205: istgt_free_config(config);
1206: usage();
1207: exit(EXIT_FAILURE);
1208: }
1209: cmd = strupr(xstrdup(argv[0]));
1210: argc--;
1211: argv++;
1212:
1213: /* get function pointer and parameters for specified command */
1214: func = NULL;
1215: req_argc = -1;
1216: req_target = -1;
1217: for (i = 0; exec_table[i].name != NULL; i++) {
1218: if (cmd[0] == exec_table[i].name[0]
1219: && strcmp(cmd, exec_table[i].name) == 0) {
1220: func = exec_table[i].func;
1221: req_argc = exec_table[i].req_argc;
1222: req_target = exec_table[i].req_target;
1223: break;
1224: }
1225: }
1226: if (func == NULL) {
1227: istgt_free_config(config);
1228: fatal("unknown command %s\n", cmd);
1229: }
1230:
1231: /* patrameter check */
1232: if (argc < req_argc) {
1233: goto error_usage_return;
1234: }
1235: #if 0
1236: if (req_target) {
1237: if (target == NULL) {
1238: goto error_usage_return;
1239: }
1240: }
1241: #endif
1242:
1243: /* take args */
1244: if (strcmp(cmd, "CHANGE") == 0) {
1245: /* change require file */
1246: mfile = argv[0];
1247: }
1248:
1249: /* build parameters */
1250: uctl_init(uctl);
1251: uctl->recvtmpcnt = 0;
1252: uctl->recvtmpidx = 0;
1253: uctl->recvtmpsize = sizeof uctl->recvtmp;
1254: uctl->recvbufsize = sizeof uctl->recvbuf;
1255: uctl->sendbufsize = sizeof uctl->sendbuf;
1256: uctl->worksize = sizeof uctl->work;
1257:
1258: /* override by command line */
1259: if (user != NULL) {
1260: xfree(uctl->auth.user);
1261: uctl->auth.user = xstrdup(user);
1262: }
1263: if (secret != NULL) {
1264: xfree(uctl->auth.secret);
1265: uctl->auth.secret = xstrdup(secret);
1266: }
1267: if (muser != NULL) {
1268: xfree(uctl->auth.muser);
1269: uctl->auth.muser = xstrdup(muser);
1270: }
1271: if (msecret != NULL) {
1272: xfree(uctl->auth.msecret);
1273: uctl->auth.msecret = xstrdup(msecret);
1274: }
1275: if (req_target) {
1276: if (uctl->iqn == NULL
1277: && target == NULL) {
1278: goto error_usage_return;
1279: }
1280: }
1281: if (req_auth >= 0) {
1282: uctl->req_auth_auto = 1;
1283: uctl->req_auth = 0;
1284: uctl->req_auth_mutual = 0;
1285: if (req_auth > 1) {
1286: uctl->req_auth_auto = 0;
1287: uctl->req_auth = 1;
1288: uctl->req_auth_mutual = 1;
1289: } else if (req_auth > 0) {
1290: uctl->req_auth_auto = 0;
1291: uctl->req_auth = 1;
1292: }
1293: }
1294: #ifdef TRACE_UCTL
1295: printf("auto=%d, auth=%d, mutual=%d\n",
1296: uctl->req_auth_auto, uctl->req_auth, uctl->req_auth_mutual);
1297: #endif /* TRACE_UCTL */
1298:
1299: if (host != NULL) {
1300: xfree(uctl->host);
1301: uctl->host = xstrdup(host);
1302: }
1303: if (port >= 0) {
1304: uctl->port = port;
1305: }
1306: if (target != NULL) {
1307: xfree(uctl->iqn);
1308: if (strcasecmp(target, "ALL") == 0) {
1309: uctl->iqn = NULL;
1310: } else {
1311: uctl->iqn = escape_string(target);
1312: }
1313: }
1314: if (lun >= 0) {
1315: uctl->lun = lun;
1316: }
1317: if (mflags != NULL) {
1318: xfree(uctl->mflags);
1319: uctl->mflags = escape_string(mflags);
1320: }
1321: uctl->mfile = escape_string(mfile);
1322: if (msize != NULL) {
1323: xfree(uctl->msize);
1324: uctl->msize = escape_string(msize);
1325: }
1326: uctl->mtype = escape_string(mtype);
1327: uctl->cmd = escape_string(cmd);
1328:
1329: /* show setting */
1330: #define NULLP(S) ((S) == NULL ? "NULL" : (S))
1331: if (verbose) {
1332: printf("iqn=%s, lun=%d\n", NULLP(uctl->iqn), uctl->lun);
1333: printf("media file=%s, flags=%s, size=%s\n",
1334: NULLP(uctl->mfile), NULLP(uctl->mflags), NULLP(uctl->msize));
1335: }
1336:
1337: /* set signals */
1338: memset(&sigact, 0, sizeof sigact);
1339: memset(&sigoldact, 0, sizeof sigoldact);
1340: sigact.sa_handler = SIG_IGN;
1341: sigemptyset(&sigact.sa_mask);
1342: if (sigaction(SIGPIPE, &sigact, &sigoldact) != 0) {
1343: istgt_free_config(config);
1344: fatal("sigaction() failed");
1345: }
1346:
1347: /* connect to target */
1348: if (verbose) {
1349: printf("connect to %s:%d\n", uctl->host, uctl->port);
1350: }
1351: sock = istgt_connect(uctl->host, uctl->port);
1352: if (sock < 0) {
1353: istgt_free_config(config);
1354: fatal("istgt_connect(%s:%d) failed\n", uctl->host, uctl->port);
1355: }
1356: uctl->sock = sock;
1357:
1358: /* get target banner (ready to send) */
1359: banner = get_banner(uctl);
1360: if (banner == NULL) {
1361: close(uctl->sock);
1362: istgt_free_config(config);
1363: fatal("get_banner() failed\n");
1364: }
1365: if (verbose) {
1366: printf("target banner \"%s\"\n", banner);
1367: }
1368:
1369: /* authentication */
1370: retry_auth:
1371: if (uctl->req_auth) {
1372: rc = do_auth(uctl);
1373: if (rc != UCTL_CMD_OK) {
1374: if (rc == UCTL_CMD_REQAUTH
1375: || rc == UCTL_CMD_CHAPSEQ) {
1376: retry_auth_auto:
1377: /* Auth negotiation */
1378: if (uctl->req_auth == 0) {
1379: #ifdef TRCAE_UCTL
1380: printf("Auto negotiation CHAP\n");
1381: #endif /* TRCAE_UCTL */
1382: uctl->req_auth = 1;
1383: goto retry_auth;
1384: } else if (uctl->req_auth_mutual == 0) {
1385: #ifdef TRCAE_UCTL
1386: printf("Auto negotiation Mutual CHAP\n");
1387: #endif /* TRCAE_UCTL */
1388: uctl->req_auth_mutual = 1;
1389: goto retry_auth;
1390: }
1391: }
1392: if (!quiet) {
1393: printf("AUTH failed\n");
1394: }
1395: exec_result = rc;
1396: goto disconnect;
1397: }
1398: }
1399:
1400: /* send specified command */
1401: rc = func(uctl);
1402: exec_result = rc;
1403: if (rc != UCTL_CMD_OK) {
1404: if (rc == UCTL_CMD_REQAUTH
1405: || rc == UCTL_CMD_CHAPSEQ) {
1406: goto retry_auth_auto;
1407: }
1408: if (!quiet) {
1409: printf("ABORT %s command\n", uctl->cmd);
1410: }
1411: } else {
1412: if (!quiet) {
1413: printf("DONE %s command\n", uctl->cmd);
1414: }
1415: }
1416:
1417: /* disconnect from target */
1418: disconnect:
1419: rc = exec_quit(uctl);
1420: if (rc != UCTL_CMD_OK) {
1421: fprintf(stderr, "QUIT failed\n");
1422: /* error but continue */
1423: }
1424:
1425: /* cleanup */
1426: close(sock);
1427: xfree(uctl->host);
1428: xfree(uctl->iqn);
1429: xfree(uctl->mflags);
1430: xfree(uctl->mfile);
1431: xfree(uctl->msize);
1432: xfree(uctl->mtype);
1433: xfree(uctl->cmd);
1434: xfree(banner);
1435: xfree(cmd);
1436: istgt_free_config(config);
1437:
1438: /* return value as execution result */
1439: if (exec_result != UCTL_CMD_OK) {
1440: exit(EXIT_FAILURE);
1441: }
1442: return EXIT_SUCCESS;
1443: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>