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