xref: /illumos-gate/usr/src/cmd/ldmad/ldmad.c (revision b63861bb)
149bfb42bSAlexandre Chartre /*
249bfb42bSAlexandre Chartre  * CDDL HEADER START
349bfb42bSAlexandre Chartre  *
449bfb42bSAlexandre Chartre  * The contents of this file are subject to the terms of the
549bfb42bSAlexandre Chartre  * Common Development and Distribution License (the "License").
649bfb42bSAlexandre Chartre  * You may not use this file except in compliance with the License.
749bfb42bSAlexandre Chartre  *
849bfb42bSAlexandre Chartre  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
949bfb42bSAlexandre Chartre  * or http://www.opensolaris.org/os/licensing.
1049bfb42bSAlexandre Chartre  * See the License for the specific language governing permissions
1149bfb42bSAlexandre Chartre  * and limitations under the License.
1249bfb42bSAlexandre Chartre  *
1349bfb42bSAlexandre Chartre  * When distributing Covered Code, include this CDDL HEADER in each
1449bfb42bSAlexandre Chartre  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1549bfb42bSAlexandre Chartre  * If applicable, add the following below this CDDL HEADER, with the
1649bfb42bSAlexandre Chartre  * fields enclosed by brackets "[]" replaced with your own identifying
1749bfb42bSAlexandre Chartre  * information: Portions Copyright [yyyy] [name of copyright owner]
1849bfb42bSAlexandre Chartre  *
1949bfb42bSAlexandre Chartre  * CDDL HEADER END
2049bfb42bSAlexandre Chartre  */
2149bfb42bSAlexandre Chartre 
2249bfb42bSAlexandre Chartre /*
23*b63861bbSAlexandre Chartre  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2449bfb42bSAlexandre Chartre  */
2549bfb42bSAlexandre Chartre 
2649bfb42bSAlexandre Chartre /*
2749bfb42bSAlexandre Chartre  * Logical Domains (LDoms) Agents Daemon
2849bfb42bSAlexandre Chartre  *
2949bfb42bSAlexandre Chartre  * The LDoms agents daemon (ldmad) runs on LDoms domains and provides
3049bfb42bSAlexandre Chartre  * information to the control domain. It is composed of a set of agents
3149bfb42bSAlexandre Chartre  * which can send and receive messages to and from the control domain.
3249bfb42bSAlexandre Chartre  * Each agent is registered as a domain service using the libds library,
3349bfb42bSAlexandre Chartre  * and is able to handle requests coming from the control domain.
3449bfb42bSAlexandre Chartre  *
3549bfb42bSAlexandre Chartre  * The control domain sends requests to an agent as messages on the
3649bfb42bSAlexandre Chartre  * corresponding domain service (identified by the agent name). All requests
3749bfb42bSAlexandre Chartre  * are received by the ldmad daemon which dispatches them to the appropriate
3849bfb42bSAlexandre Chartre  * handler function of the agent depending on the type of the message.
3949bfb42bSAlexandre Chartre  *
4049bfb42bSAlexandre Chartre  * After the request has been processed by the handler, the ldmad daemon sent
4149bfb42bSAlexandre Chartre  * a reply message back to the control domain. The reply is either a result
4249bfb42bSAlexandre Chartre  * message if the request was successfully completed, or an error message
4349bfb42bSAlexandre Chartre  * describing the failure.
4449bfb42bSAlexandre Chartre  */
4549bfb42bSAlexandre Chartre 
4649bfb42bSAlexandre Chartre #include <dirent.h>
4749bfb42bSAlexandre Chartre #include <errno.h>
4849bfb42bSAlexandre Chartre #include <fcntl.h>
4949bfb42bSAlexandre Chartre #include <libds.h>
5049bfb42bSAlexandre Chartre #include <libgen.h>
5149bfb42bSAlexandre Chartre #include <signal.h>
5249bfb42bSAlexandre Chartre #include <stdio.h>
5349bfb42bSAlexandre Chartre #include <stdlib.h>
5449bfb42bSAlexandre Chartre #include <strings.h>
5582629e30SMike Christensen #include <synch.h>
5649bfb42bSAlexandre Chartre #include <syslog.h>
5782629e30SMike Christensen #include <thread.h>
5849bfb42bSAlexandre Chartre #include <unistd.h>
5982629e30SMike Christensen #include <sys/debug.h>
6082629e30SMike Christensen #include <sys/ldoms.h>
6149bfb42bSAlexandre Chartre #include <sys/types.h>
6249bfb42bSAlexandre Chartre #include <sys/stat.h>
6349bfb42bSAlexandre Chartre #include <sys/wait.h>
6449bfb42bSAlexandre Chartre 
6549bfb42bSAlexandre Chartre #include "ldma.h"
6649bfb42bSAlexandre Chartre 
6749bfb42bSAlexandre Chartre #define	LDMA_MODULE	"ldm-agent-daemon"
6849bfb42bSAlexandre Chartre 
6949bfb42bSAlexandre Chartre #define	LDMA_CONTROL_DOMAIN_DHDL	0	/* id of the control domain */
7082629e30SMike Christensen 
7182629e30SMike Christensen typedef struct ldma_connexion_t {
7282629e30SMike Christensen 	ds_hdl_t		hdl;		/* connexion handle */
7382629e30SMike Christensen 	ds_domain_hdl_t		dhdl;		/* connexion domain handle */
7482629e30SMike Christensen 	ds_ver_t		ver;		/* connexion version */
7582629e30SMike Christensen } ldma_connexion_t;
7649bfb42bSAlexandre Chartre 
7749bfb42bSAlexandre Chartre typedef struct ldma_agent {
7849bfb42bSAlexandre Chartre 	ldma_agent_info_t	*info;		/* agent information */
7982629e30SMike Christensen 	mutex_t			conn_lock;	/* connexion table lock */
8082629e30SMike Christensen 	ldma_connexion_t	conn[LDOMS_MAX_DOMAINS]; /* connexions */
8149bfb42bSAlexandre Chartre } ldma_agent_t;
8249bfb42bSAlexandre Chartre 
8349bfb42bSAlexandre Chartre /* information about existing agents */
8449bfb42bSAlexandre Chartre extern ldma_agent_info_t ldma_device_info;
8549bfb42bSAlexandre Chartre extern ldma_agent_info_t ldma_system_info;
86fc256490SJason Beloro extern ldma_agent_info_t ldma_dio_info;
8749bfb42bSAlexandre Chartre 
8849bfb42bSAlexandre Chartre boolean_t ldma_debug = B_FALSE;
8949bfb42bSAlexandre Chartre boolean_t ldma_daemon = B_FALSE;
9049bfb42bSAlexandre Chartre 
9149bfb42bSAlexandre Chartre static ldma_agent_info_t *ldma_agent_infos[] = {
9249bfb42bSAlexandre Chartre 	&ldma_device_info,
9349bfb42bSAlexandre Chartre 	&ldma_system_info,
94fc256490SJason Beloro 	&ldma_dio_info,
9549bfb42bSAlexandre Chartre 	NULL
9649bfb42bSAlexandre Chartre };
9749bfb42bSAlexandre Chartre 
9849bfb42bSAlexandre Chartre static char *cmdname;
996b8303caSAlexandre Chartre static pid_t daemon_pid = 0;
10049bfb42bSAlexandre Chartre 
10182629e30SMike Christensen /*
10282629e30SMike Christensen  * Lookup connexion in agent connexion table.
10382629e30SMike Christensen  */
10482629e30SMike Christensen static ldma_connexion_t *
ldma_connexion_lookup(ldma_agent_t * agent,ds_hdl_t hdl)10582629e30SMike Christensen ldma_connexion_lookup(ldma_agent_t *agent, ds_hdl_t hdl)
10682629e30SMike Christensen {
10782629e30SMike Christensen 	ldma_connexion_t *connp;
10882629e30SMike Christensen 	int i;
10982629e30SMike Christensen 
11082629e30SMike Christensen 	ASSERT(MUTEX_HELD(&agent->conn_lock));
11182629e30SMike Christensen 	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
11282629e30SMike Christensen 		if (connp->hdl == hdl)
11382629e30SMike Christensen 			return (connp);
11482629e30SMike Christensen 	}
11582629e30SMike Christensen 	return (NULL);
11682629e30SMike Christensen }
11782629e30SMike Christensen 
11882629e30SMike Christensen /*
11982629e30SMike Christensen  * Add connextion to agent connexion table.
12082629e30SMike Christensen  */
12182629e30SMike Christensen static int
ldma_connexion_add(ldma_agent_t * agent,ds_hdl_t hdl,ds_domain_hdl_t dhdl,ds_ver_t * verp)12282629e30SMike Christensen ldma_connexion_add(ldma_agent_t *agent, ds_hdl_t hdl, ds_domain_hdl_t dhdl,
12382629e30SMike Christensen     ds_ver_t *verp)
12482629e30SMike Christensen {
12582629e30SMike Christensen 	ldma_connexion_t *connp;
12682629e30SMike Christensen 	ldma_connexion_t *availp = NULL;
12782629e30SMike Christensen 	int i;
12882629e30SMike Christensen 
12982629e30SMike Christensen 	(void) mutex_lock(&agent->conn_lock);
13082629e30SMike Christensen 	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
13182629e30SMike Christensen 		if (connp->hdl == hdl)
13282629e30SMike Christensen 			break;
13382629e30SMike Christensen 		if (availp == NULL && connp->hdl == DS_INVALID_HDL)
13482629e30SMike Christensen 			availp = connp;
13582629e30SMike Christensen 	}
13682629e30SMike Christensen 
13782629e30SMike Christensen 	if (i < LDOMS_MAX_DOMAINS) {
13882629e30SMike Christensen 		(void) mutex_unlock(&agent->conn_lock);
13982629e30SMike Christensen 		LDMA_INFO("agent %s hdl %llx already exists", agent->info->name,
14082629e30SMike Christensen 		    hdl);
14182629e30SMike Christensen 		return (0);
14282629e30SMike Christensen 	}
14382629e30SMike Christensen 
14482629e30SMike Christensen 	if (!availp) {
14582629e30SMike Christensen 		(void) mutex_unlock(&agent->conn_lock);
14682629e30SMike Christensen 		LDMA_INFO("agent %s too many connections", agent->info->name);
14782629e30SMike Christensen 		return (0);
14882629e30SMike Christensen 	}
14982629e30SMike Christensen 
15082629e30SMike Christensen 	LDMA_DBG("agent %s adding connection (%x) %llx, %llx, %d.%d",
15182629e30SMike Christensen 	    agent->info->name, availp, hdl, dhdl, verp->major, verp->minor);
15282629e30SMike Christensen 
15382629e30SMike Christensen 	availp->hdl = hdl;
15482629e30SMike Christensen 	availp->dhdl = dhdl;
15582629e30SMike Christensen 	availp->ver = *verp;
15682629e30SMike Christensen 	(void) mutex_unlock(&agent->conn_lock);
15782629e30SMike Christensen 	return (1);
15882629e30SMike Christensen }
15982629e30SMike Christensen 
16082629e30SMike Christensen /*
16182629e30SMike Christensen  * Delete connexion from agent connexion table.
16282629e30SMike Christensen  */
16382629e30SMike Christensen static int
ldma_connexion_delete(ldma_agent_t * agent,ds_hdl_t hdl)16482629e30SMike Christensen ldma_connexion_delete(ldma_agent_t *agent, ds_hdl_t hdl)
16582629e30SMike Christensen {
16682629e30SMike Christensen 	ldma_connexion_t *connp;
16782629e30SMike Christensen 
16882629e30SMike Christensen 	(void) mutex_lock(&agent->conn_lock);
16982629e30SMike Christensen 	if ((connp = ldma_connexion_lookup(agent, hdl)) == NULL) {
17082629e30SMike Christensen 		(void) mutex_unlock(&agent->conn_lock);
17182629e30SMike Christensen 		LDMA_INFO("agent %s connection delete failed to find %llx",
17282629e30SMike Christensen 		    agent->info->name, hdl);
17382629e30SMike Christensen 		return (0);
17482629e30SMike Christensen 	}
17582629e30SMike Christensen 
17682629e30SMike Christensen 	LDMA_DBG("agent %s deleting connection (%x) %llx", agent->info->name,
17782629e30SMike Christensen 	    connp, hdl);
17882629e30SMike Christensen 
17982629e30SMike Christensen 	connp->hdl = DS_INVALID_HDL;
18082629e30SMike Christensen 	connp->dhdl = 0;
18182629e30SMike Christensen 	connp->ver.major = 0;
18282629e30SMike Christensen 	connp->ver.minor = 0;
18382629e30SMike Christensen 	(void) mutex_unlock(&agent->conn_lock);
18482629e30SMike Christensen 	return (1);
18582629e30SMike Christensen }
18682629e30SMike Christensen 
18782629e30SMike Christensen /*
18882629e30SMike Christensen  * Initialize connexion table.
18982629e30SMike Christensen  */
19082629e30SMike Christensen static void
ldma_connexion_init(ldma_agent_t * agent)19182629e30SMike Christensen ldma_connexion_init(ldma_agent_t *agent)
19282629e30SMike Christensen {
19382629e30SMike Christensen 	ldma_connexion_t *connp;
19482629e30SMike Christensen 	int i;
19582629e30SMike Christensen 
19682629e30SMike Christensen 	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
19782629e30SMike Christensen 		connp->hdl = DS_INVALID_HDL;
19882629e30SMike Christensen 	}
19982629e30SMike Christensen }
20082629e30SMike Christensen 
20149bfb42bSAlexandre Chartre /*
20249bfb42bSAlexandre Chartre  * Allocate a new message with the specified message number (msg_num),
20349bfb42bSAlexandre Chartre  * message type (msg_type) and message data length (msg_dlen). Return
20449bfb42bSAlexandre Chartre  * NULL if the allocation has failed.
20549bfb42bSAlexandre Chartre  */
20649bfb42bSAlexandre Chartre static ldma_message_header_t *
ldma_alloc_msg(uint64_t msg_num,uint32_t msg_type,size_t msg_dlen)20749bfb42bSAlexandre Chartre ldma_alloc_msg(uint64_t msg_num, uint32_t msg_type, size_t msg_dlen)
20849bfb42bSAlexandre Chartre {
20949bfb42bSAlexandre Chartre 	ldma_message_header_t *msg;
21049bfb42bSAlexandre Chartre 	size_t msg_len;
21149bfb42bSAlexandre Chartre 
21249bfb42bSAlexandre Chartre 	msg_len = LDMA_MESSAGE_SIZE(msg_dlen);
21349bfb42bSAlexandre Chartre 	msg = malloc(msg_len);
21449bfb42bSAlexandre Chartre 	if (msg == NULL)
21549bfb42bSAlexandre Chartre 		return (NULL);
21649bfb42bSAlexandre Chartre 
21749bfb42bSAlexandre Chartre 	msg->msg_num = msg_num;
21849bfb42bSAlexandre Chartre 	msg->msg_type = msg_type;
21949bfb42bSAlexandre Chartre 	msg->msg_info = 0;
22049bfb42bSAlexandre Chartre 
22149bfb42bSAlexandre Chartre 	return (msg);
22249bfb42bSAlexandre Chartre }
22349bfb42bSAlexandre Chartre 
22449bfb42bSAlexandre Chartre /*
22549bfb42bSAlexandre Chartre  * Allocate a result message (LDMA_MSG_REQ_RESULT) with the specified message
22649bfb42bSAlexandre Chartre  * data length (msg_dlen). If the request argument is not NULL then the message
22749bfb42bSAlexandre Chartre  * is created with the same message number as the request, otherwise the message
22849bfb42bSAlexandre Chartre  * number is set to 0. Return NULL if the allocation has failed.
22949bfb42bSAlexandre Chartre  */
23049bfb42bSAlexandre Chartre ldma_message_header_t *
ldma_alloc_result_msg(ldma_message_header_t * request,size_t msg_dlen)23149bfb42bSAlexandre Chartre ldma_alloc_result_msg(ldma_message_header_t *request, size_t msg_dlen)
23249bfb42bSAlexandre Chartre {
23349bfb42bSAlexandre Chartre 	uint64_t msg_num;
23449bfb42bSAlexandre Chartre 
23549bfb42bSAlexandre Chartre 	msg_num = (request == NULL)? 0 : request->msg_num;
23649bfb42bSAlexandre Chartre 
23749bfb42bSAlexandre Chartre 	return (ldma_alloc_msg(msg_num, LDMA_MSG_RESULT, msg_dlen));
23849bfb42bSAlexandre Chartre }
23949bfb42bSAlexandre Chartre 
24049bfb42bSAlexandre Chartre /*
24149bfb42bSAlexandre Chartre  * Agent register callback. This callback is invoked when a client is registered
24249bfb42bSAlexandre Chartre  * for using the service provided by an agent. An agent will only have one
24349bfb42bSAlexandre Chartre  * consumer which is coming from the control domain.
24449bfb42bSAlexandre Chartre  */
24549bfb42bSAlexandre Chartre static void
ldma_reg_cb(ds_hdl_t hdl,ds_cb_arg_t arg,ds_ver_t * ver,ds_domain_hdl_t dhdl)24649bfb42bSAlexandre Chartre ldma_reg_cb(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver,
24749bfb42bSAlexandre Chartre     ds_domain_hdl_t dhdl)
24849bfb42bSAlexandre Chartre {
24949bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
25082629e30SMike Christensen 	char dname[LDOMS_MAX_NAME_LEN];
25149bfb42bSAlexandre Chartre 
25282629e30SMike Christensen 	if (ds_dom_hdl_to_name(dhdl, dname, LDOMS_MAX_NAME_LEN) != 0) {
25349bfb42bSAlexandre Chartre 		(void) strcpy(dname, "<unknown>");
25449bfb42bSAlexandre Chartre 	}
25549bfb42bSAlexandre Chartre 
25649bfb42bSAlexandre Chartre 	LDMA_DBG("%s: REGISTER hdl=%llx, dhdl=%llx (%s) ver=%hd.%hd",
25749bfb42bSAlexandre Chartre 	    agent->info->name, hdl, dhdl, dname, ver->major, ver->minor);
25849bfb42bSAlexandre Chartre 
25949bfb42bSAlexandre Chartre 	/*
26082629e30SMike Christensen 	 * Record client information.  Access control is done on a
26182629e30SMike Christensen 	 * message-by-message basis upon receipt of the message.
26249bfb42bSAlexandre Chartre 	 */
26382629e30SMike Christensen 	if (!ldma_connexion_add(agent, hdl, dhdl, ver)) {
26482629e30SMike Christensen 		LDMA_INFO("agent %s failed to add connection from "
26549bfb42bSAlexandre Chartre 		    "domain %s", agent->info->name, dname);
26649bfb42bSAlexandre Chartre 	}
26749bfb42bSAlexandre Chartre }
26849bfb42bSAlexandre Chartre 
26949bfb42bSAlexandre Chartre /*
27049bfb42bSAlexandre Chartre  * Agent unregister callback. This callback is invoked when a client is
27149bfb42bSAlexandre Chartre  * unregistered and stops using the service provided by an agent.
27249bfb42bSAlexandre Chartre  */
27349bfb42bSAlexandre Chartre static void
ldma_unreg_cb(ds_hdl_t hdl,ds_cb_arg_t arg)27449bfb42bSAlexandre Chartre ldma_unreg_cb(ds_hdl_t hdl, ds_cb_arg_t arg)
27549bfb42bSAlexandre Chartre {
27649bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
27749bfb42bSAlexandre Chartre 
27849bfb42bSAlexandre Chartre 	LDMA_DBG("%s: UNREGISTER hdl=%llx", agent->info->name, hdl);
27949bfb42bSAlexandre Chartre 
28082629e30SMike Christensen 	if (!ldma_connexion_delete(agent, hdl)) {
28182629e30SMike Christensen 		LDMA_INFO("agent %s failed to unregister handle %llx",
28282629e30SMike Christensen 		    agent->info->name, hdl);
28349bfb42bSAlexandre Chartre 	}
28449bfb42bSAlexandre Chartre }
28549bfb42bSAlexandre Chartre 
28649bfb42bSAlexandre Chartre /*
28749bfb42bSAlexandre Chartre  * Agent data callback. This callback is invoked when an agent receives a new
28849bfb42bSAlexandre Chartre  * message from a client. Any request from a client which is not the control
28949bfb42bSAlexandre Chartre  * domain is immediatly rejected. Otherwise the message is forwarded to the
29049bfb42bSAlexandre Chartre  * appropriate handler function provided by the agent, depending on the message
29149bfb42bSAlexandre Chartre  * type.
29249bfb42bSAlexandre Chartre  */
29349bfb42bSAlexandre Chartre static void
ldma_data_cb(ds_hdl_t hdl,ds_cb_arg_t arg,void * buf,size_t len)29449bfb42bSAlexandre Chartre ldma_data_cb(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf, size_t len)
29549bfb42bSAlexandre Chartre {
29649bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
29749bfb42bSAlexandre Chartre 	ldma_msg_handler_t *handler;
29849bfb42bSAlexandre Chartre 	ldma_message_header_t *request = buf;
29949bfb42bSAlexandre Chartre 	ldma_message_header_t *reply = NULL;
30082629e30SMike Christensen 	ldma_connexion_t *connp;
30182629e30SMike Christensen 	ds_ver_t conn_ver;
30282629e30SMike Christensen 	ds_domain_hdl_t conn_dhdl;
30349bfb42bSAlexandre Chartre 	ldma_request_status_t status;
30449bfb42bSAlexandre Chartre 	size_t request_dlen, reply_len, reply_dlen = 0;
30549bfb42bSAlexandre Chartre 	int i;
30649bfb42bSAlexandre Chartre 
30749bfb42bSAlexandre Chartre 	/* check the message size */
30849bfb42bSAlexandre Chartre 	if (len < LDMA_MESSAGE_HEADER_SIZE) {
30949bfb42bSAlexandre Chartre 		LDMA_INFO("agent %s has ignored message with an invalid "
31049bfb42bSAlexandre Chartre 		    "size of %d bytes", agent->info->name, len);
31149bfb42bSAlexandre Chartre 		return;
31249bfb42bSAlexandre Chartre 	}
31349bfb42bSAlexandre Chartre 
31449bfb42bSAlexandre Chartre 	request_dlen = LDMA_MESSAGE_DLEN(len);
31549bfb42bSAlexandre Chartre 
31649bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, request num=%llu type=0x%x info=0x%x "
31749bfb42bSAlexandre Chartre 	    "dlen=%d", agent->info->name, hdl, request->msg_num,
31849bfb42bSAlexandre Chartre 	    request->msg_type, request->msg_info, request_dlen);
31949bfb42bSAlexandre Chartre 
32082629e30SMike Christensen 	(void) mutex_lock(&agent->conn_lock);
32182629e30SMike Christensen 	connp = ldma_connexion_lookup(agent, hdl);
32282629e30SMike Christensen 	if (connp != NULL) {
32382629e30SMike Christensen 		conn_dhdl = connp->dhdl;
32482629e30SMike Christensen 		conn_ver = connp->ver;
32582629e30SMike Christensen 	}
32682629e30SMike Christensen 	(void) mutex_unlock(&agent->conn_lock);
32782629e30SMike Christensen 
32882629e30SMike Christensen 	/* reject any request which is not in the connexion table */
32982629e30SMike Christensen 	if (connp == NULL) {
33049bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
33149bfb42bSAlexandre Chartre 		    "distrusted domain", agent->info->name, hdl);
33249bfb42bSAlexandre Chartre 		status = LDMA_REQ_DENIED;
33349bfb42bSAlexandre Chartre 		goto do_reply;
33449bfb42bSAlexandre Chartre 	}
33549bfb42bSAlexandre Chartre 
33649bfb42bSAlexandre Chartre 	handler = NULL;
33749bfb42bSAlexandre Chartre 
33849bfb42bSAlexandre Chartre 	for (i = 0; i < agent->info->nhandlers; i++) {
33949bfb42bSAlexandre Chartre 		if (agent->info->handlers[i].msg_type == request->msg_type) {
34049bfb42bSAlexandre Chartre 			handler = &agent->info->handlers[i];
34149bfb42bSAlexandre Chartre 			break;
34249bfb42bSAlexandre Chartre 		}
34349bfb42bSAlexandre Chartre 	}
34449bfb42bSAlexandre Chartre 
34549bfb42bSAlexandre Chartre 	if (handler == NULL) {
34649bfb42bSAlexandre Chartre 		/* this type of message is not defined by the agent */
34749bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, unknown message type %x",
34849bfb42bSAlexandre Chartre 		    agent->info->name, hdl, request->msg_type);
34949bfb42bSAlexandre Chartre 		status = LDMA_REQ_NOTSUP;
35049bfb42bSAlexandre Chartre 		goto do_reply;
35149bfb42bSAlexandre Chartre 	}
35249bfb42bSAlexandre Chartre 
35382629e30SMike Christensen 	/* reject any request from a guest which is not allowed */
35482629e30SMike Christensen 	if ((conn_dhdl != LDMA_CONTROL_DOMAIN_DHDL) &&
35582629e30SMike Christensen 	    (handler->msg_flags & LDMA_MSGFLG_ACCESS_ANY) == 0) {
35682629e30SMike Christensen 		LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
35782629e30SMike Christensen 		    "distrusted domain", agent->info->name, hdl);
35882629e30SMike Christensen 		status = LDMA_REQ_DENIED;
35982629e30SMike Christensen 		goto do_reply;
36082629e30SMike Christensen 	}
36182629e30SMike Christensen 
36249bfb42bSAlexandre Chartre 	if (handler->msg_handler == NULL) {
36349bfb42bSAlexandre Chartre 		/*
36449bfb42bSAlexandre Chartre 		 * This type of message is defined by the agent but it
36549bfb42bSAlexandre Chartre 		 * has no handler. That means there is no processing to
36649bfb42bSAlexandre Chartre 		 * do, the message is just ignored, but the request is
36749bfb42bSAlexandre Chartre 		 * successfully completed.
36849bfb42bSAlexandre Chartre 		 */
36949bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, no handler",
37049bfb42bSAlexandre Chartre 		    agent->info->name, hdl);
37149bfb42bSAlexandre Chartre 		status = LDMA_REQ_COMPLETED;
37249bfb42bSAlexandre Chartre 		goto do_reply;
37349bfb42bSAlexandre Chartre 	}
37449bfb42bSAlexandre Chartre 
37549bfb42bSAlexandre Chartre 	/* invoke the message handler of the agent */
37682629e30SMike Christensen 	status = (*handler->msg_handler)(&conn_ver, request, request_dlen,
37782629e30SMike Christensen 	    &reply, &reply_dlen);
37849bfb42bSAlexandre Chartre 
37949bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, handler stat=%d reply=%p rlen=%d",
38049bfb42bSAlexandre Chartre 	    agent->info->name, hdl, status, (void *)reply, reply_dlen);
38149bfb42bSAlexandre Chartre 
38249bfb42bSAlexandre Chartre do_reply:
38349bfb42bSAlexandre Chartre 	/*
38449bfb42bSAlexandre Chartre 	 * If the handler has provided a reply message, we use it directly.
38549bfb42bSAlexandre Chartre 	 * Otherwise, we build a reply depending on the status of the request.
38649bfb42bSAlexandre Chartre 	 * In that case, we re-use the request buffer to build the reply
38749bfb42bSAlexandre Chartre 	 * message.
38849bfb42bSAlexandre Chartre 	 */
38949bfb42bSAlexandre Chartre 	if (reply == NULL) {
39049bfb42bSAlexandre Chartre 
39149bfb42bSAlexandre Chartre 		reply = request;
39249bfb42bSAlexandre Chartre 		reply_dlen = 0;
39349bfb42bSAlexandre Chartre 
39449bfb42bSAlexandre Chartre 		if (status == LDMA_REQ_COMPLETED) {
39549bfb42bSAlexandre Chartre 			/*
39649bfb42bSAlexandre Chartre 			 * The request was successful but no result message was
39749bfb42bSAlexandre Chartre 			 * provided so we send an empty result message.
39849bfb42bSAlexandre Chartre 			 */
39949bfb42bSAlexandre Chartre 			reply->msg_type = LDMA_MSG_RESULT;
40049bfb42bSAlexandre Chartre 			reply->msg_info = 0;
40149bfb42bSAlexandre Chartre 
40249bfb42bSAlexandre Chartre 		} else {
40349bfb42bSAlexandre Chartre 			/*
40449bfb42bSAlexandre Chartre 			 * The request has failed but no error message was
40549bfb42bSAlexandre Chartre 			 * provided so we send an error message based on the
40649bfb42bSAlexandre Chartre 			 * request status.
40749bfb42bSAlexandre Chartre 			 */
40849bfb42bSAlexandre Chartre 			reply->msg_type = LDMA_MSG_ERROR;
40949bfb42bSAlexandre Chartre 			reply->msg_info =
41049bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_NOTSUP)? LDMA_MSGERR_NOTSUP :
41149bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_INVALID)? LDMA_MSGERR_INVALID :
41249bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_DENIED)? LDMA_MSGERR_DENY :
41349bfb42bSAlexandre Chartre 			    LDMA_MSGERR_FAIL;
41449bfb42bSAlexandre Chartre 		}
41549bfb42bSAlexandre Chartre 	}
41649bfb42bSAlexandre Chartre 
41749bfb42bSAlexandre Chartre 	reply_len = LDMA_MESSAGE_SIZE(reply_dlen);
41849bfb42bSAlexandre Chartre 
41949bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, reply num=%llu type=0x%x info=0x%x "
42049bfb42bSAlexandre Chartre 	    "dlen=%d", agent->info->name, hdl, reply->msg_num,
42149bfb42bSAlexandre Chartre 	    reply->msg_type, reply->msg_info, reply_dlen);
42249bfb42bSAlexandre Chartre 
42349bfb42bSAlexandre Chartre 	if (ds_send_msg(hdl, reply, reply_len) != 0) {
42449bfb42bSAlexandre Chartre 		LDMA_ERR("agent %s has failed to send reply for request %llu",
42549bfb42bSAlexandre Chartre 		    agent->info->name, request->msg_num);
42649bfb42bSAlexandre Chartre 	}
42749bfb42bSAlexandre Chartre 
42849bfb42bSAlexandre Chartre 	if (reply != request)
42949bfb42bSAlexandre Chartre 		free(reply);
43049bfb42bSAlexandre Chartre }
43149bfb42bSAlexandre Chartre 
43249bfb42bSAlexandre Chartre /*
43349bfb42bSAlexandre Chartre  * Register an agent. Return 0 if the agent was successfully registered.
43449bfb42bSAlexandre Chartre  */
43549bfb42bSAlexandre Chartre static int
ldma_register(ldma_agent_info_t * agent_info)43649bfb42bSAlexandre Chartre ldma_register(ldma_agent_info_t *agent_info)
43749bfb42bSAlexandre Chartre {
43849bfb42bSAlexandre Chartre 	ldma_agent_t	*agent;
43949bfb42bSAlexandre Chartre 	ds_capability_t	ds_cap;
44049bfb42bSAlexandre Chartre 	ds_ops_t	ds_ops;
44149bfb42bSAlexandre Chartre 
44249bfb42bSAlexandre Chartre 	agent = malloc(sizeof (ldma_agent_t));
44349bfb42bSAlexandre Chartre 	if (agent == NULL)
44449bfb42bSAlexandre Chartre 		goto register_fail;
44549bfb42bSAlexandre Chartre 
44649bfb42bSAlexandre Chartre 	agent->info = agent_info;
44782629e30SMike Christensen 	(void) mutex_init(&agent->conn_lock, USYNC_THREAD, NULL);
44882629e30SMike Christensen 	ldma_connexion_init(agent);
44949bfb42bSAlexandre Chartre 
45049bfb42bSAlexandre Chartre 	ds_cap.svc_id = agent_info->name;
45149bfb42bSAlexandre Chartre 	ds_cap.vers = agent_info->vers;
45249bfb42bSAlexandre Chartre 	ds_cap.nvers = agent_info->nvers;
45349bfb42bSAlexandre Chartre 
45449bfb42bSAlexandre Chartre 	ds_ops.ds_reg_cb = ldma_reg_cb;
45549bfb42bSAlexandre Chartre 	ds_ops.ds_unreg_cb = ldma_unreg_cb;
45649bfb42bSAlexandre Chartre 	ds_ops.ds_data_cb = ldma_data_cb;
45749bfb42bSAlexandre Chartre 	ds_ops.cb_arg = agent;
45849bfb42bSAlexandre Chartre 
45949bfb42bSAlexandre Chartre 	if (ds_svc_reg(&ds_cap, &ds_ops) == 0) {
46049bfb42bSAlexandre Chartre 		LDMA_INFO("agent %s registered", agent_info->name);
46149bfb42bSAlexandre Chartre 		return (0);
46249bfb42bSAlexandre Chartre 	}
46349bfb42bSAlexandre Chartre 
46449bfb42bSAlexandre Chartre register_fail:
46549bfb42bSAlexandre Chartre 
46649bfb42bSAlexandre Chartre 	LDMA_ERR("agent %s has failed to register", agent_info->name);
46749bfb42bSAlexandre Chartre 	free(agent);
46849bfb42bSAlexandre Chartre 	return (-1);
46949bfb42bSAlexandre Chartre }
47049bfb42bSAlexandre Chartre 
47149bfb42bSAlexandre Chartre /*
47249bfb42bSAlexandre Chartre  * Register all known agents. Return the number of agents successfully
47349bfb42bSAlexandre Chartre  * registered.
47449bfb42bSAlexandre Chartre  */
47549bfb42bSAlexandre Chartre static int
ldma_register_agents()47649bfb42bSAlexandre Chartre ldma_register_agents()
47749bfb42bSAlexandre Chartre {
47849bfb42bSAlexandre Chartre 	int count = 0;
47949bfb42bSAlexandre Chartre 	ldma_agent_info_t **agent_infop;
48049bfb42bSAlexandre Chartre 
48149bfb42bSAlexandre Chartre 	for (agent_infop = ldma_agent_infos;
48249bfb42bSAlexandre Chartre 	    *agent_infop != NULL; agent_infop++) {
48349bfb42bSAlexandre Chartre 
48449bfb42bSAlexandre Chartre 		if (ldma_register(*agent_infop) == 0)
48549bfb42bSAlexandre Chartre 			count++;
48649bfb42bSAlexandre Chartre 	}
48749bfb42bSAlexandre Chartre 
48849bfb42bSAlexandre Chartre 	return (count);
48949bfb42bSAlexandre Chartre }
49049bfb42bSAlexandre Chartre 
49149bfb42bSAlexandre Chartre /*ARGSUSED*/
49249bfb42bSAlexandre Chartre static void
ldma_sigusr_handler(int sig,siginfo_t * sinfo,void * ucontext)49349bfb42bSAlexandre Chartre ldma_sigusr_handler(int sig, siginfo_t *sinfo, void *ucontext)
49449bfb42bSAlexandre Chartre {
4956b8303caSAlexandre Chartre 	/*
4966b8303caSAlexandre Chartre 	 * The child process can send the signal before the fork()
4976b8303caSAlexandre Chartre 	 * call has returned in the parent process. So daemon_pid
4986b8303caSAlexandre Chartre 	 * may not be set yet, and we don't check the pid in that
4996b8303caSAlexandre Chartre 	 * case.
5006b8303caSAlexandre Chartre 	 */
5016b8303caSAlexandre Chartre 	if (sig != SIGUSR1 || sinfo->si_code != SI_USER ||
5026b8303caSAlexandre Chartre 	    (daemon_pid > 0 && sinfo->si_pid != daemon_pid))
50349bfb42bSAlexandre Chartre 		return;
50449bfb42bSAlexandre Chartre 
50549bfb42bSAlexandre Chartre 	/*
50649bfb42bSAlexandre Chartre 	 * The parent process has received a USR1 signal from the child.
50749bfb42bSAlexandre Chartre 	 * This means that the daemon has correctly started and the parent
50849bfb42bSAlexandre Chartre 	 * can exit.
50949bfb42bSAlexandre Chartre 	 */
51049bfb42bSAlexandre Chartre 	exit(0);
51149bfb42bSAlexandre Chartre }
51249bfb42bSAlexandre Chartre 
51349bfb42bSAlexandre Chartre static void
ldma_start(boolean_t standalone)51449bfb42bSAlexandre Chartre ldma_start(boolean_t standalone)
51549bfb42bSAlexandre Chartre {
51649bfb42bSAlexandre Chartre 	int stat, rv;
51749bfb42bSAlexandre Chartre 	struct sigaction action;
51849bfb42bSAlexandre Chartre 
51949bfb42bSAlexandre Chartre 	if (!standalone) {
52049bfb42bSAlexandre Chartre 		/*
52149bfb42bSAlexandre Chartre 		 * Some configuration of the daemon has to be done in the
52249bfb42bSAlexandre Chartre 		 * child, but we want the parent to report if the daemon
52349bfb42bSAlexandre Chartre 		 * has successfully started or not. So we setup a signal
52449bfb42bSAlexandre Chartre 		 * handler, and the child will notify the parent using the
52549bfb42bSAlexandre Chartre 		 * USR1 signal if the setup was successful. Otherwise the
52649bfb42bSAlexandre Chartre 		 * child will exit.
52749bfb42bSAlexandre Chartre 		 */
52849bfb42bSAlexandre Chartre 		action.sa_sigaction = ldma_sigusr_handler;
52949bfb42bSAlexandre Chartre 		action.sa_flags = SA_SIGINFO;
53049bfb42bSAlexandre Chartre 
53149bfb42bSAlexandre Chartre 		if (sigemptyset(&action.sa_mask) == -1) {
53249bfb42bSAlexandre Chartre 			LDMA_ERR("sigemptyset error (%d)", errno);
53349bfb42bSAlexandre Chartre 			exit(1);
53449bfb42bSAlexandre Chartre 		}
53549bfb42bSAlexandre Chartre 
53649bfb42bSAlexandre Chartre 		if (sigaction(SIGUSR1, &action, NULL) == -1) {
53749bfb42bSAlexandre Chartre 			LDMA_ERR("sigaction() error (%d)", errno);
53849bfb42bSAlexandre Chartre 			exit(1);
53949bfb42bSAlexandre Chartre 		}
54049bfb42bSAlexandre Chartre 
54149bfb42bSAlexandre Chartre 		if (sigrelse(SIGUSR1) == -1) {
54249bfb42bSAlexandre Chartre 			LDMA_ERR("sigrelse() error (%d)", errno);
54349bfb42bSAlexandre Chartre 			exit(1);
54449bfb42bSAlexandre Chartre 		}
54549bfb42bSAlexandre Chartre 
54649bfb42bSAlexandre Chartre 		if ((daemon_pid = fork()) == -1) {
54749bfb42bSAlexandre Chartre 			LDMA_ERR("fork() error (%d)", errno);
54849bfb42bSAlexandre Chartre 			exit(1);
54949bfb42bSAlexandre Chartre 		}
55049bfb42bSAlexandre Chartre 
55149bfb42bSAlexandre Chartre 		if (daemon_pid != 0) {
55249bfb42bSAlexandre Chartre 			/*
55349bfb42bSAlexandre Chartre 			 * The parent process waits until the child exits (in
55449bfb42bSAlexandre Chartre 			 * case of an error) or sends a USR1 signal (if the
55549bfb42bSAlexandre Chartre 			 * daemon has correctly started).
55649bfb42bSAlexandre Chartre 			 */
55749bfb42bSAlexandre Chartre 			for (;;) {
55849bfb42bSAlexandre Chartre 				rv = waitpid(daemon_pid, &stat, 0);
55949bfb42bSAlexandre Chartre 				if ((rv == daemon_pid && WIFEXITED(stat)) ||
56049bfb42bSAlexandre Chartre 				    (rv == -1 && errno != EINTR)) {
56149bfb42bSAlexandre Chartre 					/* child has exited or error */
56249bfb42bSAlexandre Chartre 					exit(1);
56349bfb42bSAlexandre Chartre 				}
56449bfb42bSAlexandre Chartre 			}
56549bfb42bSAlexandre Chartre 		}
56649bfb42bSAlexandre Chartre 
56749bfb42bSAlexandre Chartre 		/*
56849bfb42bSAlexandre Chartre 		 * Initialize child process
56949bfb42bSAlexandre Chartre 		 */
57049bfb42bSAlexandre Chartre 		if (sighold(SIGUSR1) == -1) {
57149bfb42bSAlexandre Chartre 			LDMA_ERR("sighold error (%d)", errno);
57249bfb42bSAlexandre Chartre 			exit(1);
57349bfb42bSAlexandre Chartre 		}
57449bfb42bSAlexandre Chartre 
57549bfb42bSAlexandre Chartre 		if (sigignore(SIGUSR1) == -1) {
57649bfb42bSAlexandre Chartre 			LDMA_ERR("sigignore error (%d)", errno);
57749bfb42bSAlexandre Chartre 			exit(1);
57849bfb42bSAlexandre Chartre 		}
57949bfb42bSAlexandre Chartre 
58049bfb42bSAlexandre Chartre 		if (setsid() == -1) {
58149bfb42bSAlexandre Chartre 			LDMA_ERR("setsid error (%d)", errno);
58249bfb42bSAlexandre Chartre 			exit(1);
58349bfb42bSAlexandre Chartre 		}
58449bfb42bSAlexandre Chartre 
58549bfb42bSAlexandre Chartre 		if (chdir("/") == -1) {
58649bfb42bSAlexandre Chartre 			LDMA_ERR("chdir error (%d)", errno);
58749bfb42bSAlexandre Chartre 			exit(1);
58849bfb42bSAlexandre Chartre 		}
58949bfb42bSAlexandre Chartre 		(void) umask(0);
59049bfb42bSAlexandre Chartre 
59149bfb42bSAlexandre Chartre 		/*
59249bfb42bSAlexandre Chartre 		 * Initialize file descriptors. Do not touch stderr
59349bfb42bSAlexandre Chartre 		 * which is initialized by SMF to point to the daemon
59449bfb42bSAlexandre Chartre 		 * specific log file.
59549bfb42bSAlexandre Chartre 		 */
59649bfb42bSAlexandre Chartre 		(void) close(STDIN_FILENO);
59749bfb42bSAlexandre Chartre 		if (open("/dev/null", O_RDWR) == -1) {
59849bfb42bSAlexandre Chartre 			LDMA_ERR("open /dev/null error (%d)", errno);
59949bfb42bSAlexandre Chartre 			exit(1);
60049bfb42bSAlexandre Chartre 		}
60149bfb42bSAlexandre Chartre 		if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) {
60249bfb42bSAlexandre Chartre 			LDMA_ERR("dup2 error (%d)", errno);
60349bfb42bSAlexandre Chartre 			exit(1);
60449bfb42bSAlexandre Chartre 		}
60549bfb42bSAlexandre Chartre 		closefrom(STDERR_FILENO + 1);
60649bfb42bSAlexandre Chartre 
60749bfb42bSAlexandre Chartre 		/* initialize logging */
60849bfb42bSAlexandre Chartre 		openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
60949bfb42bSAlexandre Chartre 
61049bfb42bSAlexandre Chartre 		ldma_daemon = B_TRUE;
61149bfb42bSAlexandre Chartre 	}
61249bfb42bSAlexandre Chartre 
61349bfb42bSAlexandre Chartre 	/*
61449bfb42bSAlexandre Chartre 	 * Register the agents. It would be easier to do this before
61549bfb42bSAlexandre Chartre 	 * daemonizing so that any start error is directly reported. But
61649bfb42bSAlexandre Chartre 	 * this can not be done because agents are registered using libds
61749bfb42bSAlexandre Chartre 	 * and this will subscribe the daemon to some sysevents which is
61849bfb42bSAlexandre Chartre 	 * a process based subscription. Instead we notify the parent process
61949bfb42bSAlexandre Chartre 	 * either by exiting, or by sending a SIGUSR1 signal.
62049bfb42bSAlexandre Chartre 	 */
62149bfb42bSAlexandre Chartre 	if (ldma_register_agents() == 0) {
62249bfb42bSAlexandre Chartre 		/* no agent registered */
6236b8303caSAlexandre Chartre 		LDMA_ERR("Unable to register any agent");
62449bfb42bSAlexandre Chartre 		exit(1);
62549bfb42bSAlexandre Chartre 	}
62649bfb42bSAlexandre Chartre 
62749bfb42bSAlexandre Chartre 	if (!standalone) {
62849bfb42bSAlexandre Chartre 		/* signal parent that startup was successful */
62949bfb42bSAlexandre Chartre 		if (kill(getppid(), SIGUSR1) == -1)
63049bfb42bSAlexandre Chartre 			exit(1);
63149bfb42bSAlexandre Chartre 	}
63249bfb42bSAlexandre Chartre }
63349bfb42bSAlexandre Chartre 
63449bfb42bSAlexandre Chartre static void
ldma_usage()63549bfb42bSAlexandre Chartre ldma_usage()
63649bfb42bSAlexandre Chartre {
63749bfb42bSAlexandre Chartre 	(void) fprintf(stderr, "usage: %s\n", cmdname);
63849bfb42bSAlexandre Chartre }
63949bfb42bSAlexandre Chartre 
64049bfb42bSAlexandre Chartre int
main(int argc,char * argv[])64149bfb42bSAlexandre Chartre main(int argc, char *argv[])
64249bfb42bSAlexandre Chartre {
64349bfb42bSAlexandre Chartre 	int opt;
64449bfb42bSAlexandre Chartre 	boolean_t standalone = B_FALSE;
64549bfb42bSAlexandre Chartre 
64649bfb42bSAlexandre Chartre 	cmdname = basename(argv[0]);
64749bfb42bSAlexandre Chartre 
64849bfb42bSAlexandre Chartre 	/* disable getopt error messages */
64949bfb42bSAlexandre Chartre 	opterr = 0;
65049bfb42bSAlexandre Chartre 
65149bfb42bSAlexandre Chartre 	while ((opt = getopt(argc, argv, "ds")) != EOF) {
65249bfb42bSAlexandre Chartre 
65349bfb42bSAlexandre Chartre 		switch (opt) {
65449bfb42bSAlexandre Chartre 		case 'd':
65549bfb42bSAlexandre Chartre 			ldma_debug = B_TRUE;
65649bfb42bSAlexandre Chartre 			break;
65749bfb42bSAlexandre Chartre 		case 's':
65849bfb42bSAlexandre Chartre 			standalone = B_TRUE;
65949bfb42bSAlexandre Chartre 			break;
66049bfb42bSAlexandre Chartre 		default:
66149bfb42bSAlexandre Chartre 			ldma_usage();
66249bfb42bSAlexandre Chartre 			exit(1);
66349bfb42bSAlexandre Chartre 		}
66449bfb42bSAlexandre Chartre 	}
66549bfb42bSAlexandre Chartre 
66649bfb42bSAlexandre Chartre 	ldma_start(standalone);
66749bfb42bSAlexandre Chartre 
66849bfb42bSAlexandre Chartre 	/*
66949bfb42bSAlexandre Chartre 	 * Loop forever. Any incoming message will be received by libds and
67049bfb42bSAlexandre Chartre 	 * forwarded to the agent data callback (ldma_data_cb()) where it
67149bfb42bSAlexandre Chartre 	 * will be processed.
67249bfb42bSAlexandre Chartre 	 */
67349bfb42bSAlexandre Chartre 	for (;;) {
67449bfb42bSAlexandre Chartre 		(void) pause();
67549bfb42bSAlexandre Chartre 	}
67649bfb42bSAlexandre Chartre 
67749bfb42bSAlexandre Chartre 	/*NOTREACHED*/
67849bfb42bSAlexandre Chartre 	return (0);
67949bfb42bSAlexandre Chartre }
680