| /* |
| * Copyright (C) 2002 - 2003 Ardis Technologies <roman@ardistech.com> |
| * Copyright (C) 2007 - 2018 Vladislav Bolkhovitin |
| * Copyright (C) 2007 - 2018 Western Digital Corporation |
| * |
| * This program is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU General Public License |
| * as published by the Free Software Foundation, version 2 |
| * of the License. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| */ |
| |
| #include <ctype.h> |
| #include <dirent.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| |
| #include <sys/stat.h> |
| #include <errno.h> |
| |
| #include "iscsid.h" |
| |
| static int session_alloc(u32 tid, struct session **psess) |
| { |
| struct session *session; |
| struct target *target = target_find_by_id(tid); |
| int res = 0; |
| |
| if (!target) { |
| log_error("tid %x not found", tid); |
| res = -ENOENT; |
| goto out; |
| } |
| |
| if (!(session = malloc(sizeof(*session)))) { |
| res = -ENOMEM; |
| goto out; |
| } |
| |
| memset(session, 0, sizeof(*session)); |
| |
| session->target = target; |
| INIT_LIST_HEAD(&session->slist); |
| list_add_tail(&session->slist, &target->sessions_list); |
| |
| *psess = session; |
| |
| out: |
| return res; |
| } |
| |
| struct session *session_find_name(u32 tid, const char *iname, union iscsi_sid sid) |
| { |
| struct session *session; |
| struct target *target; |
| |
| if (!(target = target_find_by_id(tid))) { |
| log_error("Target tid %d not found", tid); |
| return NULL; |
| } |
| |
| log_debug(1, "Finding session %s, sid %#" PRIx64, iname, sid.id64); |
| |
| list_for_each_entry(session, &target->sessions_list, slist) { |
| if (!memcmp(sid.id.isid, session->sid.id.isid, 6) && |
| !strcmp(iname, session->initiator)) |
| return session; |
| } |
| |
| return NULL; |
| } |
| |
| struct session *session_find_id(u32 tid, u64 sid) |
| { |
| struct session *session; |
| struct target *target; |
| |
| if (!(target = target_find_by_id(tid))) |
| return NULL; |
| |
| log_debug(1, "Searching for sid %#" PRIx64, sid); |
| |
| list_for_each_entry(session, &target->sessions_list, slist) { |
| if (session->sid.id64 == sid) |
| return session; |
| } |
| |
| return NULL; |
| } |
| |
| static bool session_id_exists(u32 tid, u64 sid, struct session *exclude) |
| { |
| struct session *session; |
| struct target *target; |
| |
| if (!(target = target_find_by_id(tid))) |
| return false; |
| |
| log_debug(1, "Searching for sid %#" PRIx64, sid); |
| |
| list_for_each_entry(session, &target->sessions_list, slist) { |
| if ((session->sid.id64 == sid) && (session != exclude)) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| int session_create(struct connection *conn) |
| { |
| /* We are single threaded, so it doesn't need any protection */ |
| static u16 tsih = 1; |
| struct session *session; |
| int res = 0; |
| |
| res = session_alloc(conn->tid, &session); |
| if (res != 0) { |
| log_error("session_alloc() failed: %d", res); |
| goto out; |
| } |
| |
| session->sid = conn->sid; |
| session->sid.id.tsih = tsih; |
| INIT_LIST_HEAD(&session->conn_list); |
| |
| list_add_tail(&conn->clist, &session->conn_list); |
| conn->sess = session; |
| |
| conn->sess->initiator = strdup(conn->initiator); |
| if (conn->sess->initiator == NULL) { |
| res = -errno; |
| log_error("strdup() failed: %d", res); |
| goto out_free; |
| } |
| |
| while (1) { |
| bool e; |
| |
| e = session_id_exists(conn->tid, session->sid.id64, session); |
| if (!e) |
| break; |
| |
| log_debug(1, "tsih %x already exists", session->sid.id.tsih); |
| session->sid.id.tsih++; |
| } |
| tsih = session->sid.id.tsih + 1; |
| |
| log_debug(1, "sid %#" PRIx64, session->sid.id64); |
| |
| res = kernel_session_create(conn); |
| if (res != 0) |
| goto out_free; |
| |
| out: |
| return res; |
| |
| out_free: |
| list_del_init(&conn->clist); |
| assert(list_empty(&session->conn_list)); |
| session_free(session); |
| conn->sess = NULL; |
| goto out; |
| } |
| |
| void session_free(struct session *session) |
| { |
| log_debug(1, "Freeing session sid %#"PRIx64, session->sid.id64); |
| |
| if (session->target) { |
| struct target *target = session->target; |
| |
| kernel_session_destroy(target->tid, session->sid.id64); |
| |
| target->sessions_count--; |
| log_debug(1, "target %s, sessions_count %d", target->name, |
| target->sessions_count); |
| |
| list_del(&session->slist); |
| } |
| |
| free(session->initiator); |
| free(session); |
| return; |
| } |
| |
| struct connection *conn_find(struct session *session, u16 cid) |
| { |
| struct connection *conn; |
| |
| list_for_each_entry(conn, &session->conn_list, clist) { |
| if (conn->cid == cid) |
| return conn; |
| } |
| |
| return NULL; |
| } |