GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/iscsid/session.c Lines: 0 187 0.0 %
Date: 2017-11-07 Branches: 0 115 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: session.c,v 1.8 2015/12/05 06:37:24 mmcc 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
38
void	session_fsm_callback(int, short, void *);
39
int	sess_do_start(struct session *, struct sessev *);
40
int	sess_do_conn_loggedin(struct session *, struct sessev *);
41
int	sess_do_conn_fail(struct session *, struct sessev *);
42
int	sess_do_conn_closed(struct session *, struct sessev *);
43
int	sess_do_stop(struct session *, struct sessev *);
44
int	sess_do_free(struct session *, struct sessev *);
45
int	sess_do_reinstatement(struct session *, struct sessev *);
46
47
const char *sess_state(int);
48
const char *sess_event(enum s_event);
49
50
struct session *
51
session_find(struct initiator *i, char *name)
52
{
53
	struct session *s;
54
55
	TAILQ_FOREACH(s, &i->sessions, entry) {
56
		if (strcmp(s->config.SessionName, name) == 0)
57
			return s;
58
	}
59
	return NULL;
60
}
61
62
struct session *
63
session_new(struct initiator *i, u_int8_t st)
64
{
65
	struct session *s;
66
67
	if (!(s = calloc(1, sizeof(*s))))
68
		return NULL;
69
70
	/* use the same qualifier unless there is a conflict */
71
	s->isid_base = i->config.isid_base;
72
	s->isid_qual = i->config.isid_qual;
73
	s->cmdseqnum = arc4random();
74
	s->itt = arc4random();
75
	s->initiator = i;
76
	s->state = SESS_INIT;
77
78
	if (st == SESSION_TYPE_DISCOVERY)
79
		s->target = 0;
80
	else
81
		s->target = s->initiator->target++;
82
83
	TAILQ_INSERT_HEAD(&i->sessions, s, entry);
84
	TAILQ_INIT(&s->connections);
85
	TAILQ_INIT(&s->tasks);
86
87
	return s;
88
}
89
90
void
91
session_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
105
int
106
session_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)) {
112
		/* no active session, so do a quick cleanup */
113
		struct connection *c;
114
		while ((c = TAILQ_FIRST(&s->connections)) != NULL)
115
			conn_free(c);
116
		return 0;
117
	}
118
119
	/* cleanup task queue and issue a logout */
120
	taskq_cleanup(&s->tasks);
121
	initiator_logout(s, NULL, ISCSI_LOGOUT_CLOSE_SESS);
122
123
	return 1;
124
}
125
126
void
127
session_config(struct session *s, struct session_config *sc)
128
{
129
	free(s->config.TargetName);
130
	s->config.TargetName = NULL;
131
	free(s->config.InitiatorName);
132
	s->config.InitiatorName = NULL;
133
134
	s->config = *sc;
135
136
	if (sc->TargetName) {
137
		s->config.TargetName = strdup(sc->TargetName);
138
		if (s->config.TargetName == NULL)
139
			fatal("strdup");
140
	}
141
	if (sc->InitiatorName) {
142
		s->config.InitiatorName = strdup(sc->InitiatorName);
143
		if (s->config.InitiatorName == NULL)
144
			fatal("strdup");
145
	} else
146
		s->config.InitiatorName = default_initiator_name();
147
}
148
149
void
150
session_task_issue(struct session *s, struct task *t)
151
{
152
	TAILQ_INSERT_TAIL(&s->tasks, t, entry);
153
	session_schedule(s);
154
}
155
156
void
157
session_logout_issue(struct session *s, struct task *t)
158
{
159
	struct connection *c, *rc = NULL;
160
161
	/* find first free session or first available session */
162
	TAILQ_FOREACH(c, &s->connections, entry) {
163
		if (conn_task_ready(c)) {
164
			conn_fsm(c, CONN_EV_LOGOUT);
165
			conn_task_issue(c, t);
166
			return;
167
		}
168
		if (c->state & CONN_RUNNING)
169
			rc = c;
170
	}
171
172
	if (rc) {
173
		conn_fsm(rc, CONN_EV_LOGOUT);
174
		conn_task_issue(rc, t);
175
		return;
176
	}
177
178
	/* XXX must open new connection, gulp */
179
	fatalx("session_logout_issue needs more work");
180
}
181
182
void
183
session_schedule(struct session *s)
184
{
185
	struct task *t = TAILQ_FIRST(&s->tasks);
186
	struct connection *c;
187
188
	if (!t)
189
		return;
190
191
	/* XXX IMMEDIATE TASK NEED SPECIAL HANDLING !!!! */
192
193
	/* wake up a idle connection or a not busy one */
194
	/* XXX this needs more work as it makes the daemon go wrooOOOMM */
195
	TAILQ_FOREACH(c, &s->connections, entry)
196
		if (conn_task_ready(c)) {
197
			TAILQ_REMOVE(&s->tasks, t, entry);
198
			conn_task_issue(c, t);
199
			return;
200
		}
201
}
202
203
/*
204
 * The session FSM runs from a callback so that the connection FSM can finish.
205
 */
