xref: /illumos-gate/usr/src/cmd/ldmad/ldmad.c (revision 8fd04b8338ed5093ec2d1e668fa620b7de44c177)
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 <syslog.h>
59 #include <unistd.h>
60 #include <sys/types.h>
61 #include <sys/stat.h>
62 #include <sys/wait.h>
63 
64 #include "ldma.h"
65 
66 #define	LDMA_MODULE	"ldm-agent-daemon"
67 
68 #define	LDMA_CONTROL_DOMAIN_DHDL	0	/* id of the control domain */
69 #define	LDMA_DOMAIN_NAME_MAXLEN		MAXNAMELEN
70 
71 typedef struct ldma_agent {
72 	ldma_agent_info_t	*info;		/* agent information */
73 	ds_hdl_t		conn_hdl;	/* connexion handler */
74 	ds_ver_t		conn_ver;	/* connexion version */
75 } ldma_agent_t;
76 
77 /* information about existing agents */
78 extern ldma_agent_info_t ldma_device_info;
79 extern ldma_agent_info_t ldma_system_info;
80 extern ldma_agent_info_t ldma_dio_info;
81 
82 boolean_t ldma_debug = B_FALSE;
83 boolean_t ldma_daemon = B_FALSE;
84 
85 static ldma_agent_info_t *ldma_agent_infos[] = {
86 	&ldma_device_info,
87 	&ldma_system_info,
88 	&ldma_dio_info,
89 	NULL
90 };
91 
92 static char *cmdname;
93 static pid_t daemon_pid = 0;
94 
95 /*
96  * Allocate a new message with the specified message number (msg_num),
97  * message type (msg_type) and message data length (msg_dlen). Return
98  * NULL if the allocation has failed.
99  */
100 static ldma_message_header_t *
101 ldma_alloc_msg(uint64_t msg_num, uint32_t msg_type, size_t msg_dlen)
102 {
103 	ldma_message_header_t *msg;
104 	size_t msg_len;
105 
106 	msg_len = LDMA_MESSAGE_SIZE(msg_dlen);
107 	msg = malloc(msg_len);
108 	if (msg == NULL)
109 		return (NULL);
110 
111 	msg->msg_num = msg_num;
112 	msg->msg_type = msg_type;
113 	msg->msg_info = 0;
114 
115 	return (msg);
116 }
117 
118 /*
119  * Allocate a result message (LDMA_MSG_REQ_RESULT) with the specified message
120  * data length (msg_dlen). If the request argument is not NULL then the message
121  * is created with the same message number as the request, otherwise the message
122  * number is set to 0. Return NULL if the allocation has failed.
123  */
124 ldma_message_header_t *
125 ldma_alloc_result_msg(ldma_message_header_t *request, size_t msg_dlen)
126 {
127 	uint64_t msg_num;
128 
129 	msg_num = (request == NULL)? 0 : request->msg_num;
130 
131 	return (ldma_alloc_msg(msg_num, LDMA_MSG_RESULT, msg_dlen));
132 }
133 
134 /*
135  * Agent register callback. This callback is invoked when a client is registered
136  * for using the service provided by an agent. An agent will only have one
137  * consumer which is coming from the control domain.
138  */
139 static void
140 ldma_reg_cb(ds_hdl_t hdl, ds_cb_arg_t arg, ds_ver_t *ver,
141     ds_domain_hdl_t dhdl)
142 {
143 	ldma_agent_t *agent = (ldma_agent_t *)arg;
144 	char dname[LDMA_DOMAIN_NAME_MAXLEN];
145 
146 	if (ds_dom_hdl_to_name(dhdl, dname, LDMA_DOMAIN_NAME_MAXLEN) != 0) {
147 		(void) strcpy(dname, "<unknown>");
148 	}
149 
150 	LDMA_DBG("%s: REGISTER hdl=%llx, dhdl=%llx (%s) ver=%hd.%hd",
151 	    agent->info->name, hdl, dhdl, dname, ver->major, ver->minor);
152 
153 	/*
154 	 * Record client information if the connexion is from the control
155 	 * domain. The domain service framework only allows connexion of a
156 	 * domain with the control domain. However, if the agent is running
157 	 * on the control domain then it can see connexions coming from any
158 	 * domains. That's why we explicitly have to check if the connexion
159 	 * is effectively with the control domain.
160 	 */
161 	if (dhdl == LDMA_CONTROL_DOMAIN_DHDL) {
162 		agent->conn_hdl = hdl;
163 		agent->conn_ver.major = ver->major;
164 		agent->conn_ver.minor = ver->minor;
165 	} else {
166 		LDMA_INFO("agent %s will ignore any request from distrusted "
167 		    "domain %s", agent->info->name, dname);
168 	}
169 }
170 
171 /*
172  * Agent unregister callback. This callback is invoked when a client is
173  * unregistered and stops using the service provided by an agent.
174  */
175 static void
176 ldma_unreg_cb(ds_hdl_t hdl, ds_cb_arg_t arg)
177 {
178 	ldma_agent_t *agent = (ldma_agent_t *)arg;
179 
180 	LDMA_DBG("%s: UNREGISTER hdl=%llx", agent->info->name, hdl);
181 
182 	if (agent->conn_hdl == hdl) {
183 		agent->conn_hdl = 0;
184 		agent->conn_ver.major = 0;
185 		agent->conn_ver.minor = 0;
186 	} else {
187 		LDMA_INFO("agent %s has unregistered consumer from "
188 		    "distrusted domain", agent->info->name);
189 	}
190 }
191 
192 /*
193  * Agent data callback. This callback is invoked when an agent receives a new
194  * message from a client. Any request from a client which is not the control
195  * domain is immediatly rejected. Otherwise the message is forwarded to the
196  * appropriate handler function provided by the agent, depending on the message
197  * type.
198  */
199 static void
200 ldma_data_cb(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf, size_t len)
201 {
202 	ldma_agent_t *agent = (ldma_agent_t *)arg;
203 	ldma_msg_handler_t *handler;
204 	ldma_message_header_t *request = buf;
205 	ldma_message_header_t *reply = NULL;
206 	ldma_request_status_t status;
207 	size_t request_dlen, reply_len, reply_dlen = 0;
208 	int i;
209 
210 	/* check the message size */
211 	if (len < LDMA_MESSAGE_HEADER_SIZE) {
212 		LDMA_INFO("agent %s has ignored message with an invalid "
213 		    "size of %d bytes", agent->info->name, len);
214 		return;
215 	}
216 
217 	request_dlen = LDMA_MESSAGE_DLEN(len);
218 
219 	LDMA_DBG("%s: DATA hdl=%llx, request num=%llu type=0x%x info=0x%x "
220 	    "dlen=%d", agent->info->name, hdl, request->msg_num,
221 	    request->msg_type, request->msg_info, request_dlen);
222 
223 	/* reject any request which is not from the control domain */
224 	if (hdl != agent->conn_hdl) {
225 		LDMA_DBG("%s: DATA hdl=%llx, rejecting request from a "
226 		    "distrusted domain", agent->info->name, hdl);
227 		status = LDMA_REQ_DENIED;
228 		goto do_reply;
229 	}
230 
231 	handler = NULL;
232 
233 	for (i = 0; i < agent->info->nhandlers; i++) {
234 		if (agent->info->handlers[i].msg_type == request->msg_type) {
235 			handler = &agent->info->handlers[i];
236 			break;
237 		}
238 	}
239 
240 	if (handler == NULL) {
241 		/* this type of message is not defined by the agent */
242 		LDMA_DBG("%s: DATA hdl=%llx, unknown message type %x",
243 		    agent->info->name, hdl, request->msg_type);
244 		status = LDMA_REQ_NOTSUP;
245 		goto do_reply;
246 	}
247 
248 	if (handler->msg_handler == NULL) {
249 		/*
250 		 * This type of message is defined by the agent but it
251 		 * has no handler. That means there is no processing to
252 		 * do, the message is just ignored, but the request is
253 		 * successfully completed.
254 		 */
255 		LDMA_DBG("%s: DATA hdl=%llx, no handler",
256 		    agent->info->name, hdl);
257 		status = LDMA_REQ_COMPLETED;
258 		goto do_reply;
259 	}
260 
261 	/* invoke the message handler of the agent */
262 	status = (*handler->msg_handler)(&agent->conn_ver, request,
263 	    request_dlen, &reply, &reply_dlen);
264 
265 	LDMA_DBG("%s: DATA hdl=%llx, handler stat=%d reply=%p rlen=%d",
266 	    agent->info->name, hdl, status, (void *)reply, reply_dlen);
267 
268 do_reply:
269 	/*
270 	 * If the handler has provided a reply message, we use it directly.
271 	 * Otherwise, we build a reply depending on the status of the request.
272 	 * In that case, we re-use the request buffer to build the reply
273 	 * message.
274 	 */
275 	if (reply == NULL) {
276 
277 		reply = request;
278 		reply_dlen = 0;
279 
280 		if (status == LDMA_REQ_COMPLETED) {
281 			/*
282 			 * The request was successful but no result message was
283 			 * provided so we send an empty result message.
284 			 */
285 			reply->msg_type = LDMA_MSG_RESULT;
286 			reply->msg_info = 0;
287 
288 		} else {
289 			/*
290 			 * The request has failed but no error message was
291 			 * provided so we send an error message based on the
292 			 * request status.
293 			 */
294 			reply->msg_type = LDMA_MSG_ERROR;
295 			reply->msg_info =
296 			    (status == LDMA_REQ_NOTSUP)? LDMA_MSGERR_NOTSUP :
297 			    (status == LDMA_REQ_INVALID)? LDMA_MSGERR_INVALID :
298 			    (status == LDMA_REQ_DENIED)? LDMA_MSGERR_DENY :
299 			    LDMA_MSGERR_FAIL;
300 		}
301 	}
302 
303 	reply_len = LDMA_MESSAGE_SIZE(reply_dlen);
304 
305 	LDMA_DBG("%s: DATA hdl=%llx, reply num=%llu type=0x%x info=0x%x "
306 	    "dlen=%d", agent->info->name, hdl, reply->msg_num,
307 	    reply->msg_type, reply->msg_info, reply_dlen);
308 
309 	if (ds_send_msg(hdl, reply, reply_len) != 0) {
310 		LDMA_ERR("agent %s has failed to send reply for request %llu",
311 		    agent->info->name, request->msg_num);
312 	}
313 
314 	if (reply != request)
315 		free(reply);
316 }
317 
318 /*
319  * Register an agent. Return 0 if the agent was successfully registered.
320  */
321 static int
322 ldma_register(ldma_agent_info_t *agent_info)
323 {
324 	ldma_agent_t	*agent;
325 	ds_capability_t	ds_cap;
326 	ds_ops_t	ds_ops;
327 
328 	agent = malloc(sizeof (ldma_agent_t));
329 	if (agent == NULL)
330 		goto register_fail;
331 
332 	agent->info = agent_info;
333 	agent->conn_hdl = 0;
334 	agent->conn_ver.major = 0;
335 	agent->conn_ver.minor = 0;
336 
337 	ds_cap.svc_id = agent_info->name;
338 	ds_cap.vers = agent_info->vers;
339 	ds_cap.nvers = agent_info->nvers;
340 
341 	ds_ops.ds_reg_cb = ldma_reg_cb;
342 	ds_ops.ds_unreg_cb = ldma_unreg_cb;
343 	ds_ops.ds_data_cb = ldma_data_cb;
344 	ds_ops.cb_arg = agent;
345 
346 	if (ds_svc_reg(&ds_cap, &ds_ops) == 0) {
347 		LDMA_INFO("agent %s registered", agent_info->name);
348 		return (0);
349 	}
350 
351 register_fail:
352 
353 	LDMA_ERR("agent %s has failed to register", agent_info->name);
354 	free(agent);
355 	return (-1);
356 }
357 
358 /*
359  * Register all known agents. Return the number of agents successfully
360  * registered.
361  */
362 static int
363 ldma_register_agents()
364 {
365 	int count = 0;
366 	ldma_agent_info_t **agent_infop;
367 
368 	for (agent_infop = ldma_agent_infos;
369 	    *agent_infop != NULL; agent_infop++) {
370 
371 		if (ldma_register(*agent_infop) == 0)
372 			count++;
373 	}
374 
375 	return (count);
376 }
377 
378 /*ARGSUSED*/
379 static void
380 ldma_sigusr_handler(int sig, siginfo_t *sinfo, void *ucontext)
381 {
382 	/*
383 	 * The child process can send the signal before the fork()
384 	 * call has returned in the parent process. So daemon_pid
385 	 * may not be set yet, and we don't check the pid in that
386 	 * case.
387 	 */
388 	if (sig != SIGUSR1 || sinfo->si_code != SI_USER ||
389 	    (daemon_pid > 0 && sinfo->si_pid != daemon_pid))
390 		return;
391 
392 	/*
393 	 * The parent process has received a USR1 signal from the child.
394 	 * This means that the daemon has correctly started and the parent
395 	 * can exit.
396 	 */
397 	exit(0);
398 }
399 
400 static void
401 ldma_start(boolean_t standalone)
402 {
403 	int stat, rv;
404 	struct sigaction action;
405 
406 	if (!standalone) {
407 		/*
408 		 * Some configuration of the daemon has to be done in the
409 		 * child, but we want the parent to report if the daemon
410 		 * has successfully started or not. So we setup a signal
411 		 * handler, and the child will notify the parent using the
412 		 * USR1 signal if the setup was successful. Otherwise the
413 		 * child will exit.
414 		 */
415 		action.sa_sigaction = ldma_sigusr_handler;
416 		action.sa_flags = SA_SIGINFO;
417 
418 		if (sigemptyset(&action.sa_mask) == -1) {
419 			LDMA_ERR("sigemptyset error (%d)", errno);
420 			exit(1);
421 		}
422 
423 		if (sigaction(SIGUSR1, &action, NULL) == -1) {
424 			LDMA_ERR("sigaction() error (%d)", errno);
425 			exit(1);
426 		}
427 
428 		if (sigrelse(SIGUSR1) == -1) {
429 			LDMA_ERR("sigrelse() error (%d)", errno);
430 			exit(1);
431 		}
432 
433 		if ((daemon_pid = fork()) == -1) {
434 			LDMA_ERR("fork() error (%d)", errno);
435 			exit(1);
436 		}
437 
438 		if (daemon_pid != 0) {
439 			/*
440 			 * The parent process waits until the child exits (in
441 			 * case of an error) or sends a USR1 signal (if the
442 			 * daemon has correctly started).
443 			 */
444 			for (;;) {
445 				rv = waitpid(daemon_pid, &stat, 0);
446 				if ((rv == daemon_pid && WIFEXITED(stat)) ||
447 				    (rv == -1 && errno != EINTR)) {
448 					/* child has exited or error */
449 					exit(1);
450 				}
451 			}
452 		}
453 
454 		/*
455 		 * Initialize child process
456 		 */
457 		if (sighold(SIGUSR1) == -1) {
458 			LDMA_ERR("sighold error (%d)", errno);
459 			exit(1);
460 		}
461 
462 		if (sigignore(SIGUSR1) == -1) {
463 			LDMA_ERR("sigignore error (%d)", errno);
464 			exit(1);
465 		}
466 
467 		if (setsid() == -1) {
468 			LDMA_ERR("setsid error (%d)", errno);
469 			exit(1);
470 		}
471 
472 		if (chdir("/") == -1) {
473 			LDMA_ERR("chdir error (%d)", errno);
474 			exit(1);
475 		}
476 		(void) umask(0);
477 
478 		/*
479 		 * Initialize file descriptors. Do not touch stderr
480 		 * which is initialized by SMF to point to the daemon
481 		 * specific log file.
482 		 */
483 		(void) close(STDIN_FILENO);
484 		if (open("/dev/null", O_RDWR) == -1) {
485 			LDMA_ERR("open /dev/null error (%d)", errno);
486 			exit(1);
487 		}
488 		if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) {
489 			LDMA_ERR("dup2 error (%d)", errno);
490 			exit(1);
491 		}
492 		closefrom(STDERR_FILENO + 1);
493 
494 		/* initialize logging */
495 		openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON);
496 
497 		ldma_daemon = B_TRUE;
498 	}
499 
500 	/*
501 	 * Register the agents. It would be easier to do this before
502 	 * daemonizing so that any start error is directly reported. But
503 	 * this can not be done because agents are registered using libds
504 	 * and this will subscribe the daemon to some sysevents which is
505 	 * a process based subscription. Instead we notify the parent process
506 	 * either by exiting, or by sending a SIGUSR1 signal.
507 	 */
508 	if (ldma_register_agents() == 0) {
509 		/* no agent registered */
510 		LDMA_ERR("Unable to register any agent");
511 		exit(1);
512 	}
513 
514 	if (!standalone) {
515 		/* signal parent that startup was successful */
516 		if (kill(getppid(), SIGUSR1) == -1)
517 			exit(1);
518 	}
519 }
520 
521 static void
522 ldma_usage()
523 {
524 	(void) fprintf(stderr, "usage: %s\n", cmdname);
525 }
526 
527 int
528 main(int argc, char *argv[])
529 {
530 	int opt;
531 	boolean_t standalone = B_FALSE;
532 
533 	cmdname = basename(argv[0]);
534 
535 	/* disable getopt error messages */
536 	opterr = 0;
537 
538 	while ((opt = getopt(argc, argv, "ds")) != EOF) {
539 
540 		switch (opt) {
541 		case 'd':
542 			ldma_debug = B_TRUE;
543 			break;
544 		case 's':
545 			standalone = B_TRUE;
546 			break;
547 		default:
548 			ldma_usage();
549 			exit(1);
550 		}
551 	}
552 
553 	ldma_start(standalone);
554 
555 	/*
556 	 * Loop forever. Any incoming message will be received by libds and
557 	 * forwarded to the agent data callback (ldma_data_cb()) where it
558 	 * will be processed.
559 	 */
560 	for (;;) {
561 		(void) pause();
562 	}
563 
564 	/*NOTREACHED*/
565 	return (0);
566 }
567