iscsictl.c revision 1.11
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);
37void		 run(void);
38void		 run_command(struct pdu *);
39struct pdu	*ctl_getpdu(char *, size_t);
40int		 ctl_sendpdu(int, struct pdu *);
41void		 show_config(struct ctrlmsghdr *, struct pdu *);
42void		 show_vscsi_stats(struct ctrlmsghdr *, struct pdu *);
43
44char		cbuf[CONTROL_READ_SIZE];
45
46struct control {
47	struct pduq	channel;
48	int		fd;
49} control;
50
51__dead void
52usage(void)
53{
54	extern char *__progname;
55
56	fprintf(stderr,"usage: %s [-s socket] command [argument ...]\n",
57	    __progname);
58	exit(1);
59}
60
61int
62main (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
182void
183control_queue(void *ch, struct pdu *pdu)
184{
185	TAILQ_INSERT_TAIL(&control.channel, pdu, entry);
186}
187
188void
189run(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
199void
200run_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
244struct pdu *
245ctl_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);
267fail:
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
292int
293ctl_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
311void
312show_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
353void
354show_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