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