206
void
207
session_fsm(struct session *s, enum s_event ev, struct connection *c,
208
    unsigned int timeout)
209
{
210
	struct timeval tv;
211
	struct sessev *sev;
212
213
	log_debug("session_fsm[%s]: %s ev %s timeout %d",
214
	    s->config.SessionName, sess_state(s->state),
215
	    sess_event(ev), timeout);
216
217
	if ((sev = malloc(sizeof(*sev))) == NULL)
218
		fatal("session_fsm");
219
	sev->conn = c;
220
	sev->sess = s;
221
	sev->event = ev;
222
223
	timerclear(&tv);
224
	tv.tv_sec = timeout;
225
	if (event_once(-1, EV_TIMEOUT, session_fsm_callback, sev, &tv) == -1)
226
		fatal("session_fsm");
227
}
228
229
struct {
230
	int		state;
231
	enum s_event	event;
232
	int		(*action)(struct session *, struct sessev *);
233
} s_fsm[] = {
234
	{ SESS_INIT, SESS_EV_START, sess_do_start },
235
	{ SESS_FREE, SESS_EV_START, sess_do_start },
236
	{ SESS_FREE, SESS_EV_CONN_LOGGED_IN, sess_do_conn_loggedin },	/* N1 */
237
	{ SESS_FREE, SESS_EV_CLOSED, sess_do_stop },
238
	{ SESS_LOGGED_IN, SESS_EV_CONN_LOGGED_IN, sess_do_conn_loggedin },
239
	{ SESS_RUNNING, SESS_EV_CONN_CLOSED, sess_do_conn_closed },	/* N3 */
240
	{ SESS_RUNNING, SESS_EV_CONN_FAIL, sess_do_conn_fail },		/* N5 */
241
	{ SESS_RUNNING, SESS_EV_CLOSED, sess_do_free },		/* XXX */
242
	{ SESS_FAILED, SESS_EV_START, sess_do_start },
243
	{ SESS_FAILED, SESS_EV_TIMEOUT, sess_do_free },			/* N6 */
244
	{ SESS_FAILED, SESS_EV_FREE, sess_do_free },			/* N6 */
245
	{ SESS_FAILED, SESS_EV_CONN_LOGGED_IN, sess_do_reinstatement },	/* N4 */
246
	{ 0, 0, NULL }
247
};
248
249
/* ARGSUSED */
250
void
251
session_fsm_callback(int fd, short event, void *arg)
252
{
253
	struct sessev *sev = arg;
254
	struct session *s = sev->sess;
255
	int	i, ns;
256
257
	for (i = 0; s_fsm[i].action != NULL; i++) {
258
		if (s->state & s_fsm[i].state &&
259
		    sev->event == s_fsm[i].event) {
260
			log_debug("sess_fsm[%s]: %s ev %s",
261
			    s->config.SessionName, sess_state(s->state),
262
			    sess_event(sev->event));
263
			ns = s_fsm[i].action(s, sev);
264
			if (ns == -1)
265
				/* XXX better please */
266
				fatalx("sess_fsm: action failed");
267
			log_debug("sess_fsm[%s]: new state %s",
268
			    s->config.SessionName,
269
			    sess_state(ns));
270
			s->state = ns;
271
			break;
272
		}
273
	}
274
	if (s_fsm[i].action == NULL) {
275
		log_warnx("sess_fsm[%s]: unhandled state transition "
276
		    "[%s, %s]", s->config.SessionName,
277
		    sess_state(s->state), sess_event(sev->event));
278
		fatalx("bjork bjork bjork");
279
	}
280
	free(sev);
281
log_debug("sess_fsm: done");
282
}
283
284
int
285
sess_do_start(struct session *s, struct sessev *sev)
286
{
287
	log_debug("new connection to %s",
288
	    log_sockaddr(&s->config.connection.TargetAddr));
289
290
	/* initialize the session params */
291
	s->mine = initiator_sess_defaults;
292
	s->his = iscsi_sess_defaults;
293
	s->active = iscsi_sess_defaults;
294
295
	if (s->config.SessionType != SESSION_TYPE_DISCOVERY &&
296
	    s->config.MaxConnections)
297
		s->mine.MaxConnections = s->config.MaxConnections;
298
299
	conn_new(s, &s->config.connection);
300
301
	/* XXX kill SESS_FREE it seems to be bad */
302
	if (s->state == SESS_INIT)
303
		return SESS_FREE;
304
	else
305
		return s->state;
306
}
307
308
int
309
sess_do_conn_loggedin(struct session *s, struct sessev *sev)
310
{
311
	if (s->state & SESS_LOGGED_IN)
312
		return SESS_LOGGED_IN;
313
314
	if (s->config.SessionType == SESSION_TYPE_DISCOVERY) {
315
		initiator_discovery(s);
316
		return SESS_LOGGED_IN;
317
	}
318
319
	iscsi_merge_sess_params(&s->active, &s->mine, &s->his);
320
	vscsi_event(VSCSI_REQPROBE, s->target, -1);
321
	s->holdTimer = 0;
322
323
	return SESS_LOGGED_IN;
324
}
325
326
int
327
sess_do_conn_fail(struct session *s, struct sessev *sev)
328
{
329
	struct connection *c = sev->conn;
330
	int state = SESS_FREE;
331
332
	if (sev->conn == NULL) {
333
		log_warnx("Just what do you think you're doing, Dave?");
334
		return -1;
335
	}
336
337
	/*
338
	 * cleanup connections:
339
	 * Connections in state FREE can be removed.
340
	 * Connections in any error state will cause the session to enter
341
	 * the FAILED state. If no sessions are left and the session was
342
	 * not already FREE then implicit recovery needs to be done.
343
	 */
344
345
	switch (c->state) {
346
	case CONN_FREE:
347
		conn_free(c);
348
		break;
349
	case CONN_CLEANUP_WAIT:
350
		break;
351
	default:
352
		log_warnx("It can only be attributable to human error.");
353
		return -1;
354
	}
355
356
	TAILQ_FOREACH(c, &s->connections, entry) {
357
		if (c->state & CONN_FAILED) {
358
			state = SESS_FAILED;
359
			conn_fsm(c, CONN_EV_CLEANING_UP);
360
		} else if (c->state & CONN_RUNNING && state != SESS_FAILED)
361
			state = SESS_LOGGED_IN;
362
	}
363
364
	session_fsm(s, SESS_EV_START, NULL, s->holdTimer);
365
	/* exponential back-off on constant failure */
366
	if (s->holdTimer < ISCSID_HOLD_TIME_MAX)
367
		s->holdTimer = s->holdTimer ? s->holdTimer * 2 : 1;
368
369
	return state;
370
}
371
372
int
373
sess_do_conn_closed(struct session *s, struct sessev *sev)
374
{
375
	struct connection *c = sev->conn;
376
	int state = SESS_FREE;
377
378
	if (c == NULL || c->state != CONN_FREE) {
379
		log_warnx("Just what do you think you're doing, Dave?");
380
		return -1;
381
	}
382
	conn_free(c);
383
384
	TAILQ_FOREACH(c, &s->connections, entry) {
385
		if (c->state & CONN_FAILED) {
386
			state = SESS_FAILED;
387
			break;
388
		} else if (c->state & CONN_RUNNING)
389
			state = SESS_LOGGED_IN;
390
	}
391
392
	return state;
393
}
394
395
int
396
sess_do_stop(struct session *s, struct sessev *sev)
397
{
398
	struct connection *c;
399
400
	/* XXX do graceful closing of session and go to INIT state at the end */
401
402
	while ((c = TAILQ_FIRST(&s->connections)) != NULL)
403
		conn_free(c);
404
405
	/* XXX anything else to reset to initial state? */
406
	return SESS_INIT;
407
}
408
409
int
410
sess_do_free(struct session *s, struct sessev *sev)
411
{
412
	struct connection *c;
413
414
	while ((c = TAILQ_FIRST(&s->connections)) != NULL)
415
		conn_free(c);
416
417
	return SESS_FREE;
418
}
419
420
const char *conn_state(int);
421
422
423
int
424
sess_do_reinstatement(struct session *s, struct sessev *sev)
425
{
426
	struct connection *c, *nc;
427
428
	TAILQ_FOREACH_SAFE(c, &s->connections, entry, nc) {
429
		log_debug("sess reinstatement[%s]: %s",
430
		    s->config.SessionName, conn_state(c->state));
431
432
		if (c->state & CONN_FAILED) {
433
			conn_fsm(c, CONN_EV_FREE);
434
			TAILQ_REMOVE(&s->connections, c, entry);
435
			conn_free(c);
436
		}
437
	}
438
439
	return SESS_LOGGED_IN;
440
}
441
442
const char *
443
sess_state(int s)
444
{
445
	static char buf[15];
446
447
	switch (s) {
448
	case SESS_INIT:
449
		return "INIT";
450
	case SESS_FREE:
451
		return "FREE";
452
	case SESS_LOGGED_IN:
453
		return "LOGGED_IN";
454
	case SESS_FAILED:
455
		return "FAILED";
456
	default:
457
		snprintf(buf, sizeof(buf), "UKNWN %x", s);
458
		return buf;
459
	}
460
	/* NOTREACHED */
461
}
462
463
const char *
464
sess_event(enum s_event e)
465
{
466
	static char buf[15];
467
468
	switch (e) {
469
	case SESS_EV_START:
470
		return "start";
471
	case SESS_EV_STOP:
472
		return "stop";
473
	case SESS_EV_CONN_LOGGED_IN:
474
		return "connection logged in";
475
	case SESS_EV_CONN_FAIL:
476
		return "connection fail";
477
	case SESS_EV_CONN_CLOSED:
478
		return "connection closed";
479
	case SESS_EV_REINSTATEMENT:
480
		return "connection reinstated";
481
	case SESS_EV_CLOSED:
482
		return "session closed";
483
	case SESS_EV_TIMEOUT:
484
		return "timeout";
485
	case SESS_EV_FREE:
486
		return "free";
487
	case SESS_EV_FAIL:
488
		return "fail";
489
	}
490
491
	snprintf(buf, sizeof(buf), "UKNWN %d", e);
492
	return buf;
493
}