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