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 74 typedef 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 80 typedef 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 */ 87 extern ldma_agent_info_t ldma_device_info; 88 extern ldma_agent_info_t ldma_system_info; 89 extern ldma_agent_info_t ldma_dio_info; 90 91 boolean_t ldma_debug = B_FALSE; 92 boolean_t ldma_daemon = B_FALSE; 93 94 static ldma_agent_info_t *ldma_agent_infos[] = { 95 &ldma_device_info, 96 &ldma_system_info, 97 &ldma_dio_info, 98 NULL 99 }; 100 101 static char *cmdname; 102 static pid_t daemon_pid = 0; 103 104 /* 105 * Lookup connexion in agent connexion table. 106 */ 107 static ldma_connexion_t * 108 ldma_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 */ 124 static int 125 ldma_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 */ 166 static int 167 ldma_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 */ 193 static void 194 ldma_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 */ 209 static ldma_message_header_t * 210 ldma_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 */ 233 ldma_message_header_t * 234 ldma_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 */ 248 static void 249 ldma_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 */ 276 static void 277 ldma_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 */ 296 static void 297 ldma_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 385 do_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 */ 438 static int 439 ldma_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 467 register_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 */ 478 static int 479 ldma_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*/ 495 static void 496 ldma_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 516 static void 517 ldma_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 637 static void 638 ldma_usage() 639 { 640 (void) fprintf(stderr, "usage: %s\n", cmdname); 641 } 642 643 int 644 main(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