xref: /freebsd/usr.bin/iscsictl/iscsictl.c (revision dd3603749cb7f20a628f04d595b105962b21a3d2)
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