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