]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.bin/iscsictl/iscsictl.c
Copy head (r256279) to stable/10 as part of the 10.0-RELEASE cycle.
[FreeBSD/stable/10.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 #ifndef ICL_KERNEL_PROXY
246                 if (targ->t_protocol == PROTOCOL_ISER)
247                         errx(1, "iSER support requires ICL_KERNEL_PROXY; "
248                             "see iscsi(4) for details");
249 #endif
250                 if (targ->t_address == NULL)
251                         errx(1, "missing TargetAddress for target \"%s\"",
252                             targ->t_nickname);
253                 if (targ->t_initiator_name == NULL)
254                         targ->t_initiator_name = default_initiator_name();
255                 if (valid_iscsi_name(targ->t_initiator_name) == false)
256                         errx(1, "invalid initiator name \"%s\"",
257                             targ->t_initiator_name);
258                 if (targ->t_header_digest == DIGEST_UNSPECIFIED)
259                         targ->t_header_digest = DIGEST_NONE;
260                 if (targ->t_data_digest == DIGEST_UNSPECIFIED)
261                         targ->t_data_digest = DIGEST_NONE;
262                 if (targ->t_auth_method == AUTH_METHOD_UNSPECIFIED) {
263                         if (targ->t_user != NULL || targ->t_secret != NULL ||
264                             targ->t_mutual_user != NULL ||
265                             targ->t_mutual_secret != NULL)
266                                 targ->t_auth_method =
267                                     AUTH_METHOD_CHAP;
268                         else
269                                 targ->t_auth_method =
270                                     AUTH_METHOD_NONE;
271                 }
272                 if (targ->t_auth_method == AUTH_METHOD_CHAP) {
273                         if (targ->t_user == NULL) {
274                                 errx(1, "missing chapIName for target \"%s\"",
275                                     targ->t_nickname);
276                         }
277                         if (targ->t_secret == NULL)
278                                 errx(1, "missing chapSecret for target \"%s\"",
279                                     targ->t_nickname);
280                         if (targ->t_mutual_user != NULL ||
281                             targ->t_mutual_secret != NULL) {
282                                 if (targ->t_mutual_user == NULL)
283                                         errx(1, "missing tgtChapName for "
284                                             "target \"%s\"", targ->t_nickname);
285                                 if (targ->t_mutual_secret == NULL)
286                                         errx(1, "missing tgtChapSecret for "
287                                             "target \"%s\"", targ->t_nickname);
288                         }
289                 }
290         }
291 }
292
293 static void
294 conf_from_target(struct iscsi_session_conf *conf,
295     const struct target *targ)
296 {
297         memset(conf, 0, sizeof(*conf));
298
299         /*
300          * XXX: Check bounds and return error instead of silently truncating.
301          */
302         if (targ->t_initiator_name != NULL)
303                 strlcpy(conf->isc_initiator, targ->t_initiator_name,
304                     sizeof(conf->isc_initiator));
305         if (targ->t_initiator_address != NULL)
306                 strlcpy(conf->isc_initiator_addr, targ->t_initiator_address,
307                     sizeof(conf->isc_initiator_addr));
308         if (targ->t_initiator_alias != NULL)
309                 strlcpy(conf->isc_initiator_alias, targ->t_initiator_alias,
310                     sizeof(conf->isc_initiator_alias));
311         if (targ->t_name != NULL)
312                 strlcpy(conf->isc_target, targ->t_name,
313                     sizeof(conf->isc_target));
314         if (targ->t_address != NULL)
315                 strlcpy(conf->isc_target_addr, targ->t_address,
316                     sizeof(conf->isc_target_addr));
317         if (targ->t_user != NULL)
318                 strlcpy(conf->isc_user, targ->t_user,
319                     sizeof(conf->isc_user));
320         if (targ->t_secret != NULL)
321                 strlcpy(conf->isc_secret, targ->t_secret,
322                     sizeof(conf->isc_secret));
323         if (targ->t_mutual_user != NULL)
324                 strlcpy(conf->isc_mutual_user, targ->t_mutual_user,
325                     sizeof(conf->isc_mutual_user));
326         if (targ->t_mutual_secret != NULL)
327                 strlcpy(conf->isc_mutual_secret, targ->t_mutual_secret,
328                     sizeof(conf->isc_mutual_secret));
329         if (targ->t_session_type == SESSION_TYPE_DISCOVERY)
330                 conf->isc_discovery = 1;
331         if (targ->t_protocol == PROTOCOL_ISER)
332                 conf->isc_iser = 1;
333         if (targ->t_header_digest == DIGEST_CRC32C)
334                 conf->isc_header_digest = ISCSI_DIGEST_CRC32C;
335         else
336                 conf->isc_header_digest = ISCSI_DIGEST_NONE;
337         if (targ->t_data_digest == DIGEST_CRC32C)
338                 conf->isc_data_digest = ISCSI_DIGEST_CRC32C;
339         else
340                 conf->isc_data_digest = ISCSI_DIGEST_NONE;
341 }
342
343 static int
344 kernel_add(int iscsi_fd, const struct target *targ)
345 {
346         struct iscsi_session_add isa;
347         int error;
348
349         memset(&isa, 0, sizeof(isa));
350         conf_from_target(&isa.isa_conf, targ);
351         error = ioctl(iscsi_fd, ISCSISADD, &isa);
352         if (error != 0)
353                 warn("ISCSISADD");
354         return (error);
355 }
356
357 static int
358 kernel_remove(int iscsi_fd, const struct target *targ)
359 {
360         struct iscsi_session_remove isr;
361         int error;
362
363         memset(&isr, 0, sizeof(isr));
364         conf_from_target(&isr.isr_conf, targ);
365         error = ioctl(iscsi_fd, ISCSISREMOVE, &isr);
366         if (error != 0)
367                 warn("ISCSISREMOVE");
368         return (error);
369 }
370
371 /*
372  * XXX: Add filtering.
373  */
374 static int
375 kernel_list(int iscsi_fd, const struct target *targ __unused,
376     int verbose)
377 {
378         struct iscsi_session_state *states = NULL;
379         const struct iscsi_session_state *state;
380         const struct iscsi_session_conf *conf;
381         struct iscsi_session_list isl;
382         unsigned int i, nentries = 1;
383         int error;
384         bool show_periphs;
385
386         for (;;) {
387                 states = realloc(states,
388                     nentries * sizeof(struct iscsi_session_state));
389                 if (states == NULL)
390                         err(1, "realloc");
391
392                 memset(&isl, 0, sizeof(isl));
393                 isl.isl_nentries = nentries;
394                 isl.isl_pstates = states;
395
396                 error = ioctl(iscsi_fd, ISCSISLIST, &isl);
397                 if (error != 0 && errno == EMSGSIZE) {
398                         nentries *= 4;
399                         continue;
400                 }
401                 break;
402         }
403         if (error != 0) {
404                 warn("ISCSISLIST");
405                 return (error);
406         }
407
408         if (verbose != 0) {
409                 for (i = 0; i < isl.isl_nentries; i++) {
410                         state = &states[i];
411                         conf = &state->iss_conf;
412
413                         printf("Session ID:      %d\n", state->iss_id);
414                         printf("Initiator name:  %s\n", conf->isc_initiator);
415                         printf("Initiator addr:  %s\n",
416                             conf->isc_initiator_addr);
417                         printf("Initiator alias: %s\n",
418                             conf->isc_initiator_alias);
419                         printf("Target name:     %s\n", conf->isc_target);
420                         printf("Target addr:     %s\n",
421                             conf->isc_target_addr);
422                         printf("Target alias:    %s\n",
423                             state->iss_target_alias);
424                         printf("User:            %s\n", conf->isc_user);
425                         printf("Secret:          %s\n", conf->isc_secret);
426                         printf("Mutual user:     %s\n",
427                             conf->isc_mutual_user);
428                         printf("Mutual secret:   %s\n",
429                             conf->isc_mutual_secret);
430                         printf("Session type:    %s\n",
431                             conf->isc_discovery ? "Discovery" : "Normal");
432                         printf("Session state:   %s\n",
433                             state->iss_connected ?
434                             "Connected" : "Disconnected");
435                         printf("Failure reason:  %s\n", state->iss_reason);
436                         printf("Header digest:   %s\n",
437                             state->iss_header_digest == ISCSI_DIGEST_CRC32C ?
438                             "CRC32C" : "None");
439                         printf("Data digest:     %s\n",
440                             state->iss_data_digest == ISCSI_DIGEST_CRC32C ?
441                             "CRC32C" : "None");
442                         printf("DataSegmentLen:  %d\n",
443                             state->iss_max_data_segment_length);
444                         printf("ImmediateData:   %s\n",
445                             state->iss_immediate_data ? "Yes" : "No");
446                         printf("iSER (RDMA):     %s\n",
447                             conf->isc_iser ? "Yes" : "No");
448                         printf("Device nodes:    ");
449                         print_periphs(state->iss_id);
450                         printf("\n\n");
451                 }
452         } else {
453                 printf("%-36s %-16s %s\n",
454                     "Target name", "Target addr", "State");
455                 for (i = 0; i < isl.isl_nentries; i++) {
456                         state = &states[i];
457                         conf = &state->iss_conf;
458                         show_periphs = false;
459
460                         printf("%-36s %-16s ",
461                             conf->isc_target, conf->isc_target_addr);
462
463                         if (state->iss_reason[0] != '\0') {
464                                 printf("%s\n", state->iss_reason);
465                         } else {
466                                 if (conf->isc_discovery) {
467                                         printf("Discovery\n");
468                                 } else if (state->iss_connected) {
469                                         printf("Connected: ");
470                                         print_periphs(state->iss_id);
471                                         printf("\n");
472                                 } else {
473                                         printf("Disconnected\n");
474                                 }
475                         }
476                 }
477         }
478
479         return (0);
480 }
481
482 static void
483 usage(void)
484 {
485
486         fprintf(stderr, "usage: iscsictl -A -h host -t target "
487             "[-u user -s secret]\n");
488         fprintf(stderr, "       iscsictl -A -d discovery-host "
489             "[-u user -s secret]\n");
490         fprintf(stderr, "       iscsictl -A -a [-c path]\n");
491         fprintf(stderr, "       iscsictl -A -n nickname [-c path]\n");
492         fprintf(stderr, "       iscsictl -R [-h host] [-t target]\n");
493         fprintf(stderr, "       iscsictl -R -a\n");
494         fprintf(stderr, "       iscsictl -R -n nickname [-c path]\n");
495         fprintf(stderr, "       iscsictl -L [-v]\n");
496         exit(1);
497 }
498
499 char *
500 checked_strdup(const char *s)
501 {
502         char *c;
503
504         c = strdup(s);
505         if (c == NULL)
506                 err(1, "strdup");
507         return (c);
508 }
509
510 int
511 main(int argc, char **argv)
512 {
513         int Aflag = 0, Rflag = 0, Lflag = 0, aflag = 0, vflag = 0;
514         const char *conf_path = DEFAULT_CONFIG_PATH;
515         char *nickname = NULL, *discovery_host = NULL, *host = NULL,
516              *target = NULL, *user = NULL, *secret = NULL;
517         int ch, error, iscsi_fd, retval, saved_errno;
518         int failed = 0;
519         struct conf *conf;
520         struct target *targ;
521
522         while ((ch = getopt(argc, argv, "ARLac:d:n:h:t:u:s:v")) != -1) {
523                 switch (ch) {
524                 case 'A':
525                         Aflag = 1;
526                         break;
527                 case 'R':
528                         Rflag = 1;
529                         break;
530                 case 'L':
531                         Lflag = 1;
532                         break;
533                 case 'a':
534                         aflag = 1;
535                         break;
536                 case 'c':
537                         conf_path = optarg;
538                         break;
539                 case 'd':
540                         discovery_host = optarg;
541                         break;
542                 case 'n':
543                         nickname = optarg;
544                         break;
545                 case 'h':
546                         host = optarg;
547                         break;
548                 case 't':
549                         target = optarg;
550                         break;
551                 case 'u':
552                         user = optarg;
553                         break;
554                 case 's':
555                         secret = optarg;
556                         break;
557                 case 'v':
558                         vflag = 1;
559                         break;
560                 case '?':
561                 default:
562                         usage();
563                 }
564         }
565         argc -= optind;
566         if (argc != 0)
567                 usage();
568
569         if (Aflag + Rflag + Lflag == 0)
570                 Lflag = 1;
571         if (Aflag + Rflag + Lflag > 1)
572                 errx(1, "at most one of -A, -R, or -L may be specified");
573
574         /*
575          * Note that we ignore unneccessary/inapplicable "-c" flag; so that
576          * people can do something like "alias ISCSICTL="iscsictl -c path"
577          * in shell scripts.
578          */
579         if (Aflag != 0) {
580                 if (aflag != 0) {
581                         if (host != NULL)
582                                 errx(1, "-a and -h and mutually exclusive");
583                         if (target != NULL)
584                                 errx(1, "-a and -t and mutually exclusive");
585                         if (user != NULL)
586                                 errx(1, "-a and -u and mutually exclusive");
587                         if (secret != NULL)
588                                 errx(1, "-a and -s and mutually exclusive");
589                         if (nickname != NULL)
590                                 errx(1, "-a and -n and mutually exclusive");
591                         if (discovery_host != NULL)
592                                 errx(1, "-a and -d and mutually exclusive");
593                 } else if (nickname != NULL) {
594                         if (host != NULL)
595                                 errx(1, "-n and -h and mutually exclusive");
596                         if (target != NULL)
597                                 errx(1, "-n and -t and mutually exclusive");
598                         if (user != NULL)
599                                 errx(1, "-n and -u and mutually exclusive");
600                         if (secret != NULL)
601                                 errx(1, "-n and -s and mutually exclusive");
602                         if (discovery_host != NULL)
603                                 errx(1, "-n and -d and mutually exclusive");
604                 } else if (discovery_host != NULL) {
605                         if (host != NULL)
606                                 errx(1, "-d and -h and mutually exclusive");
607                         if (target != NULL)
608                                 errx(1, "-d and -t and mutually exclusive");
609                 } else {
610                         if (target == NULL && host == NULL)
611                                 errx(1, "must specify -a, -n or -t/-h");
612
613                         if (target != NULL && host == NULL)
614                                 errx(1, "-t must always be used with -h");
615                         if (host != NULL && target == NULL)
616                                 errx(1, "-h must always be used with -t");
617                 }
618
619                 if (user != NULL && secret == NULL)
620                         errx(1, "-u must always be used with -s");
621                 if (secret != NULL && user == NULL)
622                         errx(1, "-s must always be used with -u");
623
624                 if (vflag != 0)
625                         errx(1, "-v cannot be used with -A");
626
627         } else if (Rflag != 0) {
628                 if (user != NULL)
629                         errx(1, "-R and -u are mutually exclusive");
630                 if (secret != NULL)
631                         errx(1, "-R and -s are mutually exclusive");
632                 if (discovery_host != NULL)
633                         errx(1, "-R and -d are mutually exclusive");
634
635                 if (aflag != 0) {
636                         if (host != NULL)
637                                 errx(1, "-a and -h and mutually exclusive");
638                         if (target != NULL)
639                                 errx(1, "-a and -t and mutually exclusive");
640                         if (nickname != NULL)
641                                 errx(1, "-a and -n and mutually exclusive");
642                 } else if (nickname != NULL) {
643                         if (host != NULL)
644                                 errx(1, "-n and -h and mutually exclusive");
645                         if (target != NULL)
646                                 errx(1, "-n and -t and mutually exclusive");
647                 } else if (host != NULL) {
648                         if (target != NULL)
649                                 errx(1, "-h and -t and mutually exclusive");
650                 } else if (target != NULL) {
651                         if (host != NULL)
652                                 errx(1, "-t and -h and mutually exclusive");
653                 } else
654                         errx(1, "must specify either-a, -n, -t, or -h");
655
656                 if (vflag != 0)
657                         errx(1, "-v cannot be used with -R");
658
659         } else {
660                 assert(Lflag != 0);
661
662                 if (host != NULL)
663                         errx(1, "-L and -h and mutually exclusive");
664                 if (target != NULL)
665                         errx(1, "-L and -t and mutually exclusive");
666                 if (user != NULL)
667                         errx(1, "-L and -u and mutually exclusive");
668                 if (secret != NULL)
669                         errx(1, "-L and -s and mutually exclusive");
670                 if (nickname != NULL)
671                         errx(1, "-L and -n and mutually exclusive");
672                 if (discovery_host != NULL)
673                         errx(1, "-L and -d and mutually exclusive");
674         }
675
676         iscsi_fd = open(ISCSI_PATH, O_RDWR);
677         if (iscsi_fd < 0 && errno == ENOENT) {
678                 saved_errno = errno;
679                 retval = kldload("iscsi");
680                 if (retval != -1)
681                         iscsi_fd = open(ISCSI_PATH, O_RDWR);
682                 else
683                         errno = saved_errno;
684         }
685         if (iscsi_fd < 0)
686                 err(1, "failed to open %s", ISCSI_PATH);
687
688         if (Aflag != 0 && aflag != 0) {
689                 conf = conf_new_from_file(conf_path);
690
691                 TAILQ_FOREACH(targ, &conf->conf_targets, t_next)
692                         failed += kernel_add(iscsi_fd, targ);
693         } else if (nickname != NULL) {
694                 conf = conf_new_from_file(conf_path);
695                 targ = target_find(conf, nickname);
696                 if (targ == NULL)
697                         errx(1, "target %s not found in the configuration file",
698                             nickname);
699
700                 if (Aflag != 0)
701                         failed += kernel_add(iscsi_fd, targ);
702                 else if (Rflag != 0)
703                         failed += kernel_remove(iscsi_fd, targ);
704                 else
705                         failed += kernel_list(iscsi_fd, targ, vflag);
706         } else {
707                 if (Aflag != 0 && target != NULL) {
708                         if (valid_iscsi_name(target) == false)
709                                 errx(1, "invalid target name \"%s\"", target);
710                 }
711                 conf = conf_new();
712                 targ = target_new(conf);
713                 targ->t_initiator_name = default_initiator_name();
714                 targ->t_header_digest = DIGEST_NONE;
715                 targ->t_data_digest = DIGEST_NONE;
716                 targ->t_name = target;
717                 if (discovery_host != NULL) {
718                         targ->t_session_type = SESSION_TYPE_DISCOVERY;
719                         targ->t_address = discovery_host;
720                 } else {
721                         targ->t_session_type = SESSION_TYPE_NORMAL;
722                         targ->t_address = host;
723                 }
724                 targ->t_user = user;
725                 targ->t_secret = secret;
726
727                 if (Aflag != 0)
728                         failed += kernel_add(iscsi_fd, targ);
729                 else if (Rflag != 0)
730                         failed += kernel_remove(iscsi_fd, targ);
731                 else
732                         failed += kernel_list(iscsi_fd, targ, vflag);
733         }
734
735         error = close(iscsi_fd);
736         if (error != 0)
737                 err(1, "close");
738
739         if (failed > 0)
740                 return (1);
741         return (0);
742 }