Blame usr/session_mgmt.c

Packit eace71
/*
Packit eace71
 * iSCSI session management helpers
Packit eace71
 *
Packit eace71
 * Copyright (C) 2010 Mike Christie
Packit eace71
 * Copyright (C) 2010 Red Hat, Inc. All rights reserved.
Packit eace71
 * Copyright (C) 2011 Dell Inc.
Packit eace71
 * maintained by open-iscsi@googlegroups.com
Packit eace71
 *
Packit eace71
 * This program is free software; you can redistribute it and/or modify
Packit eace71
 * it under the terms of the GNU General Public License as published
Packit eace71
 * by the Free Software Foundation; either version 2 of the License, or
Packit eace71
 * (at your option) any later version.
Packit eace71
 *
Packit eace71
 * This program is distributed in the hope that it will be useful, but
Packit eace71
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit eace71
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Packit eace71
 * General Public License for more details.
Packit eace71
 *
Packit eace71
 * See the file COPYING included with this distribution for more details.
Packit eace71
 */
Packit eace71
Packit eace71
#include <stdlib.h>
Packit eace71
#include <stdio.h>
Packit eace71
#include <errno.h>
Packit eace71
#include <unistd.h>
Packit eace71
Packit eace71
#include "idbm.h"
Packit eace71
#include "list.h"
Packit eace71
#include "iscsi_util.h"
Packit eace71
#include "mgmt_ipc.h"
Packit eace71
#include "session_info.h"
Packit eace71
#include "iscsi_sysfs.h"
Packit eace71
#include "log.h"
Packit eace71
#include "iscsid_req.h"
Packit eace71
#include "iscsi_err.h"
Packit eace71
Packit eace71
static void log_login_msg(struct node_rec *rec, int rc)
Packit eace71
{
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not login to [iface: %s, target: %s, "
Packit eace71
			  "portal: %s,%d].", rec->iface.name,
Packit eace71
			  rec->name, rec->conn[0].address,
Packit eace71
			  rec->conn[0].port);
Packit eace71
		iscsi_err_print_msg(rc);
Packit eace71
	} else
Packit eace71
		log_info("Login to [iface: %s, target: %s, portal: "
Packit eace71
			 "%s,%d] successful.", rec->iface.name,
Packit eace71
			 rec->name, rec->conn[0].address,
Packit eace71
			 rec->conn[0].port);
Packit eace71
}
Packit eace71
Packit eace71
struct iscsid_async_req {
Packit eace71
	struct list_head list;
Packit eace71
	void *data;
Packit eace71
	int fd;
Packit eace71
};
Packit eace71
Packit eace71
/**
Packit eace71
 * iscsid_reqs_close - close open async requests
Packit eace71
 * @list: list of async reqs
Packit eace71
 *
Packit eace71
 * This just closes the socket to the daemon.
Packit eace71
 */
