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