session.c revision 1.6
1/* $OpenBSD: session.c,v 1.6 2014/04/20 20:12:31 claudio Exp $ */ 2 3/* 4 * Copyright (c) 2011 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/types.h> 20#include <sys/ioctl.h> 21#include <sys/queue.h> 22#include <sys/socket.h> 23#include <sys/uio.h> 24 25#include <scsi/iscsi.h> 26#include <scsi/scsi_all.h> 27#include <dev/vscsivar.h> 28 29#include <event.h> 30#include <stdio.h> 31#include <stdlib.h> 32#include <string.h> 33#include <unistd.h> 34 35#include "iscsid.h" 36#include "log.h" 37 38void session_fsm_callback(int, short, void *); 39int sess_do_start(struct session *, struct sessev *); 40int sess_do_conn_loggedin(struct session *, struct sessev *); 41int sess_do_conn_fail(struct session *, struct sessev *); 42int sess_do_conn_closed(struct session *, struct sessev *); 43int sess_do_down(struct session *, struct sessev *); 44 45const char *sess_state(int); 46const char *sess_event(enum s_event); 47 48struct session * 49session_find(struct initiator *i, char *name) 50{ 51 struct session *s; 52 53 TAILQ_FOREACH(s, &i->sessions, entry) { 54 if (strcmp(s->config.SessionName, name) == 0) 55 return s; 56 } 57 return NULL; 58} 59 60struct session * 61session_new(struct initiator *i, u_int8_t st) 62{ 63 struct session *s; 64 65 if (!(s = calloc(1, sizeof(*s)))) 66 return NULL; 67 68 /* use the same qualifier unless there is a conflict */ 69 s->isid_base = i->config.isid_base; 70 s->isid_qual = i->config.isid_qual; 71 s->cmdseqnum = arc4random(); 72 s->itt = arc4random(); 73 s->initiator = i; 74 s->state = SESS_INIT; 75 76 if (st == SESSION_TYPE_DISCOVERY) 77 s->target = 0; 78 else 79 s->target = s->initiator->target++; 80 81 TAILQ_INSERT_HEAD(&i->sessions, s, entry); 82 TAILQ_INIT(&s->connections); 83 TAILQ_INIT(&s->tasks); 84 SIMPLEQ_INIT(&s->fsmq); 85 evtimer_set(&s->fsm_ev, session_fsm_callback, s); 86 87 return s; 88} 89 90void 91session_cleanup(struct session *s) 92{ 93 struct connection *c; 94 95 taskq_cleanup(&s->tasks); 96 97 while ((c = TAILQ_FIRST(&s->connections)) != NULL) 98 conn_free(c); 99 100 free(s->config.TargetName); 101 free(s->config.InitiatorName); 102 free(s); 103} 104 105int 106session_shutdown(struct session *s) 107{ 108 log_debug("session[%s] going down", s->config.SessionName); 109 110 s->action = SESS_ACT_DOWN; 111 if (s->state & (SESS_INIT | SESS_FREE | SESS_DOWN)) { 112 struct connection *c; 113 while ((c = TAILQ_FIRST(&s->connections)) != NULL) 114 conn_free(c); 115 return 0; 116 } 117 118 /* cleanup task queue and issue a logout */ 119 taskq_cleanup(&s->tasks); 120 initiator_logout(s, NULL, ISCSI_LOGOUT_CLOSE_SESS); 121 122 return 1; 123} 124 125void 126session_config(struct session *s, struct session_config *sc) 127{ 128 if (s->config.TargetName) 129 free(s->config.TargetName); 130 s->config.TargetName = NULL; 131 if (s->config.InitiatorName) 132 free(s->config.InitiatorName); 133 s->config.InitiatorName = NULL; 134 135 s->config = *sc; 136 137 if (sc->TargetName) { 138 s->config.TargetName = strdup(sc->TargetName); 139 if (s->config.TargetName == NULL) 140 fatal("strdup"); 141 } 142 if (sc->InitiatorName) { 143 s->config.InitiatorName = strdup(sc->InitiatorName); 144 if (s->config.InitiatorName == NULL) 145 fatal("strdup"); 146 } else 147 s->config.InitiatorName = default_initiator_name(); 148} 149 150void 151session_task_issue(struct session *s, struct task *t) 152{ 153 TAILQ_INSERT_TAIL(&s->tasks, t, entry); 154 session_schedule(s); 155} 156 157void 158session_logout_issue(struct session *s, struct task *t) 159{ 160 struct connection *c, *rc = NULL; 161 162 /* find first free session or first available session */ 163 TAILQ_FOREACH(c, &s->connections, entry) { 164 if (conn_task_ready(c)) { 165 conn_fsm(c, CONN_EV_LOGOUT); 166 conn_task_issue(c, t); 167 return; 168 } 169 if (c->state & CONN_RUNNING) 170 rc = c; 171 } 172 173 if (rc) { 174 conn_fsm(rc, CONN_EV_LOGOUT); 175 conn_task_issue(rc, t); 176 return; 177 } 178 179 /* XXX must open new connection, gulp */ 180 fatalx("session_logout_issue needs more work"); 181} 182 183void 184session_schedule(struct session *s) 185{ 186 struct task *t = TAILQ_FIRST(&s->tasks); 187 struct connection *c; 188 189 if (!t) 190 return; 191 192 /* XXX IMMEDIATE TASK NEED SPECIAL HANDLING !!!! */ 193 194 /* wake up a idle connection or a not busy one */ 195 /* XXX this needs more work as it makes the daemon go wrooOOOMM */ 196 TAILQ_FOREACH(c, &s->connections, entry) 197 if (conn_task_ready(c)) { 198 TAILQ_REMOVE(&s->tasks, t, entry); 199 conn_task_issue(c, t); 200 return; 201 } 202} 203 204/* 205 * The session FSM runs from a callback so that the connection FSM can finish. 206 */ 207void 208session_fsm(struct session *s, enum s_event ev, struct connection *c) 209{ 210 struct timeval tv; 211 struct sessev *sev; 212 213 if ((sev = malloc(sizeof(*sev))) == NULL) 214 fatal("session_fsm"); 215 sev->conn = c; 216 sev->event = ev; 217 SIMPLEQ_INSERT_TAIL(&s->fsmq, sev, entry); 218 219 timerclear(&tv); 220 if (evtimer_add(&s->fsm_ev, &tv) == -1) 221 fatal("session_fsm"); 222} 223 224struct { 225 int state; 226 enum s_event event; 227 int (*action)(struct session *, struct sessev *); 228} s_fsm[] = { 229 { SESS_INIT, SESS_EV_START, sess_do_start }, 230 { SESS_FREE, SESS_EV_CONN_LOGGED_IN, sess_do_conn_loggedin }, 231 { SESS_LOGGED_IN, SESS_EV_CONN_LOGGED_IN, sess_do_conn_loggedin }, 232 { SESS_RUNNING, SESS_EV_CONN_FAIL, sess_do_conn_fail }, 233 { SESS_RUNNING, SESS_EV_CONN_CLOSED, sess_do_conn_closed }, 234 { SESS_RUNNING, SESS_EV_CLOSED, sess_do_down }, 235 { 0, 0, NULL } 236}; 237 238/* ARGSUSED */ 239void 240session_fsm_callback(int fd, short event, void *arg) 241{ 242 struct session *s = arg; 243 struct sessev *sev; 244 int i, ns; 245 246 while ((sev = SIMPLEQ_FIRST(&s->fsmq))) { 247 SIMPLEQ_REMOVE_HEAD(&s->fsmq, entry); 248 for (i = 0; s_fsm[i].action != NULL; i++) { 249 if (s->state & s_fsm[i].state && 250 sev->event == s_fsm[i].event) { 251 log_debug("sess_fsm[%s]: %s ev %s", 252 s->config.SessionName, sess_state(s->state), 253 sess_event(sev->event)); 254 ns = s_fsm[i].action(s, sev); 255 if (ns == -1) 256 /* XXX better please */ 257 fatalx("sess_fsm: action failed"); 258 log_debug("sess_fsm[%s]: new state %s", 259 s->config.SessionName, 260 sess_state(ns)); 261 s->state = ns; 262 break; 263 } 264 } 265 if (s_fsm[i].action == NULL) { 266 log_warnx("sess_fsm[%s]: unhandled state transition " 267 "[%s, %s]", s->config.SessionName, 268 sess_state(s->state), sess_event(sev->event)); 269 fatalx("bjork bjork bjork"); 270 } 271 free(sev); 272 } 273} 274 275int 276sess_do_start(struct session *s, struct sessev *sev) 277{ 278 log_debug("new connection to %s", 279 log_sockaddr(&s->config.connection.TargetAddr)); 280 281 /* initialize the session params */ 282 s->mine = initiator_sess_defaults; 283 s->his = iscsi_sess_defaults; 284 s->active = iscsi_sess_defaults; 285 286 if (s->config.MaxConnections) 287 s->mine.MaxConnections = s->config.MaxConnections; 288 289 conn_new(s, &s->config.connection); 290 291 return SESS_FREE; 292} 293 294int 295sess_do_conn_loggedin(struct session *s, struct sessev *sev) 296{ 297 if (s->state & SESS_LOGGED_IN) 298 return SESS_LOGGED_IN; 299 300 if (s->config.SessionType == SESSION_TYPE_DISCOVERY) { 301 initiator_discovery(s); 302 return SESS_LOGGED_IN; 303 } 304 305 iscsi_merge_sess_params(&s->active, &s->mine, &s->his); 306 vscsi_event(VSCSI_REQPROBE, s->target, -1); 307 308 return SESS_LOGGED_IN; 309} 310 311int 312sess_do_conn_fail(struct session *s, struct sessev *sev) 313{ 314 struct connection *c = sev->conn; 315 int state = SESS_FREE; 316 317 if (sev->conn == NULL) { 318 log_warnx("Just what do you think you're doing, Dave?"); 319 return -1; 320 } 321 322 /* 323 * cleanup connections: 324 * Connections in state FREE can be removed. 325 * Connections in any error state will cause the session to enter 326 * the FAILED state. If no sessions are left and the session was 327 * not already FREE then explicit recovery needs to be done. 328 */ 329 330 switch (c->state) { 331 case CONN_FREE: 332 conn_free(c); 333 break; 334 case CONN_CLEANUP_WAIT: 335 break; 336 default: 337 log_warnx("It can only be attributable to human error."); 338 return -1; 339 } 340 341 TAILQ_FOREACH(c, &s->connections, entry) { 342 if (c->state & CONN_FAILED) { 343 state = SESS_FAILED; 344 break; 345 } else if (c->state & CONN_RUNNING) 346 state = SESS_LOGGED_IN; 347 } 348 349 return state; 350} 351 352int 353sess_do_conn_closed(struct session *s, struct sessev *sev) 354{ 355 struct connection *c = sev->conn; 356 int state = SESS_FREE; 357 358 if (c == NULL || c->state != CONN_FREE) { 359 log_warnx("Just what do you think you're doing, Dave?"); 360 return -1; 361 } 362 conn_free(c); 363 364 TAILQ_FOREACH(c, &s->connections, entry) { 365 if (c->state & CONN_FAILED) { 366 state = SESS_FAILED; 367 break; 368 } else if (c->state & CONN_RUNNING) 369 state = SESS_LOGGED_IN; 370 } 371 372 return state; 373} 374 375int 376sess_do_down(struct session *s, struct sessev *sev) 377{ 378 struct connection *c; 379 380 while ((c = TAILQ_FIRST(&s->connections)) != NULL) 381 conn_free(c); 382 383 /* XXX anything else to reset to initial state? */ 384 385 return SESS_DOWN; 386} 387 388const char * 389sess_state(int s) 390{ 391 static char buf[15]; 392 393 switch (s) { 394 case SESS_INIT: 395 return "INIT"; 396 case SESS_FREE: 397 return "FREE"; 398 case SESS_LOGGED_IN: 399 return "LOGGED_IN"; 400 case SESS_FAILED: 401 return "FAILED"; 402 case SESS_DOWN: 403 return "DOWN"; 404 default: 405 snprintf(buf, sizeof(buf), "UKNWN %x", s); 406 return buf; 407 } 408 /* NOTREACHED */ 409} 410 411const char * 412sess_event(enum s_event e) 413{ 414 static char buf[15]; 415 416 switch (e) { 417 case SESS_EV_START: 418 return "start"; 419 case SESS_EV_CONN_LOGGED_IN: 420 return "connection logged in"; 421 case SESS_EV_CONN_FAIL: 422 return "connection fail"; 423 case SESS_EV_CONN_CLOSED: 424 return "connection closed"; 425 case SESS_EV_CLOSED: 426 return "session closed"; 427 case SESS_EV_FAIL: 428 return "fail"; 429 } 430 431 snprintf(buf, sizeof(buf), "UKNWN %d", e); 432 return buf; 433} 434