Blame usr/mgmt_ipc.c

Packit eace71
/*
Packit eace71
 * iSCSI Administrator Utility Socket Interface
Packit eace71
 *
Packit eace71
 * Copyright (C) 2004 Dmitry Yusupov, Alex Aizman
Packit eace71
 * Copyright (C) 2006 Mike Christie
Packit eace71
 * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
Packit eace71
 * maintained by open-iscsi@googlegroups.com
Packit eace71
 *
Packit eace71
 * Originally based on:
Packit eace71
 * (C) 2004 FUJITA Tomonori <tomof@acm.org>
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
#include <stdlib.h>
Packit eace71
#include <errno.h>
Packit eace71
#include <unistd.h>
Packit eace71
#include <pwd.h>
Packit eace71
#include <sys/un.h>
Packit eace71
Packit eace71
#include "iscsid.h"
Packit eace71
#include "idbm.h"
Packit eace71
#include "mgmt_ipc.h"
Packit eace71
#include "event_poll.h"
Packit eace71
#include "log.h"
Packit eace71
#include "transport.h"
Packit eace71
#include "sysdeps.h"
Packit eace71
#include "iscsi_ipc.h"
Packit eace71
#include "iscsi_err.h"
Packit eace71
#include "iscsi_util.h"
Packit eace71
#include "iscsid_req.h"
Packit eace71
Packit eace71
#define PEERUSER_MAX	64
Packit eace71
#define EXTMSG_MAX	(64 * 1024)
Packit eace71
#define SD_SOCKET_FDS_START 3
Packit eace71
Packit eace71
int
Packit eace71
mgmt_ipc_listen(void)
Packit eace71
{
Packit eace71
	int fd, err, addr_len;
Packit eace71
	struct sockaddr_un addr;
Packit eace71
Packit eace71
	/* first check if we have fd handled by systemd */
Packit eace71
	fd = mgmt_ipc_systemd();
Packit eace71
	if (fd >= 0)
Packit eace71
		return fd;
Packit eace71
Packit eace71
	/* manually establish a socket */
Packit eace71
	fd = socket(AF_LOCAL, SOCK_STREAM, 0);
Packit eace71
	if (fd < 0) {
Packit eace71
		log_error("Can not create IPC socket");
Packit eace71
		return fd;
Packit eace71
	}
Packit eace71
Packit eace71
	addr_len = setup_abstract_addr(&addr, iscsid_namespace);
Packit eace71
Packit eace71
	if ((err = bind(fd, (struct sockaddr *) &addr, addr_len)) < 0 ) {
Packit eace71
		log_error("Can not bind IPC socket");
Packit eace71
		close(fd);
Packit eace71
		return err;
Packit eace71
	}
Packit eace71
Packit eace71
	if ((err = listen(fd, 32)) < 0) {
Packit eace71
		log_error("Can not listen IPC socket");
Packit eace71
		close(fd);
Packit eace71
		return err;
Packit eace71
	}
Packit eace71
Packit eace71
	return fd;
