ldmad.c revision b63861bbdaea40d18f3f62a70cc3978d21a14013
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
*/
/*
* Logical Domains (LDoms) Agents Daemon
*
* The LDoms agents daemon (ldmad) runs on LDoms domains and provides
* information to the control domain. It is composed of a set of agents
* which can send and receive messages to and from the control domain.
* Each agent is registered as a domain service using the libds library,
* and is able to handle requests coming from the control domain.
*
* The control domain sends requests to an agent as messages on the
* corresponding domain service (identified by the agent name). All requests
* are received by the ldmad daemon which dispatches them to the appropriate
* handler function of the agent depending on the type of the message.
*
* After the request has been processed by the handler, the ldmad daemon sent
* a reply message back to the control domain. The reply is either a result
* message if the request was successfully completed, or an error message
* describing the failure.
*/
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <libds.h>
#include <libgen.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <synch.h>
#include <syslog.h>
#include <thread.h>
#include <unistd.h>
#include "ldma.h"
#define LDMA_MODULE "ldm-agent-daemon"
#define LDMA_CONTROL_DOMAIN_DHDL 0 /* id of the control domain */
typedef struct ldma_connexion_t {
typedef struct ldma_agent {
} ldma_agent_t;
/* information about existing agents */
extern ldma_agent_info_t ldma_device_info;
extern ldma_agent_info_t ldma_system_info;
extern ldma_agent_info_t ldma_dio_info;
static ldma_agent_info_t *ldma_agent_infos[] = {
};
static char *cmdname;
static pid_t daemon_pid = 0;
/*
* Lookup connexion in agent connexion table.
*/
static ldma_connexion_t *
{
int i;
return (connp);
}
return (NULL);
}
/*
* Add connextion to agent connexion table.
*/
static int
{
int i;
break;
}
if (i < LDOMS_MAX_DOMAINS) {
hdl);
return (0);
}
if (!availp) {
return (0);
}
LDMA_DBG("agent %s adding connection (%x) %llx, %llx, %d.%d",
return (1);
}
/*
* Delete connexion from agent connexion table.
*/
static int
{
LDMA_INFO("agent %s connection delete failed to find %llx",
return (0);
}
return (1);
}
/*
* Initialize connexion table.
*/
static void
{
int i;
}
}
/*
* Allocate a new message with the specified message number (msg_num),
* message type (msg_type) and message data length (msg_dlen). Return
* NULL if the allocation has failed.
*/
static ldma_message_header_t *
{
return (NULL);
return (msg);
}
/*
* Allocate a result message (LDMA_MSG_REQ_RESULT) with the specified message
* data length (msg_dlen). If the request argument is not NULL then the message
* is created with the same message number as the request, otherwise the message
* number is set to 0. Return NULL if the allocation has failed.
*/
{
}
/*
* Agent register callback. This callback is invoked when a client is registered
* for using the service provided by an agent. An agent will only have one
* consumer which is coming from the control domain.
*/
static void
{
char dname[LDOMS_MAX_NAME_LEN];
}
LDMA_DBG("%s: REGISTER hdl=%llx, dhdl=%llx (%s) ver=%hd.%hd",
/*
* Record client information. Access control is done on a
* message-by-message basis upon receipt of the message.
*/
LDMA_INFO("agent %s failed to add connection from "
}
}
/*
* Agent unregister callback. This callback is invoked when a client is
* unregistered and stops using the service provided by an agent.
*/
static void
{
LDMA_INFO("agent %s failed to unregister handle %llx",
}
}
/*
* Agent data callback. This callback is invoked when an agent receives a new
* message from a client. Any request from a client which is not the control
* domain is immediatly rejected. Otherwise the message is forwarded to the
* appropriate handler function provided by the agent, depending on the message
* type.
*/
static void
{
int i;
/* check the message size */
if (len < LDMA_MESSAGE_HEADER_SIZE) {
LDMA_INFO("agent %s has ignored message with an invalid "
return;
}
LDMA_DBG("%s: DATA hdl=%llx, request num=%llu type=0x%x info=0x%x "
}
/* reject any request which is not in the connexion table */
LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
goto do_reply;
}
break;
}
}
/* this type of message is not defined by the agent */
LDMA_DBG("%s: DATA hdl=%llx, unknown message type %x",
goto do_reply;
}
/* reject any request from a guest which is not allowed */
if ((conn_dhdl != LDMA_CONTROL_DOMAIN_DHDL) &&
LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
goto do_reply;
}
/*
* This type of message is defined by the agent but it
* has no handler. That means there is no processing to
* do, the message is just ignored, but the request is
* successfully completed.
*/
LDMA_DBG("%s: DATA hdl=%llx, no handler",
goto do_reply;
}
/* invoke the message handler of the agent */
&reply, &reply_dlen);
LDMA_DBG("%s: DATA hdl=%llx, handler stat=%d reply=%p rlen=%d",
/*
* If the handler has provided a reply message, we use it directly.
* Otherwise, we build a reply depending on the status of the request.
* In that case, we re-use the request buffer to build the reply
* message.
*/
reply_dlen = 0;
if (status == LDMA_REQ_COMPLETED) {
/*
* The request was successful but no result message was
* provided so we send an empty result message.
*/
} else {
/*
* The request has failed but no error message was
* provided so we send an error message based on the
* request status.
*/
}
}
LDMA_DBG("%s: DATA hdl=%llx, reply num=%llu type=0x%x info=0x%x "
LDMA_ERR("agent %s has failed to send reply for request %llu",
}
}
/*
* Register an agent. Return 0 if the agent was successfully registered.
*/
static int
{
goto register_fail;
return (0);
}
return (-1);
}
/*
* Register all known agents. Return the number of agents successfully
* registered.
*/
static int
{
int count = 0;
for (agent_infop = ldma_agent_infos;
if (ldma_register(*agent_infop) == 0)
count++;
}
return (count);
}
/*ARGSUSED*/
static void
{
/*
* The child process can send the signal before the fork()
* call has returned in the parent process. So daemon_pid
* may not be set yet, and we don't check the pid in that
* case.
*/
return;
/*
* The parent process has received a USR1 signal from the child.
* This means that the daemon has correctly started and the parent
* can exit.
*/
exit(0);
}
static void
{
if (!standalone) {
/*
* Some configuration of the daemon has to be done in the
* child, but we want the parent to report if the daemon
* has successfully started or not. So we setup a signal
* handler, and the child will notify the parent using the
* USR1 signal if the setup was successful. Otherwise the
* child will exit.
*/
exit(1);
}
exit(1);
}
exit(1);
}
exit(1);
}
if (daemon_pid != 0) {
/*
* The parent process waits until the child exits (in
* case of an error) or sends a USR1 signal (if the
* daemon has correctly started).
*/
for (;;) {
/* child has exited or error */
exit(1);
}
}
}
/*
* Initialize child process
*/
exit(1);
}
exit(1);
}
if (setsid() == -1) {
exit(1);
}
exit(1);
}
(void) umask(0);
/*
* Initialize file descriptors. Do not touch stderr
* which is initialized by SMF to point to the daemon
* specific log file.
*/
(void) close(STDIN_FILENO);
exit(1);
}
exit(1);
}
/* initialize logging */
}
/*
* Register the agents. It would be easier to do this before
* daemonizing so that any start error is directly reported. But
* this can not be done because agents are registered using libds
* and this will subscribe the daemon to some sysevents which is
* a process based subscription. Instead we notify the parent process
* either by exiting, or by sending a SIGUSR1 signal.
*/
if (ldma_register_agents() == 0) {
/* no agent registered */
LDMA_ERR("Unable to register any agent");
exit(1);
}
if (!standalone) {
/* signal parent that startup was successful */
exit(1);
}
}
static void
{
}
int
{
int opt;
/* disable getopt error messages */
opterr = 0;
switch (opt) {
case 'd':
ldma_debug = B_TRUE;
break;
case 's':
standalone = B_TRUE;
break;
default:
ldma_usage();
exit(1);
}
}
/*
* Loop forever. Any incoming message will be received by libds and
* forwarded to the agent data callback (ldma_data_cb()) where it
* will be processed.
*/
for (;;) {
(void) pause();
}
/*NOTREACHED*/
return (0);
}