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