8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / cmd / ldmad / ldmad.c
blobd2cccb8ee9198087a93a7e17125a9918e5eafa77
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
27 * Logical Domains (LDoms) Agents Daemon
29 * The LDoms agents daemon (ldmad) runs on LDoms domains and provides
30 * information to the control domain. It is composed of a set of agents
31 * which can send and receive messages to and from the control domain.
32 * Each agent is registered as a domain service using the libds library,
33 * and is able to handle requests coming from the control domain.
35 * The control domain sends requests to an agent as messages on the
36 * corresponding domain service (identified by the agent name). All requests
37 * are received by the ldmad daemon which dispatches them to the appropriate
38 * handler function of the agent depending on the type of the message.
40 * After the request has been processed by the handler, the ldmad daemon sent
41 * a reply message back to the control domain. The reply is either a result
42 * message if the request was successfully completed, or an error message
43 * describing the failure.
46 #include <dirent.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <libds.h>
50 #include <libgen.h>
51 #include <signal.h>
52 #include <stdio.h>
53 #include <stdlib.h>
54 #include <strings.h>
55 #include <synch.h>
56 #include <syslog.h>
57 #include <thread.h>
58 #include <unistd.h>
59 #include <sys/debug.h>
60 #include <sys/ldoms.h>
61 #include <sys/types.h>
62 #include <sys/stat.h>
63 #include <sys/wait.h>
65 #include "ldma.h"
67 #define LDMA_MODULE "ldm-agent-daemon"
69 #define LDMA_CONTROL_DOMAIN_DHDL 0 /* id of the control domain */
71 typedef struct ldma_connexion_t {
72 ds_hdl_t hdl; /* connexion handle */
73 ds_domain_hdl_t dhdl; /* connexion domain handle */
74 ds_ver_t ver; /* connexion version */
75 } ldma_connexion_t;
77 typedef struct ldma_agent {
78 ldma_agent_info_t *info; /* agent information */
79 mutex_t conn_lock; /* connexion table lock */
80 ldma_connexion_t conn[LDOMS_MAX_DOMAINS]; /* connexions */
81 } ldma_agent_t;
83 /* information about existing agents */
84 extern ldma_agent_info_t ldma_device_info;
85 extern ldma_agent_info_t ldma_system_info;
86 extern ldma_agent_info_t ldma_dio_info;
88 boolean_t ldma_debug = B_FALSE;
89 boolean_t ldma_daemon = B_FALSE;
91 static ldma_agent_info_t *ldma_agent_infos[] = {
92 &ldma_device_info,
93 &ldma_system_info,
94 &ldma_dio_info,
95 NULL
98 static char *cmdname;
99 static pid_t daemon_pid = 0;
102 * Lookup connexion in agent connexion table.
104 static ldma_connexion_t *
105 ldma_connexion_lookup(ldma_agent_t *agent, ds_hdl_t hdl)
107 ldma_connexion_t *connp;
108 int i;
110 ASSERT(MUTEX_HELD(&agent->conn_lock));
111 for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
112 if (connp->hdl == hdl)
113 return (connp);
115 return (NULL);
119 * Add connextion to agent connexion table.
121 static int
122 ldma_connexion_add(ldma_agent_t *agent, ds_hdl_t hdl, ds_domain_hdl_t dhdl,
123 ds_ver_t *verp)
125 ldma_connexion_t *connp;
126 ldma_connexion_t *availp = NULL;
127 int i;
129 (void) mutex_lock(&agent->conn_lock);
130 for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
131 if (connp->hdl == hdl)
132 break;
133 if (availp == NULL && connp->hdl == DS_INVALID_HDL)
134 availp = connp;
137 if (i < LDOMS_MAX_DOMAINS) {
138 (void) mutex_unlock(&agent->conn_lock);
139 LDMA_INFO("agent %s hdl %llx already exists", agent->info->name,
140 hdl);
141 return (0);
144 if (!availp) {
145 (void) mutex_unlock(&agent->conn_lock);
146 LDMA_INFO("agent %s too many connections", agent->info->name);
147 return (0);
150 LDMA_DBG("agent %s adding connection (%x) %llx, %llx, %d.%d",
151 agent->info->name, availp, hdl, dhdl, verp->major, verp->minor);
153 availp->hdl = hdl;
154 availp->dhdl = dhdl;
155 availp->ver = *verp;
156 (void) mutex_unlock(&agent->conn_lock);
157 return (1);
161 * Delete connexion from agent connexion table.
163 static int
164 ldma_connexion_delete(ldma_agent_t *agent, ds_hdl_t hdl)
166 ldma_connexion_t *connp;
168 (void) mutex_lock(&agent->conn_lock);
169 if ((connp = ldma_connexion_lookup(agent, hdl)) == NULL) {
170 (void) mutex_unlock(&agent->conn_lock);
171 LDMA_INFO("agent %s connection delete failed to find %llx",
172 agent->info->name, hdl);
173 return (0);
176 LDMA_DBG("agent %s deleting connection (%x) %llx", agent->info->name,
177 connp, hdl);
179 connp->hdl = DS_INVALID_HDL;
180 connp->dhdl = 0;
181 connp->ver.major = 0;
182 connp->ver.minor = 0;
183 (void) mutex_unlock(&agent->conn_lock);
184 return (1);
188 * Initialize connexion table.
190 static void
191 ldma_connexion_init(ldma_agent_t *agent)
193 ldma_connexion_t *connp;
194 int i;
196 for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
197 connp->hdl = DS_INVALID_HDL;
202 * Allocate a new message with the specified message number (msg_num),
203 * message type (msg_type) and message data length (msg_dlen). Return
204 * NULL if the allocation has failed.
206 static ldma_message_header_t *
207 ldma_alloc_msg(uint64_t msg_num, uint32_t msg_type, size_t msg_dlen)
209 ldma_message_header_t *msg;
210 size_t msg_len;
212 msg_len = LDMA_MESSAGE_SIZE(msg_dlen);
213 msg = malloc(msg_len);
214 if (msg == NULL)
215 return (NULL);
217 msg->msg_num = msg_num;
218 msg->msg_type = msg_type;
219 msg->msg_info = 0;
221 return (msg);
225 * Allocate a result message (LDMA_MSG_REQ_RESULT) with the specified message
226 * data length (msg_dlen). If the request argument is not NULL then the message
227 * is created with the same message number as the request, otherwise the message
228 * number is set to 0. Return NULL if the allocation has failed.
230 ldma_message_header_t *
231 ldma_alloc_result_msg(ldma_message_header_t *request, size_t msg_dlen)
233 uint64_t msg_num;
235 msg_num = (request == NULL)? 0 : request->msg_num;
237 return (ldma_alloc_msg(msg_num, LDMA_MSG_RESULT, msg_dlen));
241 * Agent register callback. This callback is invoked when a client is registered
242 * for using the service provided by an agent. An agent will only have one
243 * consumer which is coming from the control domain.
245 static void
246 ldma_reg_cb(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver,
247 ds_domain_hdl_t dhdl)
249 ldma_agent_t *agent = (ldma_agent_t *)arg;
250 char dname[LDOMS_MAX_NAME_LEN];
252 if (ds_dom_hdl_to_name(dhdl, dname, LDOMS_MAX_NAME_LEN) != 0) {
253 (void) strcpy(dname, "<unknown>");
256 LDMA_DBG("%s: REGISTER hdl=%llx, dhdl=%llx (%s) ver=%hd.%hd",
257 agent->info->name, hdl, dhdl, dname, ver->major, ver->minor);
260 * Record client information. Access control is done on a
261 * message-by-message basis upon receipt of the message.
263 if (!ldma_connexion_add(agent, hdl, dhdl, ver)) {
264 LDMA_INFO("agent %s failed to add connection from "
265 "domain %s", agent->info->name, dname);
270 * Agent unregister callback. This callback is invoked when a client is
271 * unregistered and stops using the service provided by an agent.
273 static void
274 ldma_unreg_cb(ds_hdl_t hdl, ds_cb_arg_t arg)
276 ldma_agent_t *agent = (ldma_agent_t *)arg;
278 LDMA_DBG("%s: UNREGISTER hdl=%llx", agent->info->name, hdl);
280 if (!ldma_connexion_delete(agent, hdl)) {
281 LDMA_INFO("agent %s failed to unregister handle %llx",
282 agent->info->name, hdl);
287 * Agent data callback. This callback is invoked when an agent receives a new
288 * message from a client. Any request from a client which is not the control
289 * domain is immediatly rejected. Otherwise the message is forwarded to the
290 * appropriate handler function provided by the agent, depending on the message
291 * type.
293 static void
294 ldma_data_cb(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf, size_t len)
296 ldma_agent_t *agent = (ldma_agent_t *)arg;
297 ldma_msg_handler_t *handler;
298 ldma_message_header_t *request = buf;
299 ldma_message_header_t *reply = NULL;
300 ldma_connexion_t *connp;
301 ds_ver_t conn_ver;
302 ds_domain_hdl_t conn_dhdl;
303 ldma_request_status_t status;
304 size_t request_dlen, reply_len, reply_dlen = 0;
305 int i;
307 /* check the message size */
308 if (len < LDMA_MESSAGE_HEADER_SIZE) {
309 LDMA_INFO("agent %s has ignored message with an invalid "
310 "size of %d bytes", agent->info->name, len);
311 return;
314 request_dlen = LDMA_MESSAGE_DLEN(len);
316 LDMA_DBG("%s: DATA hdl=%llx, request num=%llu type=0x%x info=0x%x "
317 "dlen=%d", agent->info->name, hdl, request->msg_num,
318 request->msg_type, request->msg_info, request_dlen);
320 (void) mutex_lock(&agent->conn_lock);
321 connp = ldma_connexion_lookup(agent, hdl);
322 if (connp != NULL) {
323 conn_dhdl = connp->dhdl;
324 conn_ver = connp->ver;
326 (void) mutex_unlock(&agent->conn_lock);
328 /* reject any request which is not in the connexion table */
329 if (connp == NULL) {
330 LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
331 "distrusted domain", agent->info->name, hdl);
332 status = LDMA_REQ_DENIED;
333 goto do_reply;
336 handler = NULL;
338 for (i = 0; i < agent->info->nhandlers; i++) {
339 if (agent->info->handlers[i].msg_type == request->msg_type) {
340 handler = &agent->info->handlers[i];
341 break;
345 if (handler == NULL) {
346 /* this type of message is not defined by the agent */
347 LDMA_DBG("%s: DATA hdl=%llx, unknown message type %x",
348 agent->info->name, hdl, request->msg_type);
349 status = LDMA_REQ_NOTSUP;
350 goto do_reply;
353 /* reject any request from a guest which is not allowed */
354 if ((conn_dhdl != LDMA_CONTROL_DOMAIN_DHDL) &&
355 (handler->msg_flags & LDMA_MSGFLG_ACCESS_ANY) == 0) {
356 LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
357 "distrusted domain", agent->info->name, hdl);
358 status = LDMA_REQ_DENIED;
359 goto do_reply;
362 if (handler->msg_handler == NULL) {
364 * This type of message is defined by the agent but it
365 * has no handler. That means there is no processing to
366 * do, the message is just ignored, but the request is
367 * successfully completed.
369 LDMA_DBG("%s: DATA hdl=%llx, no handler",
370 agent->info->name, hdl);
371 status = LDMA_REQ_COMPLETED;
372 goto do_reply;
375 /* invoke the message handler of the agent */
376 status = (*handler->msg_handler)(&conn_ver, request, request_dlen,
377 &reply, &reply_dlen);
379 LDMA_DBG("%s: DATA hdl=%llx, handler stat=%d reply=%p rlen=%d",
380 agent->info->name, hdl, status, (void *)reply, reply_dlen);
382 do_reply:
384 * If the handler has provided a reply message, we use it directly.
385 * Otherwise, we build a reply depending on the status of the request.
386 * In that case, we re-use the request buffer to build the reply
387 * message.
389 if (reply == NULL) {
391 reply = request;
392 reply_dlen = 0;
394 if (status == LDMA_REQ_COMPLETED) {
396 * The request was successful but no result message was
397 * provided so we send an empty result message.
399 reply->msg_type = LDMA_MSG_RESULT;
400 reply->msg_info = 0;
402 } else {
404 * The request has failed but no error message was
405 * provided so we send an error message based on the
406 * request status.
408 reply->msg_type = LDMA_MSG_ERROR;
409 reply->msg_info =
410 (status == LDMA_REQ_NOTSUP)? LDMA_MSGERR_NOTSUP :
411 (status == LDMA_REQ_INVALID)? LDMA_MSGERR_INVALID :
412 (status == LDMA_REQ_DENIED)? LDMA_MSGERR_DENY :
413 LDMA_MSGERR_FAIL;
417 reply_len = LDMA_MESSAGE_SIZE(reply_dlen);
419 LDMA_DBG("%s: DATA hdl=%llx, reply num=%llu type=0x%x info=0x%x "
420 "dlen=%d", agent->info->name, hdl, reply->msg_num,
421 reply->msg_type, reply->msg_info, reply_dlen);
423 if (ds_send_msg(hdl, reply, reply_len) != 0) {
424 LDMA_ERR("agent %s has failed to send reply for request %llu",
425 agent->info->name, request->msg_num);
428 if (reply != request)
429 free(reply);
433 * Register an agent. Return 0 if the agent was successfully registered.
435 static int
436 ldma_register(ldma_agent_info_t *agent_info)
438 ldma_agent_t *agent;
439 ds_capability_t ds_cap;
440 ds_ops_t ds_ops;
442 agent = malloc(sizeof (ldma_agent_t));
443 if (agent == NULL)
444 goto register_fail;
446 agent->info = agent_info;
447 (void) mutex_init(&agent->conn_lock, USYNC_THREAD, NULL);
448 ldma_connexion_init(agent);
450 ds_cap.svc_id = agent_info->name;
451 ds_cap.vers = agent_info->vers;
452 ds_cap.nvers = agent_info->nvers;
454 ds_ops.ds_reg_cb = ldma_reg_cb;
455 ds_ops.ds_unreg_cb = ldma_unreg_cb;
456 ds_ops.ds_data_cb = ldma_data_cb;
457 ds_ops.cb_arg = agent;
459 if (ds_svc_reg(&ds_cap, &ds_ops) == 0) {
460 LDMA_INFO("agent %s registered", agent_info->name);
461 return (0);
464 register_fail:
466 LDMA_ERR("agent %s has failed to register", agent_info->name);
467 free(agent);
468 return (-1);
472 * Register all known agents. Return the number of agents successfully
473 * registered.
475 static int
476 ldma_register_agents()
478 int count = 0;
479 ldma_agent_info_t **agent_infop;
481 for (agent_infop = ldma_agent_infos;
482 *agent_infop != NULL; agent_infop++) {
484 if (ldma_register(*agent_infop) == 0)
485 count++;
488 return (count);
491 /*ARGSUSED*/
492 static void
493 ldma_sigusr_handler(int sig, siginfo_t *sinfo, void *ucontext)
496 * The child process can send the signal before the fork()
497 * call has returned in the parent process. So daemon_pid
498 * may not be set yet, and we don't check the pid in that
499 * case.
501 if (sig != SIGUSR1 || sinfo->si_code != SI_USER ||
502 (daemon_pid > 0 && sinfo->si_pid != daemon_pid))
503 return;
506 * The parent process has received a USR1 signal from the child.
507 * This means that the daemon has correctly started and the parent
508 * can exit.
510 exit(0);
513 static void
514 ldma_start(boolean_t standalone)
516 int stat, rv;
517 struct sigaction action;
519 if (!standalone) {
521 * Some configuration of the daemon has to be done in the
522 * child, but we want the parent to report if the daemon
523 * has successfully started or not. So we setup a signal
524 * handler, and the child will notify the parent using the
525 * USR1 signal if the setup was successful. Otherwise the
526 * child will exit.
528 action.sa_sigaction = ldma_sigusr_handler;
529 action.sa_flags = SA_SIGINFO;
531 if (sigemptyset(&action.sa_mask) == -1) {
532 LDMA_ERR("sigemptyset error (%d)", errno);
533 exit(1);
536 if (sigaction(SIGUSR1, &action, NULL) == -1) {
537 LDMA_ERR("sigaction() error (%d)", errno);
538 exit(1);
541 if (sigrelse(SIGUSR1) == -1) {
542 LDMA_ERR("sigrelse() error (%d)", errno);
543 exit(1);
546 if ((daemon_pid = fork()) == -1) {
547 LDMA_ERR("fork() error (%d)", errno);
548 exit(1);
551 if (daemon_pid != 0) {
553 * The parent process waits until the child exits (in
554 * case of an error) or sends a USR1 signal (if the
555 * daemon has correctly started).
557 for (;;) {
558 rv = waitpid(daemon_pid, &stat, 0);
559 if ((rv == daemon_pid && WIFEXITED(stat)) ||
560 (rv == -1 && errno != EINTR)) {
561 /* child has exited or error */
562 exit(1);
568 * Initialize child process
570 if (sighold(SIGUSR1) == -1) {
571 LDMA_ERR("sighold error (%d)", errno);
572 exit(1);
575 if (sigignore(SIGUSR1) == -1) {
576 LDMA_ERR("sigignore error (%d)", errno);
577 exit(1);
580 if (setsid() == -1) {
581 LDMA_ERR("setsid error (%d)", errno);
582 exit(1);
585 if (chdir("/") == -1) {
586 LDMA_ERR("chdir error (%d)", errno);
587 exit(1);
589 (void) umask(0);
592 * Initialize file descriptors. Do not touch stderr
593 * which is initialized by SMF to point to the daemon
594 * specific log file.
596 (void) close(STDIN_FILENO);
597 if (open("/dev/null", O_RDWR) == -1) {
598 LDMA_ERR("open /dev/null error (%d)", errno);
599 exit(1);
601 if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) {
602 LDMA_ERR("dup2 error (%d)", errno);
603 exit(1);
605 closefrom(STDERR_FILENO + 1);
607 /* initialize logging */
608 openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
610 ldma_daemon = B_TRUE;
614 * Register the agents. It would be easier to do this before
615 * daemonizing so that any start error is directly reported. But
616 * this can not be done because agents are registered using libds
617 * and this will subscribe the daemon to some sysevents which is
618 * a process based subscription. Instead we notify the parent process
619 * either by exiting, or by sending a SIGUSR1 signal.
621 if (ldma_register_agents() == 0) {
622 /* no agent registered */
623 LDMA_ERR("Unable to register any agent");
624 exit(1);
627 if (!standalone) {
628 /* signal parent that startup was successful */
629 if (kill(getppid(), SIGUSR1) == -1)
630 exit(1);
634 static void
635 ldma_usage()
637 (void) fprintf(stderr, "usage: %s\n", cmdname);
641 main(int argc, char *argv[])
643 int opt;
644 boolean_t standalone = B_FALSE;
646 cmdname = basename(argv[0]);
648 /* disable getopt error messages */
649 opterr = 0;
651 while ((opt = getopt(argc, argv, "ds")) != EOF) {
653 switch (opt) {
654 case 'd':
655 ldma_debug = B_TRUE;
656 break;
657 case 's':
658 standalone = B_TRUE;
659 break;
660 default:
661 ldma_usage();
662 exit(1);
666 ldma_start(standalone);
669 * Loop forever. Any incoming message will be received by libds and
670 * forwarded to the agent data callback (ldma_data_cb()) where it
671 * will be processed.
673 for (;;) {
674 (void) pause();
677 /*NOTREACHED*/
678 return (0);