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 = 0; 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 /* 381 * The child process can send the signal before the fork() 382 * call has returned in the parent process. So daemon_pid 383 * may not be set yet, and we don't check the pid in that 384 * case. 385 */ 386 if (sig != SIGUSR1 || sinfo->si_code != SI_USER || 387 (daemon_pid > 0 && sinfo->si_pid != daemon_pid)) 388 return; 389 390 /* 391 * The parent process has received a USR1 signal from the child. 392 * This means that the daemon has correctly started and the parent 393 * can exit. 394 */ 395 exit(0); 396 } 397 398 static void 399 ldma_start(boolean_t standalone) 400 { 401 int stat, rv; 402 struct sigaction action; 403 404 if (!standalone) { 405 /* 406 * Some configuration of the daemon has to be done in the 407 * child, but we want the parent to report if the daemon 408 * has successfully started or not. So we setup a signal 409 * handler, and the child will notify the parent using the 410 * USR1 signal if the setup was successful. Otherwise the 411 * child will exit. 412 */ 413 action.sa_sigaction = ldma_sigusr_handler; 414 action.sa_flags = SA_SIGINFO; 415 416 if (sigemptyset(&action.sa_mask) == -1) { 417 LDMA_ERR("sigemptyset error (%d)", errno); 418 exit(1); 419 } 420 421 if (sigaction(SIGUSR1, &action, NULL) == -1) { 422 LDMA_ERR("sigaction() error (%d)", errno); 423 exit(1); 424 } 425 426 if (sigrelse(SIGUSR1) == -1) { 427 LDMA_ERR("sigrelse() error (%d)", errno); 428 exit(1); 429 } 430 431 if ((daemon_pid = fork()) == -1) { 432 LDMA_ERR("fork() error (%d)", errno); 433 exit(1); 434 } 435 436 if (daemon_pid != 0) { 437 /* 438 * The parent process waits until the child exits (in 439 * case of an error) or sends a USR1 signal (if the 440 * daemon has correctly started). 441 */ 442 for (;;) { 443 rv = waitpid(daemon_pid, &stat, 0); 444 if ((rv == daemon_pid && WIFEXITED(stat)) || 445 (rv == -1 && errno != EINTR)) { 446 /* child has exited or error */ 447 exit(1); 448 } 449 } 450 } 451 452 /* 453 * Initialize child process 454 */ 455 if (sighold(SIGUSR1) == -1) { 456 LDMA_ERR("sighold error (%d)", errno); 457 exit(1); 458 } 459 460 if (sigignore(SIGUSR1) == -1) { 461 LDMA_ERR("sigignore error (%d)", errno); 462 exit(1); 463 } 464 465 if (setsid() == -1) { 466 LDMA_ERR("setsid error (%d)", errno); 467 exit(1); 468 } 469 470 if (chdir("/") == -1) { 471 LDMA_ERR("chdir error (%d)", errno); 472 exit(1); 473 } 474 (void) umask(0); 475 476 /* 477 * Initialize file descriptors. Do not touch stderr 478 * which is initialized by SMF to point to the daemon 479 * specific log file. 480 */ 481 (void) close(STDIN_FILENO); 482 if (open("/dev/null", O_RDWR) == -1) { 483 LDMA_ERR("open /dev/null error (%d)", errno); 484 exit(1); 485 } 486 if (dup2(STDIN_FILENO, STDOUT_FILENO) == -1) { 487 LDMA_ERR("dup2 error (%d)", errno); 488 exit(1); 489 } 490 closefrom(STDERR_FILENO + 1); 491 492 /* initialize logging */ 493 openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON); 494 495 ldma_daemon = B_TRUE; 496 } 497 498 /* 499 * Register the agents. It would be easier to do this before 500 * daemonizing so that any start error is directly reported. But 501 * this can not be done because agents are registered using libds 502 * and this will subscribe the daemon to some sysevents which is 503 * a process based subscription. Instead we notify the parent process 504 * either by exiting, or by sending a SIGUSR1 signal. 505 */ 506 if (ldma_register_agents() == 0) { 507 /* no agent registered */ 508 LDMA_ERR("Unable to register any agent"); 509 exit(1); 510 } 511 512 if (!standalone) { 513 /* signal parent that startup was successful */ 514 if (kill(getppid(), SIGUSR1) == -1) 515 exit(1); 516 } 517 } 518 519 static void 520 ldma_usage() 521 { 522 (void) fprintf(stderr, "usage: %s\n", cmdname); 523 } 524 525 int 526 main(int argc, char *argv[]) 527 { 528 int opt; 529 boolean_t standalone = B_FALSE; 530 531 cmdname = basename(argv[0]); 532 533 /* disable getopt error messages */ 534 opterr = 0; 535 536 while ((opt = getopt(argc, argv, "ds")) != EOF) { 537 538 switch (opt) { 539 case 'd': 540 ldma_debug = B_TRUE; 541 break; 542 case 's': 543 standalone = B_TRUE; 544 break; 545 default: 546 ldma_usage(); 547 exit(1); 548 } 549 } 550 551 ldma_start(standalone); 552 553 /* 554 * Loop forever. Any incoming message will be received by libds and 555 * forwarded to the agent data callback (ldma_data_cb()) where it 556 * will be processed. 557 */ 558 for (;;) { 559 (void) pause(); 560 } 561 562 /*NOTREACHED*/ 563 return (0); 564 } 565