Packit eace71
}
Packit eace71
Packit eace71
int mgmt_ipc_systemd(void)
Packit eace71
{
Packit eace71
	const char *env;
Packit eace71
Packit eace71
	env = getenv("LISTEN_PID");
Packit eace71
Packit eace71
	if (!env || (strtoul(env, NULL, 10) != getpid()))
Packit eace71
		return -EINVAL;
Packit eace71
Packit eace71
	env = getenv("LISTEN_FDS");
Packit eace71
Packit eace71
	if (!env)
Packit eace71
		return -EINVAL;
Packit eace71
Packit eace71
	if (strtoul(env, NULL, 10) != 1) {
Packit eace71
		log_error("Did not receive exactly one IPC socket from systemd");
Packit eace71
		return -EINVAL;
Packit eace71
	}
Packit eace71
Packit eace71
	return SD_SOCKET_FDS_START;
Packit eace71
}
Packit eace71
Packit eace71
void
Packit eace71
mgmt_ipc_close(int fd)
Packit eace71
{
Packit eace71
	event_loop_exit(NULL);
Packit eace71
	if (fd >= 0)
Packit eace71
		close(fd);
Packit eace71
}
Packit eace71
Packit eace71
static int 
Packit eace71
mgmt_ipc_session_login(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	return session_login_task(&qtask->req.u.session.rec, qtask);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_session_getstats(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	int sid = qtask->req.u.session.sid;
Packit eace71
	iscsi_session_t *session;
Packit eace71
	int rc;
Packit eace71
Packit eace71
	if (!(session = session_find_by_sid(sid)))
Packit eace71
		return ISCSI_ERR_SESS_NOT_FOUND;
Packit eace71
Packit eace71
	rc = ipc->get_stats(session->t->handle,
Packit eace71
		session->id, session->conn[0].id,
Packit eace71
		(void *)&qtask->rsp.u.getstats,
Packit eace71
		MGMT_IPC_GETSTATS_BUF_MAX);
Packit eace71
	if (rc) {
Packit eace71
		log_error("get_stats(): IPC error %d "
Packit eace71
			"session [%02d]", rc, sid);
Packit eace71
		return ISCSI_ERR_INTERNAL;
Packit eace71
	}
Packit eace71
Packit eace71
	mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_send_targets(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	iscsiadm_req_t *req = &qtask->req;
Packit eace71
	int err;
Packit eace71
Packit eace71
	err = iscsi_host_send_targets(qtask, req->u.st.host_no,
Packit eace71
					  req->u.st.do_login,
Packit eace71
					  &req->u.st.ss);
Packit eace71
	mgmt_ipc_write_rsp(qtask, err);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_session_logout(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	return session_logout_task(qtask->req.u.session.sid, qtask);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_session_sync(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	struct ipc_msg_session *session= &qtask->req.u.session;
Packit eace71
Packit eace71
	return iscsi_sync_session(&session->rec, qtask, session->sid);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_cfg_initiatorname(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	if (dconfig->initiator_name)
Packit eace71
		strcpy(qtask->rsp.u.config.var, dconfig->initiator_name);
Packit eace71
	mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_session_info(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	int sid = qtask->req.u.session.sid;
Packit eace71
	iscsi_session_t *session;
Packit eace71
	struct ipc_msg_session_state *info;
Packit eace71
Packit eace71
	if (!(session = session_find_by_sid(sid))) {
Packit eace71
		log_debug(1, "session with sid %d not found!", sid);
Packit eace71
		return ISCSI_ERR_SESS_NOT_FOUND;
Packit eace71
	}
Packit eace71
Packit eace71
	info = &qtask->rsp.u.session_state;
Packit eace71
	info->conn_state = session->conn[0].state;
Packit eace71
	info->session_state = session->r_stage;
Packit eace71
Packit eace71
	mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_cfg_initiatoralias(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	strcpy(qtask->rsp.u.config.var, dconfig->initiator_alias);
Packit eace71
	mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_cfg_filename(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	strcpy(qtask->rsp.u.config.var, dconfig->config_file);
Packit eace71
	mgmt_ipc_write_rsp(qtask, ISCSI_SUCCESS);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_conn_add(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	return ISCSI_ERR;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_immediate_stop(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	event_loop_exit(qtask);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_conn_remove(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	return ISCSI_ERR;
Packit eace71
}
Packit eace71
Packit eace71
/*
Packit eace71
 * Parse a list of strings, encoded as a 32bit
Packit eace71
 * length followed by the string itself (not necessarily
Packit eace71
 * NUL-terminated).
Packit eace71
 */
Packit eace71
static int
Packit eace71
mgmt_ipc_parse_strings(queue_task_t *qtask, char ***result)
Packit eace71
{
Packit eace71
	char		*data, *endp, **argv = NULL;
Packit eace71
	unsigned int	left, argc;
Packit eace71
Packit eace71
again:
Packit eace71
	data = qtask->payload;
Packit eace71
	left = qtask->req.payload_len;
Packit eace71
	endp = NULL;
Packit eace71
	argc = 0;
Packit eace71
Packit eace71
	while (left) {
Packit eace71
		uint32_t len;
Packit eace71
Packit eace71
		if (left < 4)
Packit eace71
			return -1;
Packit eace71
		memcpy(&len, data, 4);
Packit eace71
		data += 4;
Packit eace71
Packit eace71
		if (endp)
Packit eace71
			*endp = '\0';
Packit eace71
Packit eace71
		if (len > left)
Packit eace71
			return -1;
Packit eace71
Packit eace71
		if (argv) {
Packit eace71
			argv[argc] = (char *) data;
Packit eace71
			endp = data + len;
Packit eace71
		}
Packit eace71
		data += len;
Packit eace71
		argc++;
Packit eace71
	}
Packit eace71
Packit eace71
	if (endp)
Packit eace71
		*endp = '\0';
Packit eace71
Packit eace71
	if (argv == NULL) {
Packit eace71
		argv = malloc((argc + 1) * sizeof(char *));
Packit eace71
		*result = argv;
Packit eace71
		goto again;
Packit eace71
	}
Packit eace71
Packit eace71
	argv[argc] = NULL;
Packit eace71
	return argc;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_notify_common(queue_task_t *qtask, int (*handler)(int, char **))
Packit eace71
{
Packit eace71
	char	**argv = NULL;
Packit eace71
	int	argc, err = ISCSI_ERR;
Packit eace71
Packit eace71
	argc = mgmt_ipc_parse_strings(qtask, &argv);
Packit eace71
	if (argc > 0)
Packit eace71
		err = handler(argc, argv);
Packit eace71
Packit eace71
	if (argv)
Packit eace71
		free(argv);
Packit eace71
	mgmt_ipc_write_rsp(qtask, err);
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
/* Replace these dummies as you implement them
Packit eace71
   elsewhere */
Packit eace71
static int
Packit eace71
iscsi_discovery_add_node(int argc, char **argv)
Packit eace71
{
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
iscsi_discovery_del_node(int argc, char **argv)
Packit eace71
{
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
iscsi_discovery_add_portal(int argc, char **argv)
Packit eace71
{
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
iscsi_discovery_del_portal(int argc, char **argv)
Packit eace71
{
Packit eace71
	return ISCSI_SUCCESS;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_notify_add_node(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	return mgmt_ipc_notify_common(qtask, iscsi_discovery_add_node);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_notify_del_node(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	return mgmt_ipc_notify_common(qtask, iscsi_discovery_del_node);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_notify_add_portal(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	return mgmt_ipc_notify_common(qtask, iscsi_discovery_add_portal);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_notify_del_portal(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	return mgmt_ipc_notify_common(qtask, iscsi_discovery_del_portal);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_peeruser(int sock, char *user)
Packit eace71
{
Packit eace71
	/* Linux style: use getsockopt(SO_PEERCRED) */
Packit eace71
	struct ucred peercred;
Packit eace71
	socklen_t so_len = sizeof(peercred);
Packit eace71
	struct passwd *pass;
Packit eace71
Packit eace71
	errno = 0;
Packit eace71
	if (getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &peercred,
Packit eace71
		&so_len) != 0 || so_len != sizeof(peercred)) {
Packit eace71
		/* We didn't get a valid credentials struct. */
Packit eace71
		log_error("peeruser_unux: error receiving credentials: %m");
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	pass = getpwuid(peercred.uid);
Packit eace71
	if (pass == NULL) {
Packit eace71
		log_error("peeruser_unix: unknown local user with uid %d",
Packit eace71
				(int) peercred.uid);
Packit eace71
		return 0;
Packit eace71
	}
Packit eace71
Packit eace71
	strlcpy(user, pass->pw_name, PEERUSER_MAX);
Packit eace71
	return 1;
Packit eace71
}
Packit eace71
Packit eace71
static void
Packit eace71
mgmt_ipc_destroy_queue_task(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	if (qtask->mgmt_ipc_fd >= 0)
Packit eace71
		close(qtask->mgmt_ipc_fd);
Packit eace71
	if (qtask->payload)
Packit eace71
		free(qtask->payload);
Packit eace71
	if (qtask->allocated)
Packit eace71
		free(qtask);
Packit eace71
}
Packit eace71
Packit eace71
/*
Packit eace71
 * Send the IPC response and destroy the queue_task.
Packit eace71
 * The recovery code uses a qtask which is allocated as
Packit eace71
 * part of a larger structure, and we don't want it to
Packit eace71
 * get freed when we come here. This is what qtask->allocated
Packit eace71
 * is for.
Packit eace71
 */
Packit eace71
void
Packit eace71
mgmt_ipc_write_rsp(queue_task_t *qtask, int err)
Packit eace71
{
Packit eace71
	if (!qtask)
Packit eace71
		return;
Packit eace71
	log_debug(4, "%s: rsp to fd %d", __FUNCTION__,
Packit eace71
		 qtask->mgmt_ipc_fd);
Packit eace71
Packit eace71
	if (qtask->mgmt_ipc_fd < 0) {
Packit eace71
		mgmt_ipc_destroy_queue_task(qtask);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	qtask->rsp.err = err;
Packit eace71
	if (write(qtask->mgmt_ipc_fd, &qtask->rsp, sizeof(qtask->rsp)) < 0)
Packit eace71
		log_error("IPC qtask write failed: %s", strerror(errno));
Packit eace71
	mgmt_ipc_destroy_queue_task(qtask);
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_read_data(int fd, void *ptr, size_t len)
Packit eace71
{
Packit eace71
	int	n;
Packit eace71
Packit eace71
	while (len) {
Packit eace71
		n = read(fd, ptr, len);
Packit eace71
		if (n < 0) {
Packit eace71
			if (errno == EINTR)
Packit eace71
				continue;
Packit eace71
			return -EIO;
Packit eace71
		}
Packit eace71
		if (n == 0) {
Packit eace71
			/* Client closed connection */
Packit eace71
			return -EIO;
Packit eace71
		}
Packit eace71
		ptr += n;
Packit eace71
		len -= n;
Packit eace71
	}
Packit eace71
	return 0;
Packit eace71
}
Packit eace71
Packit eace71
static int
Packit eace71
mgmt_ipc_read_req(queue_task_t *qtask)
Packit eace71
{
Packit eace71
	iscsiadm_req_t *req = &qtask->req;
Packit eace71
	int	rc;
Packit eace71
Packit eace71
	rc = mgmt_ipc_read_data(qtask->mgmt_ipc_fd, req, sizeof(*req));
Packit eace71
	if (rc >= 0 && req->payload_len > 0) {
Packit eace71
		/* Limit what we accept */
Packit eace71
		if (req->payload_len > EXTMSG_MAX)
Packit eace71
			return -EIO;
Packit eace71
Packit eace71
		/* Remember the allocated pointer in the
Packit eace71
		 * qtask - it will be freed by write_rsp.
Packit eace71
		 * Note: we allocate one byte in excess
Packit eace71
		 * so we can append a NUL byte. */
Packit eace71
		qtask->payload = malloc(req->payload_len + 1);
Packit eace71
		rc = mgmt_ipc_read_data(qtask->mgmt_ipc_fd,
Packit eace71
				qtask->payload,
Packit eace71
				req->payload_len);
Packit eace71
	}
Packit eace71
	return rc;
Packit eace71
}
Packit eace71
Packit eace71
static mgmt_ipc_fn_t *	mgmt_ipc_functions[__MGMT_IPC_MAX_COMMAND] = {
Packit eace71
[MGMT_IPC_SESSION_LOGIN]	= mgmt_ipc_session_login,
Packit eace71
[MGMT_IPC_SESSION_LOGOUT]	= mgmt_ipc_session_logout,
Packit eace71
[MGMT_IPC_SESSION_SYNC]		= mgmt_ipc_session_sync,
Packit eace71
[MGMT_IPC_SESSION_STATS]	= mgmt_ipc_session_getstats,
Packit eace71
[MGMT_IPC_SEND_TARGETS]		= mgmt_ipc_send_targets,
Packit eace71
[MGMT_IPC_SESSION_INFO]		= mgmt_ipc_session_info,
Packit eace71
[MGMT_IPC_CONN_ADD]		= mgmt_ipc_conn_add,
Packit eace71
[MGMT_IPC_CONN_REMOVE]		= mgmt_ipc_conn_remove,
Packit eace71
[MGMT_IPC_CONFIG_INAME]		= mgmt_ipc_cfg_initiatorname,
Packit eace71
[MGMT_IPC_CONFIG_IALIAS]	= mgmt_ipc_cfg_initiatoralias,
Packit eace71
[MGMT_IPC_CONFIG_FILE]		= mgmt_ipc_cfg_filename,
Packit eace71
[MGMT_IPC_IMMEDIATE_STOP]	= mgmt_ipc_immediate_stop,
Packit eace71
[MGMT_IPC_NOTIFY_ADD_NODE]	= mgmt_ipc_notify_add_node,
Packit eace71
[MGMT_IPC_NOTIFY_DEL_NODE]	= mgmt_ipc_notify_del_node,
Packit eace71
[MGMT_IPC_NOTIFY_ADD_PORTAL]	= mgmt_ipc_notify_add_portal,
Packit eace71
[MGMT_IPC_NOTIFY_DEL_PORTAL]	= mgmt_ipc_notify_del_portal,
Packit eace71
};
Packit eace71
Packit eace71
void mgmt_ipc_handle(int accept_fd)
Packit eace71
{
Packit eace71
	unsigned int command;
Packit eace71
	int fd, err;
Packit eace71
	queue_task_t *qtask = NULL;
Packit eace71
	mgmt_ipc_fn_t *handler = NULL;
Packit eace71
	char user[PEERUSER_MAX];
Packit eace71
Packit eace71
	qtask = calloc(1, sizeof(queue_task_t));
Packit eace71
	if (!qtask)
Packit eace71
		return;
Packit eace71
Packit eace71
	if ((fd = accept(accept_fd, NULL, NULL)) < 0) {
Packit eace71
		free(qtask);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	qtask->allocated = 1;
Packit eace71
	qtask->mgmt_ipc_fd = fd;
Packit eace71
Packit eace71
	if (!mgmt_peeruser(fd, user) || strncmp(user, "root", PEERUSER_MAX)) {
Packit eace71
		err = ISCSI_ERR_ACCESS;
Packit eace71
		goto err;
Packit eace71
	}
Packit eace71
Packit eace71
	if (mgmt_ipc_read_req(qtask) < 0) {
Packit eace71
		mgmt_ipc_destroy_queue_task(qtask);
Packit eace71
		return;
Packit eace71
	}
Packit eace71
Packit eace71
	command = qtask->req.command;
Packit eace71
	qtask->rsp.command = command;
Packit eace71
Packit eace71
	if (0 <= command && command < __MGMT_IPC_MAX_COMMAND)
Packit eace71
		handler = mgmt_ipc_functions[command];
Packit eace71
	if (handler != NULL) {
Packit eace71
		/* If the handler returns OK, this means it
Packit eace71
		 * already sent the reply. */
Packit eace71
		err = handler(qtask);
Packit eace71
		if (err == ISCSI_SUCCESS)
Packit eace71
			return;
Packit eace71
	} else {
Packit eace71
		log_error("unknown request: %s(%d) %u",
Packit eace71
			  __FUNCTION__, __LINE__, command);
Packit eace71
		err = ISCSI_ERR_INVALID_MGMT_REQ;
Packit eace71
	}
Packit eace71
Packit eace71
err:
Packit eace71
	/* This will send the response, close the
Packit eace71
	 * connection and free the qtask */
Packit eace71
	mgmt_ipc_write_rsp(qtask, err);
Packit eace71
}