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