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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 /* 30 * This is the main file for the Domain Configuration Server (DCS). 31 * 32 * The DCS is a server that runs on a domain and communicates with 33 * a Domain Configuration Agent (DCA) running on a remote host. The 34 * DCA initiates DR requests that the DCS performs by calling the 35 * appropriate libcfgadm(3LIB) function. 36 * 37 * This file contains functions that receive and process the messages 38 * received from the DCA. It also handles the initialization of the 39 * server and is responsible for starting a concurrent session to 40 * handle each DR request. 41 */ 42 43 #include <stdlib.h> 44 #include <stdio.h> 45 #include <unistd.h> 46 #include <string.h> 47 #include <fcntl.h> 48 #include <errno.h> 49 #include <syslog.h> 50 #include <assert.h> 51 #include <signal.h> 52 #include <netdb.h> 53 #include <config_admin.h> 54 #include <sys/param.h> 55 #include <sys/time.h> 56 #include <sys/stat.h> 57 #include <sys/socket.h> 58 #include <strings.h> 59 60 #include "dcs.h" 61 #include "remote_cfg.h" 62 #include "rdr_param_types.h" 63 #include "rdr_messages.h" 64 #include "rsrc_info.h" 65 66 67 typedef struct { 68 ushort_t major; 69 ushort_t minor; 70 } dcs_ver_t; 71 72 73 /* initialization functions */ 74 static int init_server(struct pollfd *pfd, uint8_t ah_auth_alg, 75 uint8_t esp_encr_alg, uint8_t esp_auth_alg); 76 static void init_signals(void); 77 78 /* message processing functions */ 79 static int invalid_msg(rdr_msg_hdr_t *hdr); 80 81 /* message handling functions */ 82 static int dcs_ses_req(rdr_msg_hdr_t *hdr, cfga_params_t *param); 83 static int dcs_ses_estbl(rdr_msg_hdr_t *hdr, cfga_params_t *param); 84 static int dcs_ses_end(rdr_msg_hdr_t *hdr, cfga_params_t *param); 85 static int dcs_change_state(rdr_msg_hdr_t *hdr, cfga_params_t *param); 86 static int dcs_private_func(rdr_msg_hdr_t *hdr, cfga_params_t *param); 87 static int dcs_test(rdr_msg_hdr_t *hdr, cfga_params_t *param); 88 static int dcs_list_ext(rdr_msg_hdr_t *hdr, cfga_params_t *param); 89 static int dcs_help(rdr_msg_hdr_t *hdr, cfga_params_t *param); 90 static int dcs_ap_id_cmp(rdr_msg_hdr_t *hdr, cfga_params_t *param); 91 static int dcs_abort_cmd(rdr_msg_hdr_t *hdr, cfga_params_t *param); 92 static int dcs_rsrc_info(rdr_msg_hdr_t *hdr, cfga_params_t *param); 93 static int dcs_unknown_op(rdr_msg_hdr_t *hdr, cfga_params_t *param); 94 95 /* local callback functions */ 96 static int dcs_confirm_callback(void *appdata_ptr, const char *message); 97 static int dcs_message_callback(void *appdata_ptr, const char *message); 98 99 /* utility functions */ 100 static dcs_ver_t resolve_version(ushort_t req_major, ushort_t req_minor); 101 static void filter_list_data(int perm, int *nlistp, cfga_list_data_t *linfo); 102 static rdr_list_t *generate_sort_order(cfga_list_data_t *listp, int nlist); 103 static int ldata_compare(const void *ap1, const void *ap2); 104 static int invalid_msg(rdr_msg_hdr_t *hdr); 105 static char *basename(char *path); 106 static boolean_t is_socket(int fd); 107 static uint8_t dcs_get_alg(dcs_alg_t *algs, char *arg, dcs_err_code *error); 108 static void dcs_log_bad_alg(char optopt, char *optarg); 109 110 111 /* 112 * Lookup table for handling different message types. This 113 * assumes the ordering of rdr_msg_opcode_t in remote_cfg.h. 114 * If this enum changes, the lookup table must be updated. 115 * 116 * The lookup table handles all _known_ opcodes >= 0. Unsupported 117 * opcodes, or opcodes that should not be received by the 118 * dispatcher are handled by the dcs_unknown_op() function. 119 */ 120 int (*dcs_cmd[])(rdr_msg_hdr_t *, cfga_params_t *) = { 121 dcs_unknown_op, /* 0 is an invalid opcode */ 122 dcs_ses_req, /* RDR_SES_REQ */ 123 dcs_ses_estbl, /* RDR_SES_ESTBL */ 124 dcs_ses_end, /* RDR_SES_END */ 125 dcs_change_state, /* RDR_CONF_CHANGE_STATE */ 126 dcs_private_func, /* RDR_CONF_PRIVATE_FUNC */ 127 dcs_test, /* RDR_CONF_TEST */ 128 dcs_list_ext, /* RDR_CONF_LIST_EXT */ 129 dcs_help, /* RDR_CONF_HELP */ 130 dcs_ap_id_cmp, /* RDR_CONF_AP_ID_CMP */ 131 dcs_abort_cmd, /* RDR_CONF_ABORT_CMD */ 132 dcs_unknown_op, /* RDR_CONF_CONFIRM_CALLBACK */ 133 dcs_unknown_op, /* RDR_CONF_MSG_CALLBACK */ 134 dcs_rsrc_info /* RDR_RSRC_INFO */ 135 }; 136 137 138 /* 139 * ver_supp[] is an array of the supported versions for the network 140 * transport protocol used by the DCA and DCS. Each item in the array 141 * is a pair: { major_version, minor_version }. 142 * 143 * The order of the array is significant. The first element should be 144 * the highest supported version and all successive elements should be 145 * strictly decreasing. 146 */ 147 dcs_ver_t ver_supp[] = { 148 { 1, 0 } 149 }; 150 151 #define DCS_CURR_VER ver_supp[0] 152 153 154 /* 155 * Global Data 156 */ 157 char *cmdname = NULL; /* the name of the executable */ 158 ulong_t dcs_debug = 0; /* control the amount of debugging */ 159 int standalone = 0; /* control standalone mode */ 160 boolean_t inetd = B_FALSE; /* control daemon mode */ 161 ulong_t max_sessions = DCS_MAX_SESSIONS; /* control maximum active sessions */ 162 int dcsfd = STDIN_FILENO; /* fd for the DCS reserved port */ 163 164 /* 165 * Array of acceptable -a, -e and -u arguments. 166 */ 167 static dcs_alg_t auth_algs_array[] = { 168 { "none", SADB_AALG_NONE }, /* -a none or -u none */ 169 { "md5", SADB_AALG_MD5HMAC }, /* -a md5 or -u md5 */ 170 { "sha1", SADB_AALG_SHA1HMAC }, /* -a sha1 or -u sha1 */ 171 { NULL, 0x0 } 172 }, esp_algs_array[] = { 173 { "none", SADB_EALG_NONE }, /* -e none */ 174 { "des", SADB_EALG_DESCBC }, /* -e des */ 175 { "3des", SADB_EALG_3DESCBC }, /* -e 3des */ 176 { NULL, 0x0 } 177 }; 178 179 180 /* 181 * main: 182 * 183 * Initialize the DCS and then enter an infinite loop. This loop waits 184 * for connection requests to come and then establishes a connection. 185 * It dispatches the connection to be handled in a concurrent session. 186 */ 187 int 188 main(int argc, char **argv) 189 { 190 int opt; 191 struct timeval tv; 192 struct pollfd dcs_rcv; 193 int newfd; 194 uint8_t ah_auth_alg = SADB_AALG_NONE; 195 uint8_t esp_encr_alg = SADB_EALG_NONE; 196 uint8_t esp_auth_alg = SADB_AALG_NONE; 197 dcs_err_code alg_ec = DCS_NO_ERR; 198 199 200 /* initialize globals */ 201 dcs_debug = DBG_NONE; 202 cmdname = basename(argv[0]); 203 204 /* open log file with unique prefix */ 205 openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON); 206 207 /* 208 * Process command line args 209 */ 210 opterr = 0; /* disable getopt error messages */ 211 while ((opt = getopt(argc, argv, OPT_STR)) != EOF) { 212 213 switch (opt) { 214 215 case 'd': { 216 int usr_debug; 217 char *err_str; 218 219 usr_debug = strtol(optarg, &err_str, 0); 220 221 /* 222 * The err_str parameter will be an 223 * empty string if successful. 224 */ 225 if (*err_str != '\0') { 226 dcs_log_msg(LOG_ERR, DCS_BAD_OPT_ARG, optopt, 227 optarg, "exiting"); 228 (void) rdr_reject(dcsfd); 229 exit(1); 230 } 231 232 dcs_debug = usr_debug; 233 break; 234 } 235 236 case 'S': 237 standalone++; 238 break; 239 240 case 's': { 241 int usr_ses; 242 char *err_str; 243 244 usr_ses = strtol(optarg, &err_str, 0); 245 246 if (usr_ses >= 1) { 247 max_sessions = usr_ses; 248 } else { 249 char behavior_str[MAX_MSG_LEN]; 250 251 snprintf(behavior_str, MAX_MSG_LEN, 252 "using default value (%d)", max_sessions); 253 254 dcs_log_msg(LOG_NOTICE, DCS_BAD_OPT_ARG, optopt, 255 optarg, behavior_str); 256 } 257 258 break; 259 } 260 261 case 'a': 262 case 'u': 263 if (opt == 'a') 264 ah_auth_alg = dcs_get_alg(auth_algs_array, 265 optarg, &alg_ec); 266 else /* opt == 'u' */ 267 esp_auth_alg = dcs_get_alg(auth_algs_array, 268 optarg, &alg_ec); 269 270 if (alg_ec == DCS_BAD_OPT_ARG) { 271 dcs_log_bad_alg(optopt, optarg); 272 (void) rdr_reject(dcsfd); 273 exit(1); 274 } 275 276 break; 277 278 case 'e': 279 esp_encr_alg = dcs_get_alg(esp_algs_array, optarg, 280 &alg_ec); 281 282 if (alg_ec == DCS_BAD_OPT_ARG) { 283 dcs_log_bad_alg(optopt, optarg); 284 (void) rdr_reject(dcsfd); 285 exit(1); 286 } 287 288 break; 289 290 default: 291 if (optopt == 'a' || optopt == 'e' || optopt == 'u') 292 dcs_log_bad_alg(optopt, optarg); 293 else 294 dcs_log_msg(LOG_ERR, DCS_BAD_OPT, optopt); 295 (void) rdr_reject(dcsfd); 296 exit(1); 297 298 /* NOTREACHED */ 299 break; 300 } 301 } 302 303 /* 304 * In the future if inetd supports per-socket IPsec dcs can be run 305 * under inetd. 306 * Daemonize if we were not started by inetd unless running standalone. 307 */ 308 inetd = is_socket(STDIN_FILENO); 309 if (inetd == B_FALSE && standalone == 0) { 310 closefrom(0); 311 (void) chdir("/"); 312 (void) umask(0); 313 314 if (fork() != 0) 315 exit(0); 316 317 (void) setsid(); 318 319 /* open log again after all files were closed */ 320 openlog(cmdname, LOG_CONS | LOG_NDELAY, LOG_DAEMON); 321 } 322 323 DCS_DBG(DBG_ALL, "initializing %s...", cmdname); 324 325 init_signals(); 326 327 /* must be root */ 328 if (geteuid() != 0) { 329 dcs_log_msg(LOG_ERR, DCS_NO_PRIV); 330 (void) rdr_reject(dcsfd); 331 exit(1); 332 } 333 334 /* 335 * Seed the random number generator for 336 * generating random session identifiers. 337 */ 338 gettimeofday(&tv, NULL); 339 srand48(tv.tv_usec); 340 341 /* initialize our transport endpoint */ 342 if (init_server(&dcs_rcv, ah_auth_alg, esp_encr_alg, esp_auth_alg) == 343 -1) { 344 dcs_log_msg(LOG_ERR, DCS_INIT_ERR); 345 (void) rdr_reject(dcsfd); 346 exit(1); 347 } 348 349 350 DCS_DBG(DBG_ALL, "%s initialized, debug level = 0x%X, " 351 "max sessions = %d", cmdname, dcs_debug, max_sessions); 352 353 /* 354 * Main service loop 355 */ 356 for (;;) { 357 358 /* wait for a connection request */ 359 if (ses_poll(&dcs_rcv, 1, BLOCKFOREVER) == -1) { 360 if (errno != EINTR) { 361 dcs_log_msg(LOG_ERR, DCS_INT_ERR, "poll", 362 strerror(errno)); 363 } 364 continue; 365 } 366 367 /* attempt to connect */ 368 newfd = rdr_connect_srv(dcs_rcv.fd); 369 370 if ((newfd == RDR_ERROR) || (newfd == RDR_NET_ERR)) { 371 dcs_log_msg(LOG_ERR, DCS_CONNECT_ERR); 372 continue; 373 } 374 375 376 /* process the session concurrently */ 377 if (ses_start(newfd) == -1) { 378 dcs_log_msg(LOG_ERR, DCS_SES_HAND_ERR); 379 (void) rdr_close(newfd); 380 break; 381 } 382 } 383 384 close(dcs_rcv.fd); 385 return (1); 386 } 387 388 389 /* 390 * dcs_get_alg: 391 * 392 * Returns the ID of the first algorithm found in the 'algs' array 393 * with a name matching 'arg'. If there is no matching algorithm, 394 * 'error' is set to DCS_BAD_OPT_ARG, otherwise it is set to DCS_NO_ERR. 395 * The 'algs' array must be terminated by an entry containing a NULL 396 * 'arg_name' field. The 'error' argument must be a valid pointer. 397 */ 398 static uint8_t 399 dcs_get_alg(dcs_alg_t *algs, char *arg, dcs_err_code *error) 400 { 401 dcs_alg_t *alg; 402 403 *error = DCS_NO_ERR; 404 405 for (alg = algs; alg->arg_name != NULL && arg != NULL; alg++) { 406 if (strncmp(alg->arg_name, arg, strlen(alg->arg_name) + 1) 407 == 0) { 408 return (alg->alg_id); 409 } 410 } 411 412 *error = DCS_BAD_OPT_ARG; 413 414 return (0); 415 } 416 417 418 /* 419 * dcs_log_bad_alg: 420 * 421 * Logs an appropriate message when an invalid command line argument 422 * was provided. 'optarg' is the invalid argument string for the 423 * command line option 'optopt', where 'optopt' = 'a' for the '-a' 424 * option. A NULL 'optarg' indicates the required option was not 425 * provided. 426 */ 427 static void 428 dcs_log_bad_alg(char optopt, char *optarg) 429 { 430 if (optarg == NULL) { 431 dcs_log_msg(LOG_ERR, DCS_BAD_OPT_ARG, optopt, 432 "empty string", "an argument is required, exiting"); 433 } else { 434 dcs_log_msg(LOG_ERR, DCS_BAD_OPT_ARG, optopt, 435 optarg, "exiting"); 436 } 437 } 438 439 440 /* 441 * init_server: 442 * 443 * Perform all the operations that are required to initialize the 444 * transport endpoint used by the DCS. After this routine succeeds, 445 * the DCS is ready to accept session requests on its well known 446 * port. 447 */ 448 static int 449 init_server(struct pollfd *pfd, uint8_t ah_auth_alg, uint8_t esp_encr_alg, 450 uint8_t esp_auth_alg) 451 { 452 struct servent *se; 453 struct sockaddr_storage ss; 454 struct sockaddr_in *sin; 455 struct sockaddr_in6 *sin6; 456 ipsec_req_t ipsec_req; 457 int req_port; 458 int act_port; 459 int init_status; 460 int num_sock_opts; 461 int sock_opts[] = { SO_REUSEADDR }; 462 463 464 assert(pfd); 465 pfd->fd = dcsfd; 466 pfd->events = POLLIN | POLLPRI; 467 pfd->revents = 0; 468 469 470 /* 471 * In standalone mode, we have to initialize the transport 472 * endpoint for our reserved port. In daemon mode, inetd 473 * starts the DCS and hands off STDIN_FILENO connected to 474 * our reserved port. 475 */ 476 477 if (inetd == B_FALSE || standalone) { 478 /* in standalone mode, init fd for reserved port */ 479 if ((dcsfd = rdr_open(AF_INET6)) == -1) { 480 DCS_DBG(DBG_ALL, "rdr_open failed"); 481 return (-1); 482 } 483 pfd->fd = dcsfd; 484 485 /* 486 * Enable per-socket IPsec if the user specified an 487 * AH or ESP algorithm to use. 488 */ 489 if (ah_auth_alg != SADB_AALG_NONE || 490 esp_encr_alg != SADB_EALG_NONE || 491 esp_auth_alg != SADB_AALG_NONE) { 492 int err; 493 494 bzero(&ipsec_req, sizeof (ipsec_req)); 495 496 /* Hardcoded values */ 497 ipsec_req.ipsr_self_encap_req = SELF_ENCAP_REQ; 498 /* User defined */ 499 ipsec_req.ipsr_auth_alg = ah_auth_alg; 500 ipsec_req.ipsr_esp_alg = esp_encr_alg; 501 if (ah_auth_alg != SADB_AALG_NONE) 502 ipsec_req.ipsr_ah_req = AH_REQ; 503 if (esp_encr_alg != SADB_EALG_NONE || 504 esp_auth_alg != SADB_AALG_NONE) { 505 ipsec_req.ipsr_esp_req = ESP_REQ; 506 ipsec_req.ipsr_esp_auth_alg = esp_auth_alg; 507 } 508 509 err = rdr_setsockopt(pfd->fd, IPPROTO_IPV6, 510 IPV6_SEC_OPT, (void *)&ipsec_req, 511 sizeof (ipsec_req)); 512 513 if (err != RDR_OK) { 514 DCS_DBG(DBG_ALL, "rdr_setsockopt failed"); 515 return (-1); 516 } 517 } 518 } 519 520 /* 521 * Look up our service to get the reserved port number 522 */ 523 if ((se = getservbyname(DCS_SERVICE, "tcp")) == NULL) { 524 dcs_log_msg(LOG_NOTICE, DCS_NO_SERV, DCS_SERVICE); 525 526 /* use the known port if service wasn't found */ 527 req_port = SUN_DR_PORT; 528 } else { 529 req_port = se->s_port; 530 } 531 532 /* initialize our local address */ 533 sin6 = (struct sockaddr_in6 *)&ss; 534 (void) memset(sin6, 0, sizeof (*sin6)); 535 sin6->sin6_family = AF_INET6; 536 sin6->sin6_port = htons(req_port); 537 sin6->sin6_addr = in6addr_any; 538 539 num_sock_opts = sizeof (sock_opts) / sizeof (*sock_opts); 540 541 init_status = rdr_init(pfd->fd, (struct sockaddr *)sin6, 542 sock_opts, num_sock_opts, DCS_BACKLOG); 543 544 if (init_status != RDR_OK) { 545 return (-1); 546 } 547 548 switch (ss.ss_family) { 549 case AF_INET: 550 DCS_DBG(DBG_ALL, "using AF_INET socket"); 551 sin = (struct sockaddr_in *)&ss; 552 act_port = ntohs(sin->sin_port); 553 break; 554 case AF_INET6: 555 DCS_DBG(DBG_ALL, "using AF_INET6 socket"); 556 /* sin6 already set correctly */ 557 act_port = ntohs(sin6->sin6_port); 558 break; 559 default: 560 DCS_DBG(DBG_ALL, "unknown socket type"); 561 return (-1); 562 } 563 564 /* check that we got the requested port */ 565 if (req_port != act_port) { 566 dcs_log_msg(LOG_ERR, DCS_NO_PORT, req_port); 567 return (-1); 568 } 569 570 return (0); 571 } 572 573 574 /* 575 * init_signals: 576 * 577 * Initialize signals for the current session. All signals will be 578 * blocked with two possible exceptions. SIGINT is not blocked in 579 * standalone mode, and ses_init_signals() is called to selectively 580 * unblock any signals required to handle concurrent sessions. 581 */ 582 static void 583 init_signals(void) 584 { 585 sigset_t mask; 586 587 588 /* block all signals */ 589 sigfillset(&mask); 590 591 /* in standalone, allow user to abort */ 592 if (standalone) { 593 sigdelset(&mask, SIGINT); 594 } 595 596 ses_init_signals(&mask); 597 598 (void) sigprocmask(SIG_BLOCK, &mask, NULL); 599 } 600 601 602 /* 603 * dcs_dispatch_message: 604 * 605 * This function dispatches a message to the correct function. The 606 * correct handler is determined by the opcode field of the message 607 * header. 608 */ 609 int 610 dcs_dispatch_message(rdr_msg_hdr_t *hdr, cfga_params_t *params) 611 { 612 session_t *sp; 613 614 615 assert(hdr); 616 assert(params); 617 618 /* get the current session information */ 619 if ((sp = curr_ses()) == NULL) { 620 ses_close(DCS_ERROR); 621 return (-1); 622 } 623 624 /* check the message */ 625 if (invalid_msg(hdr)) { 626 dcs_log_msg(LOG_ERR, DCS_MSG_INVAL); 627 ses_close(DCS_MSG_INVAL); 628 return (-1); 629 } 630 631 /* save the current message */ 632 sp->curr_msg.hdr = hdr; 633 sp->curr_msg.params = params; 634 635 /* 636 * hdr->message_opcode is unsigned so don't need 637 * to check for values less than zero 638 */ 639 if (hdr->message_opcode >= RDR_NUM_OPS) { 640 dcs_unknown_op(hdr, params); 641 ses_close(DCS_MSG_INVAL); 642 return (-1); 643 } 644 645 PRINT_MSG_DBG(DCS_RECEIVE, hdr); 646 647 /* dispatch the message */ 648 if ((*dcs_cmd[hdr->message_opcode])(hdr, params) == -1) { 649 dcs_log_msg(LOG_ERR, DCS_OP_FAILED); 650 ses_close(DCS_ERROR); 651 return (-1); 652 } 653 654 return (0); 655 } 656 657 658 /* 659 * init_msg: 660 * 661 * Initialize the message header with information from the current 662 * session. Fields not set directly are initialized to zero. 663 */ 664 void 665 init_msg(rdr_msg_hdr_t *hdr) 666 { 667 session_t *sp; 668 669 670 assert(hdr); 671 672 /* get the current session information */ 673 if ((sp = curr_ses()) == NULL) { 674 ses_close(DCS_ERROR); 675 return; 676 } 677 678 (void) memset(hdr, 0, sizeof (rdr_msg_hdr_t)); 679 680 /* set the session information */ 681 hdr->random_req = sp->random_req; 682 hdr->random_resp = sp->random_resp; 683 684 /* set the version being used */ 685 hdr->major_version = sp->major_version; 686 hdr->minor_version = sp->minor_version; 687 } 688 689 690 /* 691 * invalid_msg: 692 * 693 * Check if the message is valid for the current session. This 694 * is accomplished by checking various information in the header 695 * against the information for the current session. 696 */ 697 static int 698 invalid_msg(rdr_msg_hdr_t *hdr) 699 { 700 session_t *sp; 701 702 703 assert(hdr); 704 705 /* get the current session information */ 706 if ((sp = curr_ses()) == NULL) { 707 ses_close(DCS_ERROR); 708 return (-1); 709 } 710 711 /* 712 * Only perform the following checks if the message 713 * is not a session request. The information to check 714 * will not be set at the time a session request is 715 * received. 716 */ 717 if (hdr->message_opcode != RDR_SES_REQ) { 718 719 /* check major and minor version */ 720 if ((sp->major_version != hdr->major_version) || 721 (sp->minor_version != hdr->minor_version)) { 722 DCS_DBG(DBG_MSG, "unsupported version %d.%d", 723 hdr->major_version, hdr->minor_version); 724 return (-1); 725 } 726 727 /* check session identifiers */ 728 if ((sp->random_req != hdr->random_req) || 729 (sp->random_resp != hdr->random_resp)) { 730 DCS_DBG(DBG_MSG, "invalid session identifiers: " 731 "<%d, %d>", hdr->random_req, hdr->random_resp); 732 return (-1); 733 } 734 } 735 736 return (0); 737 } 738 739 740 /* 741 * dcs_ses_req: 742 * 743 * Handle a session request message (RDR_SES_REQ). 744 */ 745 static int 746 dcs_ses_req(rdr_msg_hdr_t *hdr, cfga_params_t *param) 747 { 748 session_t *sp; 749 rdr_msg_hdr_t reply_hdr; 750 cfga_params_t reply_param; 751 dcs_ver_t act_ver; 752 int snd_status; 753 static char *op_name = "session request"; 754 755 756 assert(hdr); 757 assert(param); 758 759 /* get the current session information */ 760 if ((sp = curr_ses()) == NULL) { 761 ses_close(DCS_ERROR); 762 return (-1); 763 } 764 765 /* make sure that a session hasn't been requested yet */ 766 if (sp->state != DCS_CONNECTED) { 767 dcs_log_msg(LOG_ERR, DCS_SES_SEQ_INVAL); 768 ses_close(DCS_SES_SEQ_INVAL); 769 return (-1); 770 } 771 772 ses_setlocale(param->req.locale_str); 773 774 /* get the best matching version supported */ 775 act_ver = resolve_version(hdr->major_version, hdr->minor_version); 776 777 /* initialize session information */ 778 sp->random_req = hdr->random_req; 779 sp->major_version = act_ver.major; 780 sp->minor_version = act_ver.minor; 781 782 /* prepare header information */ 783 init_msg(&reply_hdr); 784 reply_hdr.message_opcode = RDR_SES_REQ; 785 reply_hdr.data_type = RDR_REPLY; 786 reply_hdr.status = DCS_OK; 787 788 /* prepare session request specific data */ 789 (void) memset(&reply_param, 0, sizeof (cfga_params_t)); 790 reply_param.req.session_id = sp->id; 791 792 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 793 794 /* send the message */ 795 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, &reply_param, 796 DCS_SND_TIMEOUT); 797 798 if (snd_status == RDR_ABORTED) { 799 abort_handler(); 800 } 801 802 if (snd_status != RDR_OK) { 803 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 804 return (-1); 805 } 806 807 sp->state = DCS_SES_REQ; 808 return (0); 809 } 810 811 812 /* 813 * dcs_ses_estbl: 814 * 815 * Handle a session establishment message (RDR_SES_ESTBL). 816 */ 817 /* ARGSUSED */ 818 static int 819 dcs_ses_estbl(rdr_msg_hdr_t *hdr, cfga_params_t *param) 820 { 821 session_t *sp; 822 dcs_ver_t act_ver; 823 824 825 assert(hdr); 826 assert(param); 827 828 /* get the current session information */ 829 if ((sp = curr_ses()) == NULL) { 830 ses_close(DCS_ERROR); 831 return (-1); 832 } 833 834 /* 835 * Make sure that a session has not been 836 * established yet, and that a session 837 * request has already been processed. 838 */ 839 if (sp->state != DCS_SES_REQ) { 840 dcs_log_msg(LOG_ERR, DCS_SES_SEQ_INVAL); 841 ses_close(DCS_SES_SEQ_INVAL); 842 return (-1); 843 } 844 845 /* get the best matching version supported */ 846 act_ver = resolve_version(hdr->major_version, hdr->minor_version); 847 848 if ((act_ver.major != hdr->major_version) || 849 (act_ver.minor != hdr->minor_version)) { 850 851 /* end the session because protocol not supported */ 852 dcs_log_msg(LOG_ERR, DCS_VER_INVAL, hdr->major_version, 853 hdr->minor_version); 854 ses_close(DCS_VER_INVAL); 855 return (-1); 856 } 857 858 DCS_DBG(DBG_SES, "Session Established"); 859 sp->state = DCS_SES_ESTBL; 860 861 return (0); 862 } 863 864 865 /* 866 * dcs_ses_end: 867 * 868 * Handle a session end message (RDR_SES_END). 869 */ 870 static int 871 dcs_ses_end(rdr_msg_hdr_t *hdr, cfga_params_t *param) 872 { 873 session_t *sp; 874 rdr_msg_hdr_t reply_hdr; 875 cfga_params_t reply_param; 876 int snd_status; 877 static char *op_name = "session end"; 878 879 880 assert(hdr); 881 assert(param); 882 883 /* get the current session information */ 884 if ((sp = curr_ses()) == NULL) { 885 ses_close(DCS_ERROR); 886 return (-1); 887 } 888 889 /* 890 * Session end is valid from any state. However, only 891 * send back a reply if the error code is zero. A non-zero 892 * error code indicates that the session is being terminated 893 * under an error condition, and no acknowledgement is 894 * required. 895 */ 896 if (param->end.error_code == 0) { 897 898 /* prepare header information */ 899 init_msg(&reply_hdr); 900 reply_hdr.message_opcode = RDR_SES_END; 901 reply_hdr.data_type = RDR_REPLY; 902 reply_hdr.status = DCS_OK; 903 904 /* return empty data - no information needed in reply */ 905 (void) memset(&reply_param, 0, sizeof (cfga_params_t)); 906 907 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 908 909 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, &reply_param, 910 DCS_SND_TIMEOUT); 911 912 if (snd_status == RDR_ABORTED) { 913 abort_handler(); 914 } 915 916 if (snd_status != RDR_OK) { 917 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 918 } 919 } 920 921 sp->state = DCS_SES_END; 922 923 return ((snd_status != RDR_OK) ? -1 : 0); 924 } 925 926 927 /* 928 * dcs_change_state: 929 * 930 * Handle a change state request message (RDR_CONF_CHANGE_STATE). 931 */ 932 static int 933 dcs_change_state(rdr_msg_hdr_t *hdr, cfga_params_t *param) 934 { 935 session_t *sp; 936 rdr_msg_hdr_t reply_hdr; 937 change_state_params_t *op_data; 938 struct cfga_confirm local_conf_cb; 939 struct cfga_msg local_msg_cb; 940 int cfga_status = 0; 941 int snd_status; 942 char *err_str; 943 unsigned int curr_attempt; 944 unsigned int num_attempts; 945 char retry_msg[MAX_MSG_LEN]; 946 static char *op_name = "config_change_state"; 947 948 949 assert(hdr); 950 assert(param); 951 952 /* get the current session information */ 953 if ((sp = curr_ses()) == NULL) { 954 ses_close(DCS_ERROR); 955 return (-1); 956 } 957 958 op_data = ¶m->change; 959 960 /* make sure we have a session established */ 961 if (sp->state != DCS_SES_ESTBL) { 962 dcs_log_msg(LOG_ERR, DCS_NO_SES_ESTBL, op_name); 963 ses_close(DCS_NO_SES_ERR); 964 return (-1); 965 } 966 967 /* initialize local confirm callback */ 968 local_conf_cb.confirm = dcs_confirm_callback; 969 local_conf_cb.appdata_ptr = (void *)op_data->confp; 970 971 /* initialize local message callback */ 972 local_msg_cb.message_routine = dcs_message_callback; 973 local_msg_cb.appdata_ptr = (void *)op_data->msgp; 974 975 /* verify retry value */ 976 if (op_data->retries < 0) { 977 dcs_log_msg(LOG_NOTICE, DCS_BAD_RETRY_VAL, op_data->retries); 978 op_data->retries = 0; 979 } 980 981 /* verify timeout value */ 982 if (op_data->timeval < 0) { 983 dcs_log_msg(LOG_NOTICE, DCS_BAD_TIME_VAL, op_data->timeval); 984 op_data->timeval = 0; 985 } 986 987 num_attempts = 1 + op_data->retries; 988 curr_attempt = 0; 989 990 while (curr_attempt < num_attempts) { 991 992 /* don't sleep the first time around */ 993 if (curr_attempt != 0) { 994 995 /* log the error message and alert the user */ 996 err_str = dcs_cfga_str(op_data->errstring, cfga_status); 997 if (err_str) { 998 dcs_log_msg(LOG_ERR, DCS_CFGA_ERR, op_name, 999 err_str); 1000 dcs_message_callback((void *)op_data->msgp, 1001 err_str); 1002 free((void *)err_str); 1003 } else { 1004 dcs_log_msg(LOG_ERR, DCS_CFGA_UNKNOWN); 1005 dcs_message_callback((void *)op_data->msgp, 1006 dcs_strerror(DCS_CFGA_UNKNOWN)); 1007 } 1008 1009 if (op_data->errstring && *op_data->errstring) { 1010 free((void *)*op_data->errstring); 1011 *op_data->errstring = NULL; 1012 } 1013 1014 /* sleep with abort enabled */ 1015 ses_sleep(op_data->timeval); 1016 1017 /* log the retry attempt and alert the user */ 1018 dcs_log_msg(LOG_INFO, DCS_RETRY, curr_attempt); 1019 snprintf(retry_msg, MAX_MSG_LEN, 1020 dcs_strerror(DCS_RETRY), curr_attempt); 1021 dcs_message_callback((void *)op_data->msgp, retry_msg); 1022 } 1023 1024 sp->state = DCS_CONF_PENDING; 1025 1026 /* 1027 * Call into libcfgadm 1028 */ 1029 ses_abort_enable(); 1030 1031 cfga_status = config_change_state(op_data->state_change, 1032 op_data->num_ap_ids, op_data->ap_ids, op_data->options, 1033 &local_conf_cb, &local_msg_cb, op_data->errstring, 1034 op_data->flags); 1035 1036 ses_abort_disable(); 1037 1038 /* 1039 * Retry only the operations that have a chance to 1040 * succeed if retried. All libcfgadm errors not 1041 * included below will always fail, regardless of 1042 * a retry. 1043 */ 1044 if ((cfga_status != CFGA_BUSY) && 1045 (cfga_status != CFGA_SYSTEM_BUSY) && 1046 (cfga_status != CFGA_ERROR)) { 1047 break; 1048 } 1049 1050 /* prepare for another attempt */ 1051 ++curr_attempt; 1052 } 1053 1054 sp->state = DCS_CONF_DONE; 1055 1056 /* log any libcfgadm errors */ 1057 if (cfga_status != CFGA_OK) { 1058 err_str = dcs_cfga_str(op_data->errstring, cfga_status); 1059 if (err_str) { 1060 dcs_log_msg(LOG_ERR, DCS_CFGA_ERR, op_name, err_str); 1061 free((void *)err_str); 1062 } 1063 } 1064 1065 /* prepare header information */ 1066 init_msg(&reply_hdr); 1067 reply_hdr.message_opcode = RDR_CONF_CHANGE_STATE; 1068 reply_hdr.data_type = RDR_REPLY; 1069 reply_hdr.status = cfga_status; 1070 1071 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 1072 1073 /* send the message */ 1074 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, param, DCS_SND_TIMEOUT); 1075 1076 if (snd_status == RDR_ABORTED) { 1077 abort_handler(); 1078 } 1079 1080 if (snd_status != RDR_OK) { 1081 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1082 } 1083 1084 /* clean up */ 1085 if (op_data->errstring && *op_data->errstring) { 1086 free((void *)*op_data->errstring); 1087 *op_data->errstring = NULL; 1088 } 1089 1090 return ((snd_status != RDR_OK) ? -1 : 0); 1091 } 1092 1093 1094 /* 1095 * dcs_private_func: 1096 * 1097 * Handle a private function request message (RDR_CONF_PRIVATE_FUNC). 1098 */ 1099 static int 1100 dcs_private_func(rdr_msg_hdr_t *hdr, cfga_params_t *param) 1101 { 1102 session_t *sp; 1103 rdr_msg_hdr_t reply_hdr; 1104 private_func_params_t *op_data; 1105 struct cfga_confirm local_conf_cb; 1106 struct cfga_msg local_msg_cb; 1107 int cfga_status; 1108 int snd_status; 1109 char *err_str; 1110 static char *op_name = "config_private_func"; 1111 1112 1113 assert(hdr); 1114 assert(param); 1115 1116 /* get the current session information */ 1117 if ((sp = curr_ses()) == NULL) { 1118 ses_close(DCS_ERROR); 1119 return (-1); 1120 } 1121 1122 op_data = ¶m->priv; 1123 1124 /* make sure we have a session established */ 1125 if (sp->state != DCS_SES_ESTBL) { 1126 dcs_log_msg(LOG_ERR, DCS_NO_SES_ESTBL, op_name); 1127 ses_close(DCS_NO_SES_ERR); 1128 return (-1); 1129 } 1130 1131 /* initialize local confirm callback */ 1132 local_conf_cb.confirm = dcs_confirm_callback; 1133 local_conf_cb.appdata_ptr = (void *)op_data->confp; 1134 1135 /* initialize local message callback */ 1136 local_msg_cb.message_routine = dcs_message_callback; 1137 local_msg_cb.appdata_ptr = (void *)op_data->msgp; 1138 1139 sp->state = DCS_CONF_PENDING; 1140 1141 /* 1142 * Call into libcfgadm 1143 */ 1144 ses_abort_enable(); 1145 1146 cfga_status = config_private_func(op_data->function, 1147 op_data->num_ap_ids, op_data->ap_ids, op_data->options, 1148 &local_conf_cb, &local_msg_cb, op_data->errstring, op_data->flags); 1149 1150 ses_abort_disable(); 1151 1152 sp->state = DCS_CONF_DONE; 1153 1154 /* log any libcfgadm errors */ 1155 if (cfga_status != CFGA_OK) { 1156 err_str = dcs_cfga_str(op_data->errstring, cfga_status); 1157 if (err_str) { 1158 dcs_log_msg(LOG_ERR, DCS_CFGA_ERR, op_name, err_str); 1159 free((void *)err_str); 1160 } 1161 } 1162 1163 /* prepare header information */ 1164 init_msg(&reply_hdr); 1165 reply_hdr.message_opcode = RDR_CONF_PRIVATE_FUNC; 1166 reply_hdr.data_type = RDR_REPLY; 1167 reply_hdr.status = cfga_status; 1168 1169 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 1170 1171 /* send the message */ 1172 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, param, DCS_SND_TIMEOUT); 1173 1174 if (snd_status == RDR_ABORTED) { 1175 abort_handler(); 1176 } 1177 1178 if (snd_status != RDR_OK) { 1179 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1180 } 1181 1182 if (op_data->errstring && *op_data->errstring) { 1183 free((void *)*op_data->errstring); 1184 *op_data->errstring = NULL; 1185 } 1186 1187 return ((snd_status != RDR_OK) ? -1 : 0); 1188 } 1189 1190 1191 /* 1192 * dcs_test: 1193 * 1194 * Handle a test request message (RDR_CONF_TEST). 1195 */ 1196 static int 1197 dcs_test(rdr_msg_hdr_t *hdr, cfga_params_t *param) 1198 { 1199 session_t *sp; 1200 rdr_msg_hdr_t reply_hdr; 1201 test_params_t *op_data; 1202 struct cfga_msg local_msg_cb; 1203 int cfga_status; 1204 int snd_status; 1205 char *err_str; 1206 static char *op_name = "config_test"; 1207 1208 1209 assert(hdr); 1210 assert(param); 1211 1212 /* get the current session information */ 1213 if ((sp = curr_ses()) == NULL) { 1214 ses_close(DCS_ERROR); 1215 return (-1); 1216 } 1217 1218 op_data = ¶m->test; 1219 1220 /* make sure we have a session established */ 1221 if (sp->state != DCS_SES_ESTBL) { 1222 dcs_log_msg(LOG_ERR, DCS_NO_SES_ESTBL, op_name); 1223 ses_close(DCS_NO_SES_ERR); 1224 return (-1); 1225 } 1226 1227 /* initialize local message callback */ 1228 local_msg_cb.message_routine = dcs_message_callback; 1229 local_msg_cb.appdata_ptr = op_data->msgp; 1230 1231 sp->state = DCS_CONF_PENDING; 1232 1233 /* 1234 * Call into libcfgadm 1235 */ 1236 ses_abort_enable(); 1237 1238 cfga_status = config_test(op_data->num_ap_ids, op_data->ap_ids, 1239 op_data->options, &local_msg_cb, op_data->errstring, 1240 op_data->flags); 1241 1242 ses_abort_disable(); 1243 1244 sp->state = DCS_CONF_DONE; 1245 1246 /* log any libcfgadm errors */ 1247 if (cfga_status != CFGA_OK) { 1248 err_str = dcs_cfga_str(op_data->errstring, cfga_status); 1249 if (err_str) { 1250 dcs_log_msg(LOG_ERR, DCS_CFGA_ERR, op_name, err_str); 1251 free((void *)err_str); 1252 } 1253 } 1254 1255 /* prepare header information */ 1256 init_msg(&reply_hdr); 1257 reply_hdr.message_opcode = RDR_CONF_TEST; 1258 reply_hdr.data_type = RDR_REPLY; 1259 reply_hdr.status = cfga_status; 1260 1261 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 1262 1263 /* send the message */ 1264 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, param, DCS_SND_TIMEOUT); 1265 1266 if (snd_status == RDR_ABORTED) { 1267 abort_handler(); 1268 } 1269 1270 if (snd_status != RDR_OK) { 1271 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1272 } 1273 1274 if (op_data->errstring && *op_data->errstring) { 1275 free((void *)*op_data->errstring); 1276 *op_data->errstring = NULL; 1277 } 1278 1279 return ((snd_status != RDR_OK) ? -1 : 0); 1280 } 1281 1282 1283 /* 1284 * dcs_list_ext: 1285 * 1286 * Handle a list request message (RDR_CONF_LIST_EXT). 1287 */ 1288 static int 1289 dcs_list_ext(rdr_msg_hdr_t *hdr, cfga_params_t *param) 1290 { 1291 session_t *sp; 1292 rdr_msg_hdr_t reply_hdr; 1293 list_ext_params_t *op_data; 1294 int cfga_status; 1295 int snd_status; 1296 char *err_str; 1297 static char *op_name = "config_list_ext"; 1298 cfga_list_data_t *ap_ids; 1299 1300 1301 assert(hdr); 1302 assert(param); 1303 1304 /* get the current session information */ 1305 if ((sp = curr_ses()) == NULL) { 1306 ses_close(DCS_ERROR); 1307 return (-1); 1308 } 1309 1310 op_data = ¶m->list_ext; 1311 1312 /* make sure we have a session established */ 1313 if (sp->state != DCS_SES_ESTBL) { 1314 dcs_log_msg(LOG_ERR, DCS_NO_SES_ESTBL, op_name); 1315 ses_close(DCS_NO_SES_ERR); 1316 return (-1); 1317 } 1318 1319 /* 1320 * Make sure that we can retrieve the data 1321 * from libcfgadm. If not, report the error. 1322 */ 1323 if (op_data->ap_id_list == NULL) { 1324 dcs_log_msg(LOG_ERR, DCS_MSG_INVAL); 1325 ses_close(DCS_MSG_INVAL); 1326 return (-1); 1327 } 1328 1329 sp->state = DCS_CONF_PENDING; 1330 1331 /* 1332 * Call into libcfgadm 1333 */ 1334 ses_abort_enable(); 1335 1336 cfga_status = config_list_ext(op_data->num_ap_ids, op_data->ap_ids, 1337 &ap_ids, op_data->nlist, op_data->options, op_data->listopts, 1338 op_data->errstring, op_data->flags); 1339 1340 ses_abort_disable(); 1341 1342 sp->state = DCS_CONF_DONE; 1343 1344 /* 1345 * Log any libcfgadm errors at a low priority level. 1346 * Since a status request does not modify the system 1347 * in any way, we do not need to worry about these 1348 * errors here on the host. 1349 */ 1350 if (cfga_status != CFGA_OK) { 1351 err_str = dcs_cfga_str(op_data->errstring, cfga_status); 1352 if (err_str) { 1353 dcs_log_msg(LOG_INFO, DCS_CFGA_ERR, op_name, err_str); 1354 free((void *)err_str); 1355 } 1356 } 1357 1358 /* 1359 * Filter ap ids to return only appropriate information 1360 */ 1361 filter_list_data(op_data->permissions, op_data->nlist, ap_ids); 1362 1363 /* if all aps were filtered out, return an error */ 1364 if ((cfga_status == CFGA_OK) && (*op_data->nlist == 0)) { 1365 cfga_status = CFGA_APID_NOEXIST; 1366 } 1367 1368 /* calculate the sort order */ 1369 if (cfga_status == CFGA_OK) { 1370 1371 *op_data->ap_id_list = generate_sort_order(ap_ids, 1372 *op_data->nlist); 1373 1374 if (*op_data->ap_id_list == NULL) { 1375 cfga_status = CFGA_LIB_ERROR; 1376 } 1377 } 1378 1379 /* ensure that nlist is 0 for errors */ 1380 if (cfga_status != CFGA_OK) { 1381 *op_data->nlist = 0; 1382 } 1383 1384 /* prepare header information */ 1385 init_msg(&reply_hdr); 1386 reply_hdr.message_opcode = RDR_CONF_LIST_EXT; 1387 reply_hdr.data_type = RDR_REPLY; 1388 reply_hdr.status = cfga_status; 1389 1390 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 1391 1392 /* send the message */ 1393 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, param, DCS_SND_TIMEOUT); 1394 1395 if (snd_status == RDR_ABORTED) { 1396 abort_handler(); 1397 } 1398 1399 if (snd_status != RDR_OK) { 1400 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1401 } 1402 1403 if (op_data->errstring && *op_data->errstring) { 1404 free((void *)*op_data->errstring); 1405 *op_data->errstring = NULL; 1406 } 1407 1408 if (ap_ids != NULL) { 1409 free((void *)ap_ids); 1410 } 1411 1412 return ((snd_status != RDR_OK) ? -1 : 0); 1413 } 1414 1415 1416 /* 1417 * dcs_help: 1418 * 1419 * Handle a help request message (RDR_CONF_HELP). 1420 */ 1421 static int 1422 dcs_help(rdr_msg_hdr_t *hdr, cfga_params_t *param) 1423 { 1424 session_t *sp; 1425 rdr_msg_hdr_t reply_hdr; 1426 help_params_t *op_data; 1427 struct cfga_msg local_msg_cb; 1428 int cfga_status; 1429 int snd_status; 1430 char *err_str; 1431 static char *op_name = "config_help"; 1432 1433 1434 assert(hdr); 1435 assert(param); 1436 1437 /* get the current session information */ 1438 if ((sp = curr_ses()) == NULL) { 1439 ses_close(DCS_ERROR); 1440 return (-1); 1441 } 1442 1443 op_data = ¶m->help; 1444 1445 /* make sure we have a session established */ 1446 if (sp->state != DCS_SES_ESTBL) { 1447 dcs_log_msg(LOG_ERR, DCS_NO_SES_ESTBL, op_name); 1448 ses_close(DCS_NO_SES_ERR); 1449 return (-1); 1450 } 1451 1452 /* initialize local message callback */ 1453 local_msg_cb.message_routine = dcs_message_callback; 1454 local_msg_cb.appdata_ptr = op_data->msgp; 1455 1456 sp->state = DCS_CONF_PENDING; 1457 1458 /* 1459 * Call into libcfgadm 1460 */ 1461 ses_abort_enable(); 1462 1463 cfga_status = config_help(op_data->num_ap_ids, op_data->ap_ids, 1464 &local_msg_cb, op_data->options, op_data->flags); 1465 1466 ses_abort_disable(); 1467 1468 sp->state = DCS_CONF_DONE; 1469 1470 /* 1471 * Log any libcfgadm errors at a low priority level. 1472 * Since a help request does not modify the system 1473 * in any way, we do not need to worry about these 1474 * errors here on the host. 1475 */ 1476 if (cfga_status != CFGA_OK) { 1477 err_str = dcs_cfga_str(NULL, cfga_status); 1478 if (err_str) { 1479 dcs_log_msg(LOG_INFO, DCS_CFGA_ERR, op_name, err_str); 1480 free((void *)err_str); 1481 } 1482 } 1483 1484 /* prepare header information */ 1485 init_msg(&reply_hdr); 1486 reply_hdr.message_opcode = RDR_CONF_HELP; 1487 reply_hdr.data_type = RDR_REPLY; 1488 reply_hdr.status = cfga_status; 1489 1490 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 1491 1492 /* send the message */ 1493 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, param, DCS_SND_TIMEOUT); 1494 1495 if (snd_status == RDR_ABORTED) { 1496 abort_handler(); 1497 } 1498 1499 if (snd_status != RDR_OK) { 1500 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1501 } 1502 1503 return ((snd_status != RDR_OK) ? -1 : 0); 1504 } 1505 1506 1507 /* 1508 * dcs_ap_id_cmp: 1509 * 1510 * Handle an attachment point comparison request message (RDR_AP_ID_CMP). 1511 */ 1512 static int 1513 dcs_ap_id_cmp(rdr_msg_hdr_t *hdr, cfga_params_t *param) 1514 { 1515 session_t *sp; 1516 rdr_msg_hdr_t reply_hdr; 1517 ap_id_cmp_params_t *op_data; 1518 int snd_status; 1519 int cmp_result; 1520 static char *op_name = "config_ap_id_cmp"; 1521 1522 1523 assert(hdr); 1524 assert(param); 1525 1526 /* get the current session information */ 1527 if ((sp = curr_ses()) == NULL) { 1528 ses_close(DCS_ERROR); 1529 return (-1); 1530 } 1531 1532 op_data = ¶m->cmp; 1533 1534 /* make sure we have a session established */ 1535 if (sp->state != DCS_SES_ESTBL) { 1536 dcs_log_msg(LOG_ERR, DCS_NO_SES_ESTBL, op_name); 1537 ses_close(DCS_NO_SES_ERR); 1538 return (-1); 1539 } 1540 1541 sp->state = DCS_CONF_PENDING; 1542 1543 /* 1544 * Call into libcfgadm 1545 */ 1546 ses_abort_enable(); 1547 1548 cmp_result = config_ap_id_cmp(op_data->ap_log_id1, op_data->ap_log_id2); 1549 1550 ses_abort_disable(); 1551 1552 sp->state = DCS_CONF_DONE; 1553 1554 /* prepare header information */ 1555 init_msg(&reply_hdr); 1556 reply_hdr.message_opcode = RDR_CONF_AP_ID_CMP; 1557 reply_hdr.data_type = RDR_REPLY; 1558 1559 /* 1560 * Return result of comparison as error code. 1561 * Since all values are valid, it is impossible 1562 * to report an error. 1563 */ 1564 reply_hdr.status = cmp_result; 1565 1566 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 1567 1568 /* send the message */ 1569 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, param, DCS_SND_TIMEOUT); 1570 1571 if (snd_status == RDR_ABORTED) { 1572 abort_handler(); 1573 } 1574 1575 if (snd_status != RDR_OK) { 1576 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1577 } 1578 1579 return ((snd_status != RDR_OK) ? -1 : 0); 1580 } 1581 1582 1583 /* 1584 * dcs_abort_cmd: 1585 * 1586 * Handle an abort request message (RDR_CONF_ABORT_CMD). 1587 */ 1588 /* ARGSUSED */ 1589 static int 1590 dcs_abort_cmd(rdr_msg_hdr_t *hdr, cfga_params_t *param) 1591 { 1592 session_t *sp; 1593 rdr_msg_hdr_t reply_hdr; 1594 abort_cmd_params_t *op_data; 1595 int op_status = RDR_SUCCESS; 1596 int snd_status; 1597 static char *op_name = "abort command"; 1598 1599 1600 assert(hdr); 1601 assert(param); 1602 1603 /* get the current session information */ 1604 if ((sp = curr_ses()) == NULL) { 1605 ses_close(DCS_ERROR); 1606 return (-1); 1607 } 1608 1609 op_data = (abort_cmd_params_t *)param; 1610 1611 op_status = ses_abort(op_data->session_id); 1612 1613 if (op_status == -1) { 1614 dcs_log_msg(LOG_ERR, DCS_ABORT_ERR, op_data->session_id); 1615 } 1616 1617 /* prepare header information */ 1618 init_msg(&reply_hdr); 1619 reply_hdr.message_opcode = RDR_CONF_ABORT_CMD; 1620 reply_hdr.data_type = RDR_REPLY; 1621 reply_hdr.status = op_status; 1622 1623 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 1624 1625 /* send the message */ 1626 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, param, DCS_SND_TIMEOUT); 1627 1628 if (snd_status == RDR_ABORTED) { 1629 abort_handler(); 1630 } 1631 1632 if (snd_status != RDR_OK) { 1633 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1634 } 1635 1636 sp->state = DCS_CONF_DONE; 1637 1638 return ((snd_status != RDR_OK) ? -1 : 0); 1639 } 1640 1641 1642 /* 1643 * dcs_rsrc_info: 1644 * 1645 * Handle a resource info request message (RDR_RSRC_INFO). 1646 */ 1647 static int 1648 dcs_rsrc_info(rdr_msg_hdr_t *hdr, cfga_params_t *param) 1649 { 1650 session_t *sp; 1651 rdr_msg_hdr_t reply_hdr; 1652 rsrc_info_params_t *op_data; 1653 int rsrc_status; 1654 int snd_status; 1655 static char *op_name = "resource info init"; 1656 1657 assert(hdr); 1658 assert(param); 1659 1660 /* get the current session information */ 1661 if ((sp = curr_ses()) == NULL) { 1662 ses_close(DCS_ERROR); 1663 return (-1); 1664 } 1665 1666 op_data = (rsrc_info_params_t *)¶m->rsrc_info; 1667 1668 /* make sure we have a session established */ 1669 if (sp->state != DCS_SES_ESTBL) { 1670 dcs_log_msg(LOG_ERR, DCS_NO_SES_ESTBL, op_name); 1671 ses_close(DCS_NO_SES_ERR); 1672 return (-1); 1673 } 1674 1675 sp->state = DCS_CONF_PENDING; 1676 1677 /* 1678 * Request resource info data. 1679 */ 1680 ses_abort_enable(); 1681 1682 rsrc_status = ri_init(op_data->num_ap_ids, op_data->ap_ids, 1683 op_data->flags, &op_data->hdl); 1684 1685 ses_abort_disable(); 1686 1687 sp->state = DCS_CONF_DONE; 1688 1689 /* log errors */ 1690 if (rsrc_status != RI_SUCCESS) { 1691 dcs_log_msg(LOG_ERR, DCS_RSRC_ERR, rsrc_status); 1692 } 1693 1694 /* prepare header information */ 1695 init_msg(&reply_hdr); 1696 reply_hdr.message_opcode = RDR_RSRC_INFO; 1697 reply_hdr.data_type = RDR_REPLY; 1698 reply_hdr.status = rsrc_status; 1699 1700 PRINT_MSG_DBG(DCS_SEND, &reply_hdr); 1701 1702 /* send the message */ 1703 snd_status = rdr_snd_msg(sp->fd, &reply_hdr, param, DCS_SND_TIMEOUT); 1704 1705 if (snd_status == RDR_ABORTED) { 1706 abort_handler(); 1707 } 1708 1709 if (snd_status != RDR_OK) { 1710 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1711 } 1712 1713 ri_fini(op_data->hdl); 1714 1715 return ((snd_status != RDR_OK) ? -1 : 0); 1716 } 1717 1718 1719 /* 1720 * dcs_unknown_op: 1721 * 1722 * Handle all unknown requests. 1723 */ 1724 /* ARGSUSED */ 1725 static int 1726 dcs_unknown_op(rdr_msg_hdr_t *hdr, cfga_params_t *param) 1727 { 1728 session_t *sp; 1729 1730 1731 assert(hdr); 1732 assert(param); 1733 1734 assert(hdr); 1735 1736 /* get the current session information */ 1737 if ((sp = curr_ses()) == NULL) { 1738 ses_close(DCS_ERROR); 1739 return (-1); 1740 } 1741 1742 dcs_log_msg(LOG_ERR, DCS_UNKNOWN_OP, hdr->message_opcode); 1743 1744 sp->state = DCS_CONF_DONE; 1745 1746 return (-1); 1747 } 1748 1749 1750 /* 1751 * dcs_confirm_callback: 1752 * 1753 * Perform a confirm callback and wait for the reply. As defined 1754 * in the config_admin(3CFGADM) man page, 1 is returned if the 1755 * operation should be allowed to continue and 0 otherwise. 1756 */ 1757 static int 1758 dcs_confirm_callback(void *appdata_ptr, const char *message) 1759 { 1760 session_t *sp; 1761 rdr_msg_hdr_t req_hdr; 1762 cfga_params_t req_data; 1763 struct cfga_confirm *cb_data; 1764 rdr_msg_hdr_t reply_hdr; 1765 cfga_params_t reply_data; 1766 int snd_status; 1767 int rcv_status; 1768 static char *op_name = "confirm callback"; 1769 1770 1771 /* sanity check */ 1772 if (appdata_ptr == NULL) { 1773 dcs_log_msg(LOG_NOTICE, DCS_CONF_CB_ERR); 1774 return (0); 1775 } 1776 1777 /* get the current session information */ 1778 if ((sp = curr_ses()) == NULL) { 1779 dcs_log_msg(LOG_NOTICE, DCS_CONF_CB_ERR); 1780 return (0); 1781 } 1782 1783 cb_data = (struct cfga_confirm *)appdata_ptr; 1784 1785 /* prepare header information */ 1786 init_msg(&req_hdr); 1787 req_hdr.message_opcode = RDR_CONF_CONFIRM_CALLBACK; 1788 req_hdr.data_type = RDR_REQUEST; 1789 1790 /* prepare confirm callback specific data */ 1791 (void) memset(&req_data, 0, sizeof (req_data)); 1792 req_data.conf_cb.confp = cb_data; 1793 req_data.conf_cb.message = (char *)message; 1794 1795 PRINT_MSG_DBG(DCS_SEND, &req_hdr); 1796 1797 /* send the message */ 1798 snd_status = rdr_snd_msg(sp->fd, &req_hdr, &req_data, DCS_SND_TIMEOUT); 1799 1800 if (snd_status == RDR_ABORTED) { 1801 abort_handler(); 1802 } 1803 1804 if (snd_status != RDR_OK) { 1805 dcs_log_msg(LOG_NOTICE, DCS_CONF_CB_ERR); 1806 return (0); 1807 } 1808 1809 /* 1810 * Wait for response 1811 */ 1812 rcv_status = rdr_rcv_msg(sp->fd, &reply_hdr, &reply_data, 1813 DCS_RCV_CB_TIMEOUT); 1814 1815 if (rcv_status != RDR_OK) { 1816 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1817 dcs_log_msg(LOG_NOTICE, DCS_CONF_CB_ERR); 1818 return (0); 1819 } 1820 1821 /* 1822 * Perform several checks to see if we have a 1823 * valid response to the confirm callback. 1824 */ 1825 if (invalid_msg(&reply_hdr)) { 1826 dcs_log_msg(LOG_ERR, DCS_MSG_INVAL); 1827 dcs_log_msg(LOG_NOTICE, DCS_CONF_CB_ERR); 1828 return (0); 1829 } 1830 1831 /* check the opcode and type */ 1832 if ((reply_hdr.message_opcode != RDR_CONF_CONFIRM_CALLBACK) || 1833 (reply_hdr.data_type != RDR_REPLY)) { 1834 DCS_DBG(DBG_MSG, "bad opcode or message type"); 1835 dcs_log_msg(LOG_ERR, DCS_MSG_INVAL); 1836 dcs_log_msg(LOG_NOTICE, DCS_CONF_CB_ERR); 1837 return (0); 1838 } 1839 1840 PRINT_MSG_DBG(DCS_RECEIVE, &reply_hdr); 1841 1842 /* check for incorrect callback id */ 1843 if (reply_data.conf_cb.confp->confirm != cb_data->confirm) { 1844 dcs_log_msg(LOG_ERR, DCS_MSG_INVAL); 1845 dcs_log_msg(LOG_NOTICE, DCS_CONF_CB_ERR); 1846 return (0); 1847 } 1848 1849 /* 1850 * Got back valid response: return the user's answer 1851 */ 1852 return (reply_data.conf_cb.response); 1853 } 1854 1855 1856 /* 1857 * dcs_message_callback: 1858 * 1859 * Perform a message callback to display a string to the user. 1860 * 1861 * Note: There is no documentation about possible return values 1862 * for the message callback. It is assumed that the value returned 1863 * is ignored, so 0 is returned for all cases. 1864 */ 1865 static int 1866 dcs_message_callback(void *appdata_ptr, const char *message) 1867 { 1868 session_t *sp; 1869 rdr_msg_hdr_t req_hdr; 1870 cfga_params_t req_data; 1871 struct cfga_msg *cb_data; 1872 int snd_status; 1873 static char *op_name = "message callback"; 1874 1875 1876 /* sanity check */ 1877 if (appdata_ptr == NULL) { 1878 dcs_log_msg(LOG_NOTICE, DCS_MSG_CB_ERR); 1879 return (0); 1880 } 1881 1882 /* get the current session information */ 1883 if ((sp = curr_ses()) == NULL) { 1884 dcs_log_msg(LOG_NOTICE, DCS_MSG_CB_ERR); 1885 return (0); 1886 } 1887 1888 cb_data = (struct cfga_msg *)appdata_ptr; 1889 1890 /* prepare header information */ 1891 init_msg(&req_hdr); 1892 req_hdr.message_opcode = RDR_CONF_MSG_CALLBACK; 1893 req_hdr.data_type = RDR_REQUEST; 1894 1895 /* prepare message callback specific data */ 1896 (void) memset(&req_data, 0, sizeof (req_data)); 1897 req_data.msg_cb.msgp = cb_data; 1898 req_data.msg_cb.message = (char *)message; 1899 1900 PRINT_MSG_DBG(DCS_SEND, &req_hdr); 1901 1902 /* send the message */ 1903 snd_status = rdr_snd_msg(sp->fd, &req_hdr, (cfga_params_t *)&req_data, 1904 DCS_SND_TIMEOUT); 1905 1906 if (snd_status == RDR_ABORTED) { 1907 abort_handler(); 1908 } 1909 1910 if (snd_status != RDR_OK) { 1911 dcs_log_msg(LOG_ERR, DCS_OP_REPLY_ERR, op_name); 1912 dcs_log_msg(LOG_NOTICE, DCS_MSG_CB_ERR); 1913 } 1914 1915 return (0); 1916 } 1917 1918 1919 /* 1920 * resolve_version: 1921 * 1922 * Consult the list of supported versions and find the highest supported 1923 * version that is less than or equal to the version requested in the 1924 * parameters. This assumes that the list of supported versions is ordered 1925 * so that the highest supported version is the first element, and that 1926 * the versions are strictly decreasing. 1927 */ 1928 static dcs_ver_t 1929 resolve_version(ushort_t req_major, ushort_t req_minor) 1930 { 1931 int i; 1932 dcs_ver_t act_ver; 1933 int num_vers; 1934 1935 1936 num_vers = sizeof (ver_supp) / sizeof (*ver_supp); 1937 1938 /* default to the lowest version */ 1939 act_ver = ver_supp[num_vers - 1]; 1940 1941 for (i = 0; i < num_vers; i++) { 1942 1943 if (req_major == ver_supp[i].major) { 1944 1945 if (req_minor >= ver_supp[i].minor) { 1946 /* 1947 * The major version matches and the 1948 * minor version either matches, or 1949 * is the best match that we have. 1950 */ 1951 act_ver = ver_supp[i]; 1952 break; 1953 } 1954 1955 } else if (req_major > ver_supp[i].major) { 1956 /* 1957 * The requested major version is larger than 1958 * the current version we are checking. There 1959 * is not going to be a better match. 1960 */ 1961 act_ver = ver_supp[i]; 1962 break; 1963 } 1964 } 1965 1966 DCS_DBG(DBG_SES, "requested ver: %d.%d, closest match: %d.%d", 1967 req_major, req_minor, act_ver.major, act_ver.minor); 1968 1969 return (act_ver); 1970 } 1971 1972 1973 /* 1974 * filter_list_data: 1975 * 1976 * Check a list of cfga_list_data_t structures to filter out the ones 1977 * that don't have other-read permissions. All valid entries are placed 1978 * at the beginning of the array and the count of entries is updated. 1979 */ 1980 static void 1981 filter_list_data(int perm, int *nlistp, cfga_list_data_t *linfo) 1982 { 1983 int num_aps; 1984 int num_aps_ret; 1985 int curr_ap; 1986 int next_aval; 1987 int end_block; 1988 int block_size; 1989 struct stat ap_info; 1990 1991 1992 DCS_DBG(DBG_MSG, "list access = %s", (perm == RDR_PRIVILEGED) ? 1993 "RDR_PRIVILEGED" : "RDR_NOT_PRIVILEGED"); 1994 1995 /* 1996 * Check if the user has priviledged access 1997 * to view all attachment points 1998 */ 1999 if (perm == RDR_PRIVILEGED) { 2000 return; 2001 } 2002 2003 if (*nlistp < 0) { 2004 *nlistp = 0; 2005 } 2006 2007 /* 2008 * No priviledged access, check each attachment point to 2009 * see if the user has access (other:read) to view it. 2010 */ 2011 num_aps = *nlistp; 2012 next_aval = 0; 2013 num_aps_ret = 0; 2014 curr_ap = 0; 2015 2016 /* 2017 * Use a simple algorithm to compact the array so that 2018 * all attachment points that can be viewed are at the 2019 * beginning of the array. Adjust the count of the 2020 * attachment points accordingly. 2021 */ 2022 while (curr_ap < num_aps) { 2023 2024 stat(linfo[curr_ap].ap_phys_id, &ap_info); 2025 2026 /* check for unrestricted read permission */ 2027 if (ap_info.st_mode & S_IROTH) { 2028 2029 end_block = curr_ap + 1; 2030 2031 /* 2032 * Check if this is the beginning of a 2033 * block of consecutive ap ids that can 2034 * be returned. 2035 */ 2036 while (end_block < num_aps) { 2037 2038 stat(linfo[end_block].ap_phys_id, &ap_info); 2039 2040 /* search until the end of the block */ 2041 if (ap_info.st_mode & S_IROTH) { 2042 end_block++; 2043 } else { 2044 break; 2045 } 2046 } 2047 2048 block_size = end_block - curr_ap; 2049 2050 /* make sure a copy is necessary */ 2051 if (curr_ap != next_aval) { 2052 2053 /* copy the block of ap ids all at once */ 2054 (void) memmove(&linfo[next_aval], 2055 &linfo[curr_ap], 2056 block_size * sizeof (cfga_list_data_t)); 2057 } 2058 2059 /* move past the copied block */ 2060 next_aval += block_size; 2061 curr_ap = end_block; 2062 2063 num_aps_ret += block_size; 2064 } else { 2065 curr_ap++; 2066 } 2067 } 2068 2069 DCS_DBG(DBG_ALL, "filtered %d of %d ap ids", (*nlistp - num_aps_ret), 2070 *nlistp); 2071 2072 /* 2073 * return the number of aps that have the correct 2074 * access permissions. 2075 */ 2076 *nlistp = num_aps_ret; 2077 } 2078 2079 2080 /* 2081 * generate_sort_order: 2082 * 2083 * Determine the sort order of an array of cfga_list_data_t structures 2084 * and create an array of rdr_list_t structures that contain the original 2085 * elements tagged with the sort order. 2086 * 2087 * This function is used to eliminate unnecessary network traffic that 2088 * might occur if the client needs the output of config_list_ext(3CFGADM) 2089 * sorted. Since a comparison is performed in a platform specific manner 2090 * using config_ap_id_cmp(3CFGADM), a client must establish a new session 2091 * for each comparison. For a long lists of attachment points, this can 2092 * slow down a simple list_ext operation significantly. With the sort 2093 * information included in the array of rdr_list_t structures, the client 2094 * can perform the sort operation locally, thus eliminating a great deal 2095 * of network traffic. 2096 */ 2097 static rdr_list_t * 2098 generate_sort_order(cfga_list_data_t *listp, int nlist) 2099 { 2100 int curr_ap; 2101 rdr_list_t *datalp; 2102 cfga_list_data_t *sortlp; 2103 cfga_list_data_t *match; 2104 2105 2106 assert(listp); 2107 2108 if (nlist <= 0) { 2109 return (NULL); 2110 } 2111 2112 /* create our new array */ 2113 datalp = (rdr_list_t *)malloc(nlist * sizeof (rdr_list_t)); 2114 2115 if (datalp == NULL) { 2116 return (NULL); 2117 } 2118 2119 2120 /* copy over the elements, preserving the original order */ 2121 for (curr_ap = 0; curr_ap < nlist; curr_ap++) { 2122 datalp[curr_ap].ap_id_info = listp[curr_ap]; 2123 } 2124 2125 /* handle a one element list */ 2126 if (nlist == 1) { 2127 datalp[0].sort_order = 0; 2128 return (datalp); 2129 } 2130 2131 /* sort the cfga_list_data_t array */ 2132 qsort(listp, nlist, sizeof (listp[0]), ldata_compare); 2133 2134 sortlp = listp; 2135 2136 /* process each item in the original list */ 2137 for (curr_ap = 0; curr_ap < nlist; curr_ap++) { 2138 2139 /* look up the sort order in the sorted list */ 2140 match = bsearch(&datalp[curr_ap].ap_id_info, sortlp, 2141 nlist, sizeof (cfga_list_data_t), ldata_compare); 2142 2143 /* found a match */ 2144 if (match != NULL) { 2145 datalp[curr_ap].sort_order = match - sortlp; 2146 } else { 2147 /* 2148 * Should never get here. Since we did a 2149 * direct copy of the array, we should always 2150 * be able to find the ap id that we were 2151 * looking for. 2152 */ 2153 DCS_DBG(DBG_ALL, "could not find a matching " 2154 "ap id in the sorted list"); 2155 datalp[curr_ap].sort_order = 0; 2156 } 2157 } 2158 2159 return (datalp); 2160 } 2161 2162 2163 /* 2164 * ldata_compare: 2165 * 2166 * Compare the two inputs to produce a strcmp(3C) style result. It uses 2167 * config_ap_id_cmp(3CFGADM) to perform the comparison. 2168 * 2169 * This function is passed to qsort(3C) in generate_sort_order() to sort a 2170 * list of attachment points. 2171 */ 2172 static int 2173 ldata_compare(const void *ap1, const void *ap2) 2174 { 2175 cfga_list_data_t *ap_id1; 2176 cfga_list_data_t *ap_id2; 2177 2178 ap_id1 = (cfga_list_data_t *)ap1; 2179 ap_id2 = (cfga_list_data_t *)ap2; 2180 2181 return (config_ap_id_cmp(ap_id1->ap_log_id, ap_id2->ap_log_id)); 2182 } 2183 2184 2185 /* 2186 * basename: 2187 * 2188 * Find short path name of a full path name. If a short path name 2189 * is passed in, the original pointer is returned. 2190 */ 2191 static char * 2192 basename(char *cp) 2193 { 2194 char *sp; 2195 2196 if ((sp = strrchr(cp, '/')) != NULL) { 2197 return (sp + 1); 2198 } 2199 2200 return (cp); 2201 } 2202 2203 /* 2204 * is_socket: 2205 * 2206 * determine if fd represents a socket file type. 2207 */ 2208 static boolean_t 2209 is_socket(int fd) 2210 { 2211 struct stat statb; 2212 if (fstat(fd, &statb) < 0) { 2213 return (B_FALSE); 2214 } 2215 return (S_ISSOCK(statb.st_mode)); 2216 } 2217