xref: /titanic_53/usr/src/cmd/ldmad/ldmad.c (revision 82629e3015252bf18319ba3815c773df23e21436)
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 /*
23fc256490SJason Beloro  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
2449bfb42bSAlexandre Chartre  * Use is subject to license terms.
2549bfb42bSAlexandre Chartre  */
2649bfb42bSAlexandre Chartre 
2749bfb42bSAlexandre Chartre /*
2849bfb42bSAlexandre Chartre  * Logical Domains (LDoms) Agents Daemon
2949bfb42bSAlexandre Chartre  *
3049bfb42bSAlexandre Chartre  * The LDoms agents daemon (ldmad) runs on LDoms domains and provides
3149bfb42bSAlexandre Chartre  * information to the control domain. It is composed of a set of agents
3249bfb42bSAlexandre Chartre  * which can send and receive messages to and from the control domain.
3349bfb42bSAlexandre Chartre  * Each agent is registered as a domain service using the libds library,
3449bfb42bSAlexandre Chartre  * and is able to handle requests coming from the control domain.
3549bfb42bSAlexandre Chartre  *
3649bfb42bSAlexandre Chartre  * The control domain sends requests to an agent as messages on the
3749bfb42bSAlexandre Chartre  * corresponding domain service (identified by the agent name). All requests
3849bfb42bSAlexandre Chartre  * are received by the ldmad daemon which dispatches them to the appropriate
3949bfb42bSAlexandre Chartre  * handler function of the agent depending on the type of the message.
4049bfb42bSAlexandre Chartre  *
4149bfb42bSAlexandre Chartre  * After the request has been processed by the handler, the ldmad daemon sent
4249bfb42bSAlexandre Chartre  * a reply message back to the control domain. The reply is either a result
4349bfb42bSAlexandre Chartre  * message if the request was successfully completed, or an error message
4449bfb42bSAlexandre Chartre  * describing the failure.
4549bfb42bSAlexandre Chartre  */
4649bfb42bSAlexandre Chartre 
4749bfb42bSAlexandre Chartre #include <dirent.h>
4849bfb42bSAlexandre Chartre #include <dlfcn.h>
4949bfb42bSAlexandre Chartre #include <errno.h>
5049bfb42bSAlexandre Chartre #include <fcntl.h>
5149bfb42bSAlexandre Chartre #include <link.h>
5249bfb42bSAlexandre Chartre #include <libds.h>
5349bfb42bSAlexandre Chartre #include <libgen.h>
5449bfb42bSAlexandre Chartre #include <signal.h>
5549bfb42bSAlexandre Chartre #include <stdio.h>
5649bfb42bSAlexandre Chartre #include <stdlib.h>
5749bfb42bSAlexandre Chartre #include <strings.h>
58*82629e30SMike Christensen #include <synch.h>
5949bfb42bSAlexandre Chartre #include <syslog.h>
60*82629e30SMike Christensen #include <thread.h>
6149bfb42bSAlexandre Chartre #include <unistd.h>
62*82629e30SMike Christensen #include <sys/debug.h>
63*82629e30SMike Christensen #include <sys/ldoms.h>
6449bfb42bSAlexandre Chartre #include <sys/types.h>
6549bfb42bSAlexandre Chartre #include <sys/stat.h>
6649bfb42bSAlexandre Chartre #include <sys/wait.h>
6749bfb42bSAlexandre Chartre 
6849bfb42bSAlexandre Chartre #include "ldma.h"
6949bfb42bSAlexandre Chartre 
7049bfb42bSAlexandre Chartre #define	LDMA_MODULE	"ldm-agent-daemon"
7149bfb42bSAlexandre Chartre 
7249bfb42bSAlexandre Chartre #define	LDMA_CONTROL_DOMAIN_DHDL	0	/* id of the control domain */
73*82629e30SMike Christensen 
74*82629e30SMike Christensen typedef struct ldma_connexion_t {
75*82629e30SMike Christensen 	ds_hdl_t		hdl;		/* connexion handle */
76*82629e30SMike Christensen 	ds_domain_hdl_t		dhdl;		/* connexion domain handle */
77*82629e30SMike Christensen 	ds_ver_t		ver;		/* connexion version */
78*82629e30SMike Christensen } ldma_connexion_t;
7949bfb42bSAlexandre Chartre 
8049bfb42bSAlexandre Chartre typedef struct ldma_agent {
8149bfb42bSAlexandre Chartre 	ldma_agent_info_t	*info;		/* agent information */
82*82629e30SMike Christensen 	mutex_t			conn_lock;	/* connexion table lock */
83*82629e30SMike Christensen 	ldma_connexion_t	conn[LDOMS_MAX_DOMAINS]; /* connexions */
8449bfb42bSAlexandre Chartre } ldma_agent_t;
8549bfb42bSAlexandre Chartre 
8649bfb42bSAlexandre Chartre /* information about existing agents */
8749bfb42bSAlexandre Chartre extern ldma_agent_info_t ldma_device_info;
8849bfb42bSAlexandre Chartre extern ldma_agent_info_t ldma_system_info;
89fc256490SJason Beloro extern ldma_agent_info_t ldma_dio_info;
9049bfb42bSAlexandre Chartre 
9149bfb42bSAlexandre Chartre boolean_t ldma_debug = B_FALSE;
9249bfb42bSAlexandre Chartre boolean_t ldma_daemon = B_FALSE;
9349bfb42bSAlexandre Chartre 
9449bfb42bSAlexandre Chartre static ldma_agent_info_t *ldma_agent_infos[] = {
9549bfb42bSAlexandre Chartre 	&ldma_device_info,
9649bfb42bSAlexandre Chartre 	&ldma_system_info,
97fc256490SJason Beloro 	&ldma_dio_info,
9849bfb42bSAlexandre Chartre 	NULL
9949bfb42bSAlexandre Chartre };
10049bfb42bSAlexandre Chartre 
10149bfb42bSAlexandre Chartre static char *cmdname;
1026b8303caSAlexandre Chartre static pid_t daemon_pid = 0;
10349bfb42bSAlexandre Chartre 
10449bfb42bSAlexandre Chartre /*
105*82629e30SMike Christensen  * Lookup connexion in agent connexion table.
106*82629e30SMike Christensen  */
107*82629e30SMike Christensen static ldma_connexion_t *
108*82629e30SMike Christensen ldma_connexion_lookup(ldma_agent_t *agent, ds_hdl_t hdl)
109*82629e30SMike Christensen {
110*82629e30SMike Christensen 	ldma_connexion_t *connp;
111*82629e30SMike Christensen 	int i;
112*82629e30SMike Christensen 
113*82629e30SMike Christensen 	ASSERT(MUTEX_HELD(&agent->conn_lock));
114*82629e30SMike Christensen 	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
115*82629e30SMike Christensen 		if (connp->hdl == hdl)
116*82629e30SMike Christensen 			return (connp);
117*82629e30SMike Christensen 	}
118*82629e30SMike Christensen 	return (NULL);
119*82629e30SMike Christensen }
120*82629e30SMike Christensen 
121*82629e30SMike Christensen /*
122*82629e30SMike Christensen  * Add connextion to agent connexion table.
123*82629e30SMike Christensen  */
124*82629e30SMike Christensen static int
125*82629e30SMike Christensen ldma_connexion_add(ldma_agent_t *agent, ds_hdl_t hdl, ds_domain_hdl_t dhdl,
126*82629e30SMike Christensen     ds_ver_t *verp)
127*82629e30SMike Christensen {
128*82629e30SMike Christensen 	ldma_connexion_t *connp;
129*82629e30SMike Christensen 	ldma_connexion_t *availp = NULL;
130*82629e30SMike Christensen 	int i;
131*82629e30SMike Christensen 
132*82629e30SMike Christensen 	(void) mutex_lock(&agent->conn_lock);
133*82629e30SMike Christensen 	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
134*82629e30SMike Christensen 		if (connp->hdl == hdl)
135*82629e30SMike Christensen 			break;
136*82629e30SMike Christensen 		if (availp == NULL && connp->hdl == DS_INVALID_HDL)
137*82629e30SMike Christensen 			availp = connp;
138*82629e30SMike Christensen 	}
139*82629e30SMike Christensen 
140*82629e30SMike Christensen 	if (i < LDOMS_MAX_DOMAINS) {
141*82629e30SMike Christensen 		(void) mutex_unlock(&agent->conn_lock);
142*82629e30SMike Christensen 		LDMA_INFO("agent %s hdl %llx already exists", agent->info->name,
143*82629e30SMike Christensen 		    hdl);
144*82629e30SMike Christensen 		return (0);
145*82629e30SMike Christensen 	}
146*82629e30SMike Christensen 
147*82629e30SMike Christensen 	if (!availp) {
148*82629e30SMike Christensen 		(void) mutex_unlock(&agent->conn_lock);
149*82629e30SMike Christensen 		LDMA_INFO("agent %s too many connections", agent->info->name);
150*82629e30SMike Christensen 		return (0);
151*82629e30SMike Christensen 	}
152*82629e30SMike Christensen 
153*82629e30SMike Christensen 	LDMA_DBG("agent %s adding connection (%x) %llx, %llx, %d.%d",
154*82629e30SMike Christensen 	    agent->info->name, availp, hdl, dhdl, verp->major, verp->minor);
155*82629e30SMike Christensen 
156*82629e30SMike Christensen 	availp->hdl = hdl;
157*82629e30SMike Christensen 	availp->dhdl = dhdl;
158*82629e30SMike Christensen 	availp->ver = *verp;
159*82629e30SMike Christensen 	(void) mutex_unlock(&agent->conn_lock);
160*82629e30SMike Christensen 	return (1);
161*82629e30SMike Christensen }
162*82629e30SMike Christensen 
163*82629e30SMike Christensen /*
164*82629e30SMike Christensen  * Delete connexion from agent connexion table.
165*82629e30SMike Christensen  */
166*82629e30SMike Christensen static int
167*82629e30SMike Christensen ldma_connexion_delete(ldma_agent_t *agent, ds_hdl_t hdl)
168*82629e30SMike Christensen {
169*82629e30SMike Christensen 	ldma_connexion_t *connp;
170*82629e30SMike Christensen 
171*82629e30SMike Christensen 	(void) mutex_lock(&agent->conn_lock);
172*82629e30SMike Christensen 	if ((connp = ldma_connexion_lookup(agent, hdl)) == NULL) {
173*82629e30SMike Christensen 		(void) mutex_unlock(&agent->conn_lock);
174*82629e30SMike Christensen 		LDMA_INFO("agent %s connection delete failed to find %llx",
175*82629e30SMike Christensen 		    agent->info->name, hdl);
176*82629e30SMike Christensen 		return (0);
177*82629e30SMike Christensen 	}
178*82629e30SMike Christensen 
179*82629e30SMike Christensen 	LDMA_DBG("agent %s deleting connection (%x) %llx", agent->info->name,
180*82629e30SMike Christensen 	    connp, hdl);
181*82629e30SMike Christensen 
182*82629e30SMike Christensen 	connp->hdl = DS_INVALID_HDL;
183*82629e30SMike Christensen 	connp->dhdl = 0;
184*82629e30SMike Christensen 	connp->ver.major = 0;
185*82629e30SMike Christensen 	connp->ver.minor = 0;
186*82629e30SMike Christensen 	(void) mutex_unlock(&agent->conn_lock);
187*82629e30SMike Christensen 	return (1);
188*82629e30SMike Christensen }
189*82629e30SMike Christensen 
190*82629e30SMike Christensen /*
191*82629e30SMike Christensen  * Initialize connexion table.
192*82629e30SMike Christensen  */
193*82629e30SMike Christensen static void
194*82629e30SMike Christensen ldma_connexion_init(ldma_agent_t *agent)
195*82629e30SMike Christensen {
196*82629e30SMike Christensen 	ldma_connexion_t *connp;
197*82629e30SMike Christensen 	int i;
198*82629e30SMike Christensen 
199*82629e30SMike Christensen 	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
200*82629e30SMike Christensen 		connp->hdl = DS_INVALID_HDL;
201*82629e30SMike Christensen 	}
202*82629e30SMike Christensen }
203*82629e30SMike Christensen 
204*82629e30SMike Christensen /*
20549bfb42bSAlexandre Chartre  * Allocate a new message with the specified message number (msg_num),
20649bfb42bSAlexandre Chartre  * message type (msg_type) and message data length (msg_dlen). Return
20749bfb42bSAlexandre Chartre  * NULL if the allocation has failed.
20849bfb42bSAlexandre Chartre  */
20949bfb42bSAlexandre Chartre static ldma_message_header_t *
21049bfb42bSAlexandre Chartre ldma_alloc_msg(uint64_t msg_num, uint32_t msg_type, size_t msg_dlen)
21149bfb42bSAlexandre Chartre {
21249bfb42bSAlexandre Chartre 	ldma_message_header_t *msg;
21349bfb42bSAlexandre Chartre 	size_t msg_len;
21449bfb42bSAlexandre Chartre 
21549bfb42bSAlexandre Chartre 	msg_len = LDMA_MESSAGE_SIZE(msg_dlen);
21649bfb42bSAlexandre Chartre 	msg = malloc(msg_len);
21749bfb42bSAlexandre Chartre 	if (msg == NULL)
21849bfb42bSAlexandre Chartre 		return (NULL);
21949bfb42bSAlexandre Chartre 
22049bfb42bSAlexandre Chartre 	msg->msg_num = msg_num;
22149bfb42bSAlexandre Chartre 	msg->msg_type = msg_type;
22249bfb42bSAlexandre Chartre 	msg->msg_info = 0;
22349bfb42bSAlexandre Chartre 
22449bfb42bSAlexandre Chartre 	return (msg);
22549bfb42bSAlexandre Chartre }
22649bfb42bSAlexandre Chartre 
22749bfb42bSAlexandre Chartre /*
22849bfb42bSAlexandre Chartre  * Allocate a result message (LDMA_MSG_REQ_RESULT) with the specified message
22949bfb42bSAlexandre Chartre  * data length (msg_dlen). If the request argument is not NULL then the message
23049bfb42bSAlexandre Chartre  * is created with the same message number as the request, otherwise the message
23149bfb42bSAlexandre Chartre  * number is set to 0. Return NULL if the allocation has failed.
23249bfb42bSAlexandre Chartre  */
23349bfb42bSAlexandre Chartre ldma_message_header_t *
23449bfb42bSAlexandre Chartre ldma_alloc_result_msg(ldma_message_header_t *request, size_t msg_dlen)
23549bfb42bSAlexandre Chartre {
23649bfb42bSAlexandre Chartre 	uint64_t msg_num;
23749bfb42bSAlexandre Chartre 
23849bfb42bSAlexandre Chartre 	msg_num = (request == NULL)? 0 : request->msg_num;
23949bfb42bSAlexandre Chartre 
24049bfb42bSAlexandre Chartre 	return (ldma_alloc_msg(msg_num, LDMA_MSG_RESULT, msg_dlen));
24149bfb42bSAlexandre Chartre }
24249bfb42bSAlexandre Chartre 
24349bfb42bSAlexandre Chartre /*
24449bfb42bSAlexandre Chartre  * Agent register callback. This callback is invoked when a client is registered
24549bfb42bSAlexandre Chartre  * for using the service provided by an agent. An agent will only have one
24649bfb42bSAlexandre Chartre  * consumer which is coming from the control domain.
24749bfb42bSAlexandre Chartre  */
24849bfb42bSAlexandre Chartre static void
24949bfb42bSAlexandre Chartre ldma_reg_cb(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver,
25049bfb42bSAlexandre Chartre     ds_domain_hdl_t dhdl)
25149bfb42bSAlexandre Chartre {
25249bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
253*82629e30SMike Christensen 	char dname[LDOMS_MAX_NAME_LEN];
25449bfb42bSAlexandre Chartre 
255*82629e30SMike Christensen 	if (ds_dom_hdl_to_name(dhdl, dname, LDOMS_MAX_NAME_LEN) != 0) {
25649bfb42bSAlexandre Chartre 		(void) strcpy(dname, "<unknown>");
25749bfb42bSAlexandre Chartre 	}
25849bfb42bSAlexandre Chartre 
25949bfb42bSAlexandre Chartre 	LDMA_DBG("%s: REGISTER hdl=%llx, dhdl=%llx (%s) ver=%hd.%hd",
26049bfb42bSAlexandre Chartre 	    agent->info->name, hdl, dhdl, dname, ver->major, ver->minor);
26149bfb42bSAlexandre Chartre 
26249bfb42bSAlexandre Chartre 	/*
263*82629e30SMike Christensen 	 * Record client information.  Access control is done on a
264*82629e30SMike Christensen 	 * message-by-message basis upon receipt of the message.
26549bfb42bSAlexandre Chartre 	 */
266*82629e30SMike Christensen 	if (!ldma_connexion_add(agent, hdl, dhdl, ver)) {
267*82629e30SMike Christensen 		LDMA_INFO("agent %s failed to add connection from "
26849bfb42bSAlexandre Chartre 		    "domain %s", agent->info->name, dname);
26949bfb42bSAlexandre Chartre 	}
27049bfb42bSAlexandre Chartre }
27149bfb42bSAlexandre Chartre 
27249bfb42bSAlexandre Chartre /*
27349bfb42bSAlexandre Chartre  * Agent unregister callback. This callback is invoked when a client is
27449bfb42bSAlexandre Chartre  * unregistered and stops using the service provided by an agent.
27549bfb42bSAlexandre Chartre  */
27649bfb42bSAlexandre Chartre static void
27749bfb42bSAlexandre Chartre ldma_unreg_cb(ds_hdl_t hdl, ds_cb_arg_t arg)
27849bfb42bSAlexandre Chartre {
27949bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
28049bfb42bSAlexandre Chartre 
28149bfb42bSAlexandre Chartre 	LDMA_DBG("%s: UNREGISTER hdl=%llx", agent->info->name, hdl);
28249bfb42bSAlexandre Chartre 
283*82629e30SMike Christensen 	if (!ldma_connexion_delete(agent, hdl)) {
284*82629e30SMike Christensen 		LDMA_INFO("agent %s failed to unregister handle %llx",
285*82629e30SMike Christensen 		    agent->info->name, hdl);
28649bfb42bSAlexandre Chartre 	}
28749bfb42bSAlexandre Chartre }
28849bfb42bSAlexandre Chartre 
28949bfb42bSAlexandre Chartre /*
29049bfb42bSAlexandre Chartre  * Agent data callback. This callback is invoked when an agent receives a new
29149bfb42bSAlexandre Chartre  * message from a client. Any request from a client which is not the control
29249bfb42bSAlexandre Chartre  * domain is immediatly rejected. Otherwise the message is forwarded to the
29349bfb42bSAlexandre Chartre  * appropriate handler function provided by the agent, depending on the message
29449bfb42bSAlexandre Chartre  * type.
29549bfb42bSAlexandre Chartre  */
29649bfb42bSAlexandre Chartre static void
29749bfb42bSAlexandre Chartre ldma_data_cb(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf, size_t len)
29849bfb42bSAlexandre Chartre {
29949bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
30049bfb42bSAlexandre Chartre 	ldma_msg_handler_t *handler;
30149bfb42bSAlexandre Chartre 	ldma_message_header_t *request = buf;
30249bfb42bSAlexandre Chartre 	ldma_message_header_t *reply = NULL;
303*82629e30SMike Christensen 	ldma_connexion_t *connp;
304*82629e30SMike Christensen 	ds_ver_t conn_ver;
305*82629e30SMike Christensen 	ds_domain_hdl_t conn_dhdl;
30649bfb42bSAlexandre Chartre 	ldma_request_status_t status;
30749bfb42bSAlexandre Chartre 	size_t request_dlen, reply_len, reply_dlen = 0;
30849bfb42bSAlexandre Chartre 	int i;
30949bfb42bSAlexandre Chartre 
31049bfb42bSAlexandre Chartre 	/* check the message size */
31149bfb42bSAlexandre Chartre 	if (len < LDMA_MESSAGE_HEADER_SIZE) {
31249bfb42bSAlexandre Chartre 		LDMA_INFO("agent %s has ignored message with an invalid "
31349bfb42bSAlexandre Chartre 		    "size of %d bytes", agent->info->name, len);
31449bfb42bSAlexandre Chartre 		return;
31549bfb42bSAlexandre Chartre 	}
31649bfb42bSAlexandre Chartre 
31749bfb42bSAlexandre Chartre 	request_dlen = LDMA_MESSAGE_DLEN(len);
31849bfb42bSAlexandre Chartre 
31949bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, request num=%llu type=0x%x info=0x%x "
32049bfb42bSAlexandre Chartre 	    "dlen=%d", agent->info->name, hdl, request->msg_num,
32149bfb42bSAlexandre Chartre 	    request->msg_type, request->msg_info, request_dlen);
32249bfb42bSAlexandre Chartre 
323*82629e30SMike Christensen 	(void) mutex_lock(&agent->conn_lock);
324*82629e30SMike Christensen 	connp = ldma_connexion_lookup(agent, hdl);
325*82629e30SMike Christensen 	if (connp != NULL) {
326*82629e30SMike Christensen 		conn_dhdl = connp->dhdl;
327*82629e30SMike Christensen 		conn_ver = connp->ver;
328*82629e30SMike Christensen 	}
329*82629e30SMike Christensen 	(void) mutex_unlock(&agent->conn_lock);
330*82629e30SMike Christensen 
331*82629e30SMike Christensen 	/* reject any request which is not in the connexion table */
332*82629e30SMike Christensen 	if (connp == NULL) {
33349bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
33449bfb42bSAlexandre Chartre 		    "distrusted domain", agent->info->name, hdl);
33549bfb42bSAlexandre Chartre 		status = LDMA_REQ_DENIED;
33649bfb42bSAlexandre Chartre 		goto do_reply;
33749bfb42bSAlexandre Chartre 	}
33849bfb42bSAlexandre Chartre 
33949bfb42bSAlexandre Chartre 	handler = NULL;
34049bfb42bSAlexandre Chartre 
34149bfb42bSAlexandre Chartre 	for (i = 0; i < agent->info->nhandlers; i++) {
34249bfb42bSAlexandre Chartre 		if (agent->info->handlers[i].msg_type == request->msg_type) {
34349bfb42bSAlexandre Chartre 			handler = &agent->info->handlers[i];
34449bfb42bSAlexandre Chartre 			break;
34549bfb42bSAlexandre Chartre 		}
34649bfb42bSAlexandre Chartre 	}
34749bfb42bSAlexandre Chartre 
34849bfb42bSAlexandre Chartre 	if (handler == NULL) {
34949bfb42bSAlexandre Chartre 		/* this type of message is not defined by the agent */
35049bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, unknown message type %x",
35149bfb42bSAlexandre Chartre 		    agent->info->name, hdl, request->msg_type);
35249bfb42bSAlexandre Chartre 		status = LDMA_REQ_NOTSUP;
35349bfb42bSAlexandre Chartre 		goto do_reply;
35449bfb42bSAlexandre Chartre 	}
35549bfb42bSAlexandre Chartre 
356*82629e30SMike Christensen 	/* reject any request from a guest which is not allowed */
357*82629e30SMike Christensen 	if ((conn_dhdl != LDMA_CONTROL_DOMAIN_DHDL) &&
358*82629e30SMike Christensen 	    (handler->msg_flags & LDMA_MSGFLG_ACCESS_ANY) == 0) {
359*82629e30SMike Christensen 		LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
360*82629e30SMike Christensen 		    "distrusted domain", agent->info->name, hdl);
361*82629e30SMike Christensen 		status = LDMA_REQ_DENIED;
362*82629e30SMike Christensen 		goto do_reply;
363*82629e30SMike Christensen 	}
364*82629e30SMike Christensen 
36549bfb42bSAlexandre Chartre 	if (handler->msg_handler == NULL) {
36649bfb42bSAlexandre Chartre 		/*
36749bfb42bSAlexandre Chartre 		 * This type of message is defined by the agent but it
36849bfb42bSAlexandre Chartre 		 * has no handler. That means there is no processing to
36949bfb42bSAlexandre Chartre 		 * do, the message is just ignored, but the request is
37049bfb42bSAlexandre Chartre 		 * successfully completed.
37149bfb42bSAlexandre Chartre 		 */
37249bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, no handler",
37349bfb42bSAlexandre Chartre 		    agent->info->name, hdl);
37449bfb42bSAlexandre Chartre 		status = LDMA_REQ_COMPLETED;
37549bfb42bSAlexandre Chartre 		goto do_reply;
37649bfb42bSAlexandre Chartre 	}
37749bfb42bSAlexandre Chartre 
37849bfb42bSAlexandre Chartre 	/* invoke the message handler of the agent */
379*82629e30SMike Christensen 	status = (*handler->msg_handler)(&conn_ver, request, request_dlen,
380*82629e30SMike Christensen 	    &reply, &reply_dlen);
38149bfb42bSAlexandre Chartre 
38249bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, handler stat=%d reply=%p rlen=%d",
38349bfb42bSAlexandre Chartre 	    agent->info->name, hdl, status, (void *)reply, reply_dlen);
38449bfb42bSAlexandre Chartre 
38549bfb42bSAlexandre Chartre do_reply:
38649bfb42bSAlexandre Chartre 	/*
38749bfb42bSAlexandre Chartre 	 * If the handler has provided a reply message, we use it directly.
38849bfb42bSAlexandre Chartre 	 * Otherwise, we build a reply depending on the status of the request.
38949bfb42bSAlexandre Chartre 	 * In that case, we re-use the request buffer to build the reply
39049bfb42bSAlexandre Chartre 	 * message.
39149bfb42bSAlexandre Chartre 	 */
39249bfb42bSAlexandre Chartre 	if (reply == NULL) {
39349bfb42bSAlexandre Chartre 
39449bfb42bSAlexandre Chartre 		reply = request;
39549bfb42bSAlexandre Chartre 		reply_dlen = 0;
39649bfb42bSAlexandre Chartre 
39749bfb42bSAlexandre Chartre 		if (status == LDMA_REQ_COMPLETED) {
39849bfb42bSAlexandre Chartre 			/*
39949bfb42bSAlexandre Chartre 			 * The request was successful but no result message was
40049bfb42bSAlexandre Chartre 			 * provided so we send an empty result message.
40149bfb42bSAlexandre Chartre 			 */
40249bfb42bSAlexandre Chartre 			reply->msg_type = LDMA_MSG_RESULT;
40349bfb42bSAlexandre Chartre 			reply->msg_info = 0;
40449bfb42bSAlexandre Chartre 
40549bfb42bSAlexandre Chartre 		} else {
40649bfb42bSAlexandre Chartre 			/*
40749bfb42bSAlexandre Chartre 			 * The request has failed but no error message was
40849bfb42bSAlexandre Chartre 			 * provided so we send an error message based on the
40949bfb42bSAlexandre Chartre 			 * request status.
41049bfb42bSAlexandre Chartre 			 */
41149bfb42bSAlexandre Chartre 			reply->msg_type = LDMA_MSG_ERROR;
41249bfb42bSAlexandre Chartre 			reply->msg_info =
41349bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_NOTSUP)? LDMA_MSGERR_NOTSUP :
41449bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_INVALID)? LDMA_MSGERR_INVALID :
41549bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_DENIED)? LDMA_MSGERR_DENY :
41649bfb42bSAlexandre Chartre 			    LDMA_MSGERR_FAIL;
41749bfb42bSAlexandre Chartre 		}
41849bfb42bSAlexandre Chartre 	}
41949bfb42bSAlexandre Chartre 
42049bfb42bSAlexandre Chartre 	reply_len = LDMA_MESSAGE_SIZE(reply_dlen);
42149bfb42bSAlexandre Chartre 
42249bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, reply num=%llu type=0x%x info=0x%x "
42349bfb42bSAlexandre Chartre 	    "dlen=%d", agent->info->name, hdl, reply->msg_num,
42449bfb42bSAlexandre Chartre 	    reply->msg_type, reply->msg_info, reply_dlen);
42549bfb42bSAlexandre Chartre 
42649bfb42bSAlexandre Chartre 	if (ds_send_msg(hdl, reply, reply_len) != 0) {
42749bfb42bSAlexandre Chartre 		LDMA_ERR("agent %s has failed to send reply for request %llu",
42849bfb42bSAlexandre Chartre 		    agent->info->name, request->msg_num);
42949bfb42bSAlexandre Chartre 	}
43049bfb42bSAlexandre Chartre 
43149bfb42bSAlexandre Chartre 	if (reply != request)
43249bfb42bSAlexandre Chartre 		free(reply);
43349bfb42bSAlexandre Chartre }
43449bfb42bSAlexandre Chartre 
43549bfb42bSAlexandre Chartre /*
43649bfb42bSAlexandre Chartre  * Register an agent. Return 0 if the agent was successfully registered.
43749bfb42bSAlexandre Chartre  */
43849bfb42bSAlexandre Chartre static int
43949bfb42bSAlexandre Chartre ldma_register(ldma_agent_info_t *agent_info)
44049bfb42bSAlexandre Chartre {
44149bfb42bSAlexandre Chartre 	ldma_agent_t	*agent;
44249bfb42bSAlexandre Chartre 	ds_capability_t	ds_cap;
44349bfb42bSAlexandre Chartre 	ds_ops_t	ds_ops;
44449bfb42bSAlexandre Chartre 
44549bfb42bSAlexandre Chartre 	agent = malloc(sizeof (ldma_agent_t));
44649bfb42bSAlexandre Chartre 	if (agent == NULL)
44749bfb42bSAlexandre Chartre 		goto register_fail;
44849bfb42bSAlexandre Chartre 
44949bfb42bSAlexandre Chartre 	agent->info = agent_info;
450*82629e30SMike Christensen 	(void) mutex_init(&agent->conn_lock, USYNC_THREAD, NULL);
451*82629e30SMike Christensen 	ldma_connexion_init(agent);
45249bfb42bSAlexandre Chartre 
45349bfb42bSAlexandre Chartre 	ds_cap.svc_id = agent_info->name;
45449bfb42bSAlexandre Chartre 	ds_cap.vers = agent_info->vers;
45549bfb42bSAlexandre Chartre 	ds_cap.nvers = agent_info->nvers;
45649bfb42bSAlexandre Chartre 
45749bfb42bSAlexandre Chartre 	ds_ops.ds_reg_cb = ldma_reg_cb;
45849bfb42bSAlexandre Chartre 	ds_ops.ds_unreg_cb = ldma_unreg_cb;
45949bfb42bSAlexandre Chartre 	ds_ops.ds_data_cb = ldma_data_cb;
46049bfb42bSAlexandre Chartre 	ds_ops.cb_arg = agent;
46149bfb42bSAlexandre Chartre 
46249bfb42bSAlexandre Chartre 	if (ds_svc_reg(&ds_cap, &ds_ops) == 0) {
46349bfb42bSAlexandre Chartre 		LDMA_INFO("agent %s registered", agent_info->name);
46449bfb42bSAlexandre Chartre 		return (0);
46549bfb42bSAlexandre Chartre 	}
46649bfb42bSAlexandre Chartre 
46749bfb42bSAlexandre Chartre register_fail:
46849bfb42bSAlexandre Chartre 
46949bfb42bSAlexandre Chartre 	LDMA_ERR("agent %s has failed to register", agent_info->name);
47049bfb42bSAlexandre Chartre 	free(agent);
47149bfb42bSAlexandre Chartre 	return (-1);
47249bfb42bSAlexandre Chartre }
47349bfb42bSAlexandre Chartre 
47449bfb42bSAlexandre Chartre /*
47549bfb42bSAlexandre Chartre  * Register all known agents. Return the number of agents successfully
47649bfb42bSAlexandre Chartre  * registered.
47749bfb42bSAlexandre Chartre  */
47849bfb42bSAlexandre Chartre static int
47949bfb42bSAlexandre Chartre ldma_register_agents()
48049bfb42bSAlexandre Chartre {
48149bfb42bSAlexandre Chartre 	int count = 0;
48249bfb42bSAlexandre Chartre 	ldma_agent_info_t **agent_infop;
48349bfb42bSAlexandre Chartre 
48449bfb42bSAlexandre Chartre 	for (agent_infop = ldma_agent_infos;
48549bfb42bSAlexandre Chartre 	    *agent_infop != NULL; agent_infop++) {
48649bfb42bSAlexandre Chartre 
48749bfb42bSAlexandre Chartre 		if (ldma_register(*agent_infop) == 0)
48849bfb42bSAlexandre Chartre 			count++;
48949bfb42bSAlexandre Chartre 	}
49049bfb42bSAlexandre Chartre 
49149bfb42bSAlexandre Chartre 	return (count);
49249bfb42bSAlexandre Chartre }
49349bfb42bSAlexandre Chartre 
49449bfb42bSAlexandre Chartre /*ARGSUSED*/
49549bfb42bSAlexandre Chartre static void
49649bfb42bSAlexandre Chartre ldma_sigusr_handler(int sig, siginfo_t *sinfo, void *ucontext)
49749bfb42bSAlexandre Chartre {
4986b8303caSAlexandre Chartre 	/*
4996b8303caSAlexandre Chartre 	 * The child process can send the signal before the fork()
5006b8303caSAlexandre Chartre 	 * call has returned in the parent process. So daemon_pid
5016b8303caSAlexandre Chartre 	 * may not be set yet, and we don't check the pid in that
5026b8303caSAlexandre Chartre 	 * case.
5036b8303caSAlexandre Chartre 	 */
5046b8303caSAlexandre Chartre 	if (sig != SIGUSR1 || sinfo->si_code != SI_USER ||
5056b8303caSAlexandre Chartre 	    (daemon_pid > 0 && sinfo->si_pid != daemon_pid))
50649bfb42bSAlexandre Chartre 		return;
50749bfb42bSAlexandre Chartre 
50849bfb42bSAlexandre Chartre 	/*
50949bfb42bSAlexandre Chartre 	 * The parent process has received a USR1 signal from the child.
51049bfb42bSAlexandre Chartre 	 * This means that the daemon has correctly started and the parent
51149bfb42bSAlexandre Chartre 	 * can exit.
51249bfb42bSAlexandre Chartre 	 */
51349bfb42bSAlexandre Chartre 	exit(0);
51449bfb42bSAlexandre Chartre }
51549bfb42bSAlexandre Chartre 
51649bfb42bSAlexandre Chartre static void
51749bfb42bSAlexandre Chartre ldma_start(boolean_t standalone)
51849bfb42bSAlexandre Chartre {
51949bfb42bSAlexandre Chartre 	int stat, rv;
52049bfb42bSAlexandre Chartre 	struct sigaction action;
52149bfb42bSAlexandre Chartre 
52249bfb42bSAlexandre Chartre 	if (!standalone) {
52349bfb42bSAlexandre Chartre 		/*
52449bfb42bSAlexandre Chartre 		 * Some configuration of the daemon has to be done in the
52549bfb42bSAlexandre Chartre 		 * child, but we want the parent to report if the daemon
52649bfb42bSAlexandre Chartre 		 * has successfully started or not. So we setup a signal
52749bfb42bSAlexandre Chartre 		 * handler, and the child will notify the parent using the
52849bfb42bSAlexandre Chartre 		 * USR1 signal if the setup was successful. Otherwise the
52949bfb42bSAlexandre Chartre 		 * child will exit.
53049bfb42bSAlexandre Chartre 		 */
53149bfb42bSAlexandre Chartre 		action.sa_sigaction = ldma_sigusr_handler;
53249bfb42bSAlexandre Chartre 		action.sa_flags = SA_SIGINFO;
53349bfb42bSAlexandre Chartre 
53449bfb42bSAlexandre Chartre 		if (sigemptyset(&action.sa_mask) == -1) {
53549bfb42bSAlexandre Chartre 			LDMA_ERR("sigemptyset error (%d)", errno);
53649bfb42bSAlexandre Chartre 			exit(1);
53749bfb42bSAlexandre Chartre 		}
53849bfb42bSAlexandre Chartre 
53949bfb42bSAlexandre Chartre 		if (sigaction(SIGUSR1, &action, NULL) == -1) {
54049bfb42bSAlexandre Chartre 			LDMA_ERR("sigaction() error (%d)", errno);
54149bfb42bSAlexandre Chartre 			exit(1);
54249bfb42bSAlexandre Chartre 		}
54349bfb42bSAlexandre Chartre 
54449bfb42bSAlexandre Chartre 		if (sigrelse(SIGUSR1) == -1) {
54549bfb42bSAlexandre Chartre 			LDMA_ERR("sigrelse() error (%d)", errno);
54649bfb42bSAlexandre Chartre 			exit(1);
54749bfb42bSAlexandre Chartre 		}
54849bfb42bSAlexandre Chartre 
54949bfb42bSAlexandre Chartre 		if ((daemon_pid = fork()) == -1) {
55049bfb42bSAlexandre Chartre 			LDMA_ERR("fork() error (%d)", errno);
55149bfb42bSAlexandre Chartre 			exit(1);
55249bfb42bSAlexandre Chartre 		}
55349bfb42bSAlexandre Chartre 
55449bfb42bSAlexandre Chartre 		if (daemon_pid != 0) {
55549bfb42bSAlexandre Chartre 			/*
55649bfb42bSAlexandre Chartre 			 * The parent process waits until the child exits (in
55749bfb42bSAlexandre Chartre 			 * case of an error) or sends a USR1 signal (if the
55849bfb42bSAlexandre Chartre 			 * daemon has correctly started).
55949bfb42bSAlexandre Chartre 			 */
56049bfb42bSAlexandre Chartre 			for (;;) {
56149bfb42bSAlexandre Chartre 				rv = waitpid(daemon_pid, &stat, 0);
56249bfb42bSAlexandre Chartre 				if ((rv == daemon_pid && WIFEXITED(stat)) ||
56349bfb42bSAlexandre Chartre 				    (rv == -1 && errno != EINTR)) {
56449bfb42bSAlexandre Chartre 					/* child has exited or error */
56549bfb42bSAlexandre Chartre 					exit(1);
56649bfb42bSAlexandre Chartre 				}
56749bfb42bSAlexandre Chartre 			}
56849bfb42bSAlexandre Chartre 		}
56949bfb42bSAlexandre Chartre 
57049bfb42bSAlexandre Chartre 		/*
57149bfb42bSAlexandre Chartre 		 * Initialize child process
57249bfb42bSAlexandre Chartre 		 */
57349bfb42bSAlexandre Chartre 		if (sighold(SIGUSR1) == -1) {
57449bfb42bSAlexandre Chartre 			LDMA_ERR("sighold error (%d)", errno);
57549bfb42bSAlexandre Chartre 			exit(1);
57649bfb42bSAlexandre Chartre 		}
57749bfb42bSAlexandre Chartre 
57849bfb42bSAlexandre Chartre 		if (sigignore(SIGUSR1) == -1) {
57949bfb42bSAlexandre Chartre 			LDMA_ERR("sigignore error (%d)", errno);
58049bfb42bSAlexandre Chartre 			exit(1);
58149bfb42bSAlexandre Chartre 		}
58249bfb42bSAlexandre Chartre 
58349bfb42bSAlexandre Chartre 		if (setsid() == -1) {
58449bfb42bSAlexandre Chartre 			LDMA_ERR("setsid error (%d)", errno);
58549bfb42bSAlexandre Chartre 			exit(1);
58649bfb42bSAlexandre Chartre 		}
58749bfb42bSAlexandre Chartre 
58849bfb42bSAlexandre Chartre 		if (chdir("/") == -1) {
58949bfb42bSAlexandre Chartre 			LDMA_ERR("chdir error (%d)", errno);
59049bfb42bSAlexandre Chartre 			exit(1);
59149bfb42bSAlexandre Chartre 		}
59249bfb42bSAlexandre Chartre 		(void) umask(0);
59349bfb42bSAlexandre Chartre 
59449bfb42bSAlexandre Chartre 		/*
59549bfb42bSAlexandre Chartre 		 * Initialize file descriptors. Do not touch stderr
59649bfb42bSAlexandre Chartre 		 * which is initialized by SMF to point to the daemon
59749bfb42bSAlexandre Chartre 		 * specific log file.
59849bfb42bSAlexandre Chartre 		 */
59949bfb42bSAlexandre Chartre 		(void) close(STDIN_FILENO);
60049bfb42bSAlexandre Chartre 		if (open("/dev/null", O_RDWR) == -1) {
60149bfb42bSAlexandre Chartre 			LDMA_ERR("open /dev/null error (%d)", errno);
60249bfb42bSAlexandre Chartre 			exit(1);
60349bfb42bSAlexandre Chartre 		}
60449bfb42bSAlexandre Chartre 		if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) {
60549bfb42bSAlexandre Chartre 			LDMA_ERR("dup2 error (%d)", errno);
60649bfb42bSAlexandre Chartre 			exit(1);
60749bfb42bSAlexandre Chartre 		}
60849bfb42bSAlexandre Chartre 		closefrom(STDERR_FILENO + 1);
60949bfb42bSAlexandre Chartre 
61049bfb42bSAlexandre Chartre 		/* initialize logging */
61149bfb42bSAlexandre Chartre 		openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
61249bfb42bSAlexandre Chartre 
61349bfb42bSAlexandre Chartre 		ldma_daemon = B_TRUE;
61449bfb42bSAlexandre Chartre 	}
61549bfb42bSAlexandre Chartre 
61649bfb42bSAlexandre Chartre 	/*
61749bfb42bSAlexandre Chartre 	 * Register the agents. It would be easier to do this before
61849bfb42bSAlexandre Chartre 	 * daemonizing so that any start error is directly reported. But
61949bfb42bSAlexandre Chartre 	 * this can not be done because agents are registered using libds
62049bfb42bSAlexandre Chartre 	 * and this will subscribe the daemon to some sysevents which is
62149bfb42bSAlexandre Chartre 	 * a process based subscription. Instead we notify the parent process
62249bfb42bSAlexandre Chartre 	 * either by exiting, or by sending a SIGUSR1 signal.
62349bfb42bSAlexandre Chartre 	 */
62449bfb42bSAlexandre Chartre 	if (ldma_register_agents() == 0) {
62549bfb42bSAlexandre Chartre 		/* no agent registered */
6266b8303caSAlexandre Chartre 		LDMA_ERR("Unable to register any agent");
62749bfb42bSAlexandre Chartre 		exit(1);
62849bfb42bSAlexandre Chartre 	}
62949bfb42bSAlexandre Chartre 
63049bfb42bSAlexandre Chartre 	if (!standalone) {
63149bfb42bSAlexandre Chartre 		/* signal parent that startup was successful */
63249bfb42bSAlexandre Chartre 		if (kill(getppid(), SIGUSR1) == -1)
63349bfb42bSAlexandre Chartre 			exit(1);
63449bfb42bSAlexandre Chartre 	}
63549bfb42bSAlexandre Chartre }
63649bfb42bSAlexandre Chartre 
63749bfb42bSAlexandre Chartre static void
63849bfb42bSAlexandre Chartre ldma_usage()
63949bfb42bSAlexandre Chartre {
64049bfb42bSAlexandre Chartre 	(void) fprintf(stderr, "usage: %s\n", cmdname);
64149bfb42bSAlexandre Chartre }
64249bfb42bSAlexandre Chartre 
64349bfb42bSAlexandre Chartre int
64449bfb42bSAlexandre Chartre main(int argc, char *argv[])
64549bfb42bSAlexandre Chartre {
64649bfb42bSAlexandre Chartre 	int opt;
64749bfb42bSAlexandre Chartre 	boolean_t standalone = B_FALSE;
64849bfb42bSAlexandre Chartre 
64949bfb42bSAlexandre Chartre 	cmdname = basename(argv[0]);
65049bfb42bSAlexandre Chartre 
65149bfb42bSAlexandre Chartre 	/* disable getopt error messages */
65249bfb42bSAlexandre Chartre 	opterr = 0;
65349bfb42bSAlexandre Chartre 
65449bfb42bSAlexandre Chartre 	while ((opt = getopt(argc, argv, "ds")) != EOF) {
65549bfb42bSAlexandre Chartre 
65649bfb42bSAlexandre Chartre 		switch (opt) {
65749bfb42bSAlexandre Chartre 		case 'd':
65849bfb42bSAlexandre Chartre 			ldma_debug = B_TRUE;
65949bfb42bSAlexandre Chartre 			break;
66049bfb42bSAlexandre Chartre 		case 's':
66149bfb42bSAlexandre Chartre 			standalone = B_TRUE;
66249bfb42bSAlexandre Chartre 			break;
66349bfb42bSAlexandre Chartre 		default:
66449bfb42bSAlexandre Chartre 			ldma_usage();
66549bfb42bSAlexandre Chartre 			exit(1);
66649bfb42bSAlexandre Chartre 		}
66749bfb42bSAlexandre Chartre 	}
66849bfb42bSAlexandre Chartre 
66949bfb42bSAlexandre Chartre 	ldma_start(standalone);
67049bfb42bSAlexandre Chartre 
67149bfb42bSAlexandre Chartre 	/*
67249bfb42bSAlexandre Chartre 	 * Loop forever. Any incoming message will be received by libds and
67349bfb42bSAlexandre Chartre 	 * forwarded to the agent data callback (ldma_data_cb()) where it
67449bfb42bSAlexandre Chartre 	 * will be processed.
67549bfb42bSAlexandre Chartre 	 */
67649bfb42bSAlexandre Chartre 	for (;;) {
67749bfb42bSAlexandre Chartre 		(void) pause();
67849bfb42bSAlexandre Chartre 	}
67949bfb42bSAlexandre Chartre 
68049bfb42bSAlexandre Chartre 	/*NOTREACHED*/
68149bfb42bSAlexandre Chartre 	return (0);
68249bfb42bSAlexandre Chartre }
683