Bug Summary

File:src/usr.sbin/iscsid/connection.c
Warning:line 237, column 3
Attempt to free released memory

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name connection.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/iscsid/obj -resource-dir /usr/local/lib/clang/13.0.0 -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/iscsid/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/usr.sbin/iscsid/connection.c
1/* $OpenBSD: connection.c,v 1.21 2015/12/05 06:38:18 mmcc Exp $ */
2
3/*
4 * Copyright (c) 2009 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/queue.h>
21#include <sys/socket.h>
22#include <sys/uio.h>
23
24#include <netinet/in.h>
25#include <netinet/tcp.h>
26
27#include <scsi/iscsi.h>
28
29#include <errno(*__errno()).h>
30#include <event.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <unistd.h>
35
36#include "iscsid.h"
37#include "log.h"
38
39void conn_dispatch(int, short, void *);
40void conn_write_dispatch(int, short, void *);
41
42int c_do_connect(struct connection *, enum c_event);
43int c_do_login(struct connection *, enum c_event);
44int c_do_loggedin(struct connection *, enum c_event);
45int c_do_req_logout(struct connection *, enum c_event);
46int c_do_logout(struct connection *, enum c_event);
47int c_do_loggedout(struct connection *, enum c_event);
48int c_do_fail(struct connection *, enum c_event);
49int c_do_cleanup(struct connection *, enum c_event);
50
51const char *conn_state(int);
52const char *conn_event(enum c_event);
53
54void
55conn_new(struct session *s, struct connection_config *cc)
56{
57 struct connection *c;
58 int nodelay = 1;
59
60 if (!(c = calloc(1, sizeof(*c))))
61 fatal("session_add_conn");
62
63 c->fd = -1;
64 c->state = CONN_FREE0x0001;
65 c->session = s;
66 c->cid = arc4random();
67 c->config = *cc;
68 c->mine = initiator_conn_defaults;
69 c->mine.HeaderDigest = s->config.HeaderDigest;
70 c->mine.DataDigest = s->config.DataDigest;
71 c->his = iscsi_conn_defaults;
72 c->active = iscsi_conn_defaults;
73
74 TAILQ_INIT(&c->pdu_w)do { (&c->pdu_w)->tqh_first = ((void *)0); (&c->
pdu_w)->tqh_last = &(&c->pdu_w)->tqh_first; }
while (0)
;
75 TAILQ_INIT(&c->tasks)do { (&c->tasks)->tqh_first = ((void *)0); (&c->
tasks)->tqh_last = &(&c->tasks)->tqh_first; }
while (0)
;
76 TAILQ_INSERT_TAIL(&s->connections, c, entry)do { (c)->entry.tqe_next = ((void *)0); (c)->entry.tqe_prev
= (&s->connections)->tqh_last; *(&s->connections
)->tqh_last = (c); (&s->connections)->tqh_last =
&(c)->entry.tqe_next; } while (0)
;
77
78 if (pdu_readbuf_set(&c->prbuf, PDU_READ_SIZE(256 * 1024))) {
79 log_warn("conn_new");
80 conn_free(c);
81 return;
82 }
83
84 /* create socket */
85 c->fd = socket(c->config.TargetAddr.ss_family, SOCK_STREAM1, 0);
86 if (c->fd == -1) {
87 log_warn("conn_new: socket");
88 conn_free(c);
89 return;
90 }
91 if (socket_setblockmode(c->fd, 1)) {
92 log_warn("conn_new: socket_setblockmode");
93 conn_free(c);
94 return;
95 }
96
97 /* try to turn off TCP Nagle */
98 if (setsockopt(c->fd, IPPROTO_TCP6, TCP_NODELAY0x01, &nodelay,
99 sizeof(nodelay)) == -1)
100 log_warn("conn_new: setting TCP_NODELAY");
101
102 event_set(&c->ev, c->fd, EV_READ0x02|EV_PERSIST0x10, conn_dispatch, c);
103 event_set(&c->wev, c->fd, EV_WRITE0x04, conn_write_dispatch, c);
104
105 conn_fsm(c, CONN_EV_CONNECT);
106}
107
108void
109conn_free(struct connection *c)
110{
111 log_debug("conn_free");
112
113 pdu_readbuf_free(&c->prbuf);
114 pdu_free_queue(&c->pdu_w);
115
116 event_del(&c->ev);
117 event_del(&c->wev);
118 if (c->fd != -1)
119 close(c->fd);
120
121 taskq_cleanup(&c->tasks);
122
123 TAILQ_REMOVE(&c->session->connections, c, entry)do { if (((c)->entry.tqe_next) != ((void *)0)) (c)->entry
.tqe_next->entry.tqe_prev = (c)->entry.tqe_prev; else (
&c->session->connections)->tqh_last = (c)->entry
.tqe_prev; *(c)->entry.tqe_prev = (c)->entry.tqe_next; ;
; } while (0)
;
124 free(c);
125}
126
127void
128conn_dispatch(int fd, short event, void *arg)
129{
130 struct connection *c = arg;
131 ssize_t n;
132
133 if (!(event & EV_READ0x02)) {
134 log_debug("spurious read call");
135 return;
136 }
137 if ((n = pdu_read(c)) == -1) {
138 if (errno(*__errno()) == EAGAIN35 || errno(*__errno()) == ENOBUFS55 ||
139 errno(*__errno()) == EINTR4) /* try later */
140 return;
141 log_warn("pdu_read");
142 conn_fsm(c, CONN_EV_FAIL);
143 return;
144 }
145 if (n == 0) { /* connection closed */
146 conn_fsm(c, CONN_EV_CLOSED);
147 return;
148 }
149
150 pdu_parse(c);
151}
152
153void
154conn_write_dispatch(int fd, short event, void *arg)
155{
156 struct connection *c = arg;
157 ssize_t n;
158 int error;
159 socklen_t len;
160
161 if (!(event & EV_WRITE0x04)) {
162 log_debug("spurious write call");
163 return;
164 }
165
166 switch (c->state) {
167 case CONN_XPT_WAIT0x0002:
168 len = sizeof(error);
169 if (getsockopt(c->fd, SOL_SOCKET0xffff, SO_ERROR0x1007,
170 &error, &len) == -1 || (errno(*__errno()) = error)) {
171 log_warn("connect to %s failed",
172 log_sockaddr(&c->config.TargetAddr));
173 conn_fsm(c, CONN_EV_FAIL);
174 return;
175 }
176 conn_fsm(c, CONN_EV_CONNECTED);
177 break;
178 default:
179 if ((n = pdu_write(c)) == -1) {
180 log_warn("pdu_write");
181 conn_fsm(c, CONN_EV_FAIL);
182 return;
183 }
184 if (n == 0) { /* connection closed */
185 conn_fsm(c, CONN_EV_CLOSED);
186 return;
187 }
188
189 /* check if there is more to send */
190 if (pdu_pending(c))
191 event_add(&c->wev, NULL((void *)0));
192 }
193}
194
195void
196conn_fail(struct connection *c)
197{
198 log_debug("conn_fail");
199 conn_fsm(c, CONN_EV_FAIL);
200}
201
202int
203conn_task_ready(struct connection *c)
204{
205 if ((c->state & CONN_RUNNING(0x0010 | 0x0040)) && TAILQ_EMPTY(&c->tasks)(((&c->tasks)->tqh_first) == ((void *)0)))
206 return 1;
207 return 0;
208}
209
210void
211conn_task_issue(struct connection *c, struct task *t)
212{
213 TAILQ_INSERT_TAIL(&c->tasks, t, entry)do { (t)->entry.tqe_next = ((void *)0); (t)->entry.tqe_prev
= (&c->tasks)->tqh_last; *(&c->tasks)->tqh_last
= (t); (&c->tasks)->tqh_last = &(t)->entry.
tqe_next; } while (0)
;
1
Loop condition is false. Exiting loop
214 conn_task_schedule(c);
2
Calling 'conn_task_schedule'
215}
216
217void
218conn_task_schedule(struct connection *c)
219{
220 struct task *t = TAILQ_FIRST(&c->tasks)((&c->tasks)->tqh_first);
221 struct pdu *p, *np;
222
223 if (!t
15.1
't' is non-null
) {
3
Assuming 't' is non-null
4
Taking false branch
16
Taking false branch
224 log_debug("conn_task_schedule: task is hiding");
225 return;
226 }
227
228 /* move pdus to the write queue */
229 for (p = TAILQ_FIRST(&t->sendq)((&t->sendq)->tqh_first); p != NULL((void *)0); p = np) {
5
Assuming 'p' is equal to NULL
6
Loop condition is false. Execution continues on line 234
17
Assuming 'p' is equal to NULL
18
Loop condition is false. Execution continues on line 234
230 np = TAILQ_NEXT(p, entry)((p)->entry.tqe_next);
231 TAILQ_REMOVE(&t->sendq, p, entry)do { if (((p)->entry.tqe_next) != ((void *)0)) (p)->entry
.tqe_next->entry.tqe_prev = (p)->entry.tqe_prev; else (
&t->sendq)->tqh_last = (p)->entry.tqe_prev; *(p)
->entry.tqe_prev = (p)->entry.tqe_next; ; ; } while (0)
;
232 conn_pdu_write(c, p);
233 }
234 if (t->callback == NULL((void *)0)) {
7
Assuming field 'callback' is equal to NULL
8
Taking true branch
19
Assuming field 'callback' is equal to NULL
20
Taking true branch
235 /* no callback, immediate command expecting no answer */
236 conn_task_cleanup(c, t);
9
Calling 'conn_task_cleanup'
23
Returning; memory was released via 2nd parameter
237 free(t);
21
Memory is released
24
Attempt to free released memory
238 }
239}
240
241void
242conn_task_cleanup(struct connection *c, struct task *t)
243{
244 pdu_free_queue(&t->sendq);
245 pdu_free_queue(&t->recvq);
246 /* XXX need some state to know if queued or not */
247 if (c
9.1
'c' is non-null
) {
10
Taking true branch
248 TAILQ_REMOVE(&c->tasks, t, entry)do { if (((t)->entry.tqe_next) != ((void *)0)) (t)->entry
.tqe_next->entry.tqe_prev = (t)->entry.tqe_prev; else (
&c->tasks)->tqh_last = (t)->entry.tqe_prev; *(t)
->entry.tqe_prev = (t)->entry.tqe_next; ; ; } while (0)
;
11
Assuming field 'tqe_next' is equal to null
12
Taking false branch
13
Loop condition is false. Exiting loop
249 if (!TAILQ_EMPTY(&c->tasks)(((&c->tasks)->tqh_first) == ((void *)0)))
14
Taking true branch
250 conn_task_schedule(c);
15
Calling 'conn_task_schedule'
22
Returning; memory was released
251 else
252 session_schedule(c->session);
253 }
254}
255
256#define SET_NUM(p, x, v, min, max) \
257do { \
258 if (!strcmp((p)->key, #v)) { \
259 (x)->his.v = text_to_num((p)->value, (min), (max), &err); \
260 if (err) { \
261 log_warnx("bad param %s=%s: %s", \
262 (p)->key, (p)->value, err); \
263 errors++; \
264 } \
265log_debug("SET_NUM: %s = %llu", #v, (u_int64_t)(x)->his.v); \
266 } \
267} while (0)
268
269#define SET_BOOL(p, x, v) \
270do { \
271 if (!strcmp((p)->key, #v)) { \
272 (x)->his.v = text_to_bool((p)->value, &err); \
273 if (err) { \
274 log_warnx("bad param %s=%s: %s", \
275 (p)->key, (p)->value, err); \
276 errors++; \
277 } \
278log_debug("SET_BOOL: %s = %u", #v, (int)(x)->his.v); \
279 } \
280} while (0)
281
282int
283conn_parse_kvp(struct connection *c, struct kvp *kvp)
284{
285 struct kvp *k;
286 struct session *s = c->session;
287 const char *err;
288 int errors = 0;
289
290
291 for (k = kvp; k->key; k++) {
292log_debug("conn_parse_kvp: %s = %s", k->key, k->value);
293 /* XXX handle NotUnderstood|Irrelevant|Reject */
294 SET_NUM(k, s, MaxBurstLength, 512, 16777215);
295 SET_NUM(k, s, FirstBurstLength, 512, 16777215);
296 SET_NUM(k, s, DefaultTime2Wait, 0, 3600);
297 SET_NUM(k, s, DefaultTime2Retain, 0, 3600);
298 SET_NUM(k, s, MaxOutstandingR2T, 1, 65535);
299 SET_NUM(k, s, TargetPortalGroupTag, 0, 65535);
300 SET_NUM(k, s, MaxConnections, 1, 65535);
301 SET_BOOL(k, s, InitialR2T);
302 SET_BOOL(k, s, ImmediateData);
303 SET_BOOL(k, s, DataPDUInOrder);
304 SET_BOOL(k, s, DataSequenceInOrder);
305 SET_NUM(k, s, ErrorRecoveryLevel, 0, 2);
306 SET_NUM(k, c, MaxRecvDataSegmentLength, 512, 16777215);
307 }
308
309 if (errors) {
310 log_warnx("conn_parse_kvp: errors found");
311 return -1;
312 }
313 return 0;
314}
315
316#undef SET_NUM
317#undef SET_BOOL
318
319int
320conn_gen_kvp(struct connection *c, struct kvp *kvp, size_t *nkvp)
321{
322 struct session *s = c->session;
323 size_t i = 0;
324
325 if (s->mine.MaxConnections != iscsi_sess_defaults.MaxConnections) {
326 if (kvp && i < *nkvp) {
327 kvp[i].key = strdup("MaxConnections");
328 if (kvp[i].key == NULL((void *)0))
329 return -1;
330 if (asprintf(&kvp[i].value, "%hu",
331 s->mine.MaxConnections) == -1) {
332 kvp[i].value = NULL((void *)0);
333 return -1;
334 }
335 }
336 i++;
337 }
338 if (c->mine.MaxRecvDataSegmentLength !=
339 iscsi_conn_defaults.MaxRecvDataSegmentLength) {
340 if (kvp && i < *nkvp) {
341 kvp[i].key = strdup("MaxRecvDataSegmentLength");
342 if (kvp[i].key == NULL((void *)0))
343 return -1;
344 if (asprintf(&kvp[i].value, "%u",
345 c->mine.MaxRecvDataSegmentLength) == -1) {
346 kvp[i].value = NULL((void *)0);
347 return -1;
348 }
349 }
350 i++;
351 }
352
353 *nkvp = i;
354 return 0;
355}
356
357void
358conn_pdu_write(struct connection *c, struct pdu *p)
359{
360 struct iscsi_pdu *ipdu;
361
362/* XXX I GUESS THIS SHOULD BE MOVED TO PDU SOMEHOW... */
363 ipdu = pdu_getbuf(p, NULL((void *)0), PDU_HEADER0);
364 switch (ISCSI_PDU_OPCODE(ipdu->opcode)((ipdu->opcode) & 0x3f)) {
365 case ISCSI_OP_I_NOP0x00:
366 case ISCSI_OP_SCSI_REQUEST0x01:
367 case ISCSI_OP_TASK_REQUEST0x02:
368 case ISCSI_OP_LOGIN_REQUEST0x03:
369 case ISCSI_OP_TEXT_REQUEST0x04:
370 case ISCSI_OP_DATA_OUT0x05:
371 case ISCSI_OP_LOGOUT_REQUEST0x06:
372 case ISCSI_OP_SNACK_REQUEST0x10:
373 ipdu->expstatsn = ntohl(c->expstatsn)(__uint32_t)(__builtin_constant_p(c->expstatsn) ? (__uint32_t
)(((__uint32_t)(c->expstatsn) & 0xff) << 24 | ((
__uint32_t)(c->expstatsn) & 0xff00) << 8 | ((__uint32_t
)(c->expstatsn) & 0xff0000) >> 8 | ((__uint32_t)
(c->expstatsn) & 0xff000000) >> 24) : __swap32md
(c->expstatsn))
;
374 break;
375 }
376
377 TAILQ_INSERT_TAIL(&c->pdu_w, p, entry)do { (p)->entry.tqe_next = ((void *)0); (p)->entry.tqe_prev
= (&c->pdu_w)->tqh_last; *(&c->pdu_w)->tqh_last
= (p); (&c->pdu_w)->tqh_last = &(p)->entry.
tqe_next; } while (0)
;
378 event_add(&c->wev, NULL((void *)0));
379}
380
381/* connection state machine more or less as specified in the RFC */
382struct {
383 int state;
384 enum c_event event;
385 int (*action)(struct connection *, enum c_event);
386} fsm[] = {
387 { CONN_FREE0x0001, CONN_EV_CONNECT, c_do_connect }, /* T1 */
388 { CONN_XPT_WAIT0x0002, CONN_EV_CONNECTED, c_do_login }, /* T4 */
389 { CONN_IN_LOGIN0x0008, CONN_EV_LOGGED_IN, c_do_loggedin }, /* T5 */
390 { CONN_LOGGED_IN0x0010, CONN_EV_LOGOUT, c_do_logout }, /* T9 */
391 { CONN_LOGGED_IN0x0010, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T11 */
392 { CONN_LOGOUT_REQ0x0040, CONN_EV_LOGOUT, c_do_logout }, /* T10 */
393 { CONN_LOGOUT_REQ0x0040, CONN_EV_REQ_LOGOUT, c_do_req_logout},/* T12 */
394 { CONN_LOGOUT_REQ0x0040, CONN_EV_LOGGED_OUT, c_do_loggedout },/* T18 */
395 { CONN_IN_LOGOUT0x0020, CONN_EV_LOGGED_OUT, c_do_loggedout }, /* T13 */
396 { CONN_IN_LOGOUT0x0020, CONN_EV_REQ_LOGOUT, c_do_req_logout },/* T14 */
397 { CONN_CLEANUP_WAIT0x0080, CONN_EV_CLEANING_UP, c_do_cleanup},/* M2 */
398 { CONN_CLEANUP_WAIT0x0080, CONN_EV_FREE, c_do_loggedout }, /* M1 */
399 { CONN_IN_CLEANUP0x0100, CONN_EV_FREE, c_do_loggedout }, /* M4 */
400 { CONN_IN_CLEANUP0x0100, CONN_EV_CLEANING_UP, c_do_cleanup},
401 /* either one of T2, T7, T15, T16, T17, M3 */
402 { CONN_ANYSTATE0xffff, CONN_EV_CLOSED, c_do_fail },
403 { CONN_ANYSTATE0xffff, CONN_EV_FAIL, c_do_fail },
404 { CONN_ANYSTATE0xffff, CONN_EV_FREE, c_do_fail },
405 { 0, 0, NULL((void *)0) }
406};
407
408void
409conn_fsm(struct connection *c, enum c_event event)
410{
411 int i, ns;
412
413 for (i = 0; fsm[i].action != NULL((void *)0); i++) {
414 if (c->state & fsm[i].state && event == fsm[i].event) {
415 log_debug("conn_fsm[%s]: %s ev %s",
416 c->session->config.SessionName,
417 conn_state(c->state), conn_event(event));
418 ns = fsm[i].action(c, event);
419 if (ns == -1)
420 /* XXX better please */
421 fatalx("conn_fsm: action failed");
422 log_debug("conn_fsm[%s]: new state %s",
423 c->session->config.SessionName, conn_state(ns));
424 c->state = ns;
425 return;
426 }
427 }
428 log_warnx("conn_fsm[%s]: unhandled state transition [%s, %s]",
429 c->session->config.SessionName, conn_state(c->state),
430 conn_event(event));
431 fatalx("bork bork bork");
432}
433
434int
435c_do_connect(struct connection *c, enum c_event ev)
436{
437 if (c->fd == -1) {
438 log_warnx("connect(%s), lost socket",
439 log_sockaddr(&c->config.TargetAddr));
440 session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
441 return CONN_FREE0x0001;
442 }
443 if (c->config.LocalAddr.ss_len != 0) {
444 if (bind(c->fd, (struct sockaddr *)&c->config.LocalAddr,
445 c->config.LocalAddr.ss_len) == -1) {
446 log_warn("bind(%s)",
447 log_sockaddr(&c->config.LocalAddr));
448 session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
449 return CONN_FREE0x0001;
450 }
451 }
452 if (connect(c->fd, (struct sockaddr *)&c->config.TargetAddr,
453 c->config.TargetAddr.ss_len) == -1) {
454 if (errno(*__errno()) == EINPROGRESS36) {
455 event_add(&c->wev, NULL((void *)0));
456 event_add(&c->ev, NULL((void *)0));
457 return CONN_XPT_WAIT0x0002;
458 } else {
459 log_warn("connect(%s)",
460 log_sockaddr(&c->config.TargetAddr));
461 session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
462 return CONN_FREE0x0001;
463 }
464 }
465 event_add(&c->ev, NULL((void *)0));
466 /* move forward */
467 return c_do_login(c, CONN_EV_CONNECTED);
468}
469
470int
471c_do_login(struct connection *c, enum c_event ev)
472{
473 /* start a login session and hope for the best ... */
474 initiator_login(c);
475 return CONN_IN_LOGIN0x0008;
476}
477
478int
479c_do_loggedin(struct connection *c, enum c_event ev)
480{
481 iscsi_merge_conn_params(&c->active, &c->mine, &c->his);
482 session_fsm(c->session, SESS_EV_CONN_LOGGED_IN, c, 0);
483
484 return CONN_LOGGED_IN0x0010;
485}
486
487int
488c_do_req_logout(struct connection *c, enum c_event ev)
489{
490 /* target requested logout. XXX implement async handler */
491
492 if (c->state & CONN_IN_LOGOUT0x0020)
493 return CONN_IN_LOGOUT0x0020;
494 else
495 return CONN_LOGOUT_REQ0x0040;
496}
497
498int
499c_do_logout(struct connection *c, enum c_event ev)
500{
501 /* logout is in progress ... */
502 return CONN_IN_LOGOUT0x0020;
503}
504
505int
506c_do_loggedout(struct connection *c, enum c_event ev)
507{
508 /*
509 * Called by the session fsm before calling conn_free.
510 * Doing this so the state transition is logged.
511 */
512 return CONN_FREE0x0001;
513}
514
515int
516c_do_fail(struct connection *c, enum c_event ev)
517{
518 log_debug("c_do_fail");
519
520 /* cleanup events so that the connection does not retrigger */
521 event_del(&c->ev);
522 event_del(&c->wev);
523 close(c->fd);
524 c->fd = -1; /* make sure this fd is not closed again */
525
526 /* all pending task have failed so clean them up */
527 taskq_cleanup(&c->tasks);
528
529 /* session will take care of cleaning up the mess */
530 session_fsm(c->session, SESS_EV_CONN_FAIL, c, 0);
531
532 if (ev == CONN_EV_FREE || c->state & CONN_NEVER_LOGGED_IN(0x0001 | 0x0002 | 0x0004 | 0x0008))
533 return CONN_FREE0x0001;
534 return CONN_CLEANUP_WAIT0x0080;
535}
536
537int
538c_do_cleanup(struct connection *c, enum c_event ev)
539{
540 /* nothing to do here just adjust state */
541 return CONN_IN_CLEANUP0x0100;
542}
543
544const char *
545conn_state(int s)
546{
547 static char buf[15];
548
549 switch (s) {
550 case CONN_FREE0x0001:
551 return "FREE";
552 case CONN_XPT_WAIT0x0002:
553 return "XPT_WAIT";
554 case CONN_XPT_UP0x0004:
555 return "XPT_UP";
556 case CONN_IN_LOGIN0x0008:
557 return "IN_LOGIN";
558 case CONN_LOGGED_IN0x0010:
559 return "LOGGED_IN";
560 case CONN_IN_LOGOUT0x0020:
561 return "IN_LOGOUT";
562 case CONN_LOGOUT_REQ0x0040:
563 return "LOGOUT_REQ";
564 case CONN_CLEANUP_WAIT0x0080:
565 return "CLEANUP_WAIT";
566 case CONN_IN_CLEANUP0x0100:
567 return "IN_CLEANUP";
568 default:
569 snprintf(buf, sizeof(buf), "UKNWN %x", s);
570 return buf;
571 }
572 /* NOTREACHED */
573}
574
575const char *
576conn_event(enum c_event e)
577{
578 static char buf[15];
579
580 switch (e) {
581 case CONN_EV_FAIL:
582 return "fail";
583 case CONN_EV_CONNECT:
584 return "connect";
585 case CONN_EV_CONNECTED:
586 return "connected";
587 case CONN_EV_LOGGED_IN:
588 return "logged in";
589 case CONN_EV_REQ_LOGOUT:
590 return "logout requested";
591 case CONN_EV_LOGOUT:
592 return "logout";
593 case CONN_EV_LOGGED_OUT:
594 return "logged out";
595 case CONN_EV_CLEANING_UP:
596 return "cleaning up";
597 case CONN_EV_CLOSED:
598 return "closed";
599 case CONN_EV_FREE:
600 return "forced free";
601 }
602
603 snprintf(buf, sizeof(buf), "UKNWN %d", e);
604 return buf;
605}