]> CyberLeo.Net >> Repos - FreeBSD/releng/10.3.git/blob - usr.bin/iscsictl/iscsictl.c
- Copy stable/10@296371 to releng/10.3 in preparation for 10.3-RC1
[FreeBSD/releng/10.3.git] / usr.bin / iscsictl / iscsictl.c
1 /*-
2  * Copyright (c) 2012 The FreeBSD Foundation
3  * All rights reserved.
4  *
5  * This software was developed by Edward Tomasz Napierala under sponsorship
6  * from the FreeBSD Foundation.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  *
29  */
30
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 #include <sys/ioctl.h>
35 #include <sys/param.h>
36 #include <sys/linker.h>
37 #include <assert.h>
38 #include <ctype.h>
39 #include <err.h>
40 #include <errno.h>
41 #include <fcntl.h>
42 #include <limits.h>
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <string.h>
46 #include <unistd.h>
47
48 #include <iscsi_ioctl.h>
49 #include "iscsictl.h"
50
51 struct conf *
52 conf_new(void)
53 {
54         struct conf *conf;
55
56         conf = calloc(1, sizeof(*conf));
57         if (conf == NULL)
58                 err(1, "calloc");
59
60         TAILQ_INIT(&conf->conf_targets);
61
62         return (conf);
63 }
64
65 struct target *
66 target_find(struct conf *conf, const char *nickname)
67 {
68         struct target *targ;
69
70         TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
71                 if (targ->t_nickname != NULL &&
72                     strcasecmp(targ->t_nickname, nickname) == 0)
73                         return (targ);
74         }
75
76         return (NULL);
77 }
78
79 struct target *
80 target_new(struct conf *conf)
81 {
82         struct target *targ;
83
84         targ = calloc(1, sizeof(*targ));
85         if (targ == NULL)
86                 err(1, "calloc");
87         targ->t_conf = conf;
88         TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next);
89
90         return (targ);
91 }
92
93 void
94 target_delete(struct target *targ)
95 {
96
97         TAILQ_REMOVE(&targ->t_conf->conf_targets, targ, t_next);
98         free(targ);
99 }
100
101
102 static char *
103 default_initiator_name(void)
104 {
105         char *name;
106         size_t namelen;
107         int error;
108
109         namelen = _POSIX_HOST_NAME_MAX + strlen(DEFAULT_IQN);
110
111         name = calloc(1, namelen + 1);
112         if (name == NULL)
113                 err(1, "calloc");
114         strcpy(name, DEFAULT_IQN);
115         error = gethostname(name + strlen(DEFAULT_IQN),
116             namelen - strlen(DEFAULT_IQN));
117         if (error != 0)
118                 err(1, "gethostname");
119
120         return (name);
121 }
122
123 static bool
124 valid_hex(const char ch)
125 {
126         switch (ch) {
127         case '0':
128         case '1':
129         case '2':
130         case '3':
131         case '4':
132         case '5':
133         case '6':
134         case '7':
135         case '8':
136         case '9':
137         case 'a':
138         case 'A':
139         case 'b':
140         case 'B':
141         case 'c':
142         case 'C':
143         case 'd':
144         case 'D':
145         case 'e':
146         case 'E':
147         case 'f':
148         case 'F':
149                 return (true);
150         default:
151                 return (false);
152         }
153 }
154
155 bool
156 valid_iscsi_name(const char *name)
157 {
158         int i;
159
160         if (strlen(name) >= MAX_NAME_LEN) {
161                 warnx("overlong name for \"%s\"; max length allowed "
162                     "by iSCSI specification is %d characters",
163                     name, MAX_NAME_LEN);
164                 return (false);
165         }
166
167         /*
168          * In the cases below, we don't return an error, just in case the admin
169          * was right, and we're wrong.
170          */
171         if (strncasecmp(name, "iqn.", strlen("iqn.")) == 0) {
172                 for (i = strlen("iqn."); name[i] != '\0'; i++) {
173                         /*
174                          * XXX: We should verify UTF-8 normalisation, as defined
175                          *      by 3.2.6.2: iSCSI Name Encoding.
176                          */
177                         if (isalnum(name[i]))
178                                 continue;
179                         if (name[i] == '-' || name[i] == '.' || name[i] == ':')
180                                 continue;
181                         warnx("invalid character \"%c\" in iSCSI name "
182                             "\"%s\"; allowed characters are letters, digits, "
183                             "'-', '.', and ':'", name[i], name);
184                         break;
185                 }
186                 /*
187                  * XXX: Check more stuff: valid date and a valid reversed domain.
188                  */
189         } else if (strncasecmp(name, "eui.", strlen("eui.")) == 0) {
190                 if (strlen(name) != strlen("eui.") + 16)
191                         warnx("invalid iSCSI name \"%s\"; the \"eui.\" "
192                             "should be followed by exactly 16 hexadecimal "
193                             "digits", name);
194                 for (i = strlen("eui."); name[i] != '\0'; i++) {
195                         if (!valid_hex(name[i])) {
196                                 warnx("invalid character \"%c\" in iSCSI "
197                                     "name \"%s\"; allowed characters are 1-9 "
198                                     "and A-F", name[i], name);
199                                 break;
200                         }
201                 }
202         } else if (strncasecmp(name, "naa.", strlen("naa.")) == 0) {
203                 if (strlen(name) > strlen("naa.") + 32)
204                         warnx("invalid iSCSI name \"%s\"; the \"naa.\" "
205                             "should be followed by at most 32 hexadecimal "
206                             "digits", name);
207                 for (i = strlen("naa."); name[i] != '\0'; i++) {
208                         if (!valid_hex(name[i])) {
209                                 warnx("invalid character \"%c\" in ISCSI "
210                                     "name \"%s\"; allowed characters are 1-9 "
211                                     "and A-F", name[i], name);
212                                 break;
213                         }
214                 }
215         } else {
216                 warnx("invalid iSCSI name \"%s\"; should start with "
217                     "either \".iqn\", \"eui.\", or \"naa.\"",
218                     name);
219         }
220         return (true);
221 }
222
223 void
224 conf_verify(struct conf *conf)
225 {
226         struct target *targ;
227
228         TAILQ_FOREACH(targ, &conf->conf_targets, t_next) {
229                 assert(targ->t_nickname != NULL);
230                 if (targ->t_session_type == SESSION_TYPE_UNSPECIFIED)
231                         targ->t_session_type = SESSION_TYPE_NORMAL;
232                 if (targ->t_session_type == SESSION_TYPE_NORMAL &&
233                     targ->t_name == NULL)
234                         errx(1, "missing TargetName for target \"%s\"",
235                             targ->t_nickname);
236                 if (targ->t_session_type == SESSION_TYPE_DISCOVERY &&
237                     targ->t_name != NULL)
238                         errx(1, "cannot specify TargetName for discovery "
239                             "sessions for target \"%s\"", targ->t_nickname);
240                 if (targ->t_name != NULL) {
241                         if (valid_iscsi_name(targ->t_name) == false)
242                                 errx(1, "invalid target name \"%s\"",
243                                     targ->t_name);
244                 }
245                 if (targ->t_protocol == PROTOCOL_UNSPECIFIED)
246                         targ->t_protocol = PROTOCOL_ISCSI;
247                 if (targ->t_address == NULL)
248                         errx(1, "missing TargetAddress for target \"%s\"",
249                             targ->t_nickname);
250                 if (targ->t_initiator_name == NULL)
251                         targ->t_initiator_name = default_initiator_name();
252                 if (valid_iscsi_name(targ->t_initiator_name) == false)
253                         errx(1, "invalid initiator name \"%s\"",
254                             targ->t_initiator_name);
255                 if (targ->t_header_digest == DIGEST_UNSPECIFIED)
256                         targ->t_header_digest = DIGEST_NONE;
257                 if (targ->t_data_digest == DIGEST_UNSPECIFIED)
258                         targ->t_data_digest = DIGEST_NONE;
259                 if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) {
260                         if (targ->t_user != NULL || targ->t_secret != NULL ||
261                             targ->t_mutual_user != NULL ||
262                             targ->t_mutual_secret != NULL)
263                                 targ->t_auth_method =
264                                     AUTH_METHOD_CHAP;
265                         else
266                                 targ->t_auth_method =
267                                     AUTH_METHOD_NONE;
268                 }
269                 if (targ->t_auth_method == AUTH_METHOD_CHAP) {
270                         if (targ->t_user == NULL) {
271                                 errx(1, "missing chapIName for target \"%s\"",
272                                     targ->t_nickname);
273                         }
274                         if (targ->t_secret == NULL)
275                                 errx(1, "missing chapSecret for target \"%s\"",
276                                     targ->t_nickname);
277                         if (targ->t_mutual_user != NULL ||
278                             targ->t_mutual_secret != NULL) {
279                                 if (targ->t_mutual_user == NULL)
280                                         errx(1, "missing tgtChapName for "
281                                             "target \"%s\"", targ->t_nickname);
282                                 if (targ->t_mutual_secret == NULL)
283                                         errx(1, "missing tgtChapSecret for "
284                                             "target \"%s\"", targ->t_nickname);
285                         }
286                 }
287         }
288 }
289
290 static void
291 conf_from_target(struct iscsi_session_conf *conf,
292     const struct target *targ)
293 {
294         memset(conf, 0, sizeof(*conf));
295
296         /*
297          * XXX: Check bounds and return error instead of silently truncating.
298          */
299         if (targ->t_initiator_name != NULL)
300                 strlcpy(conf->isc_initiator, targ->t_initiator_name,
301                     sizeof(conf->isc_initiator));
302         if (targ->t_initiator_address != NULL)
303                 strlcpy(conf->isc_initiator_addr, targ->t_initiator_address,
304                     sizeof(conf->isc_initiator_addr));
305         if (targ->t_initiator_alias != NULL)
306                 strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias,
307                     sizeof(conf->isc_initiator_alias));
308         if (targ->t_name != NULL)
309                 strlcpy(conf->isc_target, targ->t_name,
310                     sizeof(conf->isc_target));
311         if (targ->t_address != NULL)
312                 strlcpy(conf->isc_target_addr, targ->t_address,
313                     sizeof(conf->isc_target_addr));
314         if (targ->t_user != NULL)
315                 strlcpy(conf->isc_user, targ->t_user,
316                     sizeof(conf->isc_user));
317         if (targ->t_secret != NULL)
318                 strlcpy(conf->isc_secret, targ->t_secret,
319                     sizeof(conf->isc_secret));
320         if (targ->t_mutual_user != NULL)
321                 strlcpy(conf->isc_mutual_user, targ->t_mutual_user,
322                     sizeof(conf->isc_mutual_user));
323         if (targ->t_mutual_secret != NULL)
324                 strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret,
325                     sizeof(conf->isc_mutual_secret));
326         if (targ->t_session_type == SESSION_TYPE_DISCOVERY)
327                 conf->isc_discovery = 1;
328         if (targ->t_protocol == PROTOCOL_ISER)
329                 conf->isc_iser = 1;
330         if (targ->t_header_digest == DIGEST_CRC32C)
331                 conf->isc_header_digest = ISCSI_DIGEST_CRC32C;
332         else
333                 conf->isc_header_digest = ISCSI_DIGEST_NONE;
334         if (targ->t_data_digest == DIGEST_CRC32C)
335                 conf->isc_data_digest = ISCSI_DIGEST_CRC32C;
336         else
337                 conf->isc_data_digest = ISCSI_DIGEST_NONE;
338 }
339
340 static int
341 kernel_add(int iscsi_fd, const struct target *targ)
342 {
343         struct iscsi_session_add isa;
344         int error;
345
346         memset(&isa, 0, sizeof(isa));
347         conf_from_target(&isa.isa_conf, targ);
348         error = ioctl(iscsi_fd, ISCSISADD, &isa);
349         if (error != 0)
350                 warn("ISCSISADD");
351         return (error);
352 }
353
354 static int
355 kernel_modify(int iscsi_fd, unsigned int session_id, const struct target *targ)
356 {
357         struct iscsi_session_modify ism;
358         int error;
359
360         memset(&ism, 0, sizeof(ism));
361         ism.ism_session_id = session_id;
362         conf_from_target(&ism.ism_conf, targ);
363         error = ioctl(iscsi_fd, ISCSISMODIFY, &ism);
364         if (error != 0)
365                 warn("ISCSISMODIFY");
366         return (error);
367 }
368
369 static void
370 kernel_modify_some(int iscsi_fd, unsigned int session_id, const char *target,
371   const char *target_addr, const char *user, const char *secret)
372 {
373         struct iscsi_session_state *states = NULL;
374         struct iscsi_session_state *state;
375         struct iscsi_session_conf *conf;
376         struct iscsi_session_list isl;
377         struct iscsi_session_modify ism;
378         unsigned int i, nentries = 1;
379         int error;
380
381         for (;;) {
382                 states = realloc(states,
383                     nentries * sizeof(struct iscsi_session_state));
384                 if (states == NULL)
385                         err(1, "realloc");
386
387                 memset(&isl, 0, sizeof(isl));
388                 isl.isl_nentries = nentries;
389                 isl.isl_pstates = states;
390
391                 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
392                 if (error != 0 && errno == EMSGSIZE) {
393                         nentries *= 4;
394                         continue;
395                 }
396                 break;
397         }
398         if (error != 0)
399                 errx(1, "ISCSISLIST");
400
401         for (i = 0; i < isl.isl_nentries; i++) {
402                 state = &states[i];
403
404                 if (state->iss_id == session_id)
405                         break;
406         }
407         if (i == isl.isl_nentries)
408                 errx(1, "session-id %u not found", session_id);
409
410         conf = &state->iss_conf;
411
412         if (target != NULL)
413                 strlcpy(conf->isc_target, target, sizeof(conf->isc_target));
414         if (target_addr != NULL)
415                 strlcpy(conf->isc_target_addr, target_addr,
416                     sizeof(conf->isc_target_addr));
417         if (user != NULL)
418                 strlcpy(conf->isc_user, user, sizeof(conf->isc_user));
419         if (secret != NULL)
420                 strlcpy(conf->isc_secret, secret, sizeof(conf->isc_secret));
421
422         memset(&ism, 0, sizeof(ism));
423         ism.ism_session_id = session_id;
424         memcpy(&ism.ism_conf, conf, sizeof(ism.ism_conf));
425         error = ioctl(iscsi_fd, ISCSISMODIFY, &ism);
426         if (error != 0)
427                 warn("ISCSISMODIFY");
428 }
429
430 static int
431 kernel_remove(int iscsi_fd, const struct target *targ)
432 {
433         struct iscsi_session_remove isr;
434         int error;
435
436         memset(&isr, 0, sizeof(isr));
437         conf_from_target(&isr.isr_conf, targ);
438         error = ioctl(iscsi_fd, ISCSISREMOVE, &isr);
439         if (error != 0)
440                 warn("ISCSISREMOVE");
441         return (error);
442 }
443
444 /*
445  * XXX: Add filtering.
446  */
447 static int
448 kernel_list(int iscsi_fd, const struct target *targ __unused,
449     int verbose)
450 {
451         struct iscsi_session_state *states = NULL;
452         const struct iscsi_session_state *state;
453         const struct iscsi_session_conf *conf;
454         struct iscsi_session_list isl;
455         unsigned int i, nentries = 1;
456         int error;
457
458         for (;;) {
459                 states = realloc(states,
460                     nentries * sizeof(struct iscsi_session_state));
461                 if (states == NULL)
462                         err(1, "realloc");
463
464                 memset(&isl, 0, sizeof(isl));
465                 isl.isl_nentries = nentries;
466                 isl.isl_pstates = states;
467
468                 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
469                 if (error != 0 && errno == EMSGSIZE) {
470                         nentries *= 4;
471                         continue;
472                 }
473                 break;
474         }
475         if (error != 0) {
476                 warn("ISCSISLIST");
477                 return (error);
478         }
479
480         if (verbose != 0) {
481                 for (i = 0; i < isl.isl_nentries; i++) {
482                         state = &states[i];
483                         conf = &state->iss_conf;
484
485                         printf("Session ID:       %u\n", state->iss_id);
486                         printf("Initiator name:   %s\n", conf->isc_initiator);
487                         printf("Initiator portal: %s\n",
488                             conf->isc_initiator_addr);
489                         printf("Initiator alias:  %s\n",
490                             conf->isc_initiator_alias);
491                         printf("Target name:      %s\n", conf->isc_target);
492                         printf("Target portal:    %s\n",
493                             conf->isc_target_addr);
494                         printf("Target alias:     %s\n",
495                             state->iss_target_alias);
496                         printf("User:             %s\n", conf->isc_user);
497                         printf("Secret:           %s\n", conf->isc_secret);
498                         printf("Mutual user:      %s\n",
499                             conf->isc_mutual_user);
500                         printf("Mutual secret:    %s\n",
501                             conf->isc_mutual_secret);
502                         printf("Session type:     %s\n",
503                             conf->isc_discovery ? "Discovery" : "Normal");
504                         printf("Session state:    %s\n",
505                             state->iss_connected ?
506                             "Connected" : "Disconnected");
507                         printf("Failure reason:   %s\n", state->iss_reason);
508                         printf("Header digest:    %s\n",
509                             state->iss_header_digest == ISCSI_DIGEST_CRC32C ?
510                             "CRC32C" : "None");
511                         printf("Data digest:      %s\n",
512                             state->iss_data_digest == ISCSI_DIGEST_CRC32C ?
513                             "CRC32C" : "None");
514                         printf("DataSegmentLen:   %d\n",
515                             state->iss_max_data_segment_length);
516                         printf("ImmediateData:    %s\n",
517                             state->iss_immediate_data ? "Yes" : "No");
518                         printf("iSER (RDMA):      %s\n",
519                             conf->isc_iser ? "Yes" : "No");
520                         printf("Device nodes:     ");
521                         print_periphs(state->iss_id);
522                         printf("\n\n");
523                 }
524         } else {
525                 printf("%-36s %-16s %s\n",
526                     "Target name", "Target portal", "State");
527                 for (i = 0; i < isl.isl_nentries; i++) {
528                         state = &states[i];
529                         conf = &state->iss_conf;
530
531                         printf("%-36s %-16s ",
532                             conf->isc_target, conf->isc_target_addr);
533
534                         if (state->iss_reason[0] != '\0') {
535                                 printf("%s\n", state->iss_reason);
536                         } else {
537                                 if (conf->isc_discovery) {
538                                         printf("Discovery\n");
539                                 } else if (state->iss_connected) {
540                                         printf("Connected: ");
541                                         print_periphs(state->iss_id);
542                                         printf("\n");
543                                 } else {
544                                         printf("Disconnected\n");
545                                 }
546                         }
547                 }
548         }
549
550         return (0);
551 }
552
553 static int
554 kernel_wait(int iscsi_fd, int timeout)
555 {
556         struct iscsi_session_state *states = NULL;
557         const struct iscsi_session_state *state;
558         const struct iscsi_session_conf *conf;
559         struct iscsi_session_list isl;
560         unsigned int i, nentries = 1;
561         bool all_connected;
562         int error;
563
564         for (;;) {
565                 for (;;) {
566                         states = realloc(states,
567                             nentries * sizeof(struct iscsi_session_state));
568                         if (states == NULL)
569                                 err(1, "realloc");
570
571                         memset(&isl, 0, sizeof(isl));
572                         isl.isl_nentries = nentries;
573                         isl.isl_pstates = states;
574
575                         error = ioctl(iscsi_fd, ISCSISLIST, &isl);
576                         if (error != 0 && errno == EMSGSIZE) {
577                                 nentries *= 4;
578                                 continue;
579                         }
580                         break;
581                 }
582                 if (error != 0) {
583                         warn("ISCSISLIST");
584                         return (error);
585                 }
586
587                 all_connected = true;
588                 for (i = 0; i < isl.isl_nentries; i++) {
589                         state = &states[i];
590                         conf = &state->iss_conf;
591
592                         if (!state->iss_connected) {
593                                 all_connected = false;
594                                 break;
595                         }
596                 }
597
598                 if (all_connected)
599                         return (0);
600
601                 sleep(1);
602
603                 if (timeout > 0) {
604                         timeout--;
605                         if (timeout == 0)
606                                 return (1);
607                 }
608         }
609 }
610
611 static void
612 usage(void)
613 {
614
615         fprintf(stderr, "usage: iscsictl -A -p portal -t target "
616             "[-u user -s secret] [-w timeout]\n");
617         fprintf(stderr, "       iscsictl -A -d discovery-host "
618             "[-u user -s secret]\n");
619         fprintf(stderr, "       iscsictl -A -a [-c path]\n");
620         fprintf(stderr, "       iscsictl -A -n nickname [-c path]\n");
621         fprintf(stderr, "       iscsictl -M -i session-id [-p portal] "
622             "[-t target] [-u user] [-s secret]\n");
623         fprintf(stderr, "       iscsictl -M -i session-id -n nickname "
624             "[-c path]\n");
625         fprintf(stderr, "       iscsictl -R [-p portal] [-t target]\n");
626         fprintf(stderr, "       iscsictl -R -a\n");
627         fprintf(stderr, "       iscsictl -R -n nickname [-c path]\n");
628         fprintf(stderr, "       iscsictl -L [-v] [-w timeout]\n");
629         exit(1);
630 }
631
632 char *
633 checked_strdup(const char *s)
634 {
635         char *c;
636
637         c = strdup(s);
638         if (c == NULL)
639                 err(1, "strdup");
640         return (c);
641 }
642
643 int
644 main(int argc, char **argv)
645 {
646         int Aflag = 0, Mflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0;
647         const char *conf_path = DEFAULT_CONFIG_PATH;
648         char *nickname = NULL, *discovery_host = NULL, *portal = NULL,
649              *target = NULL, *user = NULL, *secret = NULL;
650         int timeout = -1;
651         long long session_id = -1;
652         char *end;
653         int ch, error, iscsi_fd, retval, saved_errno;
654         int failed = 0;
655         struct conf *conf;
656         struct target *targ;
657
658         while ((ch = getopt(argc, argv, "AMRLac:d:i:n:p:t:u:s:vw:")) != -1) {
659                 switch (ch) {
660                 case 'A':
661                         Aflag = 1;
662                         break;
663                 case 'M':
664                         Mflag = 1;
665                         break;
666                 case 'R':
667                         Rflag = 1;
668                         break;
669                 case 'L':
670                         Lflag = 1;
671                         break;
672                 case 'a':
673                         aflag = 1;
674                         break;
675                 case 'c':
676                         conf_path = optarg;
677                         break;
678                 case 'd':
679                         discovery_host = optarg;
680                         break;
681                 case 'i':
682                         session_id = strtol(optarg, &end, 10);
683                         if ((size_t)(end - optarg) != strlen(optarg))
684                                 errx(1, "trailing characters after session-id");
685                         if (session_id < 0)
686                                 errx(1, "session-id cannot be negative");
687                         if (session_id > UINT_MAX)
688                                 errx(1, "session-id cannot be greater than %u",
689                                     UINT_MAX);
690                         break;
691                 case 'n':
692                         nickname = optarg;
693                         break;
694                 case 'p':
695                         portal = optarg;
696                         break;
697                 case 't':
698                         target = optarg;
699                         break;
700                 case 'u':
701                         user = optarg;
702                         break;
703                 case 's':
704                         secret = optarg;
705                         break;
706                 case 'v':
707                         vflag = 1;
708                         break;
709                 case 'w':
710                         timeout = strtol(optarg, &end, 10);
711                         if ((size_t)(end - optarg) != strlen(optarg))
712                                 errx(1, "trailing characters after timeout");
713                         if (timeout < 0)
714                                 errx(1, "timeout cannot be negative");
715                         break;
716                 case '?':
717                 default:
718                         usage();
719                 }
720         }
721         argc -= optind;
722         if (argc != 0)
723                 usage();
724
725         if (Aflag + Mflag + Rflag + Lflag == 0)
726                 Lflag = 1;
727         if (Aflag + Mflag + Rflag + Lflag > 1)
728                 errx(1, "at most one of -A, -M, -R, or -L may be specified");
729
730         /*
731          * Note that we ignore unneccessary/inapplicable "-c" flag; so that
732          * people can do something like "alias ISCSICTL="iscsictl -c path"
733          * in shell scripts.
734          */
735         if (Aflag != 0) {
736                 if (aflag != 0) {
737                         if (portal != NULL)
738                                 errx(1, "-a and -p and mutually exclusive");
739                         if (target != NULL)
740                                 errx(1, "-a and -t and mutually exclusive");
741                         if (user != NULL)
742                                 errx(1, "-a and -u and mutually exclusive");
743                         if (secret != NULL)
744                                 errx(1, "-a and -s and mutually exclusive");
745                         if (nickname != NULL)
746                                 errx(1, "-a and -n and mutually exclusive");
747                         if (discovery_host != NULL)
748                                 errx(1, "-a and -d and mutually exclusive");
749                 } else if (nickname != NULL) {
750                         if (portal != NULL)
751                                 errx(1, "-n and -p and mutually exclusive");
752                         if (target != NULL)
753                                 errx(1, "-n and -t and mutually exclusive");
754                         if (user != NULL)
755                                 errx(1, "-n and -u and mutually exclusive");
756                         if (secret != NULL)
757                                 errx(1, "-n and -s and mutually exclusive");
758                         if (discovery_host != NULL)
759                                 errx(1, "-n and -d and mutually exclusive");
760                 } else if (discovery_host != NULL) {
761                         if (portal != NULL)
762                                 errx(1, "-d and -p and mutually exclusive");
763                         if (target != NULL)
764                                 errx(1, "-d and -t and mutually exclusive");
765                 } else {
766                         if (target == NULL && portal == NULL)
767                                 errx(1, "must specify -a, -n or -t/-p");
768
769                         if (target != NULL && portal == NULL)
770                                 errx(1, "-t must always be used with -p");
771                         if (portal != NULL && target == NULL)
772                                 errx(1, "-p must always be used with -t");
773                 }
774
775                 if (user != NULL && secret == NULL)
776                         errx(1, "-u must always be used with -s");
777                 if (secret != NULL && user == NULL)
778                         errx(1, "-s must always be used with -u");
779
780                 if (session_id != -1)
781                         errx(1, "-i cannot be used with -A");
782                 if (vflag != 0)
783                         errx(1, "-v cannot be used with -A");
784
785         } else if (Mflag != 0) {
786                 if (session_id == -1)
787                         errx(1, "-M requires -i");
788
789                 if (discovery_host != NULL)
790                         errx(1, "-M and -d are mutually exclusive");
791                 if (aflag != 0)
792                         errx(1, "-M and -a are mutually exclusive");
793                 if (nickname != NULL) {
794                         if (portal != NULL)
795                                 errx(1, "-n and -p and mutually exclusive");
796                         if (target != NULL)
797                                 errx(1, "-n and -t and mutually exclusive");
798                         if (user != NULL)
799                                 errx(1, "-n and -u and mutually exclusive");
800                         if (secret != NULL)
801                                 errx(1, "-n and -s and mutually exclusive");
802                 }
803
804                 if (vflag != 0)
805                         errx(1, "-v cannot be used with -M");
806                 if (timeout != -1)
807                         errx(1, "-w cannot be used with -M");
808
809         } else if (Rflag != 0) {
810                 if (user != NULL)
811                         errx(1, "-R and -u are mutually exclusive");
812                 if (secret != NULL)
813                         errx(1, "-R and -s are mutually exclusive");
814                 if (discovery_host != NULL)
815                         errx(1, "-R and -d are mutually exclusive");
816
817                 if (aflag != 0) {
818                         if (portal != NULL)
819                                 errx(1, "-a and -p and mutually exclusive");
820                         if (target != NULL)
821                                 errx(1, "-a and -t and mutually exclusive");
822                         if (nickname != NULL)
823                                 errx(1, "-a and -n and mutually exclusive");
824                 } else if (nickname != NULL) {
825                         if (portal != NULL)
826                                 errx(1, "-n and -p and mutually exclusive");
827                         if (target != NULL)
828                                 errx(1, "-n and -t and mutually exclusive");
829                 } else if (target == NULL && portal == NULL) {
830                         errx(1, "must specify either -a, -n, -t, or -p");
831                 }
832
833                 if (session_id != -1)
834                         errx(1, "-i cannot be used with -R");
835                 if (vflag != 0)
836                         errx(1, "-v cannot be used with -R");
837                 if (timeout != -1)
838                         errx(1, "-w cannot be used with -R");
839
840         } else {
841                 assert(Lflag != 0);
842
843                 if (portal != NULL)
844                         errx(1, "-L and -p and mutually exclusive");
845                 if (target != NULL)
846                         errx(1, "-L and -t and mutually exclusive");
847                 if (user != NULL)
848                         errx(1, "-L and -u and mutually exclusive");
849                 if (secret != NULL)
850                         errx(1, "-L and -s and mutually exclusive");
851                 if (nickname != NULL)
852                         errx(1, "-L and -n and mutually exclusive");
853                 if (discovery_host != NULL)
854                         errx(1, "-L and -d and mutually exclusive");
855
856                 if (session_id != -1)
857                         errx(1, "-i cannot be used with -L");
858         }
859
860         iscsi_fd = open(ISCSI_PATH, O_RDWR);
861         if (iscsi_fd < 0 && errno == ENOENT) {
862                 saved_errno = errno;
863                 retval = kldload("iscsi");
864                 if (retval != -1)
865                         iscsi_fd = open(ISCSI_PATH, O_RDWR);
866                 else
867                         errno = saved_errno;
868         }
869         if (iscsi_fd < 0)
870                 err(1, "failed to open %s", ISCSI_PATH);
871
872         if (Aflag != 0 && aflag != 0) {
873                 conf = conf_new_from_file(conf_path);
874
875                 TAILQ_FOREACH(targ, &conf->conf_targets, t_next)
876                         failed += kernel_add(iscsi_fd, targ);
877         } else if (nickname != NULL) {
878                 conf = conf_new_from_file(conf_path);
879                 targ = target_find(conf, nickname);
880                 if (targ == NULL)
881                         errx(1, "target %s not found in %s",
882                             nickname, conf_path);
883
884                 if (Aflag != 0)
885                         failed += kernel_add(iscsi_fd, targ);
886                 else if (Mflag != 0)
887                         failed += kernel_modify(iscsi_fd, session_id, targ);
888                 else if (Rflag != 0)
889                         failed += kernel_remove(iscsi_fd, targ);
890                 else
891                         failed += kernel_list(iscsi_fd, targ, vflag);
892         } else if (Mflag != 0) {
893                 kernel_modify_some(iscsi_fd, session_id, target, portal,
894                     user, secret);
895         } else {
896                 if (Aflag != 0 && target != NULL) {
897                         if (valid_iscsi_name(target) == false)
898                                 errx(1, "invalid target name \"%s\"", target);
899                 }
900                 conf = conf_new();
901                 targ = target_new(conf);
902                 targ->t_initiator_name = default_initiator_name();
903                 targ->t_header_digest = DIGEST_NONE;
904                 targ->t_data_digest = DIGEST_NONE;
905                 targ->t_name = target;
906                 if (discovery_host != NULL) {
907                         targ->t_session_type = SESSION_TYPE_DISCOVERY;
908                         targ->t_address = discovery_host;
909                 } else {
910                         targ->t_session_type = SESSION_TYPE_NORMAL;
911                         targ->t_address = portal;
912                 }
913                 targ->t_user = user;
914                 targ->t_secret = secret;
915
916                 if (Aflag != 0)
917                         failed += kernel_add(iscsi_fd, targ);
918                 else if (Rflag != 0)
919                         failed += kernel_remove(iscsi_fd, targ);
920                 else
921                         failed += kernel_list(iscsi_fd, targ, vflag);
922         }
923
924         if (timeout != -1)
925                 failed += kernel_wait(iscsi_fd, timeout);
926
927         error = close(iscsi_fd);
928         if (error != 0)
929                 err(1, "close");
930
931         if (failed > 0)
932                 return (1);
933         return (0);
934 }