1*7b833938Sclaudio /* $OpenBSD: session.c,v 1.13 2025/01/22 16:06:36 claudio Exp $ */ 236af5452Sclaudio 336af5452Sclaudio /* 436af5452Sclaudio * Copyright (c) 2011 Claudio Jeker <claudio@openbsd.org> 536af5452Sclaudio * 636af5452Sclaudio * Permission to use, copy, modify, and distribute this software for any 736af5452Sclaudio * purpose with or without fee is hereby granted, provided that the above 836af5452Sclaudio * copyright notice and this permission notice appear in all copies. 936af5452Sclaudio * 1036af5452Sclaudio * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1136af5452Sclaudio * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1236af5452Sclaudio * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1336af5452Sclaudio * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1436af5452Sclaudio * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1536af5452Sclaudio * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1636af5452Sclaudio * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1736af5452Sclaudio */ 1836af5452Sclaudio 1936af5452Sclaudio #include <sys/types.h> 2012bd4d7fSclaudio #include <sys/ioctl.h> 2136af5452Sclaudio #include <sys/queue.h> 2236af5452Sclaudio #include <sys/socket.h> 2336af5452Sclaudio #include <sys/uio.h> 2436af5452Sclaudio 2536af5452Sclaudio #include <scsi/iscsi.h> 2612bd4d7fSclaudio #include <scsi/scsi_all.h> 2712bd4d7fSclaudio #include <dev/vscsivar.h> 2836af5452Sclaudio 2936af5452Sclaudio #include <event.h> 3036af5452Sclaudio #include <stdio.h> 3136af5452Sclaudio #include <stdlib.h> 3236af5452Sclaudio #include <string.h> 3336af5452Sclaudio #include <unistd.h> 3436af5452Sclaudio 3536af5452Sclaudio #include "iscsid.h" 3636af5452Sclaudio #include "log.h" 3736af5452Sclaudio 38cb408c6cSclaudio int sess_do_start(struct session *, struct sessev *); 3912bd4d7fSclaudio int sess_do_conn_loggedin(struct session *, struct sessev *); 4012bd4d7fSclaudio int sess_do_conn_fail(struct session *, struct sessev *); 4112bd4d7fSclaudio int sess_do_conn_closed(struct session *, struct sessev *); 42daed3c83Sclaudio int sess_do_stop(struct session *, struct sessev *); 43daed3c83Sclaudio int sess_do_free(struct session *, struct sessev *); 44daed3c83Sclaudio int sess_do_reinstatement(struct session *, struct sessev *); 45cb408c6cSclaudio 46cb408c6cSclaudio const char *sess_state(int); 47cb408c6cSclaudio const char *sess_event(enum s_event); 48cb408c6cSclaudio 4936af5452Sclaudio void 50cb408c6cSclaudio session_cleanup(struct session *s) 5136af5452Sclaudio { 5236af5452Sclaudio struct connection *c; 5336af5452Sclaudio 5412bd4d7fSclaudio taskq_cleanup(&s->tasks); 5512bd4d7fSclaudio 5636af5452Sclaudio while ((c = TAILQ_FIRST(&s->connections)) != NULL) 5736af5452Sclaudio conn_free(c); 5836af5452Sclaudio 5936af5452Sclaudio free(s->config.TargetName); 6036af5452Sclaudio free(s->config.InitiatorName); 6136af5452Sclaudio free(s); 6236af5452Sclaudio } 6336af5452Sclaudio 6412bd4d7fSclaudio int 6512bd4d7fSclaudio session_shutdown(struct session *s) 6612bd4d7fSclaudio { 6712bd4d7fSclaudio log_debug("session[%s] going down", s->config.SessionName); 6812bd4d7fSclaudio 6912bd4d7fSclaudio s->action = SESS_ACT_DOWN; 70daed3c83Sclaudio if (s->state & (SESS_INIT | SESS_FREE)) { 71daed3c83Sclaudio /* no active session, so do a quick cleanup */ 7212bd4d7fSclaudio struct connection *c; 7312bd4d7fSclaudio while ((c = TAILQ_FIRST(&s->connections)) != NULL) 7412bd4d7fSclaudio conn_free(c); 7512bd4d7fSclaudio return 0; 7612bd4d7fSclaudio } 7712bd4d7fSclaudio 7812bd4d7fSclaudio /* cleanup task queue and issue a logout */ 7912bd4d7fSclaudio taskq_cleanup(&s->tasks); 8012bd4d7fSclaudio initiator_logout(s, NULL, ISCSI_LOGOUT_CLOSE_SESS); 8112bd4d7fSclaudio 8212bd4d7fSclaudio return 1; 8312bd4d7fSclaudio } 8412bd4d7fSclaudio 8536af5452Sclaudio void 8636af5452Sclaudio session_config(struct session *s, struct session_config *sc) 8736af5452Sclaudio { 8836af5452Sclaudio free(s->config.TargetName); 8936af5452Sclaudio s->config.TargetName = NULL; 9036af5452Sclaudio free(s->config.InitiatorName); 9136af5452Sclaudio s->config.InitiatorName = NULL; 9236af5452Sclaudio 9336af5452Sclaudio s->config = *sc; 9436af5452Sclaudio 9536af5452Sclaudio if (sc->TargetName) { 9636af5452Sclaudio s->config.TargetName = strdup(sc->TargetName); 9736af5452Sclaudio if (s->config.TargetName == NULL) 9836af5452Sclaudio fatal("strdup"); 9936af5452Sclaudio } 10036af5452Sclaudio if (sc->InitiatorName) { 10136af5452Sclaudio s->config.InitiatorName = strdup(sc->InitiatorName); 10236af5452Sclaudio if (s->config.InitiatorName == NULL) 10336af5452Sclaudio fatal("strdup"); 10436af5452Sclaudio } else 10536af5452Sclaudio s->config.InitiatorName = default_initiator_name(); 10636af5452Sclaudio } 10736af5452Sclaudio 10836af5452Sclaudio void 10936af5452Sclaudio session_task_issue(struct session *s, struct task *t) 11036af5452Sclaudio { 11136af5452Sclaudio TAILQ_INSERT_TAIL(&s->tasks, t, entry); 11236af5452Sclaudio session_schedule(s); 11336af5452Sclaudio } 11436af5452Sclaudio 11536af5452Sclaudio void 11612bd4d7fSclaudio session_logout_issue(struct session *s, struct task *t) 11712bd4d7fSclaudio { 11812bd4d7fSclaudio struct connection *c, *rc = NULL; 11912bd4d7fSclaudio 12012bd4d7fSclaudio /* find first free session or first available session */ 12112bd4d7fSclaudio TAILQ_FOREACH(c, &s->connections, entry) { 12212bd4d7fSclaudio if (conn_task_ready(c)) { 12312bd4d7fSclaudio conn_fsm(c, CONN_EV_LOGOUT); 12412bd4d7fSclaudio conn_task_issue(c, t); 12512bd4d7fSclaudio return; 12612bd4d7fSclaudio } 12712bd4d7fSclaudio if (c->state & CONN_RUNNING) 12812bd4d7fSclaudio rc = c; 12912bd4d7fSclaudio } 13012bd4d7fSclaudio 13112bd4d7fSclaudio if (rc) { 13212bd4d7fSclaudio conn_fsm(rc, CONN_EV_LOGOUT); 13312bd4d7fSclaudio conn_task_issue(rc, t); 13412bd4d7fSclaudio return; 13512bd4d7fSclaudio } 13612bd4d7fSclaudio 13712bd4d7fSclaudio /* XXX must open new connection, gulp */ 13812bd4d7fSclaudio fatalx("session_logout_issue needs more work"); 13912bd4d7fSclaudio } 14012bd4d7fSclaudio 14112bd4d7fSclaudio void 14236af5452Sclaudio session_schedule(struct session *s) 14336af5452Sclaudio { 14436af5452Sclaudio struct task *t = TAILQ_FIRST(&s->tasks); 14536af5452Sclaudio struct connection *c; 14636af5452Sclaudio 14736af5452Sclaudio if (!t) 14836af5452Sclaudio return; 14936af5452Sclaudio 15036af5452Sclaudio /* XXX IMMEDIATE TASK NEED SPECIAL HANDLING !!!! */ 15136af5452Sclaudio 15236af5452Sclaudio /* wake up a idle connection or a not busy one */ 15336af5452Sclaudio /* XXX this needs more work as it makes the daemon go wrooOOOMM */ 15436af5452Sclaudio TAILQ_FOREACH(c, &s->connections, entry) 155cb408c6cSclaudio if (conn_task_ready(c)) { 156cb408c6cSclaudio TAILQ_REMOVE(&s->tasks, t, entry); 157cb408c6cSclaudio conn_task_issue(c, t); 15836af5452Sclaudio return; 159cb408c6cSclaudio } 160cb408c6cSclaudio } 161cb408c6cSclaudio 162cb408c6cSclaudio /* 163cb408c6cSclaudio * The session FSM runs from a callback so that the connection FSM can finish. 164cb408c6cSclaudio */ 165cb408c6cSclaudio void 1660908bd10Sclaudio session_fsm(struct sessev *sev, enum s_event event, unsigned int timeout) 167cb408c6cSclaudio { 1680908bd10Sclaudio struct session *s = sev->sess; 169cb408c6cSclaudio struct timeval tv; 170cb408c6cSclaudio 171daed3c83Sclaudio log_debug("session_fsm[%s]: %s ev %s timeout %d", 172daed3c83Sclaudio s->config.SessionName, sess_state(s->state), 1730908bd10Sclaudio sess_event(event), timeout); 174daed3c83Sclaudio 1750908bd10Sclaudio sev->event = event; 176cb408c6cSclaudio 177cb408c6cSclaudio timerclear(&tv); 178daed3c83Sclaudio tv.tv_sec = timeout; 1790908bd10Sclaudio if (evtimer_add(&sev->ev, &tv) == -1) 180cb408c6cSclaudio fatal("session_fsm"); 181cb408c6cSclaudio } 182cb408c6cSclaudio 183cb408c6cSclaudio struct { 184cb408c6cSclaudio int state; 185cb408c6cSclaudio enum s_event event; 186cb408c6cSclaudio int (*action)(struct session *, struct sessev *); 187cb408c6cSclaudio } s_fsm[] = { 188cb408c6cSclaudio { SESS_INIT, SESS_EV_START, sess_do_start }, 189daed3c83Sclaudio { SESS_FREE, SESS_EV_START, sess_do_start }, 190daed3c83Sclaudio { SESS_FREE, SESS_EV_CONN_LOGGED_IN, sess_do_conn_loggedin }, /* N1 */ 191daed3c83Sclaudio { SESS_FREE, SESS_EV_CLOSED, sess_do_stop }, 19212bd4d7fSclaudio { SESS_LOGGED_IN, SESS_EV_CONN_LOGGED_IN, sess_do_conn_loggedin }, 193daed3c83Sclaudio { SESS_RUNNING, SESS_EV_CONN_CLOSED, sess_do_conn_closed }, /* N3 */ 194daed3c83Sclaudio { SESS_RUNNING, SESS_EV_CONN_FAIL, sess_do_conn_fail }, /* N5 */ 195daed3c83Sclaudio { SESS_RUNNING, SESS_EV_CLOSED, sess_do_free }, /* XXX */ 196daed3c83Sclaudio { SESS_FAILED, SESS_EV_START, sess_do_start }, 197daed3c83Sclaudio { SESS_FAILED, SESS_EV_TIMEOUT, sess_do_free }, /* N6 */ 198daed3c83Sclaudio { SESS_FAILED, SESS_EV_FREE, sess_do_free }, /* N6 */ 199daed3c83Sclaudio { SESS_FAILED, SESS_EV_CONN_LOGGED_IN, sess_do_reinstatement }, /* N4 */ 200cb408c6cSclaudio { 0, 0, NULL } 201cb408c6cSclaudio }; 202cb408c6cSclaudio 203cb408c6cSclaudio void 204cb408c6cSclaudio session_fsm_callback(int fd, short event, void *arg) 205cb408c6cSclaudio { 206daed3c83Sclaudio struct sessev *sev = arg; 207daed3c83Sclaudio struct session *s = sev->sess; 208cb408c6cSclaudio int i, ns; 209cb408c6cSclaudio 210cb408c6cSclaudio for (i = 0; s_fsm[i].action != NULL; i++) { 211cb408c6cSclaudio if (s->state & s_fsm[i].state && 212cb408c6cSclaudio sev->event == s_fsm[i].event) { 213cb408c6cSclaudio log_debug("sess_fsm[%s]: %s ev %s", 214cb408c6cSclaudio s->config.SessionName, sess_state(s->state), 215cb408c6cSclaudio sess_event(sev->event)); 216cb408c6cSclaudio ns = s_fsm[i].action(s, sev); 217cb408c6cSclaudio if (ns == -1) 218cb408c6cSclaudio /* XXX better please */ 219cb408c6cSclaudio fatalx("sess_fsm: action failed"); 220cb408c6cSclaudio log_debug("sess_fsm[%s]: new state %s", 221cb408c6cSclaudio s->config.SessionName, 222cb408c6cSclaudio sess_state(ns)); 223cb408c6cSclaudio s->state = ns; 224cb408c6cSclaudio break; 225cb408c6cSclaudio } 226cb408c6cSclaudio } 227cb408c6cSclaudio if (s_fsm[i].action == NULL) { 228cb408c6cSclaudio log_warnx("sess_fsm[%s]: unhandled state transition " 229cb408c6cSclaudio "[%s, %s]", s->config.SessionName, 230cb408c6cSclaudio sess_state(s->state), sess_event(sev->event)); 231cb408c6cSclaudio fatalx("bjork bjork bjork"); 232cb408c6cSclaudio } 233cb408c6cSclaudio } 234cb408c6cSclaudio 235cb408c6cSclaudio int 236cb408c6cSclaudio sess_do_start(struct session *s, struct sessev *sev) 237cb408c6cSclaudio { 238cb408c6cSclaudio log_debug("new connection to %s", 239cb408c6cSclaudio log_sockaddr(&s->config.connection.TargetAddr)); 2400933eeb9Sclaudio 241aeec9c16Sclaudio /* initialize the session params, and reset the active state */ 2420933eeb9Sclaudio s->mine = initiator_sess_defaults; 2430933eeb9Sclaudio s->his = iscsi_sess_defaults; 2440933eeb9Sclaudio s->active = iscsi_sess_defaults; 2450933eeb9Sclaudio 246daed3c83Sclaudio if (s->config.SessionType != SESSION_TYPE_DISCOVERY && 247daed3c83Sclaudio s->config.MaxConnections) 2480933eeb9Sclaudio s->mine.MaxConnections = s->config.MaxConnections; 2490933eeb9Sclaudio 250cb408c6cSclaudio conn_new(s, &s->config.connection); 251cb408c6cSclaudio 252daed3c83Sclaudio /* XXX kill SESS_FREE it seems to be bad */ 253daed3c83Sclaudio if (s->state == SESS_INIT) 25412bd4d7fSclaudio return SESS_FREE; 255daed3c83Sclaudio else 256daed3c83Sclaudio return s->state; 257cb408c6cSclaudio } 258cb408c6cSclaudio 259cb408c6cSclaudio int 26012bd4d7fSclaudio sess_do_conn_loggedin(struct session *s, struct sessev *sev) 261cb408c6cSclaudio { 26212bd4d7fSclaudio if (s->state & SESS_LOGGED_IN) 26312bd4d7fSclaudio return SESS_LOGGED_IN; 26412bd4d7fSclaudio 2657bc8b37fSclaudio if (s->config.SessionType == SESSION_TYPE_DISCOVERY) { 26612bd4d7fSclaudio initiator_discovery(s); 2677bc8b37fSclaudio return SESS_LOGGED_IN; 2687bc8b37fSclaudio } 2697bc8b37fSclaudio 2707bc8b37fSclaudio iscsi_merge_sess_params(&s->active, &s->mine, &s->his); 27112bd4d7fSclaudio vscsi_event(VSCSI_REQPROBE, s->target, -1); 272daed3c83Sclaudio s->holdTimer = 0; 27312bd4d7fSclaudio 27412bd4d7fSclaudio return SESS_LOGGED_IN; 27512bd4d7fSclaudio } 27612bd4d7fSclaudio 27712bd4d7fSclaudio int 27812bd4d7fSclaudio sess_do_conn_fail(struct session *s, struct sessev *sev) 27912bd4d7fSclaudio { 28012bd4d7fSclaudio struct connection *c = sev->conn; 28112bd4d7fSclaudio int state = SESS_FREE; 28212bd4d7fSclaudio 28312bd4d7fSclaudio if (sev->conn == NULL) { 28412bd4d7fSclaudio log_warnx("Just what do you think you're doing, Dave?"); 28512bd4d7fSclaudio return -1; 28612bd4d7fSclaudio } 28712bd4d7fSclaudio 288cb408c6cSclaudio /* 289cb408c6cSclaudio * cleanup connections: 290cb408c6cSclaudio * Connections in state FREE can be removed. 291cb408c6cSclaudio * Connections in any error state will cause the session to enter 292cb408c6cSclaudio * the FAILED state. If no sessions are left and the session was 293daed3c83Sclaudio * not already FREE then implicit recovery needs to be done. 294cb408c6cSclaudio */ 29512bd4d7fSclaudio 29612bd4d7fSclaudio switch (c->state) { 29712bd4d7fSclaudio case CONN_FREE: 29812bd4d7fSclaudio conn_free(c); 29912bd4d7fSclaudio break; 30012bd4d7fSclaudio case CONN_CLEANUP_WAIT: 30112bd4d7fSclaudio break; 30212bd4d7fSclaudio default: 30312bd4d7fSclaudio log_warnx("It can only be attributable to human error."); 30412bd4d7fSclaudio return -1; 30512bd4d7fSclaudio } 30612bd4d7fSclaudio 30712bd4d7fSclaudio TAILQ_FOREACH(c, &s->connections, entry) { 30812bd4d7fSclaudio if (c->state & CONN_FAILED) { 30912bd4d7fSclaudio state = SESS_FAILED; 310daed3c83Sclaudio conn_fsm(c, CONN_EV_CLEANING_UP); 311daed3c83Sclaudio } else if (c->state & CONN_RUNNING && state != SESS_FAILED) 31212bd4d7fSclaudio state = SESS_LOGGED_IN; 31312bd4d7fSclaudio } 31412bd4d7fSclaudio 3150908bd10Sclaudio session_fsm(&s->sev, SESS_EV_START, s->holdTimer); 316daed3c83Sclaudio /* exponential back-off on constant failure */ 317daed3c83Sclaudio if (s->holdTimer < ISCSID_HOLD_TIME_MAX) 318daed3c83Sclaudio s->holdTimer = s->holdTimer ? s->holdTimer * 2 : 1; 319daed3c83Sclaudio 32012bd4d7fSclaudio return state; 32112bd4d7fSclaudio } 32212bd4d7fSclaudio 32312bd4d7fSclaudio int 32412bd4d7fSclaudio sess_do_conn_closed(struct session *s, struct sessev *sev) 32512bd4d7fSclaudio { 32612bd4d7fSclaudio struct connection *c = sev->conn; 32712bd4d7fSclaudio int state = SESS_FREE; 32812bd4d7fSclaudio 32912bd4d7fSclaudio if (c == NULL || c->state != CONN_FREE) { 33012bd4d7fSclaudio log_warnx("Just what do you think you're doing, Dave?"); 33112bd4d7fSclaudio return -1; 33212bd4d7fSclaudio } 33312bd4d7fSclaudio conn_free(c); 33412bd4d7fSclaudio 33512bd4d7fSclaudio TAILQ_FOREACH(c, &s->connections, entry) { 33612bd4d7fSclaudio if (c->state & CONN_FAILED) { 33712bd4d7fSclaudio state = SESS_FAILED; 33812bd4d7fSclaudio break; 33912bd4d7fSclaudio } else if (c->state & CONN_RUNNING) 34012bd4d7fSclaudio state = SESS_LOGGED_IN; 34112bd4d7fSclaudio } 34212bd4d7fSclaudio 34312bd4d7fSclaudio return state; 34412bd4d7fSclaudio } 34512bd4d7fSclaudio 34612bd4d7fSclaudio int 347daed3c83Sclaudio sess_do_stop(struct session *s, struct sessev *sev) 348daed3c83Sclaudio { 349daed3c83Sclaudio struct connection *c; 350daed3c83Sclaudio 351daed3c83Sclaudio /* XXX do graceful closing of session and go to INIT state at the end */ 352daed3c83Sclaudio 353daed3c83Sclaudio while ((c = TAILQ_FIRST(&s->connections)) != NULL) 354daed3c83Sclaudio conn_free(c); 355daed3c83Sclaudio 356daed3c83Sclaudio /* XXX anything else to reset to initial state? */ 357daed3c83Sclaudio return SESS_INIT; 358daed3c83Sclaudio } 359daed3c83Sclaudio 360daed3c83Sclaudio int 361daed3c83Sclaudio sess_do_free(struct session *s, struct sessev *sev) 36212bd4d7fSclaudio { 36312bd4d7fSclaudio struct connection *c; 36412bd4d7fSclaudio 36512bd4d7fSclaudio while ((c = TAILQ_FIRST(&s->connections)) != NULL) 36612bd4d7fSclaudio conn_free(c); 36712bd4d7fSclaudio 368daed3c83Sclaudio return SESS_FREE; 369daed3c83Sclaudio } 37012bd4d7fSclaudio 371daed3c83Sclaudio const char *conn_state(int); 372daed3c83Sclaudio 373daed3c83Sclaudio 374daed3c83Sclaudio int 375daed3c83Sclaudio sess_do_reinstatement(struct session *s, struct sessev *sev) 376daed3c83Sclaudio { 377daed3c83Sclaudio struct connection *c, *nc; 378daed3c83Sclaudio 379daed3c83Sclaudio TAILQ_FOREACH_SAFE(c, &s->connections, entry, nc) { 380daed3c83Sclaudio log_debug("sess reinstatement[%s]: %s", 381daed3c83Sclaudio s->config.SessionName, conn_state(c->state)); 382daed3c83Sclaudio 383daed3c83Sclaudio if (c->state & CONN_FAILED) { 384daed3c83Sclaudio conn_fsm(c, CONN_EV_FREE); 385daed3c83Sclaudio conn_free(c); 386daed3c83Sclaudio } 387daed3c83Sclaudio } 388daed3c83Sclaudio 389daed3c83Sclaudio return SESS_LOGGED_IN; 390cb408c6cSclaudio } 391cb408c6cSclaudio 392cb408c6cSclaudio const char * 393cb408c6cSclaudio sess_state(int s) 394cb408c6cSclaudio { 395cb408c6cSclaudio static char buf[15]; 396cb408c6cSclaudio 397cb408c6cSclaudio switch (s) { 398cb408c6cSclaudio case SESS_INIT: 399cb408c6cSclaudio return "INIT"; 400cb408c6cSclaudio case SESS_FREE: 401cb408c6cSclaudio return "FREE"; 402cb408c6cSclaudio case SESS_LOGGED_IN: 403cb408c6cSclaudio return "LOGGED_IN"; 404cb408c6cSclaudio case SESS_FAILED: 405cb408c6cSclaudio return "FAILED"; 406cb408c6cSclaudio default: 407cb408c6cSclaudio snprintf(buf, sizeof(buf), "UKNWN %x", s); 408cb408c6cSclaudio return buf; 409cb408c6cSclaudio } 410cb408c6cSclaudio /* NOTREACHED */ 411cb408c6cSclaudio } 412cb408c6cSclaudio 413cb408c6cSclaudio const char * 414cb408c6cSclaudio sess_event(enum s_event e) 415cb408c6cSclaudio { 416cb408c6cSclaudio static char buf[15]; 417cb408c6cSclaudio 418cb408c6cSclaudio switch (e) { 419cb408c6cSclaudio case SESS_EV_START: 420cb408c6cSclaudio return "start"; 421daed3c83Sclaudio case SESS_EV_STOP: 422daed3c83Sclaudio return "stop"; 42312bd4d7fSclaudio case SESS_EV_CONN_LOGGED_IN: 42412bd4d7fSclaudio return "connection logged in"; 425cb408c6cSclaudio case SESS_EV_CONN_FAIL: 426cb408c6cSclaudio return "connection fail"; 427cb408c6cSclaudio case SESS_EV_CONN_CLOSED: 428cb408c6cSclaudio return "connection closed"; 429daed3c83Sclaudio case SESS_EV_REINSTATEMENT: 430daed3c83Sclaudio return "connection reinstated"; 43112bd4d7fSclaudio case SESS_EV_CLOSED: 43212bd4d7fSclaudio return "session closed"; 433daed3c83Sclaudio case SESS_EV_TIMEOUT: 434daed3c83Sclaudio return "timeout"; 435daed3c83Sclaudio case SESS_EV_FREE: 436daed3c83Sclaudio return "free"; 437cb408c6cSclaudio case SESS_EV_FAIL: 438cb408c6cSclaudio return "fail"; 439cb408c6cSclaudio } 440cb408c6cSclaudio 441cb408c6cSclaudio snprintf(buf, sizeof(buf), "UKNWN %d", e); 442cb408c6cSclaudio return buf; 44336af5452Sclaudio } 444