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