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