ldmad.c revision 11833:3b3fe296598b
1/*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21
22/*
23 * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*
28 * Logical Domains (LDoms) Agents Daemon
29 *
30 * The LDoms agents daemon (ldmad) runs on LDoms domains and provides
31 * information to the control domain. It is composed of a set of agents
32 * which can send and receive messages to and from the control domain.
33 * Each agent is registered as a domain service using the libds library,
34 * and is able to handle requests coming from the control domain.
35 *
36 * The control domain sends requests to an agent as messages on the
37 * corresponding domain service (identified by the agent name). All requests
38 * are received by the ldmad daemon which dispatches them to the appropriate
39 * handler function of the agent depending on the type of the message.
40 *
41 * After the request has been processed by the handler, the ldmad daemon sent
42 * a reply message back to the control domain. The reply is either a result
43 * message if the request was successfully completed, or an error message
44 * describing the failure.
45 */
46
47#include <dirent.h>
48#include <dlfcn.h>
49#include <errno.h>
50#include <fcntl.h>
51#include <link.h>
52#include <libds.h>
53#include <libgen.h>
54#include <signal.h>
55#include <stdio.h>
56#include <stdlib.h>
57#include <strings.h>
58#include <synch.h>
59#include <syslog.h>
60#include <thread.h>
61#include <unistd.h>
62#include <sys/debug.h>
63#include <sys/ldoms.h>
64#include <sys/types.h>
65#include <sys/stat.h>
66#include <sys/wait.h>
67
68#include "ldma.h"
69
70#define	LDMA_MODULE	"ldm-agent-daemon"
71
72#define	LDMA_CONTROL_DOMAIN_DHDL	0	/* id of the control domain */
73
74typedef struct ldma_connexion_t {
75	ds_hdl_t		hdl;		/* connexion handle */
76	ds_domain_hdl_t		dhdl;		/* connexion domain handle */
77	ds_ver_t		ver;		/* connexion version */
78} ldma_connexion_t;
79
80typedef struct ldma_agent {
81	ldma_agent_info_t	*info;		/* agent information */
82	mutex_t			conn_lock;	/* connexion table lock */
83	ldma_connexion_t	conn[LDOMS_MAX_DOMAINS]; /* connexions */
84} ldma_agent_t;
85
86/* information about existing agents */
87extern ldma_agent_info_t ldma_device_info;
88extern ldma_agent_info_t ldma_system_info;
89extern ldma_agent_info_t ldma_dio_info;
90
91boolean_t ldma_debug = B_FALSE;
92boolean_t ldma_daemon = B_FALSE;
93
94static ldma_agent_info_t *ldma_agent_infos[] = {
95	&ldma_device_info,
96	&ldma_system_info,
97	&ldma_dio_info,
98	NULL
99};
100
101static char *cmdname;
102static pid_t daemon_pid = 0;
103
104/*
105 * Lookup connexion in agent connexion table.
106 */
107static ldma_connexion_t *
108ldma_connexion_lookup(ldma_agent_t *agent, ds_hdl_t hdl)
109{
110	ldma_connexion_t *connp;
111	int i;
112
113	ASSERT(MUTEX_HELD(&agent->conn_lock));
114	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
115		if (connp->hdl == hdl)
116			return (connp);
117	}
118	return (NULL);
119}
120
121/*
122 * Add connextion to agent connexion table.
123 */
124static int
125ldma_connexion_add(ldma_agent_t *agent, ds_hdl_t hdl, ds_domain_hdl_t dhdl,
126    ds_ver_t *verp)
127{
128	ldma_connexion_t *connp;
129	ldma_connexion_t *availp = NULL;
130	int i;
131
132	(void) mutex_lock(&agent->conn_lock);
133	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
134		if (connp->hdl == hdl)
135			break;
136		if (availp == NULL && connp->hdl == DS_INVALID_HDL)
137			availp = connp;
138	}
139
140	if (i < LDOMS_MAX_DOMAINS) {
141		(void) mutex_unlock(&agent->conn_lock);
142		LDMA_INFO("agent %s hdl %llx already exists", agent->info->name,
143		    hdl);
144		return (0);
145	}
146
147	if (!availp) {
148		(void) mutex_unlock(&agent->conn_lock);
149		LDMA_INFO("agent %s too many connections", agent->info->name);
150		return (0);
151	}
152
153	LDMA_DBG("agent %s adding connection (%x) %llx, %llx, %d.%d",
154	    agent->info->name, availp, hdl, dhdl, verp->major, verp->minor);
155
156	availp->hdl = hdl;
157	availp->dhdl = dhdl;
158	availp->ver = *verp;
159	(void) mutex_unlock(&agent->conn_lock);
160	return (1);
161}
162
163/*
164 * Delete connexion from agent connexion table.
165 */
166static int
167ldma_connexion_delete(ldma_agent_t *agent, ds_hdl_t hdl)
168{
169	ldma_connexion_t *connp;
170
171	(void) mutex_lock(&agent->conn_lock);
172	if ((connp = ldma_connexion_lookup(agent, hdl)) == NULL) {
173		(void) mutex_unlock(&agent->conn_lock);
174		LDMA_INFO("agent %s connection delete failed to find %llx",
175		    agent->info->name, hdl);
176		return (0);
177	}
178
179	LDMA_DBG("agent %s deleting connection (%x) %llx", agent->info->name,
180	    connp, hdl);
181
182	connp->hdl = DS_INVALID_HDL;
183	connp->dhdl = 0;
184	connp->ver.major = 0;
185	connp->ver.minor = 0;
186	(void) mutex_unlock(&agent->conn_lock);
187	return (1);
188}
189
190/*
191 * Initialize connexion table.
192 */
193static void
194ldma_connexion_init(ldma_agent_t *agent)
195{
196	ldma_connexion_t *connp;
197	int i;
198
199	for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) {
200		connp->hdl = DS_INVALID_HDL;
201	}
202}
203
204/*
205 * Allocate a new message with the specified message number (msg_num),
206 * message type (msg_type) and message data length (msg_dlen). Return
207 * NULL if the allocation has failed.
208 */
209static ldma_message_header_t *
210ldma_alloc_msg(uint64_t msg_num, uint32_t msg_type, size_t msg_dlen)
211{
212	ldma_message_header_t *msg;
213	size_t msg_len;
214
215	msg_len = LDMA_MESSAGE_SIZE(msg_dlen);
216	msg = malloc(msg_len);
217	if (msg == NULL)
218		return (NULL);
219
220	msg->msg_num = msg_num;
221	msg->msg_type = msg_type;
222	msg->msg_info = 0;
223
224	return (msg);
225}
226
227/*
228 * Allocate a result message (LDMA_MSG_REQ_RESULT) with the specified message
229 * data length (msg_dlen). If the request argument is not NULL then the message
230 * is created with the same message number as the request, otherwise the message
231 * number is set to 0. Return NULL if the allocation has failed.
232 */
233ldma_message_header_t *
234ldma_alloc_result_msg(ldma_message_header_t *request, size_t msg_dlen)
235{
236	uint64_t msg_num;
237
238	msg_num = (request == NULL)? 0 : request->msg_num;
239
240	return (ldma_alloc_msg(msg_num, LDMA_MSG_RESULT, msg_dlen));
241}
242
243/*
244 * Agent register callback. This callback is invoked when a client is registered
245 * for using the service provided by an agent. An agent will only have one
246 * consumer which is coming from the control domain.
247 */
248static void
249ldma_reg_cb(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver,
250    ds_domain_hdl_t dhdl)
251{
252	ldma_agent_t *agent = (ldma_agent_t *)arg;
253	char dname[LDOMS_MAX_NAME_LEN];
254
255	if (ds_dom_hdl_to_name(dhdl, dname, LDOMS_MAX_NAME_LEN) != 0) {
256		(void) strcpy(dname, "<unknown>");
257	}
258
259	LDMA_DBG("%s: REGISTER hdl=%llx, dhdl=%llx (%s) ver=%hd.%hd",
260	    agent->info->name, hdl, dhdl, dname, ver->major, ver->minor);
261
262	/*
263	 * Record client information.  Access control is done on a
264	 * message-by-message basis upon receipt of the message.
265	 */
266	if (!ldma_connexion_add(agent, hdl, dhdl, ver)) {
267		LDMA_INFO("agent %s failed to add connection from "
268		    "domain %s", agent->info->name, dname);
269	}
270}
271
272/*
273 * Agent unregister callback. This callback is invoked when a client is
274 * unregistered and stops using the service provided by an agent.
275 */
276static void
277ldma_unreg_cb(ds_hdl_t hdl, ds_cb_arg_t arg)
278{
279	ldma_agent_t *agent = (ldma_agent_t *)arg;
280
281	LDMA_DBG("%s: UNREGISTER hdl=%llx", agent->info->name, hdl);
282
283	if (!ldma_connexion_delete(agent, hdl)) {
284		LDMA_INFO("agent %s failed to unregister handle %llx",
285		    agent->info->name, hdl);
286	}
287}
288
289/*
290 * Agent data callback. This callback is invoked when an agent receives a new
291 * message from a client. Any request from a client which is not the control
292 * domain is immediatly rejected. Otherwise the message is forwarded to the
293 * appropriate handler function provided by the agent, depending on the message
294 * type.
295 */
296static void
297ldma_data_cb(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf, size_t len)
298{
299	ldma_agent_t *agent = (ldma_agent_t *)arg;
300	ldma_msg_handler_t *handler;
301	ldma_message_header_t *request = buf;
302	ldma_message_header_t *reply = NULL;
303	ldma_connexion_t *connp;
304	ds_ver_t conn_ver;
305	ds_domain_hdl_t conn_dhdl;
306	ldma_request_status_t status;
307	size_t request_dlen, reply_len, reply_dlen = 0;
308	int i;
309
310	/* check the message size */
311	if (len < LDMA_MESSAGE_HEADER_SIZE) {
312		LDMA_INFO("agent %s has ignored message with an invalid "
313		    "size of %d bytes", agent->info->name, len);
314		return;
315	}
316
317	request_dlen = LDMA_MESSAGE_DLEN(len);
318
319	LDMA_DBG("%s: DATA hdl=%llx, request num=%llu type=0x%x info=0x%x "
320	    "dlen=%d", agent->info->name, hdl, request->msg_num,
321	    request->msg_type, request->msg_info, request_dlen);
322
323	(void) mutex_lock(&agent->conn_lock);
324	connp = ldma_connexion_lookup(agent, hdl);
325	if (connp != NULL) {
326		conn_dhdl = connp->dhdl;
327		conn_ver = connp->ver;
328	}
329	(void) mutex_unlock(&agent->conn_lock);
330
331	/* reject any request which is not in the connexion table */
332	if (connp == NULL) {
333		LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
334		    "distrusted domain", agent->info->name, hdl);
335		status = LDMA_REQ_DENIED;
336		goto do_reply;
337	}
338
339	handler = NULL;
340
341	for (i = 0; i < agent->info->nhandlers; i++) {
342		if (agent->info->handlers[i].msg_type == request->msg_type) {
343			handler = &agent->info->handlers[i];
344			break;
345		}
346	}
347
348	if (handler == NULL) {
349		/* this type of message is not defined by the agent */
350		LDMA_DBG("%s: DATA hdl=%llx, unknown message type %x",
351		    agent->info->name, hdl, request->msg_type);
352		status = LDMA_REQ_NOTSUP;
353		goto do_reply;
354	}
355
356	/* reject any request from a guest which is not allowed */
357	if ((conn_dhdl != LDMA_CONTROL_DOMAIN_DHDL) &&
358	    (handler->msg_flags & LDMA_MSGFLG_ACCESS_ANY) == 0) {
359		LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
360		    "distrusted domain", agent->info->name, hdl);
361		status = LDMA_REQ_DENIED;
362		goto do_reply;
363	}
364
365	if (handler->msg_handler == NULL) {
366		/*
367		 * This type of message is defined by the agent but it
368		 * has no handler. That means there is no processing to
369		 * do, the message is just ignored, but the request is
370		 * successfully completed.
371		 */
372		LDMA_DBG("%s: DATA hdl=%llx, no handler",
373		    agent->info->name, hdl);
374		status = LDMA_REQ_COMPLETED;
375		goto do_reply;
376	}
377
378	/* invoke the message handler of the agent */
379	status = (*handler->msg_handler)(&conn_ver, request, request_dlen,
380	    &reply, &reply_dlen);
381
382	LDMA_DBG("%s: DATA hdl=%llx, handler stat=%d reply=%p rlen=%d",
383	    agent->info->name, hdl, status, (void *)reply, reply_dlen);
384
385do_reply:
386	/*
387	 * If the handler has provided a reply message, we use it directly.
388	 * Otherwise, we build a reply depending on the status of the request.
389	 * In that case, we re-use the request buffer to build the reply
390	 * message.
391	 */
392	if (reply == NULL) {
393
394		reply = request;
395		reply_dlen = 0;
396
397		if (status == LDMA_REQ_COMPLETED) {
398			/*
399			 * The request was successful but no result message was
400			 * provided so we send an empty result message.
401			 */
402			reply->msg_type = LDMA_MSG_RESULT;
403			reply->msg_info = 0;
404
405		} else {
406			/*
407			 * The request has failed but no error message was
408			 * provided so we send an error message based on the
409			 * request status.
410			 */
411			reply->msg_type = LDMA_MSG_ERROR;
412			reply->msg_info =
413			    (status == LDMA_REQ_NOTSUP)? LDMA_MSGERR_NOTSUP :
414			    (status == LDMA_REQ_INVALID)? LDMA_MSGERR_INVALID :
415			    (status == LDMA_REQ_DENIED)? LDMA_MSGERR_DENY :
416			    LDMA_MSGERR_FAIL;
417		}
418	}
419
420	reply_len = LDMA_MESSAGE_SIZE(reply_dlen);
421
422	LDMA_DBG("%s: DATA hdl=%llx, reply num=%llu type=0x%x info=0x%x "
423	    "dlen=%d", agent->info->name, hdl, reply->msg_num,
424	    reply->msg_type, reply->msg_info, reply_dlen);
425
426	if (ds_send_msg(hdl, reply, reply_len) != 0) {
427		LDMA_ERR("agent %s has failed to send reply for request %llu",
428		    agent->info->name, request->msg_num);
429	}
430
431	if (reply != request)
432		free(reply);
433}
434
435/*
436 * Register an agent. Return 0 if the agent was successfully registered.
437 */
438static int
439ldma_register(ldma_agent_info_t *agent_info)
440{
441	ldma_agent_t	*agent;
442	ds_capability_t	ds_cap;
443	ds_ops_t	ds_ops;
444
445	agent = malloc(sizeof (ldma_agent_t));
446	if (agent == NULL)
447		goto register_fail;
448
449	agent->info = agent_info;
450	(void) mutex_init(&agent->conn_lock, USYNC_THREAD, NULL);
451	ldma_connexion_init(agent);
452
453	ds_cap.svc_id = agent_info->name;
454	ds_cap.vers = agent_info->vers;
455	ds_cap.nvers = agent_info->nvers;
456
457	ds_ops.ds_reg_cb = ldma_reg_cb;
458	ds_ops.ds_unreg_cb = ldma_unreg_cb;
459	ds_ops.ds_data_cb = ldma_data_cb;
460	ds_ops.cb_arg = agent;
461
462	if (ds_svc_reg(&ds_cap, &ds_ops) == 0) {
463		LDMA_INFO("agent %s registered", agent_info->name);
464		return (0);
465	}
466
467register_fail:
468
469	LDMA_ERR("agent %s has failed to register", agent_info->name);
470	free(agent);
471	return (-1);
472}
473
474/*
475 * Register all known agents. Return the number of agents successfully
476 * registered.
477 */
478static int
479ldma_register_agents()
480{
481	int count = 0;
482	ldma_agent_info_t **agent_infop;
483
484	for (agent_infop = ldma_agent_infos;
485	    *agent_infop != NULL; agent_infop++) {
486
487		if (ldma_register(*agent_infop) == 0)
488			count++;
489	}
490
491	return (count);
492}
493
494/*ARGSUSED*/
495static void
496ldma_sigusr_handler(int sig, siginfo_t *sinfo, void *ucontext)
497{
498	/*
499	 * The child process can send the signal before the fork()
500	 * call has returned in the parent process. So daemon_pid
501	 * may not be set yet, and we don't check the pid in that
502	 * case.
503	 */
504	if (sig != SIGUSR1 || sinfo->si_code != SI_USER ||
505	    (daemon_pid > 0 && sinfo->si_pid != daemon_pid))
506		return;
507
508	/*
509	 * The parent process has received a USR1 signal from the child.
510	 * This means that the daemon has correctly started and the parent
511	 * can exit.
512	 */
513	exit(0);
514}
515
516static void
517ldma_start(boolean_t standalone)
518{
519	int stat, rv;
520	struct sigaction action;
521
522	if (!standalone) {
523		/*
524		 * Some configuration of the daemon has to be done in the
525		 * child, but we want the parent to report if the daemon
526		 * has successfully started or not. So we setup a signal
527		 * handler, and the child will notify the parent using the
528		 * USR1 signal if the setup was successful. Otherwise the
529		 * child will exit.
530		 */
531		action.sa_sigaction = ldma_sigusr_handler;
532		action.sa_flags = SA_SIGINFO;
533
534		if (sigemptyset(&action.sa_mask) == -1) {
535			LDMA_ERR("sigemptyset error (%d)", errno);
536			exit(1);
537		}
538
539		if (sigaction(SIGUSR1, &action, NULL) == -1) {
540			LDMA_ERR("sigaction() error (%d)", errno);
541			exit(1);
542		}
543
544		if (sigrelse(SIGUSR1) == -1) {
545			LDMA_ERR("sigrelse() error (%d)", errno);
546			exit(1);
547		}
548
549		if ((daemon_pid = fork()) == -1) {
550			LDMA_ERR("fork() error (%d)", errno);
551			exit(1);
552		}
553
554		if (daemon_pid != 0) {
555			/*
556			 * The parent process waits until the child exits (in
557			 * case of an error) or sends a USR1 signal (if the
558			 * daemon has correctly started).
559			 */
560			for (;;) {
561				rv = waitpid(daemon_pid, &stat, 0);
562				if ((rv == daemon_pid && WIFEXITED(stat)) ||
563				    (rv == -1 && errno != EINTR)) {
564					/* child has exited or error */
565					exit(1);
566				}
567			}
568		}
569
570		/*
571		 * Initialize child process
572		 */
573		if (sighold(SIGUSR1) == -1) {
574			LDMA_ERR("sighold error (%d)", errno);
575			exit(1);
576		}
577
578		if (sigignore(SIGUSR1) == -1) {
579			LDMA_ERR("sigignore error (%d)", errno);
580			exit(1);
581		}
582
583		if (setsid() == -1) {
584			LDMA_ERR("setsid error (%d)", errno);
585			exit(1);
586		}
587
588		if (chdir("/") == -1) {
589			LDMA_ERR("chdir error (%d)", errno);
590			exit(1);
591		}
592		(void) umask(0);
593
594		/*
595		 * Initialize file descriptors. Do not touch stderr
596		 * which is initialized by SMF to point to the daemon
597		 * specific log file.
598		 */
599		(void) close(STDIN_FILENO);
600		if (open("/dev/null", O_RDWR) == -1) {
601			LDMA_ERR("open /dev/null error (%d)", errno);
602			exit(1);
603		}
604		if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) {
605			LDMA_ERR("dup2 error (%d)", errno);
606			exit(1);
607		}
608		closefrom(STDERR_FILENO + 1);
609
610		/* initialize logging */
611		openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
612
613		ldma_daemon = B_TRUE;
614	}
615
616	/*
617	 * Register the agents. It would be easier to do this before
618	 * daemonizing so that any start error is directly reported. But
619	 * this can not be done because agents are registered using libds
620	 * and this will subscribe the daemon to some sysevents which is
621	 * a process based subscription. Instead we notify the parent process
622	 * either by exiting, or by sending a SIGUSR1 signal.
623	 */
624	if (ldma_register_agents() == 0) {
625		/* no agent registered */
626		LDMA_ERR("Unable to register any agent");
627		exit(1);
628	}
629
630	if (!standalone) {
631		/* signal parent that startup was successful */
632		if (kill(getppid(), SIGUSR1) == -1)
633			exit(1);
634	}
635}
636
637static void
638ldma_usage()
639{
640	(void) fprintf(stderr, "usage: %s\n", cmdname);
641}
642
643int
644main(int argc, char *argv[])
645{
646	int opt;
647	boolean_t standalone = B_FALSE;
648
649	cmdname = basename(argv[0]);
650
651	/* disable getopt error messages */
652	opterr = 0;
653
654	while ((opt = getopt(argc, argv, "ds")) != EOF) {
655
656		switch (opt) {
657		case 'd':
658			ldma_debug = B_TRUE;
659			break;
660		case 's':
661			standalone = B_TRUE;
662			break;
663		default:
664			ldma_usage();
665			exit(1);
666		}
667	}
668
669	ldma_start(standalone);
670
671	/*
672	 * Loop forever. Any incoming message will be received by libds and
673	 * forwarded to the agent data callback (ldma_data_cb()) where it
674	 * will be processed.
675	 */
676	for (;;) {
677		(void) pause();
678	}
679
680	/*NOTREACHED*/
681	return (0);
682}
683