1 /* $OpenBSD: iscsictl.c,v 1.13 2023/02/21 15:45:40 mbuhl 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
usage(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
main(int argc,char * argv[])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
control_queue(void * ch,struct pdu * pdu)198 control_queue(void *ch, struct pdu *pdu)
199 {
200 TAILQ_INSERT_TAIL(&control.channel, pdu, entry);
201 }
202
203 void
run(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
run_command(struct pdu * pdu)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 *
ctl_getpdu(char * buf,size_t len)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
ctl_sendpdu(int fd,struct pdu * pdu)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
show_config(struct ctrlmsghdr * cmh,struct pdu * pdu)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 qualifier %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
show_vscsi_stats(struct ctrlmsghdr * cmh,struct pdu * pdu)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
poll_session_status(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
poll_and_wait(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
register_poll(struct ctrlmsghdr * cmh,struct pdu * pdu)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
poll_print(struct session_poll * p)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