xref: /openbsd/usr.sbin/iscsictl/iscsictl.c (revision 76d0caae)
1 /*	$OpenBSD: iscsictl.c,v 1.12 2021/04/16 14:39:33 claudio Exp $ */
2 
3 /*
4  * Copyright (c) 2010 Claudio Jeker <claudio@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/queue.h>
20 #include <sys/socket.h>
21 #include <sys/uio.h>
22 #include <sys/un.h>
23 
24 #include <event.h>
25 #include <err.h>
26 #include <errno.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <util.h>
32 
33 #include "iscsid.h"
34 #include "iscsictl.h"
35 
36 __dead void	 usage(void);
37 void		 run(void);
38 void		 run_command(struct pdu *);
39 struct pdu	*ctl_getpdu(char *, size_t);
40 int		 ctl_sendpdu(int, struct pdu *);
41 void		 show_config(struct ctrlmsghdr *, struct pdu *);
42 void		 show_vscsi_stats(struct ctrlmsghdr *, struct pdu *);
43 void             poll_and_wait(void);
44 void             poll_session_status(void);
45 void             register_poll(struct ctrlmsghdr *, struct pdu *);
46 void             poll_print(struct session_poll *);
47 
48 char		cbuf[CONTROL_READ_SIZE];
49 
50 struct control {
51 	struct pduq	channel;
52 	int		fd;
53 } control;
54 
55 
56 struct session_poll poll_result;
57 #define POLL_DELAY_SEC	1
58 #define POLL_ATTEMPTS	10
59 
60 __dead void
61 usage(void)
62 {
63 	extern char *__progname;
64 
65 	fprintf(stderr,"usage: %s [-s socket] command [argument ...]\n",
66 	    __progname);
67 	exit(1);
68 }
69 
70 int
71 main (int argc, char* argv[])
72 {
73 	struct sockaddr_un sun;
74 	struct session_config sc;
75 	struct parse_result *res;
76 	char *confname = ISCSID_CONFIG;
77 	char *sockname = ISCSID_CONTROL;
78 	struct session_ctlcfg *s;
79 	struct iscsi_config *cf;
80 	int ch, poll = 0, val = 0;
81 
82 	/* check flags */
83 	while ((ch = getopt(argc, argv, "f:s:")) != -1) {
84 		switch (ch) {
85 		case 'f':
86 			confname = optarg;
87 			break;
88 		case 's':
89 			sockname = optarg;
90 			break;
91 		default:
92 			usage();
93 			/* NOTREACHED */
94 		}
95 	}
96 	argc -= optind;
97 	argv += optind;
98 
99 	/* parse options */
100 	if ((res = parse(argc, argv)) == NULL)
101 		exit(1);
102 
103 	/* connect to iscsid control socket */
104 	TAILQ_INIT(&control.channel);
105 	if ((control.fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1)
106 		err(1, "socket");
107 
108 	bzero(&sun, sizeof(sun));
109 	sun.sun_family = AF_UNIX;
110 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
111 
112 	if (connect(control.fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
113 		err(1, "connect: %s", sockname);
114 
115 	if (pledge("stdio rpath dns", NULL) == -1)
116 		err(1, "pledge");
117 
118 	switch (res->action) {
119 	case NONE:
120 	case LOG_VERBOSE:
121 		val = 1;
122 		/* FALLTHROUGH */
123 	case LOG_BRIEF:
124 		if (control_compose(NULL, CTRL_LOG_VERBOSE,
125 		    &val, sizeof(int)) == -1)
126 			err(1, "control_compose");
127 		break;
128 	case SHOW_SUM:
129 		if (control_compose(NULL, CTRL_SHOW_SUM, NULL, 0) == -1)
130 			err(1, "control_compose");
131 		break;
132 	case SHOW_SESS:
133 		usage();
134 		/* NOTREACHED */
135 	case SHOW_VSCSI_STATS:
136 		if (control_compose(NULL, CTRL_VSCSI_STATS, NULL, 0) == -1)
137 			err(1, "control_compose");
138 		break;
139 	case RELOAD:
140 		if ((cf = parse_config(confname)) == NULL)
141 			errx(1, "errors while loading configuration file.");
142 		if (cf->initiator.isid_base != 0) {
143 			if (control_compose(NULL, CTRL_INITIATOR_CONFIG,
144 			    &cf->initiator, sizeof(cf->initiator)) == -1)
145 				err(1, "control_compose");
146 		}
147 
148 		SIMPLEQ_FOREACH(s, &cf->sessions, entry) {
149 			struct ctrldata cdv[3];
150 			bzero(cdv, sizeof(cdv));
151 
152 			cdv[0].buf = &s->session;
153 			cdv[0].len = sizeof(s->session);
154 
155 			if (s->session.TargetName) {
156 				cdv[1].buf = s->session.TargetName;
157 				cdv[1].len =
158 				    strlen(s->session.TargetName) + 1;
159 			}
160 			if (s->session.InitiatorName) {
161 				cdv[2].buf = s->session.InitiatorName;
162 				cdv[2].len =
163 				    strlen(s->session.InitiatorName) + 1;
164 			}
165 
166 			if (control_build(NULL, CTRL_SESSION_CONFIG,
167 			    nitems(cdv), cdv) == -1)
168 				err(1, "control_build");
169 		}
170 
171 		/* Reloading, so poll for connection establishment. */
172 		poll = 1;
173 		break;
174 	case DISCOVERY:
175 		printf("discover %s\n", log_sockaddr(&res->addr));
176 
177 		bzero(&sc, sizeof(sc));
178 		snprintf(sc.SessionName, sizeof(sc.SessionName),
179 		    "discovery.%d", (int)getpid());
180 		bcopy(&res->addr, &sc.connection.TargetAddr, res->addr.ss_len);
181 		sc.SessionType = SESSION_TYPE_DISCOVERY;
182 
183 		if (control_compose(NULL, CTRL_SESSION_CONFIG,
184 		    &sc, sizeof(sc)) == -1)
185 			err(1, "control_compose");
186 	}
187 
188 	run();
189 
190 	if (poll)
191 		poll_and_wait();
192 
193 	close(control.fd);
194 	return (0);
195 }
196 
197 void
198 control_queue(void *ch, struct pdu *pdu)
199 {
200 	TAILQ_INSERT_TAIL(&control.channel, pdu, entry);
201 }
202 
203 void
204 run(void)
205 {
206 	struct pdu *pdu;
207 
208 	while ((pdu = TAILQ_FIRST(&control.channel)) != NULL) {
209 		TAILQ_REMOVE(&control.channel, pdu, entry);
210 		run_command(pdu);
211 	}
212 }
213 
214 void
215 run_command(struct pdu *pdu)
216 {
217 	struct ctrlmsghdr *cmh;
218 	int done = 0;
219 	ssize_t n;
220 
221 	if (ctl_sendpdu(control.fd, pdu) == -1)
222 		err(1, "send");
223 	while (!done) {
224 		if ((n = recv(control.fd, cbuf, sizeof(cbuf), 0)) == -1 &&
225 		    !(errno == EAGAIN || errno == EINTR))
226 			err(1, "recv");
227 
228 		if (n == 0)
229 			errx(1, "connection to iscsid closed");
230 
231 		pdu = ctl_getpdu(cbuf, n);
232 		cmh = pdu_getbuf(pdu, NULL, 0);
233 		if (cmh == NULL)
234 			break;
235 		switch (cmh->type) {
236 		case CTRL_SUCCESS:
237 			printf("command successful\n");
238 			done = 1;
239 			break;
240 		case CTRL_FAILURE:
241 			printf("command failed\n");
242 			done = 1;
243 			break;
244 		case CTRL_INPROGRESS:
245 			printf("command in progress...\n");
246 			break;
247 		case CTRL_INITIATOR_CONFIG:
248 		case CTRL_SESSION_CONFIG:
249 			show_config(cmh, pdu);
250 			break;
251 		case CTRL_VSCSI_STATS:
252 			show_vscsi_stats(cmh, pdu);
253 			done = 1;
254 			break;
255 		case CTRL_SESS_POLL:
256 			register_poll(cmh, pdu);
257 			done = 1;
258 			break;
259 
260 		}
261 	}
262 }
263 
264 struct pdu *
265 ctl_getpdu(char *buf, size_t len)
266 {
267 	struct pdu *p;
268 	struct ctrlmsghdr *cmh;
269 	void *data;
270 	size_t n;
271 	int i;
272 
273 	if (len < sizeof(*cmh))
274 		return NULL;
275 
276 	if (!(p = pdu_new()))
277 		return NULL;
278 
279 	n = sizeof(*cmh);
280 	cmh = pdu_alloc(n);
281 	bcopy(buf, cmh, n);
282 	buf += n;
283 	len -= n;
284 
285 	if (pdu_addbuf(p, cmh, n, 0)) {
286 		free(cmh);
287 fail:
288 		pdu_free(p);
289 		return NULL;
290 	}
291 
292 	for (i = 0; i < 3; i++) {
293 		n = cmh->len[i];
294 		if (n == 0)
295 			continue;
296 		if (PDU_LEN(n) > len)
297 			goto fail;
298 		if (!(data = pdu_alloc(n)))
299 			goto fail;
300 		bcopy(buf, data, n);
301 		if (pdu_addbuf(p, data, n, i + 1)) {
302 			free(data);
303 			goto fail;
304 		}
305 		buf += PDU_LEN(n);
306 		len -= PDU_LEN(n);
307 	}
308 
309 	return p;
310 }
311 
312 int
313 ctl_sendpdu(int fd, struct pdu *pdu)
314 {
315 	struct iovec iov[PDU_MAXIOV];
316 	struct msghdr msg;
317 	unsigned int niov = 0;
318 
319 	for (niov = 0; niov < PDU_MAXIOV; niov++) {
320 		iov[niov].iov_base = pdu->iov[niov].iov_base;
321 		iov[niov].iov_len = pdu->iov[niov].iov_len;
322 	}
323 	bzero(&msg, sizeof(msg));
324 	msg.msg_iov = iov;
325 	msg.msg_iovlen = niov;
326 	if (sendmsg(fd, &msg, 0) == -1)
327 		return -1;
328 	return 0;
329 }
330 
331 void
332 show_config(struct ctrlmsghdr *cmh, struct pdu *pdu)
333 {
334 	struct initiator_config	*ic;
335 	struct session_config	*sc;
336 	char *name;
337 
338 	switch (cmh->type) {
339 	case CTRL_INITIATOR_CONFIG:
340 		if (cmh->len[0] != sizeof(*ic))
341 			errx(1, "bad size of response");
342 		ic = pdu_getbuf(pdu, NULL, 1);
343 		if (ic == NULL)
344 			return;
345 
346 		printf("Initiator: ISID base %x qalifier %hx\n",
347 		    ic->isid_base, ic->isid_qual);
348 		break;
349 	case CTRL_SESSION_CONFIG:
350 		if (cmh->len[0] != sizeof(*sc))
351 			errx(1, "bad size of response");
352 		sc = pdu_getbuf(pdu, NULL, 1);
353 		if (sc == NULL)
354 			return;
355 
356 		printf("\nSession '%s':%s\n", sc->SessionName,
357 		    sc->disabled ? " disabled" : "");
358 		printf("    SessionType: %s\tMaxConnections: %hd\n",
359 		    sc->SessionType == SESSION_TYPE_DISCOVERY ? "discovery" :
360 		    "normal", sc->MaxConnections);
361 		if ((name = pdu_getbuf(pdu, NULL, 2)))
362 			printf("    TargetName: %s\n", name);
363 		printf("    TargetAddr: %s\n",
364 		    log_sockaddr(&sc->connection.TargetAddr));
365 		if ((name = pdu_getbuf(pdu, NULL, 3)))
366 			printf("    InitiatorName: %s\n", name);
367 		printf("    InitiatorAddr: %s\n",
368 		    log_sockaddr(&sc->connection.LocalAddr));
369 		break;
370 	}
371 }
372 
373 void
374 show_vscsi_stats(struct ctrlmsghdr *cmh, struct pdu *pdu)
375 {
376 	struct vscsi_stats *vs;
377 	char buf[FMT_SCALED_STRSIZE];
378 
379 	if (cmh->len[0] != sizeof(struct vscsi_stats))
380 		errx(1, "bad size of response");
381 	vs = pdu_getbuf(pdu, NULL, 1);
382 	if (vs == NULL)
383 		return;
384 
385 	printf("VSCSI ioctl statistics:\n");
386 	printf("%u probe calls and %u detach calls\n",
387 	    vs->cnt_probe, vs->cnt_detach);
388 	printf("%llu I2T calls (%llu read, %llu writes)\n",
389 	    vs->cnt_i2t,
390 	    vs->cnt_i2t_dir[1],
391 	    vs->cnt_i2t_dir[2]);
392 
393 	if (fmt_scaled(vs->bytes_rd, buf) != 0)
394 		(void)strlcpy(buf, "NaN", sizeof(buf));
395 	printf("%llu data reads (%s bytes read)\n", vs->cnt_read, buf);
396 	if (fmt_scaled(vs->bytes_wr, buf) != 0)
397 		(void)strlcpy(buf, "NaN", sizeof(buf));
398 	printf("%llu data writes (%s bytes written)\n", vs->cnt_write, buf);
399 
400 	printf("%llu T2I calls (%llu done, %llu sense errors, %llu errors)\n",
401 	    vs->cnt_t2i,
402 	    vs->cnt_t2i_status[0],
403 	    vs->cnt_t2i_status[1],
404 	    vs->cnt_t2i_status[2]);
405 }
406 
407 void
408 poll_session_status(void)
409 {
410 	struct pdu *pdu;
411 
412 	if (control_compose(NULL, CTRL_SESS_POLL, NULL, 0) == -1)
413 		err(1, "control_compose");
414 
415 	while ((pdu = TAILQ_FIRST(&control.channel)) != NULL) {
416 		TAILQ_REMOVE(&control.channel, pdu, entry);
417 		run_command(pdu);
418 	}
419 }
420 
421 void
422 poll_and_wait(void)
423 {
424 	int attempts;
425 
426 	printf("waiting for config to settle..");
427 	fflush(stdout);
428 
429 	for (attempts = 0; attempts < POLL_ATTEMPTS; attempts++) {
430 
431 		poll_session_status();
432 
433 		/* Poll says we are good to go. */
434 		if (poll_result.sess_conn_status != 0) {
435 			printf("ok\n");
436 			/* wait a bit longer so all is settled. */
437 			sleep(POLL_DELAY_SEC);
438 			return;
439 		}
440 
441 		/* Poll says we should wait... */
442 		printf(".");
443 		fflush(stdout);
444 		sleep(POLL_DELAY_SEC);
445 	}
446 
447 	printf("giving up.\n");
448 
449 	poll_print(&poll_result);
450 }
451 
452 void
453 register_poll(struct ctrlmsghdr *cmh, struct pdu *pdu)
454 {
455 	if (cmh->len[0] != sizeof(poll_result))
456 		errx(1, "poll: bad size of response");
457 
458 	poll_result = *((struct session_poll *)pdu_getbuf(pdu, NULL, 1));
459 }
460 
461 void
462 poll_print(struct session_poll *p)
463 {
464 	printf("Configured sessions: %d\n", p->session_count);
465 	printf("Sessions initializing: %d\n", p->session_init_count);
466 	printf("Sessions started/failed: %d\n", p->session_running_count);
467 	printf("Sessions logged in: %d\n", p->conn_logged_in_count);
468 	printf("Sessions with failed connections: %d\n", p->conn_failed_count);
469 	printf("Sessions still attempting to connect: %d\n",
470 	    p->conn_waiting_count);
471 }
472