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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * Logical Domains (LDoms) Agents Daemon 28 * 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. 34 * 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. 39 * 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. 44 */ 45 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> 64 65 #include "ldma.h" 66 67 #define LDMA_MODULE "ldm-agent-daemon" 68 69 #define LDMA_CONTROL_DOMAIN_DHDL 0 /* id of the control domain */ 70 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; 76 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; 82 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; 87 88 boolean_t ldma_debug = B_FALSE; 89 boolean_t ldma_daemon = B_FALSE; 90 91 static ldma_agent_info_t *ldma_agent_infos[] = { 92 &ldma_device_info, 93 &ldma_system_info, 94 &ldma_dio_info, 95 NULL 96 }; 97 98 static char *cmdname; 99 static pid_t daemon_pid = 0; 100 101 /* 102 * Lookup connexion in agent connexion table. 103 */ 104 static ldma_connexion_t * 105 ldma_connexion_lookup(ldma_agent_t *agent, ds_hdl_t hdl) 106 { 107 ldma_connexion_t *connp; 108 int i; 109 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); 114 } 115 return (NULL); 116 } 117 118 /* 119 * Add connextion to agent connexion table. 120 */ 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) 124 { 125 ldma_connexion_t *connp; 126 ldma_connexion_t *availp = NULL; 127 int i; 128 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; 135 } 136 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); 142 } 143 144 if (!availp) { 145 (void) mutex_unlock(&agent->conn_lock); 146 LDMA_INFO("agent %s too many connections", agent->info->name); 147 return (0); 148 } 149 150 LDMA_DBG("agent %s adding connection (%x) %llx, %llx, %d.%d", 151 agent->info->name, availp, hdl, dhdl, verp->major, verp->minor); 152 153 availp->hdl = hdl; 154 availp->dhdl = dhdl; 155 availp->ver = *verp; 156 (void) mutex_unlock(&agent->conn_lock); 157 return (1); 158 } 159 160 /* 161 * Delete connexion from agent connexion table. 162 */ 163 static int 164 ldma_connexion_delete(ldma_agent_t *agent, ds_hdl_t hdl) 165 { 166 ldma_connexion_t *connp; 167 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); 174 } 175 176 LDMA_DBG("agent %s deleting connection (%x) %llx", agent->info->name, 177 connp, hdl); 178 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); 185 } 186 187 /* 188 * Initialize connexion table. 189 */ 190 static void 191 ldma_connexion_init(ldma_agent_t *agent) 192 { 193 ldma_connexion_t *connp; 194 int i; 195 196 for (connp = agent->conn, i = 0; i < LDOMS_MAX_DOMAINS; i++, connp++) { 197 connp->hdl = DS_INVALID_HDL; 198 } 199 } 200 201 /* 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. 205 */ 206 static ldma_message_header_t * 207 ldma_alloc_msg(uint64_t msg_num, uint32_t msg_type, size_t msg_dlen) 208 { 209 ldma_message_header_t *msg; 210 size_t msg_len; 211 212 msg_len = LDMA_MESSAGE_SIZE(msg_dlen); 213 msg = malloc(msg_len); 214 if (msg == NULL) 215 return (NULL); 216 217 msg->msg_num = msg_num; 218 msg->msg_type = msg_type; 219 msg->msg_info = 0; 220 221 return (msg); 222 } 223 224 /* 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. 229 */ 230 ldma_message_header_t * 231 ldma_alloc_result_msg(ldma_message_header_t *request, size_t msg_dlen) 232 { 233 uint64_t msg_num; 234 235 msg_num = (request == NULL)? 0 : request->msg_num; 236 237 return (ldma_alloc_msg(msg_num, LDMA_MSG_RESULT, msg_dlen)); 238 } 239 240 /* 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. 244 */ 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) 248 { 249 ldma_agent_t *agent = (ldma_agent_t *)arg; 250 char dname[LDOMS_MAX_NAME_LEN]; 251 252 if (ds_dom_hdl_to_name(dhdl, dname, LDOMS_MAX_NAME_LEN) != 0) { 253 (void) strcpy(dname, "<unknown>"); 254 } 255 256 LDMA_DBG("%s: REGISTER hdl=%llx, dhdl=%llx (%s) ver=%hd.%hd", 257 agent->info->name, hdl, dhdl, dname, ver->major, ver->minor); 258 259 /* 260 * Record client information. Access control is done on a 261 * message-by-message basis upon receipt of the message. 262 */ 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); 266 } 267 } 268 269 /* 270 * Agent unregister callback. This callback is invoked when a client is 271 * unregistered and stops using the service provided by an agent. 272 */ 273 static void 274 ldma_unreg_cb(ds_hdl_t hdl, ds_cb_arg_t arg) 275 { 276 ldma_agent_t *agent = (ldma_agent_t *)arg; 277 278 LDMA_DBG("%s: UNREGISTER hdl=%llx", agent->info->name, hdl); 279 280 if (!ldma_connexion_delete(agent, hdl)) { 281 LDMA_INFO("agent %s failed to unregister handle %llx", 282 agent->info->name, hdl); 283 } 284 } 285 286 /* 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. 292 */ 293 static void 294 ldma_data_cb(ds_hdl_t hdl, ds_cb_arg_t arg, void *buf, size_t len) 295 { 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; 306 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; 312 } 313 314 request_dlen = LDMA_MESSAGE_DLEN(len); 315 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); 319 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; 325 } 326 (void) mutex_unlock(&agent->conn_lock); 327 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; 334 } 335 336 handler = NULL; 337 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; 342 } 343 } 344 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; 351 } 352 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; 360 } 361 362 if (handler->msg_handler == NULL) { 363 /* 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. 368 */ 369 LDMA_DBG("%s: DATA hdl=%llx, no handler", 370 agent->info->name, hdl); 371 status = LDMA_REQ_COMPLETED; 372 goto do_reply; 373 } 374 375 /* invoke the message handler of the agent */ 376 status = (*handler->msg_handler)(&conn_ver, request, request_dlen, 377 &reply, &reply_dlen); 378 379 LDMA_DBG("%s: DATA hdl=%llx, handler stat=%d reply=%p rlen=%d", 380 agent->info->name, hdl, status, (void *)reply, reply_dlen); 381 382 do_reply: 383 /* 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. 388 */ 389 if (reply == NULL) { 390 391 reply = request; 392 reply_dlen = 0; 393 394 if (status == LDMA_REQ_COMPLETED) { 395 /* 396 * The request was successful but no result message was 397 * provided so we send an empty result message. 398 */ 399 reply->msg_type = LDMA_MSG_RESULT; 400 reply->msg_info = 0; 401 402 } else { 403 /* 404 * The request has failed but no error message was 405 * provided so we send an error message based on the 406 * request status. 407 */ 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; 414 } 415 } 416 417 reply_len = LDMA_MESSAGE_SIZE(reply_dlen); 418 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); 422 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); 426 } 427 428 if (reply != request) 429 free(reply); 430 } 431 432 /* 433 * Register an agent. Return 0 if the agent was successfully registered. 434 */ 435 static int 436 ldma_register(ldma_agent_info_t *agent_info) 437 { 438 ldma_agent_t *agent; 439 ds_capability_t ds_cap; 440 ds_ops_t ds_ops; 441 442 agent = malloc(sizeof (ldma_agent_t)); 443 if (agent == NULL) 444 goto register_fail; 445 446 agent->info = agent_info; 447 (void) mutex_init(&agent->conn_lock, USYNC_THREAD, NULL); 448 ldma_connexion_init(agent); 449 450 ds_cap.svc_id = agent_info->name; 451 ds_cap.vers = agent_info->vers; 452 ds_cap.nvers = agent_info->nvers; 453 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; 458 459 if (ds_svc_reg(&ds_cap, &ds_ops) == 0) { 460 LDMA_INFO("agent %s registered", agent_info->name); 461 return (0); 462 } 463 464 register_fail: 465 466 LDMA_ERR("agent %s has failed to register", agent_info->name); 467 free(agent); 468 return (-1); 469 } 470 471 /* 472 * Register all known agents. Return the number of agents successfully 473 * registered. 474 */ 475 static int 476 ldma_register_agents() 477 { 478 int count = 0; 479 ldma_agent_info_t **agent_infop; 480 481 for (agent_infop = ldma_agent_infos; 482 *agent_infop != NULL; agent_infop++) { 483 484 if (ldma_register(*agent_infop) == 0) 485 count++; 486 } 487 488 return (count); 489 } 490 491 /*ARGSUSED*/ 492 static void 493 ldma_sigusr_handler(int sig, siginfo_t *sinfo, void *ucontext) 494 { 495 /* 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. 500 */ 501 if (sig != SIGUSR1 || sinfo->si_code != SI_USER || 502 (daemon_pid > 0 && sinfo->si_pid != daemon_pid)) 503 return; 504 505 /* 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. 509 */ 510 exit(0); 511 } 512 513 static void 514 ldma_start(boolean_t standalone) 515 { 516 int stat, rv; 517 struct sigaction action; 518 519 if (!standalone) { 520 /* 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. 527 */ 528 action.sa_sigaction = ldma_sigusr_handler; 529 action.sa_flags = SA_SIGINFO; 530 531 if (sigemptyset(&action.sa_mask) == -1) { 532 LDMA_ERR("sigemptyset error (%d)", errno); 533 exit(1); 534 } 535 536 if (sigaction(SIGUSR1, &action, NULL) == -1) { 537 LDMA_ERR("sigaction() error (%d)", errno); 538 exit(1); 539 } 540 541 if (sigrelse(SIGUSR1) == -1) { 542 LDMA_ERR("sigrelse() error (%d)", errno); 543 exit(1); 544 } 545 546 if ((daemon_pid = fork()) == -1) { 547 LDMA_ERR("fork() error (%d)", errno); 548 exit(1); 549 } 550 551 if (daemon_pid != 0) { 552 /* 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). 556 */ 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); 563 } 564 } 565 } 566 567 /* 568 * Initialize child process 569 */ 570 if (sighold(SIGUSR1) == -1) { 571 LDMA_ERR("sighold error (%d)", errno); 572 exit(1); 573 } 574 575 if (sigignore(SIGUSR1) == -1) { 576 LDMA_ERR("sigignore error (%d)", errno); 577 exit(1); 578 } 579 580 if (setsid() == -1) { 581 LDMA_ERR("setsid error (%d)", errno); 582 exit(1); 583 } 584 585 if (chdir("/") == -1) { 586 LDMA_ERR("chdir error (%d)", errno); 587 exit(1); 588 } 589 (void) umask(0); 590 591 /* 592 * Initialize file descriptors. Do not touch stderr 593 * which is initialized by SMF to point to the daemon 594 * specific log file. 595 */ 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); 600 } 601 if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) { 602 LDMA_ERR("dup2 error (%d)", errno); 603 exit(1); 604 } 605 closefrom(STDERR_FILENO + 1); 606 607 /* initialize logging */ 608 openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON); 609 610 ldma_daemon = B_TRUE; 611 } 612 613 /* 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. 620 */ 621 if (ldma_register_agents() == 0) { 622 /* no agent registered */ 623 LDMA_ERR("Unable to register any agent"); 624 exit(1); 625 } 626 627 if (!standalone) { 628 /* signal parent that startup was successful */ 629 if (kill(getppid(), SIGUSR1) == -1) 630 exit(1); 631 } 632 } 633 634 static void 635 ldma_usage() 636 { 637 (void) fprintf(stderr, "usage: %s\n", cmdname); 638 } 639 640 int 641 main(int argc, char *argv[]) 642 { 643 int opt; 644 boolean_t standalone = B_FALSE; 645 646 cmdname = basename(argv[0]); 647 648 /* disable getopt error messages */ 649 opterr = 0; 650 651 while ((opt = getopt(argc, argv, "ds")) != EOF) { 652 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); 663 } 664 } 665 666 ldma_start(standalone); 667 668 /* 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. 672 */ 673 for (;;) { 674 (void) pause(); 675 } 676 677 /*NOTREACHED*/ 678 return (0); 679 } 680