xref: /titanic_54/usr/src/cmd/ldmad/ldmad.c (revision 6b8303caf3c5c70ae1c1d362bf6de0c55eb622bb)
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 /*
2349bfb42bSAlexandre Chartre  * Copyright 2009 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>
5849bfb42bSAlexandre Chartre #include <syslog.h>
5949bfb42bSAlexandre Chartre #include <unistd.h>
6049bfb42bSAlexandre Chartre #include <sys/types.h>
6149bfb42bSAlexandre Chartre #include <sys/stat.h>
6249bfb42bSAlexandre Chartre #include <sys/wait.h>
6349bfb42bSAlexandre Chartre 
6449bfb42bSAlexandre Chartre #include "ldma.h"
6549bfb42bSAlexandre Chartre 
6649bfb42bSAlexandre Chartre #define	LDMA_MODULE	"ldm-agent-daemon"
6749bfb42bSAlexandre Chartre 
6849bfb42bSAlexandre Chartre #define	LDMA_CONTROL_DOMAIN_DHDL	0	/* id of the control domain */
6949bfb42bSAlexandre Chartre #define	LDMA_DOMAIN_NAME_MAXLEN		MAXNAMELEN
7049bfb42bSAlexandre Chartre 
7149bfb42bSAlexandre Chartre typedef struct ldma_agent {
7249bfb42bSAlexandre Chartre 	ldma_agent_info_t	*info;		/* agent information */
7349bfb42bSAlexandre Chartre 	ds_hdl_t		conn_hdl;	/* connexion handler */
7449bfb42bSAlexandre Chartre 	ds_ver_t		conn_ver;	/* connexion version */
7549bfb42bSAlexandre Chartre } ldma_agent_t;
7649bfb42bSAlexandre Chartre 
7749bfb42bSAlexandre Chartre /* information about existing agents */
7849bfb42bSAlexandre Chartre extern ldma_agent_info_t ldma_device_info;
7949bfb42bSAlexandre Chartre extern ldma_agent_info_t ldma_system_info;
8049bfb42bSAlexandre Chartre 
8149bfb42bSAlexandre Chartre boolean_t ldma_debug = B_FALSE;
8249bfb42bSAlexandre Chartre boolean_t ldma_daemon = B_FALSE;
8349bfb42bSAlexandre Chartre 
8449bfb42bSAlexandre Chartre static ldma_agent_info_t *ldma_agent_infos[] = {
8549bfb42bSAlexandre Chartre 	&ldma_device_info,
8649bfb42bSAlexandre Chartre 	&ldma_system_info,
8749bfb42bSAlexandre Chartre 	NULL
8849bfb42bSAlexandre Chartre };
8949bfb42bSAlexandre Chartre 
9049bfb42bSAlexandre Chartre static char *cmdname;
91*6b8303caSAlexandre Chartre static pid_t daemon_pid = 0;
9249bfb42bSAlexandre Chartre 
9349bfb42bSAlexandre Chartre /*
9449bfb42bSAlexandre Chartre  * Allocate a new message with the specified message number (msg_num),
9549bfb42bSAlexandre Chartre  * message type (msg_type) and message data length (msg_dlen). Return
9649bfb42bSAlexandre Chartre  * NULL if the allocation has failed.
9749bfb42bSAlexandre Chartre  */
9849bfb42bSAlexandre Chartre static ldma_message_header_t *
9949bfb42bSAlexandre Chartre ldma_alloc_msg(uint64_t msg_num, uint32_t msg_type, size_t msg_dlen)
10049bfb42bSAlexandre Chartre {
10149bfb42bSAlexandre Chartre 	ldma_message_header_t *msg;
10249bfb42bSAlexandre Chartre 	size_t msg_len;
10349bfb42bSAlexandre Chartre 
10449bfb42bSAlexandre Chartre 	msg_len = LDMA_MESSAGE_SIZE(msg_dlen);
10549bfb42bSAlexandre Chartre 	msg = malloc(msg_len);
10649bfb42bSAlexandre Chartre 	if (msg == NULL)
10749bfb42bSAlexandre Chartre 		return (NULL);
10849bfb42bSAlexandre Chartre 
10949bfb42bSAlexandre Chartre 	msg->msg_num = msg_num;
11049bfb42bSAlexandre Chartre 	msg->msg_type = msg_type;
11149bfb42bSAlexandre Chartre 	msg->msg_info = 0;
11249bfb42bSAlexandre Chartre 
11349bfb42bSAlexandre Chartre 	return (msg);
11449bfb42bSAlexandre Chartre }
11549bfb42bSAlexandre Chartre 
11649bfb42bSAlexandre Chartre /*
11749bfb42bSAlexandre Chartre  * Allocate a result message (LDMA_MSG_REQ_RESULT) with the specified message
11849bfb42bSAlexandre Chartre  * data length (msg_dlen). If the request argument is not NULL then the message
11949bfb42bSAlexandre Chartre  * is created with the same message number as the request, otherwise the message
12049bfb42bSAlexandre Chartre  * number is set to 0. Return NULL if the allocation has failed.
12149bfb42bSAlexandre Chartre  */
12249bfb42bSAlexandre Chartre ldma_message_header_t *
12349bfb42bSAlexandre Chartre ldma_alloc_result_msg(ldma_message_header_t *request, size_t msg_dlen)
12449bfb42bSAlexandre Chartre {
12549bfb42bSAlexandre Chartre 	uint64_t msg_num;
12649bfb42bSAlexandre Chartre 
12749bfb42bSAlexandre Chartre 	msg_num = (request == NULL)? 0 : request->msg_num;
12849bfb42bSAlexandre Chartre 
12949bfb42bSAlexandre Chartre 	return (ldma_alloc_msg(msg_num, LDMA_MSG_RESULT, msg_dlen));
13049bfb42bSAlexandre Chartre }
13149bfb42bSAlexandre Chartre 
13249bfb42bSAlexandre Chartre /*
13349bfb42bSAlexandre Chartre  * Agent register callback. This callback is invoked when a client is registered
13449bfb42bSAlexandre Chartre  * for using the service provided by an agent. An agent will only have one
13549bfb42bSAlexandre Chartre  * consumer which is coming from the control domain.
13649bfb42bSAlexandre Chartre  */
13749bfb42bSAlexandre Chartre static void
13849bfb42bSAlexandre Chartre ldma_reg_cb(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver,
13949bfb42bSAlexandre Chartre     ds_domain_hdl_t dhdl)
14049bfb42bSAlexandre Chartre {
14149bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
14249bfb42bSAlexandre Chartre 	char dname[LDMA_DOMAIN_NAME_MAXLEN];
14349bfb42bSAlexandre Chartre 
14449bfb42bSAlexandre Chartre 	if (ds_dom_hdl_to_name(dhdl, dname, LDMA_DOMAIN_NAME_MAXLEN) != 0) {
14549bfb42bSAlexandre Chartre 		(void) strcpy(dname, "<unknown>");
14649bfb42bSAlexandre Chartre 	}
14749bfb42bSAlexandre Chartre 
14849bfb42bSAlexandre Chartre 	LDMA_DBG("%s: REGISTER hdl=%llx, dhdl=%llx (%s) ver=%hd.%hd",
14949bfb42bSAlexandre Chartre 	    agent->info->name, hdl, dhdl, dname, ver->major, ver->minor);
15049bfb42bSAlexandre Chartre 
15149bfb42bSAlexandre Chartre 	/*
15249bfb42bSAlexandre Chartre 	 * Record client information if the connexion is from the control
15349bfb42bSAlexandre Chartre 	 * domain. The domain service framework only allows connexion of a
15449bfb42bSAlexandre Chartre 	 * domain with the control domain. However, if the agent is running
15549bfb42bSAlexandre Chartre 	 * on the control domain then it can see connexions coming from any
15649bfb42bSAlexandre Chartre 	 * domains. That's why we explicitly have to check if the connexion
15749bfb42bSAlexandre Chartre 	 * is effectively with the control domain.
15849bfb42bSAlexandre Chartre 	 */
15949bfb42bSAlexandre Chartre 	if (dhdl == LDMA_CONTROL_DOMAIN_DHDL) {
16049bfb42bSAlexandre Chartre 		agent->conn_hdl = hdl;
16149bfb42bSAlexandre Chartre 		agent->conn_ver.major = ver->major;
16249bfb42bSAlexandre Chartre 		agent->conn_ver.minor = ver->minor;
16349bfb42bSAlexandre Chartre 	} else {
16449bfb42bSAlexandre Chartre 		LDMA_INFO("agent %s will ignore any request from distrusted "
16549bfb42bSAlexandre Chartre 		    "domain %s", agent->info->name, dname);
16649bfb42bSAlexandre Chartre 	}
16749bfb42bSAlexandre Chartre }
16849bfb42bSAlexandre Chartre 
16949bfb42bSAlexandre Chartre /*
17049bfb42bSAlexandre Chartre  * Agent unregister callback. This callback is invoked when a client is
17149bfb42bSAlexandre Chartre  * unregistered and stops using the service provided by an agent.
17249bfb42bSAlexandre Chartre  */
17349bfb42bSAlexandre Chartre static void
17449bfb42bSAlexandre Chartre ldma_unreg_cb(ds_hdl_t hdl, ds_cb_arg_t arg)
17549bfb42bSAlexandre Chartre {
17649bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
17749bfb42bSAlexandre Chartre 
17849bfb42bSAlexandre Chartre 	LDMA_DBG("%s: UNREGISTER hdl=%llx", agent->info->name, hdl);
17949bfb42bSAlexandre Chartre 
18049bfb42bSAlexandre Chartre 	if (agent->conn_hdl == hdl) {
18149bfb42bSAlexandre Chartre 		agent->conn_hdl = 0;
18249bfb42bSAlexandre Chartre 		agent->conn_ver.major = 0;
18349bfb42bSAlexandre Chartre 		agent->conn_ver.minor = 0;
18449bfb42bSAlexandre Chartre 	} else {
18549bfb42bSAlexandre Chartre 		LDMA_INFO("agent %s has unregistered consumer from "
18649bfb42bSAlexandre Chartre 		    "distrusted domain", agent->info->name);
18749bfb42bSAlexandre Chartre 	}
18849bfb42bSAlexandre Chartre }
18949bfb42bSAlexandre Chartre 
19049bfb42bSAlexandre Chartre /*
19149bfb42bSAlexandre Chartre  * Agent data callback. This callback is invoked when an agent receives a new
19249bfb42bSAlexandre Chartre  * message from a client. Any request from a client which is not the control
19349bfb42bSAlexandre Chartre  * domain is immediatly rejected. Otherwise the message is forwarded to the
19449bfb42bSAlexandre Chartre  * appropriate handler function provided by the agent, depending on the message
19549bfb42bSAlexandre Chartre  * type.
19649bfb42bSAlexandre Chartre  */
19749bfb42bSAlexandre Chartre static void
19849bfb42bSAlexandre Chartre ldma_data_cb(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf, size_t len)
19949bfb42bSAlexandre Chartre {
20049bfb42bSAlexandre Chartre 	ldma_agent_t *agent = (ldma_agent_t *)arg;
20149bfb42bSAlexandre Chartre 	ldma_msg_handler_t *handler;
20249bfb42bSAlexandre Chartre 	ldma_message_header_t *request = buf;
20349bfb42bSAlexandre Chartre 	ldma_message_header_t *reply = NULL;
20449bfb42bSAlexandre Chartre 	ldma_request_status_t status;
20549bfb42bSAlexandre Chartre 	size_t request_dlen, reply_len, reply_dlen = 0;
20649bfb42bSAlexandre Chartre 	int i;
20749bfb42bSAlexandre Chartre 
20849bfb42bSAlexandre Chartre 	/* check the message size */
20949bfb42bSAlexandre Chartre 	if (len < LDMA_MESSAGE_HEADER_SIZE) {
21049bfb42bSAlexandre Chartre 		LDMA_INFO("agent %s has ignored message with an invalid "
21149bfb42bSAlexandre Chartre 		    "size of %d bytes", agent->info->name, len);
21249bfb42bSAlexandre Chartre 		return;
21349bfb42bSAlexandre Chartre 	}
21449bfb42bSAlexandre Chartre 
21549bfb42bSAlexandre Chartre 	request_dlen = LDMA_MESSAGE_DLEN(len);
21649bfb42bSAlexandre Chartre 
21749bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, request num=%llu type=0x%x info=0x%x "
21849bfb42bSAlexandre Chartre 	    "dlen=%d", agent->info->name, hdl, request->msg_num,
21949bfb42bSAlexandre Chartre 	    request->msg_type, request->msg_info, request_dlen);
22049bfb42bSAlexandre Chartre 
22149bfb42bSAlexandre Chartre 	/* reject any request which is not from the control domain */
22249bfb42bSAlexandre Chartre 	if (hdl != agent->conn_hdl) {
22349bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
22449bfb42bSAlexandre Chartre 		    "distrusted domain", agent->info->name, hdl);
22549bfb42bSAlexandre Chartre 		status = LDMA_REQ_DENIED;
22649bfb42bSAlexandre Chartre 		goto do_reply;
22749bfb42bSAlexandre Chartre 	}
22849bfb42bSAlexandre Chartre 
22949bfb42bSAlexandre Chartre 	handler = NULL;
23049bfb42bSAlexandre Chartre 
23149bfb42bSAlexandre Chartre 	for (i = 0; i < agent->info->nhandlers; i++) {
23249bfb42bSAlexandre Chartre 		if (agent->info->handlers[i].msg_type == request->msg_type) {
23349bfb42bSAlexandre Chartre 			handler = &agent->info->handlers[i];
23449bfb42bSAlexandre Chartre 			break;
23549bfb42bSAlexandre Chartre 		}
23649bfb42bSAlexandre Chartre 	}
23749bfb42bSAlexandre Chartre 
23849bfb42bSAlexandre Chartre 	if (handler == NULL) {
23949bfb42bSAlexandre Chartre 		/* this type of message is not defined by the agent */
24049bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, unknown message type %x",
24149bfb42bSAlexandre Chartre 		    agent->info->name, hdl, request->msg_type);
24249bfb42bSAlexandre Chartre 		status = LDMA_REQ_NOTSUP;
24349bfb42bSAlexandre Chartre 		goto do_reply;
24449bfb42bSAlexandre Chartre 	}
24549bfb42bSAlexandre Chartre 
24649bfb42bSAlexandre Chartre 	if (handler->msg_handler == NULL) {
24749bfb42bSAlexandre Chartre 		/*
24849bfb42bSAlexandre Chartre 		 * This type of message is defined by the agent but it
24949bfb42bSAlexandre Chartre 		 * has no handler. That means there is no processing to
25049bfb42bSAlexandre Chartre 		 * do, the message is just ignored, but the request is
25149bfb42bSAlexandre Chartre 		 * successfully completed.
25249bfb42bSAlexandre Chartre 		 */
25349bfb42bSAlexandre Chartre 		LDMA_DBG("%s: DATA hdl=%llx, no handler",
25449bfb42bSAlexandre Chartre 		    agent->info->name, hdl);
25549bfb42bSAlexandre Chartre 		status = LDMA_REQ_COMPLETED;
25649bfb42bSAlexandre Chartre 		goto do_reply;
25749bfb42bSAlexandre Chartre 	}
25849bfb42bSAlexandre Chartre 
25949bfb42bSAlexandre Chartre 	/* invoke the message handler of the agent */
26049bfb42bSAlexandre Chartre 	status = (*handler->msg_handler)(&agent->conn_ver, request,
26149bfb42bSAlexandre Chartre 	    request_dlen, &reply, &reply_dlen);
26249bfb42bSAlexandre Chartre 
26349bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, handler stat=%d reply=%p rlen=%d",
26449bfb42bSAlexandre Chartre 	    agent->info->name, hdl, status, (void *)reply, reply_dlen);
26549bfb42bSAlexandre Chartre 
26649bfb42bSAlexandre Chartre do_reply:
26749bfb42bSAlexandre Chartre 	/*
26849bfb42bSAlexandre Chartre 	 * If the handler has provided a reply message, we use it directly.
26949bfb42bSAlexandre Chartre 	 * Otherwise, we build a reply depending on the status of the request.
27049bfb42bSAlexandre Chartre 	 * In that case, we re-use the request buffer to build the reply
27149bfb42bSAlexandre Chartre 	 * message.
27249bfb42bSAlexandre Chartre 	 */
27349bfb42bSAlexandre Chartre 	if (reply == NULL) {
27449bfb42bSAlexandre Chartre 
27549bfb42bSAlexandre Chartre 		reply = request;
27649bfb42bSAlexandre Chartre 		reply_dlen = 0;
27749bfb42bSAlexandre Chartre 
27849bfb42bSAlexandre Chartre 		if (status == LDMA_REQ_COMPLETED) {
27949bfb42bSAlexandre Chartre 			/*
28049bfb42bSAlexandre Chartre 			 * The request was successful but no result message was
28149bfb42bSAlexandre Chartre 			 * provided so we send an empty result message.
28249bfb42bSAlexandre Chartre 			 */
28349bfb42bSAlexandre Chartre 			reply->msg_type = LDMA_MSG_RESULT;
28449bfb42bSAlexandre Chartre 			reply->msg_info = 0;
28549bfb42bSAlexandre Chartre 
28649bfb42bSAlexandre Chartre 		} else {
28749bfb42bSAlexandre Chartre 			/*
28849bfb42bSAlexandre Chartre 			 * The request has failed but no error message was
28949bfb42bSAlexandre Chartre 			 * provided so we send an error message based on the
29049bfb42bSAlexandre Chartre 			 * request status.
29149bfb42bSAlexandre Chartre 			 */
29249bfb42bSAlexandre Chartre 			reply->msg_type = LDMA_MSG_ERROR;
29349bfb42bSAlexandre Chartre 			reply->msg_info =
29449bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_NOTSUP)? LDMA_MSGERR_NOTSUP :
29549bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_INVALID)? LDMA_MSGERR_INVALID :
29649bfb42bSAlexandre Chartre 			    (status == LDMA_REQ_DENIED)? LDMA_MSGERR_DENY :
29749bfb42bSAlexandre Chartre 			    LDMA_MSGERR_FAIL;
29849bfb42bSAlexandre Chartre 		}
29949bfb42bSAlexandre Chartre 	}
30049bfb42bSAlexandre Chartre 
30149bfb42bSAlexandre Chartre 	reply_len = LDMA_MESSAGE_SIZE(reply_dlen);
30249bfb42bSAlexandre Chartre 
30349bfb42bSAlexandre Chartre 	LDMA_DBG("%s: DATA hdl=%llx, reply num=%llu type=0x%x info=0x%x "
30449bfb42bSAlexandre Chartre 	    "dlen=%d", agent->info->name, hdl, reply->msg_num,
30549bfb42bSAlexandre Chartre 	    reply->msg_type, reply->msg_info, reply_dlen);
30649bfb42bSAlexandre Chartre 
30749bfb42bSAlexandre Chartre 	if (ds_send_msg(hdl, reply, reply_len) != 0) {
30849bfb42bSAlexandre Chartre 		LDMA_ERR("agent %s has failed to send reply for request %llu",
30949bfb42bSAlexandre Chartre 		    agent->info->name, request->msg_num);
31049bfb42bSAlexandre Chartre 	}
31149bfb42bSAlexandre Chartre 
31249bfb42bSAlexandre Chartre 	if (reply != request)
31349bfb42bSAlexandre Chartre 		free(reply);
31449bfb42bSAlexandre Chartre }
31549bfb42bSAlexandre Chartre 
31649bfb42bSAlexandre Chartre /*
31749bfb42bSAlexandre Chartre  * Register an agent. Return 0 if the agent was successfully registered.
31849bfb42bSAlexandre Chartre  */
31949bfb42bSAlexandre Chartre static int
32049bfb42bSAlexandre Chartre ldma_register(ldma_agent_info_t *agent_info)
32149bfb42bSAlexandre Chartre {
32249bfb42bSAlexandre Chartre 	ldma_agent_t	*agent;
32349bfb42bSAlexandre Chartre 	ds_capability_t	ds_cap;
32449bfb42bSAlexandre Chartre 	ds_ops_t	ds_ops;
32549bfb42bSAlexandre Chartre 
32649bfb42bSAlexandre Chartre 	agent = malloc(sizeof (ldma_agent_t));
32749bfb42bSAlexandre Chartre 	if (agent == NULL)
32849bfb42bSAlexandre Chartre 		goto register_fail;
32949bfb42bSAlexandre Chartre 
33049bfb42bSAlexandre Chartre 	agent->info = agent_info;
33149bfb42bSAlexandre Chartre 	agent->conn_hdl = 0;
33249bfb42bSAlexandre Chartre 	agent->conn_ver.major = 0;
33349bfb42bSAlexandre Chartre 	agent->conn_ver.minor = 0;
33449bfb42bSAlexandre Chartre 
33549bfb42bSAlexandre Chartre 	ds_cap.svc_id = agent_info->name;
33649bfb42bSAlexandre Chartre 	ds_cap.vers = agent_info->vers;
33749bfb42bSAlexandre Chartre 	ds_cap.nvers = agent_info->nvers;
33849bfb42bSAlexandre Chartre 
33949bfb42bSAlexandre Chartre 	ds_ops.ds_reg_cb = ldma_reg_cb;
34049bfb42bSAlexandre Chartre 	ds_ops.ds_unreg_cb = ldma_unreg_cb;
34149bfb42bSAlexandre Chartre 	ds_ops.ds_data_cb = ldma_data_cb;
34249bfb42bSAlexandre Chartre 	ds_ops.cb_arg = agent;
34349bfb42bSAlexandre Chartre 
34449bfb42bSAlexandre Chartre 	if (ds_svc_reg(&ds_cap, &ds_ops) == 0) {
34549bfb42bSAlexandre Chartre 		LDMA_INFO("agent %s registered", agent_info->name);
34649bfb42bSAlexandre Chartre 		return (0);
34749bfb42bSAlexandre Chartre 	}
34849bfb42bSAlexandre Chartre 
34949bfb42bSAlexandre Chartre register_fail:
35049bfb42bSAlexandre Chartre 
35149bfb42bSAlexandre Chartre 	LDMA_ERR("agent %s has failed to register", agent_info->name);
35249bfb42bSAlexandre Chartre 	free(agent);
35349bfb42bSAlexandre Chartre 	return (-1);
35449bfb42bSAlexandre Chartre }
35549bfb42bSAlexandre Chartre 
35649bfb42bSAlexandre Chartre /*
35749bfb42bSAlexandre Chartre  * Register all known agents. Return the number of agents successfully
35849bfb42bSAlexandre Chartre  * registered.
35949bfb42bSAlexandre Chartre  */
36049bfb42bSAlexandre Chartre static int
36149bfb42bSAlexandre Chartre ldma_register_agents()
36249bfb42bSAlexandre Chartre {
36349bfb42bSAlexandre Chartre 	int count = 0;
36449bfb42bSAlexandre Chartre 	ldma_agent_info_t **agent_infop;
36549bfb42bSAlexandre Chartre 
36649bfb42bSAlexandre Chartre 	for (agent_infop = ldma_agent_infos;
36749bfb42bSAlexandre Chartre 	    *agent_infop != NULL; agent_infop++) {
36849bfb42bSAlexandre Chartre 
36949bfb42bSAlexandre Chartre 		if (ldma_register(*agent_infop) == 0)
37049bfb42bSAlexandre Chartre 			count++;
37149bfb42bSAlexandre Chartre 	}
37249bfb42bSAlexandre Chartre 
37349bfb42bSAlexandre Chartre 	return (count);
37449bfb42bSAlexandre Chartre }
37549bfb42bSAlexandre Chartre 
37649bfb42bSAlexandre Chartre /*ARGSUSED*/
37749bfb42bSAlexandre Chartre static void
37849bfb42bSAlexandre Chartre ldma_sigusr_handler(int sig, siginfo_t *sinfo, void *ucontext)
37949bfb42bSAlexandre Chartre {
380*6b8303caSAlexandre Chartre 	/*
381*6b8303caSAlexandre Chartre 	 * The child process can send the signal before the fork()
382*6b8303caSAlexandre Chartre 	 * call has returned in the parent process. So daemon_pid
383*6b8303caSAlexandre Chartre 	 * may not be set yet, and we don't check the pid in that
384*6b8303caSAlexandre Chartre 	 * case.
385*6b8303caSAlexandre Chartre 	 */
386*6b8303caSAlexandre Chartre 	if (sig != SIGUSR1 || sinfo->si_code != SI_USER ||
387*6b8303caSAlexandre Chartre 	    (daemon_pid > 0 && sinfo->si_pid != daemon_pid))
38849bfb42bSAlexandre Chartre 		return;
38949bfb42bSAlexandre Chartre 
39049bfb42bSAlexandre Chartre 	/*
39149bfb42bSAlexandre Chartre 	 * The parent process has received a USR1 signal from the child.
39249bfb42bSAlexandre Chartre 	 * This means that the daemon has correctly started and the parent
39349bfb42bSAlexandre Chartre 	 * can exit.
39449bfb42bSAlexandre Chartre 	 */
39549bfb42bSAlexandre Chartre 	exit(0);
39649bfb42bSAlexandre Chartre }
39749bfb42bSAlexandre Chartre 
39849bfb42bSAlexandre Chartre static void
39949bfb42bSAlexandre Chartre ldma_start(boolean_t standalone)
40049bfb42bSAlexandre Chartre {
40149bfb42bSAlexandre Chartre 	int stat, rv;
40249bfb42bSAlexandre Chartre 	struct sigaction action;
40349bfb42bSAlexandre Chartre 
40449bfb42bSAlexandre Chartre 	if (!standalone) {
40549bfb42bSAlexandre Chartre 		/*
40649bfb42bSAlexandre Chartre 		 * Some configuration of the daemon has to be done in the
40749bfb42bSAlexandre Chartre 		 * child, but we want the parent to report if the daemon
40849bfb42bSAlexandre Chartre 		 * has successfully started or not. So we setup a signal
40949bfb42bSAlexandre Chartre 		 * handler, and the child will notify the parent using the
41049bfb42bSAlexandre Chartre 		 * USR1 signal if the setup was successful. Otherwise the
41149bfb42bSAlexandre Chartre 		 * child will exit.
41249bfb42bSAlexandre Chartre 		 */
41349bfb42bSAlexandre Chartre 		action.sa_sigaction = ldma_sigusr_handler;
41449bfb42bSAlexandre Chartre 		action.sa_flags = SA_SIGINFO;
41549bfb42bSAlexandre Chartre 
41649bfb42bSAlexandre Chartre 		if (sigemptyset(&action.sa_mask) == -1) {
41749bfb42bSAlexandre Chartre 			LDMA_ERR("sigemptyset error (%d)", errno);
41849bfb42bSAlexandre Chartre 			exit(1);
41949bfb42bSAlexandre Chartre 		}
42049bfb42bSAlexandre Chartre 
42149bfb42bSAlexandre Chartre 		if (sigaction(SIGUSR1, &action, NULL) == -1) {
42249bfb42bSAlexandre Chartre 			LDMA_ERR("sigaction() error (%d)", errno);
42349bfb42bSAlexandre Chartre 			exit(1);
42449bfb42bSAlexandre Chartre 		}
42549bfb42bSAlexandre Chartre 
42649bfb42bSAlexandre Chartre 		if (sigrelse(SIGUSR1) == -1) {
42749bfb42bSAlexandre Chartre 			LDMA_ERR("sigrelse() error (%d)", errno);
42849bfb42bSAlexandre Chartre 			exit(1);
42949bfb42bSAlexandre Chartre 		}
43049bfb42bSAlexandre Chartre 
43149bfb42bSAlexandre Chartre 		if ((daemon_pid = fork()) == -1) {
43249bfb42bSAlexandre Chartre 			LDMA_ERR("fork() error (%d)", errno);
43349bfb42bSAlexandre Chartre 			exit(1);
43449bfb42bSAlexandre Chartre 		}
43549bfb42bSAlexandre Chartre 
43649bfb42bSAlexandre Chartre 		if (daemon_pid != 0) {
43749bfb42bSAlexandre Chartre 			/*
43849bfb42bSAlexandre Chartre 			 * The parent process waits until the child exits (in
43949bfb42bSAlexandre Chartre 			 * case of an error) or sends a USR1 signal (if the
44049bfb42bSAlexandre Chartre 			 * daemon has correctly started).
44149bfb42bSAlexandre Chartre 			 */
44249bfb42bSAlexandre Chartre 			for (;;) {
44349bfb42bSAlexandre Chartre 				rv = waitpid(daemon_pid, &stat, 0);
44449bfb42bSAlexandre Chartre 				if ((rv == daemon_pid && WIFEXITED(stat)) ||
44549bfb42bSAlexandre Chartre 				    (rv == -1 && errno != EINTR)) {
44649bfb42bSAlexandre Chartre 					/* child has exited or error */
44749bfb42bSAlexandre Chartre 					exit(1);
44849bfb42bSAlexandre Chartre 				}
44949bfb42bSAlexandre Chartre 			}
45049bfb42bSAlexandre Chartre 		}
45149bfb42bSAlexandre Chartre 
45249bfb42bSAlexandre Chartre 		/*
45349bfb42bSAlexandre Chartre 		 * Initialize child process
45449bfb42bSAlexandre Chartre 		 */
45549bfb42bSAlexandre Chartre 		if (sighold(SIGUSR1) == -1) {
45649bfb42bSAlexandre Chartre 			LDMA_ERR("sighold error (%d)", errno);
45749bfb42bSAlexandre Chartre 			exit(1);
45849bfb42bSAlexandre Chartre 		}
45949bfb42bSAlexandre Chartre 
46049bfb42bSAlexandre Chartre 		if (sigignore(SIGUSR1) == -1) {
46149bfb42bSAlexandre Chartre 			LDMA_ERR("sigignore error (%d)", errno);
46249bfb42bSAlexandre Chartre 			exit(1);
46349bfb42bSAlexandre Chartre 		}
46449bfb42bSAlexandre Chartre 
46549bfb42bSAlexandre Chartre 		if (setsid() == -1) {
46649bfb42bSAlexandre Chartre 			LDMA_ERR("setsid error (%d)", errno);
46749bfb42bSAlexandre Chartre 			exit(1);
46849bfb42bSAlexandre Chartre 		}
46949bfb42bSAlexandre Chartre 
47049bfb42bSAlexandre Chartre 		if (chdir("/") == -1) {
47149bfb42bSAlexandre Chartre 			LDMA_ERR("chdir error (%d)", errno);
47249bfb42bSAlexandre Chartre 			exit(1);
47349bfb42bSAlexandre Chartre 		}
47449bfb42bSAlexandre Chartre 		(void) umask(0);
47549bfb42bSAlexandre Chartre 
47649bfb42bSAlexandre Chartre 		/*
47749bfb42bSAlexandre Chartre 		 * Initialize file descriptors. Do not touch stderr
47849bfb42bSAlexandre Chartre 		 * which is initialized by SMF to point to the daemon
47949bfb42bSAlexandre Chartre 		 * specific log file.
48049bfb42bSAlexandre Chartre 		 */
48149bfb42bSAlexandre Chartre 		(void) close(STDIN_FILENO);
48249bfb42bSAlexandre Chartre 		if (open("/dev/null", O_RDWR) == -1) {
48349bfb42bSAlexandre Chartre 			LDMA_ERR("open /dev/null error (%d)", errno);
48449bfb42bSAlexandre Chartre 			exit(1);
48549bfb42bSAlexandre Chartre 		}
48649bfb42bSAlexandre Chartre 		if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) {
48749bfb42bSAlexandre Chartre 			LDMA_ERR("dup2 error (%d)", errno);
48849bfb42bSAlexandre Chartre 			exit(1);
48949bfb42bSAlexandre Chartre 		}
49049bfb42bSAlexandre Chartre 		closefrom(STDERR_FILENO + 1);
49149bfb42bSAlexandre Chartre 
49249bfb42bSAlexandre Chartre 		/* initialize logging */
49349bfb42bSAlexandre Chartre 		openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
49449bfb42bSAlexandre Chartre 
49549bfb42bSAlexandre Chartre 		ldma_daemon = B_TRUE;
49649bfb42bSAlexandre Chartre 	}
49749bfb42bSAlexandre Chartre 
49849bfb42bSAlexandre Chartre 	/*
49949bfb42bSAlexandre Chartre 	 * Register the agents. It would be easier to do this before
50049bfb42bSAlexandre Chartre 	 * daemonizing so that any start error is directly reported. But
50149bfb42bSAlexandre Chartre 	 * this can not be done because agents are registered using libds
50249bfb42bSAlexandre Chartre 	 * and this will subscribe the daemon to some sysevents which is
50349bfb42bSAlexandre Chartre 	 * a process based subscription. Instead we notify the parent process
50449bfb42bSAlexandre Chartre 	 * either by exiting, or by sending a SIGUSR1 signal.
50549bfb42bSAlexandre Chartre 	 */
50649bfb42bSAlexandre Chartre 	if (ldma_register_agents() == 0) {
50749bfb42bSAlexandre Chartre 		/* no agent registered */
508*6b8303caSAlexandre Chartre 		LDMA_ERR("Unable to register any agent");
50949bfb42bSAlexandre Chartre 		exit(1);
51049bfb42bSAlexandre Chartre 	}
51149bfb42bSAlexandre Chartre 
51249bfb42bSAlexandre Chartre 	if (!standalone) {
51349bfb42bSAlexandre Chartre 		/* signal parent that startup was successful */
51449bfb42bSAlexandre Chartre 		if (kill(getppid(), SIGUSR1) == -1)
51549bfb42bSAlexandre Chartre 			exit(1);
51649bfb42bSAlexandre Chartre 	}
51749bfb42bSAlexandre Chartre }
51849bfb42bSAlexandre Chartre 
51949bfb42bSAlexandre Chartre static void
52049bfb42bSAlexandre Chartre ldma_usage()
52149bfb42bSAlexandre Chartre {
52249bfb42bSAlexandre Chartre 	(void) fprintf(stderr, "usage: %s\n", cmdname);
52349bfb42bSAlexandre Chartre }
52449bfb42bSAlexandre Chartre 
52549bfb42bSAlexandre Chartre int
52649bfb42bSAlexandre Chartre main(int argc, char *argv[])
52749bfb42bSAlexandre Chartre {
52849bfb42bSAlexandre Chartre 	int opt;
52949bfb42bSAlexandre Chartre 	boolean_t standalone = B_FALSE;
53049bfb42bSAlexandre Chartre 
53149bfb42bSAlexandre Chartre 	cmdname = basename(argv[0]);
53249bfb42bSAlexandre Chartre 
53349bfb42bSAlexandre Chartre 	/* disable getopt error messages */
53449bfb42bSAlexandre Chartre 	opterr = 0;
53549bfb42bSAlexandre Chartre 
53649bfb42bSAlexandre Chartre 	while ((opt = getopt(argc, argv, "ds")) != EOF) {
53749bfb42bSAlexandre Chartre 
53849bfb42bSAlexandre Chartre 		switch (opt) {
53949bfb42bSAlexandre Chartre 		case 'd':
54049bfb42bSAlexandre Chartre 			ldma_debug = B_TRUE;
54149bfb42bSAlexandre Chartre 			break;
54249bfb42bSAlexandre Chartre 		case 's':
54349bfb42bSAlexandre Chartre 			standalone = B_TRUE;
54449bfb42bSAlexandre Chartre 			break;
54549bfb42bSAlexandre Chartre 		default:
54649bfb42bSAlexandre Chartre 			ldma_usage();
54749bfb42bSAlexandre Chartre 			exit(1);
54849bfb42bSAlexandre Chartre 		}
54949bfb42bSAlexandre Chartre 	}
55049bfb42bSAlexandre Chartre 
55149bfb42bSAlexandre Chartre 	ldma_start(standalone);
55249bfb42bSAlexandre Chartre 
55349bfb42bSAlexandre Chartre 	/*
55449bfb42bSAlexandre Chartre 	 * Loop forever. Any incoming message will be received by libds and
55549bfb42bSAlexandre Chartre 	 * forwarded to the agent data callback (ldma_data_cb()) where it
55649bfb42bSAlexandre Chartre 	 * will be processed.
55749bfb42bSAlexandre Chartre 	 */
55849bfb42bSAlexandre Chartre 	for (;;) {
55949bfb42bSAlexandre Chartre 		(void) pause();
56049bfb42bSAlexandre Chartre 	}
56149bfb42bSAlexandre Chartre 
56249bfb42bSAlexandre Chartre 	/*NOTREACHED*/
56349bfb42bSAlexandre Chartre 	return (0);
56449bfb42bSAlexandre Chartre }
565