Packit eace71
static void iscsid_reqs_close(struct list_head *list)
Packit eace71
{
Packit eace71
	struct iscsid_async_req *tmp, *curr;
Packit eace71
Packit eace71
	list_for_each_entry_safe(curr, tmp, list, list) {
Packit eace71
		close(curr->fd);
Packit eace71
		list_del(&curr->list);
Packit eace71
		free(curr);
Packit eace71
	}
Packit eace71
}
Packit eace71
Packit eace71
static int iscsid_login_reqs_wait(struct list_head *list)
Packit eace71
{
Packit eace71
	struct iscsid_async_req *tmp, *curr;
Packit eace71
	struct node_rec *rec;
Packit eace71
	int ret = 0;
Packit eace71
Packit eace71
	list_for_each_entry_safe(curr, tmp, list, list) {
Packit eace71
		int err;
Packit eace71
Packit eace71
		rec = curr->data;
Packit eace71
		err = iscsid_req_wait(MGMT_IPC_SESSION_LOGIN, curr->fd);
Packit eace71
		if (err && !ret)
Packit eace71
			ret = err;
Packit eace71
		log_login_msg(rec, err);
Packit eace71
		list_del(&curr->list);
Packit eace71
		free(curr);
Packit eace71
	}
Packit eace71
	return ret;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 * __iscsi_login_portal - request iscsid to login to portal
Packit eace71
 * @data: If set, copies the session.multiple value to the portal record
Packit eace71
 *        so it is propagated to iscsid.
Packit eace71
 * @list: If async, list to add session to
Packit eace71
 * @rec: portal rec to log into
Packit eace71
 */
Packit eace71
static int
Packit eace71
__iscsi_login_portal(void *data, struct list_head *list, struct node_rec *rec)
Packit eace71
{
Packit eace71
	struct iscsid_async_req *async_req = NULL;
Packit eace71
	int rc = 0, fd;
Packit eace71
Packit eace71
	if (data && !rec->session.multiple) {
Packit eace71
		struct node_rec *pattern_rec = data;
Packit eace71
		rec->session.multiple = pattern_rec->session.multiple;
Packit eace71
	}
Packit eace71
Packit eace71
	log_info("Logging in to [iface: %s, target: %s, portal: %s,%d]%s",
Packit eace71
		 rec->iface.name, rec->name, rec->conn[0].address,
Packit eace71
		 rec->conn[0].port,
Packit eace71
		 (rec->session.multiple ? " (multiple)" : ""));
Packit eace71
Packit eace71
	if (list) {
Packit eace71
		async_req = calloc(1, sizeof(*async_req));
Packit eace71
		if (!async_req)
Packit eace71
			log_info("Could not allocate memory for async login "
Packit eace71
				 "handling. Using sequential login instead.");
Packit eace71
		else
Packit eace71
			INIT_LIST_HEAD(&async_req->list);
Packit eace71
	}
Packit eace71
Packit eace71
	if (async_req)
Packit eace71
		rc = iscsid_req_by_rec_async(MGMT_IPC_SESSION_LOGIN,
Packit eace71
					     rec, &fd;;
Packit eace71
	else
Packit eace71
		rc = iscsid_req_by_rec(MGMT_IPC_SESSION_LOGIN, rec);
Packit eace71
Packit eace71
	if (rc) {
Packit eace71
		log_login_msg(rec, rc);
Packit eace71
		if (async_req)
Packit eace71
			free(async_req);
Packit eace71
		return rc;
Packit eace71
	}
Packit eace71
Packit eace71
	if (async_req) {
Packit eace71
		list_add_tail(&async_req->list, list);
Packit eace71
		async_req->fd = fd;
Packit eace71
		async_req->data = rec;
Packit eace71
	} else
Packit eace71
		log_login_msg(rec, rc);
Packit eace71
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 * iscsi_login_portal - request iscsid to login to portal multiple
Packit eace71
 * times, based on the session.nr_sessions in the portal record.
Packit eace71
 * @data: If set, session.multiple will cause an additional session to
Packit eace71
 *        be created regardless of the value of session.nr_sessions
Packit eace71
 * @list: If async, list to add session to
Packit eace71
 * @rec: portal rec to log into
Packit eace71
 */
Packit eace71
int iscsi_login_portal(void *data, struct list_head *list, struct node_rec *rec)
Packit eace71
{
Packit eace71
	struct node_rec *pattern_rec = data;
Packit eace71
	int rc = 0, session_count = 0, i;
Packit eace71
Packit eace71
	/*
Packit eace71
	 * If pattern_rec->session.multiple is set, just add a single new
Packit eace71
	 * session by passing things along to __iscsi_login_portal
Packit eace71
	 */
Packit eace71
	if (pattern_rec && pattern_rec->session.multiple)
Packit eace71
		return __iscsi_login_portal(data, list, rec);
Packit eace71
Packit eace71
	/*
Packit eace71
	 * Count the current number of sessions, and only create those
Packit eace71
	 * that are missing.
Packit eace71
	 */
Packit eace71
	rc = iscsi_sysfs_for_each_session(rec, &session_count,
Packit eace71
					  iscsi_match_session_count, 0);
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not count current number of sessions");
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
	if (session_count >= rec->session.nr_sessions) {
Packit Service 35e5c4
		log_debug(1, "%s: %d session%s requested, but %d "
Packit eace71
			  "already present.",
Packit eace71
			  rec->iface.name, rec->session.nr_sessions,
Packit eace71
			  rec->session.nr_sessions == 1 ? "" : "s",
Packit eace71
			  session_count);
Packit Service 35e5c4
		rc = 0;
Packit eace71
		goto done;
Packit eace71
	}
Packit eace71
Packit eace71
	/*
Packit eace71
	 * Ensure the record's 'multiple' flag is set so __iscsi_login_portal
Packit eace71
	 * will allow multiple logins, but only if configured for more
Packit eace71
	 * than one 
Packit eace71
	 */
Packit eace71
	if (rec->session.nr_sessions > 1)
Packit eace71
		rec->session.multiple = 1;
Packit eace71
	for (i = session_count; i < rec->session.nr_sessions; ++i) {
Packit eace71
		log_debug(1, "%s: Creating session %d/%d", rec->iface.name,
Packit eace71
			  i + 1, rec->session.nr_sessions);
Packit eace71
		int err = __iscsi_login_portal(pattern_rec, list, rec);
Packit eace71
		if (err && !rc)
Packit eace71
			rc = err;
Packit eace71
	}
Packit eace71
Packit eace71
done:
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 * iscsi_login_portal_nowait - request iscsid to login to portal
Packit eace71
 * @rec: portal rec to log into
Packit eace71
 *
Packit eace71
 * This sends the login request, but does not wait for the result.
Packit eace71
 */
Packit eace71
int iscsi_login_portal_nowait(struct node_rec *rec)
Packit eace71
{
Packit eace71
	struct list_head list;
Packit eace71
	int err;
Packit eace71
Packit eace71
	INIT_LIST_HEAD(&list);
Packit eace71
	err = iscsi_login_portal(NULL, &list, rec);
Packit eace71
	if (err > 0)
Packit eace71
		return err;
Packit eace71
	iscsid_reqs_close(&list);
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 * __iscsi_login_portals - login into portals on @rec_list,
Packit eace71
 * @data: data to pass to login_fn
Packit eace71
 * @nr_found: returned with number of portals logged into
Packit eace71
 * @wait: bool indicating if the fn should wait for the result
Packit eace71
 * @rec_list: list of portals to log into
Packit eace71
 * @clear_list: If set, delete and free rec_list after iterating through.
Packit eace71
 * @login_fn: list iter function
Packit eace71
 *
Packit eace71
 * This will loop over the list of portals and login. It
Packit eace71
 * will attempt to login asynchronously, and then wait for
Packit eace71
 * them to complete if wait is set.
Packit eace71
 */
Packit eace71
static
Packit eace71
int __iscsi_login_portals(void *data, int *nr_found, int wait,
Packit eace71
			struct list_head *rec_list, int clear_list,
Packit eace71
			int (*login_fn)(void *, struct list_head *,
Packit eace71
					 struct node_rec *))
Packit eace71
{
Packit eace71
	struct node_rec *curr_rec, *tmp;
Packit eace71
	struct list_head login_list;
Packit eace71
	int ret = 0, err;
Packit eace71
Packit eace71
	*nr_found = 0;
Packit eace71
	INIT_LIST_HEAD(&login_list);
Packit eace71
Packit eace71
	list_for_each_entry(curr_rec, rec_list, list) {
Packit eace71
		err = login_fn(data, &login_list, curr_rec);
Packit eace71
		if (err > 0 && !ret)
Packit eace71
			ret = err;
Packit eace71
		if (!err)
Packit eace71
			(*nr_found)++;
Packit eace71
	}
Packit eace71
	if (wait) {
Packit eace71
		err = iscsid_login_reqs_wait(&login_list);
Packit eace71
		if (err && !ret)
Packit eace71
			ret = err;
Packit eace71
	} else
Packit eace71
		iscsid_reqs_close(&login_list);
Packit eace71
Packit eace71
	if (clear_list) {
Packit eace71
		list_for_each_entry_safe(curr_rec, tmp, rec_list, list) {
Packit eace71
			list_del(&curr_rec->list);
Packit eace71
			free(curr_rec);
Packit eace71
		}
Packit eace71
	}
Packit eace71
	return ret;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 * iscsi_login_portals - login into portals on @rec_list,
Packit eace71
 * @data: data to pass to login_fn
Packit eace71
 * @nr_found: returned with number of portals logged into
Packit eace71
 * @wait: bool indicating if the fn should wait for the result
Packit eace71
 * @rec_list: list of portals to log into.  This list is deleted after
Packit eace71
 *            iterating through it.
Packit eace71
 * @login_fn: list iter function
Packit eace71
 *
Packit eace71
 * This will loop over the list of portals and login. It
Packit eace71
 * will attempt to login asynchronously, and then wait for
Packit eace71
 * them to complete if wait is set.
Packit eace71
 */
Packit eace71
int iscsi_login_portals(void *data, int *nr_found, int wait,
Packit eace71
			struct list_head *rec_list,
Packit eace71
			int (*login_fn)(void *, struct list_head *,
Packit eace71
					 struct node_rec *))
Packit eace71
{
Packit eace71
	return __iscsi_login_portals(data, nr_found, wait, rec_list,
Packit eace71
				     1, login_fn);
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 * iscsi_login_portals_safe - login into portals on @rec_list, but do not
Packit eace71
 *			      clear out rec_list.
Packit eace71
 */
Packit eace71
int iscsi_login_portals_safe(void *data, int *nr_found, int wait,
Packit eace71
			struct list_head *rec_list,
Packit eace71
			int (*login_fn)(void *, struct list_head *,
Packit eace71
					 struct node_rec *))
Packit eace71
{
Packit eace71
	return __iscsi_login_portals(data, nr_found, wait, rec_list,
Packit eace71
				     0, login_fn);
Packit eace71
}
Packit eace71
Packit eace71
static void log_logout_msg(struct session_info *info, int rc)
Packit eace71
{
Packit eace71
	if (rc) {
Packit eace71
		log_error("Could not logout of [sid: %d, target: %s, "
Packit eace71
			  "portal: %s,%d].", info->sid,
Packit eace71
			  info->targetname,
Packit eace71
			  info->persistent_address, info->port);
Packit eace71
		iscsi_err_print_msg(rc);
Packit eace71
	} else
Packit eace71
		log_info("Logout of [sid: %d, target: %s, "
Packit eace71
			 "portal: %s,%d] successful.",
Packit eace71
			 info->sid, info->targetname,
Packit eace71
			 info->persistent_address, info->port);
Packit eace71
}
Packit eace71
Packit eace71
static int iscsid_logout_reqs_wait(struct list_head *list)
Packit eace71
{
Packit eace71
	struct iscsid_async_req *tmp, *curr;
Packit eace71
	struct session_info *info;
Packit eace71
	int ret = 0;
Packit eace71
Packit eace71
	list_for_each_entry_safe(curr, tmp, list, list) {
Packit eace71
		int err;
Packit eace71
Packit eace71
		info  = curr->data;
Packit eace71
		err = iscsid_req_wait(MGMT_IPC_SESSION_LOGOUT, curr->fd);
Packit eace71
		log_logout_msg(info, err);
Packit eace71
		if (err)
Packit eace71
			ret = err;
Packit eace71
		list_del(&curr->list);
Packit eace71
		free(curr);
Packit eace71
	}
Packit eace71
	return ret;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 * iscsi_logout_portal - logout of portal
Packit eace71
 * @info: session to log out of
Packit eace71
 * @list: if async, this is the list to add the logout req to
Packit eace71
 */
Packit eace71
int iscsi_logout_portal(struct session_info *info, struct list_head *list)
Packit eace71
{
Packit eace71
	struct iscsid_async_req *async_req = NULL;
Packit eace71
	int fd, rc;
Packit eace71
Packit eace71
	/* TODO: add fn to add session prefix info like dev_printk */
Packit eace71
	log_info("Logging out of session [sid: %d, target: %s, portal: "
Packit eace71
		 "%s,%d]",
Packit eace71
		 info->sid, info->targetname, info->persistent_address,
Packit eace71
		 info->port);
Packit eace71
Packit eace71
	if (list) {
Packit eace71
		async_req = calloc(1, sizeof(*async_req));
Packit eace71
		if (!async_req)
Packit eace71
			log_info("Could not allocate memory for async logout "
Packit eace71
				 "handling. Using sequential logout instead.");
Packit eace71
	}
Packit eace71
Packit eace71
	if (!async_req)
Packit eace71
		rc = iscsid_req_by_sid(MGMT_IPC_SESSION_LOGOUT, info->sid);
Packit eace71
	else {
Packit eace71
		INIT_LIST_HEAD(&async_req->list);
Packit eace71
		rc = iscsid_req_by_sid_async(MGMT_IPC_SESSION_LOGOUT,
Packit eace71
					     info->sid, &fd;;
Packit eace71
	}
Packit eace71
Packit eace71
	/* we raced with another app or instance of iscsiadm */
Packit eace71
	if (rc) {
Packit eace71
		log_logout_msg(info, rc);
Packit eace71
		if (async_req)
Packit eace71
			free(async_req);
Packit eace71
		return rc;
Packit eace71
	}
Packit eace71
Packit eace71
	if (async_req) {
Packit eace71
		list_add_tail(&async_req->list, list);
Packit eace71
		async_req->fd = fd;
Packit eace71
		async_req->data = info;
Packit eace71
	} else
Packit eace71
		log_logout_msg(info, rc);
Packit eace71
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
/**
Packit eace71
 * iscsi_logout_portals - logout portals
Packit eace71
 * @data: data to pass to iter logout_fn
Packit eace71
 * @nr_found: number of sessions logged out
Packit eace71
 * @wait: bool indicating if the fn should wait for the result
Packit eace71
 * @logout_fn: logout iter function
Packit eace71
 *
Packit eace71
 * This will loop over the list of sessions and run the logout fn
Packit eace71
 * on them. It will attempt to logout asynchronously, and then wait for
Packit eace71
 * them to complete if wait is set.
Packit eace71
 */
Packit eace71
int iscsi_logout_portals(void *data, int *nr_found, int wait,
Packit eace71
			 int (*logout_fn)(void *, struct list_head *,
Packit eace71
					  struct session_info *))
Packit eace71
{
Packit eace71
	struct session_info *curr_info;
Packit eace71
	struct session_link_info link_info;
Packit eace71
	struct list_head session_list, logout_list;
Packit eace71
	int ret = 0, err;
Packit eace71
Packit eace71
	INIT_LIST_HEAD(&session_list);
Packit eace71
	INIT_LIST_HEAD(&logout_list);
Packit eace71
Packit eace71
	memset(&link_info, 0, sizeof(link_info));
Packit eace71
	link_info.list = &session_list;
Packit eace71
	link_info.data = NULL;
Packit eace71
	link_info.match_fn = NULL;
Packit eace71
	*nr_found = 0;
Packit eace71
Packit eace71
	err = iscsi_sysfs_for_each_session(&link_info, nr_found,
Packit eace71
					   session_info_create_list, 0);
Packit eace71
	if (err && !list_empty(&session_list))
Packit eace71
		log_error("Could not read in all sessions: %s",
Packit eace71
			  iscsi_err_to_str(err));
Packit eace71
	else if (err && list_empty(&session_list)) {
Packit eace71
		log_error("Could not read session info.");
Packit eace71
		return err;
Packit eace71
	} else if (list_empty(&session_list))
Packit eace71
		return ISCSI_ERR_NO_OBJS_FOUND;
Packit eace71
	ret = err;
Packit eace71
	*nr_found = 0;
Packit eace71
Packit eace71
	list_for_each_entry(curr_info, &session_list, list) {
Packit eace71
		err = logout_fn(data, &logout_list, curr_info);
Packit eace71
		if (err > 0 && !ret)
Packit eace71
			ret = err;
Packit eace71
		if (!err)
Packit eace71
			(*nr_found)++;
Packit eace71
	}
Packit eace71
Packit eace71
	if (!*nr_found) {
Packit eace71
		ret = ISCSI_ERR_NO_OBJS_FOUND;
Packit eace71
		goto free_list;
Packit eace71
	}
Packit eace71
Packit eace71
	if (wait) {
Packit eace71
		err = iscsid_logout_reqs_wait(&logout_list);
Packit eace71
		if (err && !ret)
Packit eace71
			ret = err;
Packit eace71
	} else
Packit eace71
		iscsid_reqs_close(&logout_list);
Packit eace71
Packit eace71
	if (ret)
Packit eace71
		log_error("Could not logout of all requested sessions");
Packit eace71
Packit eace71
free_list:
Packit eace71
	session_info_free_list(&session_list);
Packit eace71
	return ret;
Packit eace71
}
Packit eace71
Packit eace71
/* TODO merge with initiator.c implementation */
Packit eace71
/* And add locking */
Packit eace71
int iscsi_check_for_running_session(struct node_rec *rec)
Packit eace71
{
Packit eace71
	int nr_found = 0;
Packit eace71
	if (iscsi_sysfs_for_each_session(rec, &nr_found, iscsi_match_session,
Packit eace71
					 0))
Packit eace71
		return 1;
Packit eace71
	return 0;
Packit eace71
}