xref: /openbsd-src/usr.sbin/iscsictl/iscsictl.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: iscsictl.c,v 1.11 2016/08/16 18:41:57 tedu 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 
44 char		cbuf[CONTROL_READ_SIZE];
45 
46 struct control {
47 	struct pduq	channel;
48 	int		fd;
49 } control;
50 
51 __dead void
52 usage(void)
53 {
54 	extern char *__progname;
55 
56 	fprintf(stderr,"usage: %s [-s socket] command [argument ...]\n",
57 	    __progname);
58 	exit(1);
59 }
60 
61 int
62 main (int argc, char* argv[])
63 {
64 	struct sockaddr_un sun;
65 	struct session_config sc;
66 	struct parse_result *res;
67 	char *confname = ISCSID_CONFIG;
68 	char *sockname = ISCSID_CONTROL;
69 	struct session_ctlcfg *s;
70 	struct iscsi_config *cf;
71 	int ch, val = 0;
72 
73 	/* check flags */
74 	while ((ch = getopt(argc, argv, "f:s:")) != -1) {
75 		switch (ch) {
76 		case 'f':
77 			confname = optarg;
78 			break;
79 		case 's':
80 			sockname = optarg;
81 			break;
82 		default:
83 			usage();
84 			/* NOTREACHED */
85 		}
86 	}
87 	argc -= optind;
88 	argv += optind;
89 
90 	/* parse options */
91 	if ((res = parse(argc, argv)) == NULL)
92 		exit(1);
93 
94 	/* connect to iscsid control socket */
95 	TAILQ_INIT(&control.channel);
96 	if ((control.fd = socket(AF_UNIX, SOCK_SEQPACKET, 0)) == -1)
97 		err(1, "socket");
98 
99 	bzero(&sun, sizeof(sun));
100 	sun.sun_family = AF_UNIX;
101 	strlcpy(sun.sun_path, sockname, sizeof(sun.sun_path));
102 
103 	if (connect(control.fd, (struct sockaddr *)&sun, sizeof(sun)) == -1)
104 		err(1, "connect: %s", sockname);
105 
106 	if (pledge("stdio rpath dns", NULL) == -1)
107 		err(1, "pledge");
108 
109 	switch (res->action) {
110 	case NONE:
111 	case LOG_VERBOSE:
112 		val = 1;
113 		/* FALLTHROUGH */
114 	case LOG_BRIEF:
115 		if (control_compose(NULL, CTRL_LOG_VERBOSE,
116 		    &val, sizeof(int)) == -1)
117 			err(1, "control_compose");
118 		break;
119 	case SHOW_SUM:
120 		if (control_compose(NULL, CTRL_SHOW_SUM, NULL, 0) == -1)
121 			err(1, "control_compose");
122 		break;
123 	case SHOW_SESS:
124 		usage();
125 		/* NOTREACHED */
126 	case SHOW_VSCSI_STATS:
127 		if (control_compose(NULL, CTRL_VSCSI_STATS, NULL, 0) == -1)
128 			err(1, "control_compose");
129 		break;
130 	case RELOAD:
131 		if ((cf = parse_config(confname)) == NULL)
132 			errx(1, "errors while loading configuration file.");
133 		if (cf->initiator.isid_base != 0) {
134 			if (control_compose(NULL, CTRL_INITIATOR_CONFIG,
135 			    &cf->initiator, sizeof(cf->initiator)) == -1)
136 				err(1, "control_compose");
137 		}
138 		SIMPLEQ_FOREACH(s, &cf->sessions, entry) {
139 			struct ctrldata cdv[3];
140 			bzero(cdv, sizeof(cdv));
141 
142 			cdv[0].buf = &s->session;
143 			cdv[0].len = sizeof(s->session);
144 
145 			if (s->session.TargetName) {
146 				cdv[1].buf = s->session.TargetName;
147 				cdv[1].len =
148 				    strlen(s->session.TargetName) + 1;
149 			}
150 			if (s->session.InitiatorName) {
151 				cdv[2].buf = s->session.InitiatorName;
152 				cdv[2].len =
153 				    strlen(s->session.InitiatorName) + 1;
154 			}
155 
156 			if (control_build(NULL, CTRL_SESSION_CONFIG,
157 			    nitems(cdv), cdv) == -1)
158 				err(1, "control_build");
159 		}
160 		break;
161 	case DISCOVERY:
162 		printf("discover %s\n", log_sockaddr(&res->addr));
163 
164 		bzero(&sc, sizeof(sc));
165 		snprintf(sc.SessionName, sizeof(sc.SessionName),
166 		    "discovery.%d", (int)getpid());
167 		bcopy(&res->addr, &sc.connection.TargetAddr, res->addr.ss_len);
168 		sc.SessionType = SESSION_TYPE_DISCOVERY;
169 
170 		if (control_compose(NULL, CTRL_SESSION_CONFIG,
171 		    &sc, sizeof(sc)) == -1)
172 			err(1, "control_compose");
173 	}
174 
175 	run();
176 
177 	close(control.fd);
178 
179 	return (0);
180 }
181 
182 void
183 control_queue(void *ch, struct pdu *pdu)
184 {
185 	TAILQ_INSERT_TAIL(&control.channel, pdu, entry);
186 }
187 
188 void
189 run(void)
190 {
191 	struct pdu *pdu;
192 
193 	while ((pdu = TAILQ_FIRST(&control.channel)) != NULL) {
194 		TAILQ_REMOVE(&control.channel, pdu, entry);
195 		run_command(pdu);
196 	}
197 }
198 
199 void
200 run_command(struct pdu *pdu)
201 {
202 	struct ctrlmsghdr *cmh;
203 	int done = 0;
204 	ssize_t n;
205 
206 	if (ctl_sendpdu(control.fd, pdu) == -1)
207 		err(1, "send");
208 	while (!done) {
209 		if ((n = recv(control.fd, cbuf, sizeof(cbuf), 0)) == -1 &&
210 		    !(errno == EAGAIN || errno == EINTR))
211 			err(1, "recv");
212 
213 		if (n == 0)
214 			errx(1, "connection to iscsid closed");
215 
216 		pdu = ctl_getpdu(cbuf, n);
217 		cmh = pdu_getbuf(pdu, NULL, 0);
218 		if (cmh == NULL)
219 			break;
220 		switch (cmh->type) {
221 		case CTRL_SUCCESS:
222 			printf("command successful\n");
223 			done = 1;
224 			break;
225 		case CTRL_FAILURE:
226 			printf("command failed\n");
227 			done = 1;
228 			break;
229 		case CTRL_INPROGRESS:
230 			printf("command in progress...\n");
231 			break;
232 		case CTRL_INITIATOR_CONFIG:
233 		case CTRL_SESSION_CONFIG:
234 			show_config(cmh, pdu);
235 			break;
236 		case CTRL_VSCSI_STATS:
237 			show_vscsi_stats(cmh, pdu);
238 			done = 1;
239 			break;
240 		}
241 	}
242 }
243 
244 struct pdu *
245 ctl_getpdu(char *buf, size_t len)
246 {
247 	struct pdu *p;
248 	struct ctrlmsghdr *cmh;
249 	void *data;
250 	size_t n;
251 	int i;
252 
253 	if (len < sizeof(*cmh))
254 		return NULL;
255 
256 	if (!(p = pdu_new()))
257 		return NULL;
258 
259 	n = sizeof(*cmh);
260 	cmh = pdu_alloc(n);
261 	bcopy(buf, cmh, n);
262 	buf += n;
263 	len -= n;
264 
265 	if (pdu_addbuf(p, cmh, n, 0)) {
266 		free(cmh);
267 fail:
268 		pdu_free(p);
269 		return NULL;
270 	}
271 
272 	for (i = 0; i < 3; i++) {
273 		n = cmh->len[i];
274 		if (n == 0)
275 			continue;
276 		if (PDU_LEN(n) > len)
277 			goto fail;
278 		if (!(data = pdu_alloc(n)))
279 			goto fail;
280 		bcopy(buf, data, n);
281 		if (pdu_addbuf(p, data, n, i + 1)) {
282 			free(data);
283 			goto fail;
284 		}
285 		buf += PDU_LEN(n);
286 		len -= PDU_LEN(n);
287 	}
288 
289 	return p;
290 }
291 
292 int
293 ctl_sendpdu(int fd, struct pdu *pdu)
294 {
295 	struct iovec iov[PDU_MAXIOV];
296 	struct msghdr msg;
297 	unsigned int niov = 0;
298 
299 	for (niov = 0; niov < PDU_MAXIOV; niov++) {
300 		iov[niov].iov_base = pdu->iov[niov].iov_base;
301 		iov[niov].iov_len = pdu->iov[niov].iov_len;
302 	}
303 	bzero(&msg, sizeof(msg));
304 	msg.msg_iov = iov;
305 	msg.msg_iovlen = niov;
306 	if (sendmsg(fd, &msg, 0) == -1)
307 		return -1;
308 	return 0;
309 }
310 
311 void
312 show_config(struct ctrlmsghdr *cmh, struct pdu *pdu)
313 {
314 	struct initiator_config	*ic;
315 	struct session_config	*sc;
316 	char *name;
317 
318 	switch (cmh->type) {
319 	case CTRL_INITIATOR_CONFIG:
320 		if (cmh->len[0] != sizeof(*ic))
321 			errx(1, "bad size of response");
322 		ic = pdu_getbuf(pdu, NULL, 1);
323 		if (ic == NULL)
324 			return;
325 
326 		printf("Initiator: ISID base %x qalifier %hx\n",
327 		    ic->isid_base, ic->isid_qual);
328 		break;
329 	case CTRL_SESSION_CONFIG:
330 		if (cmh->len[0] != sizeof(*sc))
331 			errx(1, "bad size of response");
332 		sc = pdu_getbuf(pdu, NULL, 1);
333 		if (sc == NULL)
334 			return;
335 
336 		printf("\nSession '%s':%s\n", sc->SessionName,
337 		    sc->disabled ? " disabled" : "");
338 		printf("    SessionType: %s\tMaxConnections: %hd\n",
339 		    sc->SessionType == SESSION_TYPE_DISCOVERY ? "discovery" :
340 		    "normal", sc->MaxConnections);
341 		if ((name = pdu_getbuf(pdu, NULL, 2)))
342 			printf("    TargetName: %s\n", name);
343 		printf("    TargetAddr: %s\n",
344 		    log_sockaddr(&sc->connection.TargetAddr));
345 		if ((name = pdu_getbuf(pdu, NULL, 3)))
346 			printf("    InitiatorName: %s\n", name);
347 		printf("    InitiatorAddr: %s\n",
348 		    log_sockaddr(&sc->connection.LocalAddr));
349 		break;
350 	}
351 }
352 
353 void
354 show_vscsi_stats(struct ctrlmsghdr *cmh, struct pdu *pdu)
355 {
356 	struct vscsi_stats *vs;
357 	char buf[FMT_SCALED_STRSIZE];
358 
359 	if (cmh->len[0] != sizeof(struct vscsi_stats))
360 		errx(1, "bad size of response");
361 	vs = pdu_getbuf(pdu, NULL, 1);
362 	if (vs == NULL)
363 		return;
364 
365 	printf("VSCSI ioctl statistics:\n");
366 	printf("%u probe calls and %u detach calls\n",
367 	    vs->cnt_probe, vs->cnt_detach);
368 	printf("%llu I2T calls (%llu read, %llu writes)\n",
369 	    vs->cnt_i2t,
370 	    vs->cnt_i2t_dir[1],
371 	    vs->cnt_i2t_dir[2]);
372 
373 	if (fmt_scaled(vs->bytes_rd, buf) != 0)
374 		(void)strlcpy(buf, "NaN", sizeof(buf));
375 	printf("%llu data reads (%s bytes read)\n", vs->cnt_read, buf);
376 	if (fmt_scaled(vs->bytes_wr, buf) != 0)
377 		(void)strlcpy(buf, "NaN", sizeof(buf));
378 	printf("%llu data writes (%s bytes written)\n", vs->cnt_write, buf);
379 
380 	printf("%llu T2I calls (%llu done, %llu sense errors, %llu errors)\n",
381 	    vs->cnt_t2i,
382 	    vs->cnt_t2i_status[0],
383 	    vs->cnt_t2i_status[1],
384 	    vs->cnt_t2i_status[2]);
385 }
386