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 2003 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 program file for the configuration administration 31 * command as set out in manual page cfgadm(1M). It uses the configuration 32 * administration library interface, libcfgadm, as set out in manual 33 * page config_admin(3X). 34 */ 35 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <locale.h> 40 #include <langinfo.h> 41 #include <time.h> 42 #include <assert.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/param.h> 46 #include <sys/sunddi.h> 47 #include <sys/openpromio.h> 48 #include <sys/ddi_impldefs.h> 49 #include <sys/systeminfo.h> 50 #include <ctype.h> 51 52 #include <config_admin.h> 53 #include "cfgadm.h" 54 55 #define S_FREE(x) (((x) != NULL) ? (free(x), (x) = NULL) : (void *)0) 56 #define GET_DYN(a) (strstr((a), CFGA_DYN_SEP)) 57 /* 58 * forward declarations 59 */ 60 static char *basename(char *); 61 static void cfgadm_error(int, char *); 62 static int confirm_interactive(void *, const char *); 63 static int confirm_no(void *, const char *); 64 static int confirm_yes(void *, const char *); 65 static void usage(void); 66 static void usage_field(void); 67 static int extract_list_suboptions(char *, char **, char **, char **, 68 int *, char **, char **, char **); 69 static int message_output(void *appdata_ptr, const char *message); 70 static void *config_calloc_check(size_t, size_t); 71 static cfga_ap_types_t find_arg_type(const char *); 72 static int yesno(char *, char *); 73 74 75 static int compare_ap_id(cfga_list_data_t *, cfga_list_data_t *, match_type_t); 76 static int compare_r_state(cfga_list_data_t *, cfga_list_data_t *, 77 match_type_t); 78 static int compare_o_state(cfga_list_data_t *, cfga_list_data_t *, 79 match_type_t); 80 static int compare_cond(cfga_list_data_t *, cfga_list_data_t *, match_type_t); 81 static int compare_time(cfga_list_data_t *, cfga_list_data_t *, match_type_t); 82 static int compare_info(cfga_list_data_t *, cfga_list_data_t *, match_type_t); 83 static int compare_type(cfga_list_data_t *, cfga_list_data_t *, match_type_t); 84 static int compare_busy(cfga_list_data_t *, cfga_list_data_t *, match_type_t); 85 static int compare_class(cfga_list_data_t *, cfga_list_data_t *, match_type_t); 86 static int compare_null(cfga_list_data_t *, cfga_list_data_t *, match_type_t); 87 static void print_log_id(cfga_list_data_t *, int, char *); 88 static void print_r_state(cfga_list_data_t *, int, char *); 89 static void print_o_state(cfga_list_data_t *, int, char *); 90 static void print_cond(cfga_list_data_t *, int, char *); 91 static void print_time(cfga_list_data_t *, int, char *); 92 static void print_time_p(cfga_list_data_t *, int, char *); 93 static void print_info(cfga_list_data_t *, int, char *); 94 static void print_type(cfga_list_data_t *, int, char *); 95 static void print_busy(cfga_list_data_t *, int, char *); 96 static void print_phys_id(cfga_list_data_t *, int, char *); 97 static void print_class(cfga_list_data_t *, int, char *); 98 static void print_null(cfga_list_data_t *, int, char *); 99 static int count_fields(char *, char); 100 static int process_sort_fields(int, struct sort_el *, char *); 101 static int process_fields(int, struct print_col *, int, char *); 102 static cfga_err_t print_fields(int, struct print_col *, int, int, char *, 103 cfga_list_data_t *, FILE *); 104 static int ldata_compare(const void *, const void *); 105 106 static void arg_got_resp(ap_arg_t *inp, ap_out_t *out_array, int nouts, 107 int dyn_exp); 108 static void out_was_req(ap_out_t *outp, ap_arg_t *in_array, int nargs, 109 int no_dyn); 110 static void report_no_response(ap_arg_t *arg_array, int napids_to_list); 111 112 static cfga_err_t set_log_flt(cfga_list_data_t *p, const char *val); 113 static cfga_err_t set_type_flt(cfga_list_data_t *p, const char *val); 114 static cfga_err_t set_class_flt(cfga_list_data_t *p, const char *val); 115 116 static char *get_dyn(const char *ap_id); 117 static void remove_dyn(char *ap_id); 118 119 /* 120 * global data 121 */ 122 /* command name for messages */ 123 static char *cmdname; 124 125 /* 126 * control for comparing, printing and filtering cfga_list_data 127 * NOTE:Field names (i.e. member 0 of field_info struct) may not contain '('. 128 * The post filtering code depends on it. 129 * NOTE:A NULL value for the set_filter member indicates that filtering based 130 * on that field is currently not supported. 131 */ 132 static struct field_info all_fields[] = { 133 {"ap_id", "Ap_Id", SZ_EL(ap_log_id), compare_ap_id, print_log_id, set_log_flt}, 134 {"r_state", "Receptacle", STATE_WIDTH, compare_r_state, print_r_state, NULL}, 135 {"o_state", "Occupant", STATE_WIDTH, compare_o_state, print_o_state, NULL}, 136 {"condition", "Condition", COND_WIDTH, compare_cond, print_cond, NULL}, 137 {"status_time", "When", TIME_WIDTH, compare_time, print_time, NULL}, 138 {"status_time_p", "When", TIME_P_WIDTH, compare_time, print_time_p, NULL}, 139 {"info", "Information", SZ_EL(ap_info), compare_info, print_info, NULL}, 140 {"type", "Type", SZ_EL(ap_type), compare_type, print_type, set_type_flt}, 141 {"busy", "Busy", 8, compare_busy, print_busy, NULL}, 142 {"physid", "Phys_Id", SZ_EL(ap_phys_id), compare_ap_id, print_phys_id, NULL}, 143 {"class", "Class", SZ_EL(ap_class), compare_class, print_class, set_class_flt} 144 }; 145 146 #define PREFILT_CLASS_STR "class=" 147 148 typedef struct { 149 cfga_list_data_t ldata; /* Selection criteria */ 150 match_type_t match_type_p[N_FIELDS]; /* Type of match */ 151 } post_filter_t; 152 153 static struct field_info null_field = 154 {"null", "", 0, compare_null, print_null, NULL}; 155 156 static struct sort_el *sort_list; /* Used in ldata_compare() */ 157 static int nsort_list; 158 static char unk_field[] = "%s: field \"%s\" unknown\n"; 159 160 static char aptype_no_dyn[] = "%s: Invalid ap_id: %s\n"; 161 162 /* strings that make up the usage message */ 163 static char *usage_tab[] = { 164 " %s [-f] [-y|-n] [-v] [-o hardware_opts ] -c function ap_id [ap_id...]\n", 165 " %s [-f] [-y|-n] [-v] [-o hardware_opts ] -x function ap_id [ap_id...]\n", 166 " %s [-v] [-s listing_options ] [-o hardware_opts ] [-a]\n" 167 "\t[-l [ap_id|ap_type...]]\n", 168 " %s [-v] [-o hardware_opts ] -t ap_id [ap_id...]\n", 169 " %s [-v] [-o hardware_opts ] -h [ap_id|ap_type...]\n", 170 }; 171 172 /* Type of matches currently supported by the select sub-option */ 173 static match_cvt_t match_type_array[] = { 174 {"partial", CFGA_MATCH_PARTIAL}, 175 {"exact", CFGA_MATCH_EXACT} 176 }; 177 178 #define N_MATCH_TYPES (sizeof (match_type_array)/sizeof (match_type_array[0])) 179 180 static cfga_err_t setup_filter(const char *selectp, const char *matchp, 181 post_filter_t *post_filtp, char **prefilt_optpp); 182 static cfga_err_t parse_select_opt(const char *selectp, 183 post_filter_t *post_filtp, match_type_t match_type); 184 static int do_config_list(int, char *[], cfga_list_data_t *, int, char *, 185 char *, char *, int, char *, post_filter_t *, int); 186 static void do_post_filter(ap_out_t *outp, post_filter_t *post_filtp, int *jp); 187 188 189 /* 190 * main - the main routine of cfgadm, processes the command line 191 * and dispatches functions off to libraries. 192 */ 193 void 194 main( 195 int argc, 196 char *argv[]) 197 { 198 extern char *optarg; 199 extern int optind; 200 int c; 201 char *subopts; 202 char *subvalue; 203 char *const *ap_args = NULL; 204 cfga_cmd_t sc_opt = NULL; 205 struct cfga_confirm confirm; 206 struct cfga_msg message; 207 int ret = CFGA_ERROR; 208 int i; 209 char *estrp = NULL; 210 cfga_op_t action = CFGA_OP_NONE; 211 char *plat_opts = NULL; 212 char *act_arg = NULL; 213 enum confirm confarg = CONFIRM_DEFAULT; 214 char *list_opts = NULL; 215 cfga_flags_t flags = 0; 216 int arg_error = 0; 217 int dyn_exp = 0; 218 219 estrp = NULL; 220 if (argc > 0) 221 cmdname = basename(argv[0]); 222 else 223 cmdname = "cfgadm"; 224 (void) setlocale(LC_ALL, ""); 225 #if !defined(TEXT_DOMAIN) 226 #define TEXT_DOMAIN "SYS_TEST" 227 #endif 228 (void) textdomain(TEXT_DOMAIN); 229 230 while ((c = getopt(argc, argv, OPTIONS)) != EOF) { 231 static char dup_action[] = 232 "%s: more than one action specified (-c,-l,-t,-x)\n"; 233 static char dup_option[] = 234 "%s: more than one -%c option specified\n"; 235 switch (c) { 236 case 'a': 237 if (dyn_exp) { 238 arg_error = 1; 239 (void) fprintf(stderr, gettext(dup_option), 240 cmdname, c); 241 } 242 dyn_exp = 1; 243 break; 244 case 'c': 245 if (action != CFGA_OP_NONE) { 246 arg_error = 1; 247 (void) fprintf(stderr, gettext(dup_action), 248 cmdname); 249 } 250 action = CFGA_OP_CHANGE_STATE; 251 subopts = optarg; 252 subvalue = NULL; 253 /* 254 * Reject -c suboption if they are unrecognized 255 * or more than one or have a associated value. 256 */ 257 if ((sc_opt = getsubopt(&subopts, state_opts, 258 &subvalue)) == -1 || *subopts != '\0' || 259 subvalue != NULL) { 260 arg_error = 1; 261 break; 262 } 263 break; 264 case 'f': 265 if ((flags & CFGA_FLAG_FORCE) != 0) { 266 arg_error = 1; 267 (void) fprintf(stderr, gettext(dup_option), 268 cmdname, c); 269 } 270 flags |= CFGA_FLAG_FORCE; 271 break; 272 case 'h': 273 if (action != CFGA_OP_NONE) { 274 arg_error = 1; 275 (void) fprintf(stderr, gettext(dup_action), 276 cmdname); 277 } 278 action = CFGA_OP_HELP; 279 break; 280 case 'l': 281 if (action != CFGA_OP_NONE) { 282 arg_error = 1; 283 (void) fprintf(stderr, gettext(dup_action), 284 cmdname); 285 } 286 action = CFGA_OP_LIST; 287 break; 288 case 'n': 289 if (confarg != CONFIRM_DEFAULT) { 290 arg_error = 1; 291 (void) fprintf(stderr, gettext(dup_option), 292 cmdname, c); 293 } 294 confarg = CONFIRM_NO; 295 break; 296 case 'o': 297 if (plat_opts != NULL) { 298 arg_error = 1; 299 (void) fprintf(stderr, gettext(dup_option), 300 cmdname, c); 301 } 302 plat_opts = optarg; 303 break; 304 case 's': 305 if (list_opts != NULL) { 306 arg_error = 1; 307 (void) fprintf(stderr, gettext(dup_option), 308 cmdname, c); 309 } 310 list_opts = optarg; 311 break; 312 case 't': 313 if (action != CFGA_OP_NONE) { 314 arg_error = 1; 315 (void) fprintf(stderr, gettext(dup_action), 316 cmdname); 317 } 318 action = CFGA_OP_TEST; 319 break; 320 case 'x': 321 if (action != CFGA_OP_NONE) { 322 arg_error = 1; 323 (void) fprintf(stderr, gettext(dup_action), 324 cmdname); 325 } 326 action = CFGA_OP_PRIVATE; 327 act_arg = optarg; 328 break; 329 case 'v': 330 if ((flags & CFGA_FLAG_VERBOSE) != 0) { 331 arg_error = 1; 332 (void) fprintf(stderr, gettext(dup_option), 333 cmdname, c); 334 } 335 flags |= CFGA_FLAG_VERBOSE; 336 break; 337 case 'y': 338 if (confarg != CONFIRM_DEFAULT) { 339 arg_error = 1; 340 (void) fprintf(stderr, gettext(dup_option), 341 cmdname, c); 342 } 343 confarg = CONFIRM_YES; 344 break; 345 case '?': /* getopts issues message is this case */ 346 default: /* catch programming errors */ 347 arg_error = 1; 348 break; 349 } 350 } 351 352 /* default action is list */ 353 if (action == CFGA_OP_NONE) 354 action = CFGA_OP_LIST; 355 356 /* -s and -a option only for list */ 357 if (action != CFGA_OP_LIST && (list_opts != NULL || dyn_exp)) { 358 arg_error = 1; 359 } 360 361 if (arg_error) { 362 usage(); 363 exit(EXIT_ARGERROR); 364 /*NOTREACHED*/ 365 } 366 367 ap_args = &argv[optind]; 368 369 /* 370 * If neither -n of -y was specified, interactive confirmation 371 * is used. Check if the program has terminal I/O and 372 * enforce -n if not. 373 */ 374 (void) memset(&confirm, 0, sizeof (confirm)); 375 if (action == CFGA_OP_CHANGE_STATE || action == CFGA_OP_PRIVATE) { 376 if (confarg == CONFIRM_DEFAULT && 377 !(isatty(fileno(stdin)) && isatty(fileno(stderr)))) 378 confarg = CONFIRM_NO; 379 switch (confarg) { 380 case CONFIRM_DEFAULT: 381 confirm.confirm = confirm_interactive; 382 break; 383 case CONFIRM_NO: 384 confirm.confirm = confirm_no; 385 break; 386 case CONFIRM_YES: 387 confirm.confirm = confirm_yes; 388 break; 389 default: /* paranoia */ 390 abort(); 391 /*NOTREACHED*/ 392 } 393 } 394 395 /* 396 * set up message output routine 397 */ 398 message.message_routine = message_output; 399 400 switch (action) { 401 case CFGA_OP_CHANGE_STATE: 402 /* Sanity check - requires an argument */ 403 if ((argc - optind) <= 0) { 404 usage(); 405 break; 406 } 407 /* Sanity check - args cannot be ap_types */ 408 for (i = 0; i < (argc - optind); i++) { 409 if (find_arg_type(ap_args[i]) == AP_TYPE) { 410 usage(); 411 exit(EXIT_ARGERROR); 412 /*NOTREACHED*/ 413 } 414 } 415 ret = config_change_state(sc_opt, argc - optind, ap_args, plat_opts, 416 &confirm, &message, &estrp, flags); 417 if (ret != CFGA_OK) 418 cfgadm_error(ret, estrp); 419 break; 420 case CFGA_OP_PRIVATE: 421 /* Sanity check - requires an argument */ 422 if ((argc - optind) <= 0) { 423 usage(); 424 break; 425 } 426 /* Sanity check - args cannot be ap_types */ 427 for (i = 0; i < (argc - optind); i++) { 428 if (find_arg_type(ap_args[i]) == AP_TYPE) { 429 usage(); 430 exit(EXIT_ARGERROR); 431 /*NOTREACHED*/ 432 } 433 } 434 435 ret = config_private_func(act_arg, argc - optind, ap_args, 436 plat_opts, &confirm, &message, &estrp, flags); 437 438 if (ret != CFGA_OK) 439 cfgadm_error(ret, estrp); 440 break; 441 case CFGA_OP_TEST: 442 /* Sanity check - requires an argument */ 443 if ((argc - optind) <= 0) { 444 usage(); 445 break; 446 } 447 448 if ((flags & ~CFGA_FLAG_VERBOSE) != 0) { 449 usage(); 450 exit(EXIT_ARGERROR); 451 /*NOTREACHED*/ 452 } 453 454 /* Sanity check - args cannot be ap_types */ 455 for (i = 0; i < (argc - optind); i++) { 456 if (find_arg_type(ap_args[i]) == AP_TYPE) { 457 usage(); 458 exit(EXIT_ARGERROR); 459 /*NOTREACHED*/ 460 } 461 } 462 ret = config_test(argc - optind, ap_args, plat_opts, &message, 463 &estrp, flags); 464 if (ret != CFGA_OK) 465 cfgadm_error(ret, estrp); 466 break; 467 case CFGA_OP_HELP: 468 469 if ((flags & ~CFGA_FLAG_VERBOSE) != 0) { 470 usage(); 471 exit(EXIT_ARGERROR); 472 /*NOTREACHED*/ 473 } 474 475 /* always do usage? */ 476 usage(); 477 ret = config_help(argc - optind, ap_args, &message, plat_opts, 478 flags); 479 if (ret != CFGA_OK) 480 cfgadm_error(ret, estrp); 481 break; 482 483 case CFGA_OP_LIST: { 484 cfga_list_data_t *list_array = NULL; 485 int nlist = 0; 486 char *sort_fields = DEF_SORT_FIELDS; 487 char *cols = DEF_COLS; 488 char *cols2 = DEF_COLS2; 489 int noheadings = 0; 490 char *delim = DEF_DELIM; 491 int exitcode = EXIT_OK; 492 int i; 493 int type = 0; 494 char *selectp = NULL, *matchp = NULL, *prefilt_optp = NULL; 495 post_filter_t *post_filtp = NULL; 496 497 if ((flags & ~CFGA_FLAG_VERBOSE) != 0) { 498 usage(); 499 exit(EXIT_ARGERROR); 500 /*NOTREACHED*/ 501 } 502 503 if (flags & CFGA_FLAG_VERBOSE) { 504 cols = DEF_COLS_VERBOSE; 505 cols2 = DEF_COLS2_VERBOSE; 506 } 507 508 if (list_opts != NULL && !extract_list_suboptions(list_opts, 509 &sort_fields, &cols, &cols2, &noheadings, &delim, 510 &selectp, &matchp)) { 511 usage_field(); 512 exit(EXIT_ARGERROR); 513 /*NOTREACHED*/ 514 } 515 516 /* 517 * Scan any args and see if there are any ap_types. 518 * If there are we get all attachment point stats and 519 * then filter what gets printed. 520 */ 521 522 type = 0; 523 for (i = 0; i < (argc - optind); i++) { 524 if (find_arg_type(ap_args[i]) == AP_TYPE) { 525 type = 1; 526 /* ap_types cannot have dynamic components */ 527 if (get_dyn(ap_args[i]) != NULL) { 528 (void) fprintf(stderr, 529 gettext(aptype_no_dyn), 530 cmdname, ap_args[i]); 531 exit(EXIT_ARGERROR); 532 /*NOTREACHED*/ 533 } 534 break; 535 } 536 } 537 538 /* Setup filter */ 539 post_filtp = config_calloc_check(1, sizeof (*post_filtp)); 540 if (post_filtp == NULL) { 541 exit(EXIT_OPFAILED); 542 /*NOTREACHED*/ 543 } 544 if (setup_filter(selectp, matchp, post_filtp, &prefilt_optp) 545 != CFGA_OK) { 546 S_FREE(post_filtp); 547 exit(EXIT_ARGERROR); 548 /*NOTREACHED*/ 549 } 550 551 list_array = NULL; 552 exitcode = EXIT_OK; 553 554 /* 555 * Check for args. No args means find all libs 556 * and call the cfga_list_ext routine with no ap_ids specified. 557 * With args, if any one of the args are ap_types we 558 * again find all attachment points as in the 559 * no-args case above and then select which attachment points 560 * are actually displayed. 561 */ 562 if (((argc - optind) == 0) || (type == 1)) { 563 /* 564 * No args, or atleast 1 ap_type arg 565 */ 566 ret = config_list_ext(0, NULL, &list_array, 567 &nlist, plat_opts, prefilt_optp, &estrp, 568 dyn_exp ? CFGA_FLAG_LIST_ALL : 0); 569 } else { 570 /* 571 * If the args are all ap_ids (no ap_types) we call the 572 * cfga_list_ext routine with those specific ap_ids. 573 */ 574 ret = config_list_ext(argc - optind, ap_args, 575 &list_array, &nlist, plat_opts, prefilt_optp, 576 &estrp, dyn_exp ? CFGA_FLAG_LIST_ALL : 0); 577 } 578 579 S_FREE(prefilt_optp); 580 581 if (ret == CFGA_OK) { 582 583 if (do_config_list( 584 (argc - optind), &argv[optind], list_array, nlist, 585 sort_fields, cols, cols2, noheadings, delim, 586 post_filtp, dyn_exp) != CFGA_OK) { 587 exitcode = EXIT_ARGERROR; 588 } else { 589 exitcode = EXIT_OK; 590 } 591 592 S_FREE(list_array); 593 S_FREE(post_filtp); 594 595 if (exitcode != EXIT_OK) { 596 exit(exitcode); 597 /*NOTREACHED*/ 598 } 599 } else { 600 601 S_FREE(post_filtp); 602 cfgadm_error(ret, estrp); 603 } 604 break; 605 } 606 default: /* paranoia */ 607 abort(); 608 /*NOTREACHED*/ 609 } 610 611 if (ret == CFGA_NOTSUPP) { 612 exit(EXIT_NOTSUPP); 613 } else if (ret != CFGA_OK) { 614 exit(EXIT_OPFAILED); 615 } else { 616 exit(EXIT_OK); 617 } 618 /*NOTREACHED*/ 619 } 620 621 /* 622 * usage - outputs the usage help message. 623 */ 624 static void 625 usage( 626 void) 627 { 628 int i; 629 630 (void) fprintf(stderr, "%s\n", gettext("Usage:")); 631 for (i = 0; i < sizeof (usage_tab)/sizeof (usage_tab[0]); i++) { 632 (void) fprintf(stderr, gettext(usage_tab[i]), cmdname); 633 } 634 } 635 636 /* 637 * Emit an error message. 638 * As a side-effect the hardware specific error message is deallocated 639 * as described in config_admin(3X). 640 */ 641 static void 642 cfgadm_error(int errnum, char *estrp) 643 { 644 const char *ep; 645 646 ep = config_strerror(errnum); 647 if (ep == NULL) 648 ep = gettext("configuration administration unknown error"); 649 if (estrp != NULL && *estrp != '\0') { 650 (void) fprintf(stderr, "%s: %s: %s\n", cmdname, ep, estrp); 651 } else { 652 (void) fprintf(stderr, "%s: %s\n", cmdname, ep); 653 } 654 if (estrp != NULL) 655 free((void *)estrp); 656 if (errnum == CFGA_INVAL) 657 usage(); 658 } 659 660 /* 661 * confirm_interactive - prompt user for confirmation 662 */ 663 static int 664 confirm_interactive( 665 void *appdata_ptr, 666 const char *message) 667 { 668 static char yeschr[YESNO_STR_MAX + 2]; 669 static char nochr[YESNO_STR_MAX + 2]; 670 static int inited = 0; 671 int isyes; 672 673 #ifdef lint 674 appdata_ptr = appdata_ptr; 675 #endif /* lint */ 676 /* 677 * First time through initialisation. In the original 678 * version of this command this function is only called once, 679 * but this function is generalized for the future. 680 */ 681 if (!inited) { 682 (void) strncpy(yeschr, nl_langinfo(YESSTR), YESNO_STR_MAX + 1); 683 (void) strncpy(nochr, nl_langinfo(NOSTR), YESNO_STR_MAX + 1); 684 inited = 1; 685 } 686 687 do { 688 (void) fprintf(stderr, "%s (%s/%s)? ", message, yeschr, nochr); 689 isyes = yesno(yeschr, nochr); 690 } while (isyes == -1); 691 return (isyes); 692 } 693 694 /* 695 * If any text is input it must sub-string match either yes or no. 696 * Failure of this match is indicated by return of -1. 697 * If an empty line is input, this is taken as no. 698 */ 699 static int 700 yesno( 701 char *yesp, 702 char *nop) 703 { 704 int i, b; 705 char ans[YESNO_STR_MAX + 1]; 706 707 i = 0; 708 709 /*CONSTCOND*/ 710 while (1) { 711 b = getc(stdin); /* more explicit that rm.c version */ 712 if (b == '\n' || b == '\0' || b == EOF) { 713 if (i < YESNO_STR_MAX) /* bug fix to rm.c version */ 714 ans[i] = 0; 715 break; 716 } 717 if (i < YESNO_STR_MAX) 718 ans[i] = b; 719 i++; 720 } 721 if (i >= YESNO_STR_MAX) { 722 i = YESNO_STR_MAX; 723 ans[YESNO_STR_MAX] = 0; 724 } 725 /* changes to rm.c version follow */ 726 if (i == 0) 727 return (0); 728 if (strncmp(nop, ans, i) == 0) 729 return (0); 730 if (strncmp(yesp, ans, i) == 0) 731 return (1); 732 return (-1); 733 } 734 735 /*ARGSUSED*/ 736 static int 737 confirm_no( 738 void *appdata_ptr, 739 const char *message) 740 { 741 return (0); 742 } 743 744 /*ARGSUSED*/ 745 static int 746 confirm_yes( 747 void *appdata_ptr, 748 const char *message) 749 { 750 return (1); 751 } 752 753 /* 754 * Find base name of filename. 755 */ 756 static char * 757 basename( 758 char *cp) 759 { 760 char *sp; 761 762 if ((sp = strrchr(cp, '/')) != NULL) 763 return (sp + 1); 764 return (cp); 765 } 766 767 /*ARGSUSED*/ 768 static int 769 message_output( 770 void *appdata_ptr, 771 const char *message) 772 { 773 (void) fprintf(stderr, "%s", message); 774 return (CFGA_OK); 775 776 } 777 778 /* 779 * extract_list_suboptions - process list option string 780 */ 781 static int 782 extract_list_suboptions( 783 char *arg, 784 char **sortpp, 785 char **colspp, 786 char **cols2pp, 787 int *noheadingsp, 788 char **delimpp, 789 char **selectpp, 790 char **matchpp) 791 { 792 char *value = NULL; 793 int subopt = 0; 794 int err = 0; 795 796 while (*arg != '\0') { 797 static char need_value[] = 798 "%s: sub-option \"%s\" requires a value\n"; 799 static char no_value[] = 800 "%s: sub-option \"%s\" does not take a value\n"; 801 static char unk_subopt[] = 802 "%s: sub-option \"%s\" unknown\n"; 803 char **pptr; 804 805 subopt = getsubopt(&arg, list_options, &value); 806 switch (subopt) { 807 case LIST_SORT: 808 pptr = sortpp; 809 goto valcom; 810 case LIST_COLS: 811 pptr = colspp; 812 goto valcom; 813 case LIST_COLS2: 814 pptr = cols2pp; 815 goto valcom; 816 case LIST_SELECT: 817 pptr = selectpp; 818 goto valcom; 819 case LIST_MATCH: 820 pptr = matchpp; 821 goto valcom; 822 case LIST_DELIM: 823 pptr = delimpp; 824 valcom: 825 if (value == NULL) { 826 (void) fprintf(stderr, gettext(need_value), 827 cmdname, list_options[subopt]); 828 err = 1; 829 } else 830 *pptr = value; 831 break; 832 case LIST_NOHEADINGS: 833 if (value != NULL) { 834 (void) fprintf(stderr, gettext(no_value), 835 cmdname, list_options[subopt]); 836 err = 1; 837 } else 838 *noheadingsp = 1; 839 break; 840 default: 841 (void) fprintf(stderr, gettext(unk_subopt), 842 cmdname, value); 843 err = 1; 844 break; 845 } 846 } 847 return (err == 0); 848 } 849 850 static cfga_err_t 851 setup_prefilter(post_filter_t *post_filtp, char **prefilt_optpp) 852 { 853 size_t len; 854 const char *clopt = PREFILT_CLASS_STR; 855 int idx; 856 857 858 *prefilt_optpp = NULL; 859 860 /* Get the index for the "class" field */ 861 for (idx = 0; idx < N_FIELDS; idx++) { 862 if (strcmp(all_fields[idx].name, PREFILT_CLASS_STR) == 0) 863 break; 864 } 865 866 /* 867 * Currently pre-filter available only for class fld w/ EXACT match 868 */ 869 if (idx >= N_FIELDS || 870 post_filtp->match_type_p[idx] != CFGA_MATCH_EXACT) { 871 return (CFGA_OK); 872 } 873 874 len = strlen(clopt) + strlen(post_filtp->ldata.ap_class) + 1; 875 if ((*prefilt_optpp = config_calloc_check(1, len)) == NULL) { 876 return (CFGA_LIB_ERROR); 877 } 878 879 (void) strcpy(*prefilt_optpp, clopt); 880 (void) strcat(*prefilt_optpp, post_filtp->ldata.ap_class); 881 882 /* 883 * Since it is being pre-filtered, this attribute does not need 884 * post-filtering. 885 */ 886 post_filtp->match_type_p[idx] = CFGA_MATCH_NOFILTER; 887 if (all_fields[idx].set_filter != NULL) { 888 (void) all_fields[idx].set_filter(&post_filtp->ldata, ""); 889 } 890 891 return (CFGA_OK); 892 } 893 894 static cfga_err_t 895 set_attrval( 896 const char *attr, 897 const char *val, 898 post_filter_t *post_filtp, 899 match_type_t match_type) 900 { 901 int fld = 0; 902 cfga_err_t ret = CFGA_ERROR; 903 904 for (fld = 0; fld < N_FIELDS; fld++) { 905 if (strcmp(attr, all_fields[fld].name) == 0) 906 break; 907 } 908 909 /* Valid field or is the select option supported for this field */ 910 if (fld >= N_FIELDS || all_fields[fld].set_filter == NULL) { 911 return (CFGA_ATTR_INVAL); 912 } 913 914 if ((ret = all_fields[fld].set_filter(&post_filtp->ldata, val)) 915 == CFGA_OK) { 916 post_filtp->match_type_p[fld] = match_type; 917 } 918 919 return (ret); 920 921 } 922 923 static char inval_optarg[] = 924 "%s: invalid value \"%s\" for %s suboption.\n"; 925 926 /* 927 * Parses the "select" string and fills in the post_filter structure 928 */ 929 static cfga_err_t 930 parse_select_opt( 931 const char *selectp, 932 post_filter_t *post_filtp, 933 match_type_t match_type) 934 { 935 parse_state_t state = CFGA_PSTATE_INIT; 936 char *cp = NULL, *optstr = NULL, *attr = NULL, *val = NULL; 937 int bal = 0; /* Tracks balancing */ 938 char chr; 939 cfga_err_t ret; 940 941 942 if (selectp == NULL || post_filtp == NULL) { 943 return (CFGA_ERROR); 944 } 945 946 optstr = config_calloc_check(1, strlen(selectp) + 1); 947 if (optstr == NULL) { 948 return (CFGA_LIB_ERROR); 949 } 950 951 (void) strcpy(optstr, selectp); 952 953 /* Init */ 954 ret = CFGA_ATTR_INVAL; 955 bal = 0; 956 cp = attr = optstr; 957 state = CFGA_PSTATE_INIT; 958 959 for (; *cp != '\0'; cp++) { 960 switch (state) { 961 case CFGA_PSTATE_INIT: 962 if (*cp != LEFT_PAREN) 963 break; 964 *cp = '\0'; 965 val = cp + 1; 966 bal = 1; 967 state = CFGA_PSTATE_ATTR_DONE; 968 break; 969 case CFGA_PSTATE_ATTR_DONE: 970 chr = *cp; 971 switch (chr) { 972 case LEFT_PAREN: 973 bal++; 974 break; 975 case RIGHT_PAREN: 976 bal--; 977 if (bal == 0) { 978 *cp = '\0'; 979 state = CFGA_PSTATE_VAL_DONE; 980 } 981 break; 982 } 983 break; 984 case CFGA_PSTATE_VAL_DONE: 985 if (*cp != ':') { 986 state = CFGA_PSTATE_ERR; 987 goto out; 988 } 989 990 *cp = '\0'; 991 if (set_attrval(attr, val, post_filtp, 992 match_type) != CFGA_OK) { 993 state = CFGA_PSTATE_ERR; 994 goto out; 995 } 996 state = CFGA_PSTATE_INIT; 997 attr = cp + 1; 998 break; 999 default: 1000 state = CFGA_PSTATE_ERR; 1001 /* FALLTHROUGH */ 1002 case CFGA_PSTATE_ERR: 1003 goto out; 1004 } 1005 } 1006 1007 /*FALLTHRU*/ 1008 out: 1009 if (state == CFGA_PSTATE_VAL_DONE) { 1010 ret = set_attrval(attr, val, post_filtp, match_type); 1011 } else { 1012 ret = CFGA_ATTR_INVAL; 1013 } 1014 1015 if (ret != CFGA_OK) { 1016 (void) fprintf(stderr, gettext(inval_optarg), cmdname, 1017 selectp, list_options[LIST_SELECT]); 1018 } 1019 1020 S_FREE(optstr); 1021 return (ret); 1022 } 1023 1024 1025 1026 static cfga_err_t 1027 setup_filter( 1028 const char *selectp, 1029 const char *matchp, 1030 post_filter_t *post_filtp, 1031 char **prefilt_optpp) 1032 { 1033 cfga_err_t ret = CFGA_ERROR; 1034 match_type_t match_type = CFGA_MATCH_NOFILTER; 1035 int i; 1036 1037 static char match_needs_select[] = 1038 "%s: %s suboption can only be used with %s suboption.\n"; 1039 1040 1041 *prefilt_optpp = NULL; 1042 1043 /* 1044 * Initial: no filtering. 1045 * CFGA_MATCH_NOFILTER is NOT a valid user input 1046 */ 1047 for (i = 0; i < N_FIELDS; i++) { 1048 post_filtp->match_type_p[i] = CFGA_MATCH_NOFILTER; 1049 } 1050 1051 /* Determine type of match */ 1052 if (matchp == NULL && selectp == NULL) { 1053 /* No filtering */ 1054 return (CFGA_OK); 1055 } else if (matchp == NULL && selectp != NULL) { 1056 match_type = CFGA_DEFAULT_MATCH; 1057 } else if (matchp != NULL && selectp == NULL) { 1058 /* If only match specified, select criteria also needed */ 1059 (void) fprintf(stderr, gettext(match_needs_select), 1060 cmdname, list_options[LIST_MATCH], 1061 list_options[LIST_SELECT]); 1062 return (CFGA_ERROR); 1063 } else { 1064 for (i = 0; i < N_MATCH_TYPES; i++) { 1065 if (strcmp(matchp, match_type_array[i].str) == 0) { 1066 match_type = match_type_array[i].type; 1067 break; 1068 } 1069 } 1070 if (i >= N_MATCH_TYPES) { 1071 (void) fprintf(stderr, gettext(inval_optarg), cmdname, 1072 matchp, list_options[LIST_MATCH]); 1073 return (CFGA_ERROR); 1074 } 1075 } 1076 1077 if ((ret = parse_select_opt(selectp, post_filtp, match_type)) 1078 != CFGA_OK) { 1079 return (ret); 1080 } 1081 1082 /* Handle pre-filtering. */ 1083 if ((ret = setup_prefilter(post_filtp, prefilt_optpp)) != CFGA_OK) { 1084 /* Cleanup */ 1085 for (i = 0; i < N_FIELDS; i++) { 1086 post_filtp->match_type_p[i] = CFGA_MATCH_NOFILTER; 1087 } 1088 return (ret); 1089 } 1090 1091 1092 return (CFGA_OK); 1093 } 1094 1095 /* 1096 * compare_ap_id - compare two ap_id's 1097 * 1098 * For partial matches, argument order is significant. The filtering criterion 1099 * should be the first argument. 1100 */ 1101 1102 static int 1103 compare_ap_id( 1104 cfga_list_data_t *p1, 1105 cfga_list_data_t *p2, 1106 match_type_t match_type) 1107 { 1108 1109 switch (match_type) { 1110 case CFGA_MATCH_NOFILTER: 1111 return (0); /* No filtering. all pass */ 1112 case CFGA_MATCH_PARTIAL: 1113 return (strncmp(p1->ap_log_id, p2->ap_log_id, 1114 strlen(p1->ap_log_id))); 1115 case CFGA_MATCH_EXACT: 1116 return (strcmp(p1->ap_log_id, p2->ap_log_id)); 1117 case CFGA_MATCH_ORDER: 1118 default: 1119 return (config_ap_id_cmp(p1->ap_log_id, p2->ap_log_id)); 1120 } 1121 } 1122 1123 /* 1124 * print_log_id - print logical ap_id 1125 */ 1126 static void 1127 print_log_id( 1128 cfga_list_data_t *p, 1129 int width, 1130 char *lp) 1131 { 1132 (void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_log_id), 1133 p->ap_log_id); 1134 } 1135 1136 /* 1137 * set_log_flt - Setup filter for logical ap_id 1138 */ 1139 static cfga_err_t 1140 set_log_flt( 1141 cfga_list_data_t *p, 1142 const char *val) 1143 { 1144 if (strlen(val) > sizeof (p->ap_log_id) - 1) 1145 return (CFGA_ATTR_INVAL); 1146 1147 (void) strcpy(p->ap_log_id, val); 1148 1149 return (CFGA_OK); 1150 } 1151 1152 /* 1153 * set_type_flt - Setup filter for type field 1154 */ 1155 1156 static cfga_err_t 1157 set_type_flt( 1158 cfga_list_data_t *p, 1159 const char *val) 1160 { 1161 if (strlen(val) > sizeof (p->ap_type) - 1) 1162 return (CFGA_ATTR_INVAL); 1163 1164 (void) strcpy(p->ap_type, val); 1165 1166 return (CFGA_OK); 1167 } 1168 1169 /* 1170 * set_class_flt - Setup filter for class field 1171 */ 1172 static cfga_err_t 1173 set_class_flt( 1174 cfga_list_data_t *p, 1175 const char *val) 1176 { 1177 if (strlen(val) > sizeof (p->ap_class) - 1) 1178 return (CFGA_ATTR_INVAL); 1179 1180 (void) strcpy(p->ap_class, val); 1181 1182 return (CFGA_OK); 1183 } 1184 1185 1186 /* 1187 * compare_r_state - compare receptacle state of two ap_id's 1188 */ 1189 static int 1190 compare_r_state( 1191 cfga_list_data_t *p1, 1192 cfga_list_data_t *p2, 1193 match_type_t match_type) 1194 { 1195 switch (match_type) { 1196 case CFGA_MATCH_NOFILTER: /* no filtering. pass all */ 1197 return (0); 1198 case CFGA_MATCH_ORDER: 1199 default: 1200 return (p1->ap_r_state - p2->ap_r_state); 1201 } 1202 } 1203 1204 /* 1205 * compare_o_state - compare occupant state of two ap_id's 1206 */ 1207 static int 1208 compare_o_state( 1209 cfga_list_data_t *p1, 1210 cfga_list_data_t *p2, 1211 match_type_t match_type) 1212 { 1213 switch (match_type) { 1214 case CFGA_MATCH_NOFILTER: /* no filtering. all pass */ 1215 return (0); 1216 case CFGA_MATCH_ORDER: 1217 default: 1218 return (p1->ap_o_state - p2->ap_o_state); 1219 } 1220 } 1221 1222 /* 1223 * compare_busy - compare busy field of two ap_id's 1224 */ 1225 static int 1226 compare_busy( 1227 cfga_list_data_t *p1, 1228 cfga_list_data_t *p2, 1229 match_type_t match_type) 1230 { 1231 1232 switch (match_type) { 1233 case CFGA_MATCH_NOFILTER: /* no filtering. all pass */ 1234 return (0); 1235 case CFGA_MATCH_ORDER: 1236 default: 1237 return (p1->ap_busy - p2->ap_busy); 1238 } 1239 } 1240 1241 /* 1242 * print_r_state - print receptacle state 1243 */ 1244 static void 1245 print_r_state( 1246 cfga_list_data_t *p, 1247 int width, 1248 char *lp) 1249 { 1250 char *cp; 1251 1252 switch (p->ap_r_state) { 1253 case CFGA_STAT_EMPTY: 1254 cp = "empty"; 1255 break; 1256 case CFGA_STAT_CONNECTED: 1257 cp = "connected"; 1258 break; 1259 case CFGA_STAT_DISCONNECTED: 1260 cp = "disconnected"; 1261 break; 1262 default: 1263 cp = "???"; 1264 break; 1265 } 1266 (void) sprintf(lp, "%-*s", width, cp); 1267 } 1268 1269 /* 1270 * print_o_state - print occupant state 1271 */ 1272 static void 1273 print_o_state( 1274 cfga_list_data_t *p, 1275 int width, 1276 char *lp) 1277 { 1278 char *cp; 1279 1280 switch (p->ap_o_state) { 1281 case CFGA_STAT_UNCONFIGURED: 1282 cp = "unconfigured"; 1283 break; 1284 case CFGA_STAT_CONFIGURED: 1285 cp = "configured"; 1286 break; 1287 default: 1288 cp = "???"; 1289 break; 1290 } 1291 (void) sprintf(lp, "%-*s", width, cp); 1292 } 1293 1294 /* 1295 * compare_cond - compare condition field of two ap_id's 1296 */ 1297 static int 1298 compare_cond( 1299 cfga_list_data_t *p1, 1300 cfga_list_data_t *p2, 1301 match_type_t match_type) 1302 { 1303 1304 switch (match_type) { 1305 case CFGA_MATCH_NOFILTER: 1306 return (0); 1307 case CFGA_MATCH_ORDER: 1308 default: 1309 return (p1->ap_cond - p2->ap_cond); 1310 } 1311 } 1312 1313 /* 1314 * print_cond - print attachment point condition 1315 */ 1316 static void 1317 print_cond( 1318 cfga_list_data_t *p, 1319 int width, 1320 char *lp) 1321 { 1322 char *cp; 1323 1324 switch (p->ap_cond) { 1325 case CFGA_COND_UNKNOWN: 1326 cp = "unknown"; 1327 break; 1328 case CFGA_COND_UNUSABLE: 1329 cp = "unusable"; 1330 break; 1331 case CFGA_COND_FAILING: 1332 cp = "failing"; 1333 break; 1334 case CFGA_COND_FAILED: 1335 cp = "failed"; 1336 break; 1337 case CFGA_COND_OK: 1338 cp = "ok"; 1339 break; 1340 default: 1341 cp = "???"; 1342 break; 1343 } 1344 (void) sprintf(lp, "%-*s", width, cp); 1345 } 1346 1347 /* 1348 * compare_time - compare time field of two ap_id's 1349 */ 1350 static int 1351 compare_time( 1352 cfga_list_data_t *p1, 1353 cfga_list_data_t *p2, 1354 match_type_t match_type) 1355 { 1356 switch (match_type) { 1357 case CFGA_MATCH_NOFILTER: 1358 return (0); 1359 case CFGA_MATCH_ORDER: 1360 default: 1361 return (p1->ap_status_time - p2->ap_status_time); 1362 } 1363 } 1364 1365 1366 /* 1367 * print_time - print time from cfga_list_data. 1368 * Time print based on ls(1). 1369 */ 1370 static void 1371 print_time( 1372 cfga_list_data_t *p, 1373 int width, 1374 char *lp) 1375 { 1376 static time_t year, now; 1377 time_t stime; 1378 char time_buf[50]; /* array to hold day and time */ 1379 1380 if (year == 0) { 1381 now = time((long *)NULL); 1382 year = now - 6L*30L*24L*60L*60L; /* 6 months ago */ 1383 now = now + 60; 1384 } 1385 stime = p->ap_status_time; 1386 if (stime == (time_t)-1) { 1387 (void) sprintf(lp, "%-*s", width, gettext("unavailable")); 1388 return; 1389 } 1390 1391 if ((stime < year) || (stime > now)) { 1392 (void) strftime(time_buf, sizeof (time_buf), 1393 dcgettext(NULL, FORMAT1, LC_TIME), localtime(&stime)); 1394 } else { 1395 (void) strftime(time_buf, sizeof (time_buf), 1396 dcgettext(NULL, FORMAT2, LC_TIME), localtime(&stime)); 1397 } 1398 (void) sprintf(lp, "%-*s", width, time_buf); 1399 } 1400 1401 /* 1402 * print_time_p - print time from cfga_list_data. 1403 */ 1404 static void 1405 print_time_p( 1406 cfga_list_data_t *p, 1407 int width, 1408 char *lp) 1409 { 1410 struct tm *tp; 1411 char tstr[TIME_P_WIDTH+1]; 1412 1413 tp = localtime(&p->ap_status_time); 1414 (void) sprintf(tstr, "%04d%02d%02d%02d%02d%02d", tp->tm_year + 1900, 1415 tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec); 1416 (void) sprintf(lp, "%-*s", width, tstr); 1417 } 1418 1419 /* 1420 * compare_info - compare info from two cfga_list_data structs 1421 */ 1422 static int 1423 compare_info( 1424 cfga_list_data_t *p1, 1425 cfga_list_data_t *p2, 1426 match_type_t match_type) 1427 { 1428 switch (match_type) { 1429 case CFGA_MATCH_NOFILTER: 1430 return (0); 1431 case CFGA_MATCH_ORDER: 1432 default: 1433 return (strncmp(p1->ap_info, p2->ap_info, 1434 sizeof (p2->ap_info))); 1435 } 1436 } 1437 1438 /* 1439 * print_info - print info from cfga_list_data struct 1440 */ 1441 static void 1442 print_info( 1443 cfga_list_data_t *p, 1444 int width, 1445 char *lp) 1446 { 1447 (void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_info), p->ap_info); 1448 } 1449 1450 /* 1451 * compare_type - compare type from two cfga_list_data structs 1452 * 1453 * For partial matches, argument order is significant. The filtering criterion 1454 * should be the first argument. 1455 */ 1456 static int 1457 compare_type( 1458 cfga_list_data_t *p1, 1459 cfga_list_data_t *p2, 1460 match_type_t match_type) 1461 { 1462 switch (match_type) { 1463 case CFGA_MATCH_NOFILTER: 1464 return (0); 1465 case CFGA_MATCH_PARTIAL: 1466 return (strncmp(p1->ap_type, p2->ap_type, strlen(p1->ap_type))); 1467 case CFGA_MATCH_EXACT: 1468 case CFGA_MATCH_ORDER: 1469 default: 1470 return (strncmp(p1->ap_type, p2->ap_type, 1471 sizeof (p2->ap_type))); 1472 } 1473 } 1474 1475 /* 1476 * print_type - print type from cfga_list_data struct 1477 */ 1478 static void 1479 print_type( 1480 cfga_list_data_t *p, 1481 int width, 1482 char *lp) 1483 { 1484 (void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_type), p->ap_type); 1485 } 1486 1487 1488 /* 1489 * compare_class - compare class from two cfga_list_data structs 1490 * 1491 * For partial matches, argument order is significant. The filtering criterion 1492 * should be the first argument. 1493 */ 1494 static int 1495 compare_class( 1496 cfga_list_data_t *p1, 1497 cfga_list_data_t *p2, 1498 match_type_t match_type) 1499 { 1500 1501 switch (match_type) { 1502 case CFGA_MATCH_NOFILTER: 1503 return (0); 1504 case CFGA_MATCH_PARTIAL: 1505 return (strncmp(p1->ap_class, p2->ap_class, 1506 strlen(p1->ap_class))); 1507 case CFGA_MATCH_EXACT: 1508 case CFGA_MATCH_ORDER: 1509 default: 1510 return (strncmp(p1->ap_class, p2->ap_class, 1511 sizeof (p2->ap_class))); 1512 } 1513 } 1514 1515 /* 1516 * print_class - print class from cfga_list_data struct 1517 */ 1518 static void 1519 print_class( 1520 cfga_list_data_t *p, 1521 int width, 1522 char *lp) 1523 { 1524 (void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_class), p->ap_class); 1525 } 1526 /* 1527 * print_busy - print busy from cfga_list_data struct 1528 */ 1529 /* ARGSUSED */ 1530 static void 1531 print_busy( 1532 cfga_list_data_t *p, 1533 int width, 1534 char *lp) 1535 { 1536 if (p->ap_busy) 1537 (void) sprintf(lp, "%-*.*s", width, width, "y"); 1538 else 1539 (void) sprintf(lp, "%-*.*s", width, width, "n"); 1540 } 1541 1542 /* 1543 * print_phys_id - print physical ap_id 1544 */ 1545 static void 1546 print_phys_id( 1547 cfga_list_data_t *p, 1548 int width, 1549 char *lp) 1550 { 1551 (void) sprintf(lp, "%-*.*s", width, sizeof (p->ap_phys_id), 1552 p->ap_phys_id); 1553 } 1554 1555 1556 /* 1557 * find_field - find the named field 1558 */ 1559 static struct field_info * 1560 find_field(char *fname) 1561 { 1562 struct field_info *fldp; 1563 1564 for (fldp = all_fields; fldp < &all_fields[N_FIELDS]; fldp++) 1565 if (strcmp(fname, fldp->name) == 0) 1566 return (fldp); 1567 return (NULL); 1568 } 1569 1570 /* 1571 * usage_field - print field usage 1572 */ 1573 static void 1574 usage_field() 1575 { 1576 struct field_info *fldp = NULL; 1577 const char *sep; 1578 static char field_list[] = "%s: print or sort fields must be one of:"; 1579 1580 (void) fprintf(stderr, gettext(field_list), cmdname); 1581 sep = ""; 1582 1583 for (fldp = all_fields; fldp < &all_fields[N_FIELDS]; fldp++) { 1584 (void) fprintf(stderr, "%s %s", sep, fldp->name); 1585 sep = ","; 1586 } 1587 (void) fprintf(stderr, "\n"); 1588 } 1589 1590 /* 1591 * compare_null - null comparison routine 1592 */ 1593 /*ARGSUSED*/ 1594 static int 1595 compare_null( 1596 cfga_list_data_t *p1, 1597 cfga_list_data_t *p2, 1598 match_type_t match_type) 1599 { 1600 return (0); 1601 } 1602 1603 /* 1604 * print_null - print out a field of spaces 1605 */ 1606 /*ARGSUSED*/ 1607 static void 1608 print_null( 1609 cfga_list_data_t *p, 1610 int width, 1611 char *lp) 1612 { 1613 (void) sprintf(lp, "%-*s", width, ""); 1614 } 1615 1616 /* 1617 * do_config_list - directs the output of the listing functions 1618 */ 1619 static int 1620 do_config_list( 1621 int l_argc, 1622 char *l_argv[], 1623 cfga_list_data_t *statlist, 1624 int nlist, 1625 char *sortp, 1626 char *colsp, 1627 char *cols2p, 1628 int noheadings, 1629 char *delimp, 1630 post_filter_t *post_filtp, 1631 int dyn_exp) 1632 { 1633 int nprcols = 0, ncols2 = 0; 1634 struct print_col *prnt_list = NULL; 1635 int napids_to_list = 0; 1636 FILE *fp = NULL; 1637 int f_err; 1638 cfga_list_data_t **sel_boards = NULL; 1639 int nsel = 0; 1640 int i, j; 1641 cfga_err_t ret; 1642 1643 ap_arg_t *arg_array = NULL; 1644 ap_out_t *out_array = NULL; 1645 1646 1647 sort_list = NULL; 1648 f_err = 0; 1649 fp = stdout; 1650 nsort_list = count_fields(sortp, FDELIM); 1651 if (nsort_list != 0) { 1652 sort_list = config_calloc_check(nsort_list, 1653 sizeof (*sort_list)); 1654 if (sort_list == NULL) { 1655 ret = CFGA_LIB_ERROR; 1656 goto out; 1657 } 1658 f_err |= process_sort_fields(nsort_list, sort_list, sortp); 1659 } else 1660 sort_list = NULL; 1661 1662 nprcols = count_fields(colsp, FDELIM); 1663 if ((ncols2 = count_fields(cols2p, FDELIM)) > nprcols) 1664 nprcols = ncols2; 1665 if (nprcols != 0) { 1666 prnt_list = config_calloc_check(nprcols, sizeof (*prnt_list)); 1667 if (prnt_list == NULL) { 1668 ret = CFGA_LIB_ERROR; 1669 goto out; 1670 } 1671 f_err |= process_fields(nprcols, prnt_list, 0, colsp); 1672 if (ncols2 != 0) 1673 f_err |= process_fields(nprcols, prnt_list, 1, cols2p); 1674 } else 1675 prnt_list = NULL; 1676 1677 if (f_err) { 1678 usage_field(); 1679 ret = CFGA_ERROR; 1680 goto out; 1681 } 1682 1683 /* Create an array of all user args (if any) */ 1684 if (l_argc != 0) { 1685 int i, j; 1686 1687 napids_to_list = 0; 1688 1689 for (i = 0; i < l_argc; i++) { 1690 napids_to_list += count_fields(l_argv[i], ARG_DELIM); 1691 } 1692 1693 arg_array = config_calloc_check(napids_to_list, 1694 sizeof (*arg_array)); 1695 if (arg_array == NULL) { 1696 ret = CFGA_LIB_ERROR; 1697 goto out; 1698 } 1699 1700 for (i = 0, j = 0; i < l_argc; i++) { 1701 int n; 1702 1703 n = count_fields(l_argv[i], ARG_DELIM); 1704 if (n == 0) { 1705 continue; 1706 } else if (n == 1) { 1707 arg_array[j].arg = l_argv[i]; 1708 arg_array[j].resp = 0; 1709 j++; 1710 } else { 1711 char *cp, *ncp; 1712 1713 cp = l_argv[i]; 1714 for (;;) { 1715 arg_array[j].arg = cp; 1716 arg_array[j].resp = 0; 1717 j++; 1718 ncp = strchr(cp, ARG_DELIM); 1719 if (ncp == NULL) 1720 break; 1721 *ncp = '\0'; 1722 cp = ncp + 1; 1723 } 1724 } 1725 } 1726 assert(j == napids_to_list); 1727 } else { 1728 napids_to_list = 0; 1729 arg_array = NULL; 1730 } 1731 1732 assert(nlist != 0); 1733 1734 out_array = config_calloc_check(nlist, sizeof (*out_array)); 1735 if (out_array == NULL) { 1736 ret = CFGA_LIB_ERROR; 1737 goto out; 1738 } 1739 1740 1741 /* create a list of output stat data */ 1742 for (i = 0; i < nlist; i++) { 1743 out_array[i].ldatap = &statlist[i]; 1744 out_array[i].req = 0; 1745 } 1746 1747 /* 1748 * Mark all user input which got atleast 1 stat data in response 1749 */ 1750 for (i = 0; i < napids_to_list; i++) { 1751 arg_got_resp(&arg_array[i], out_array, nlist, dyn_exp); 1752 } 1753 1754 /* 1755 * Process output data 1756 */ 1757 nsel = 0; 1758 for (i = 0; i < nlist; i++) { 1759 /* 1760 * Mark all the stats which were actually requested by user 1761 */ 1762 out_was_req(&out_array[i], arg_array, napids_to_list, 0); 1763 if (out_array[i].req == 0 && dyn_exp) { 1764 /* 1765 * Try again without the dynamic component for the 1766 * if dynamic expansion was requested. 1767 */ 1768 out_was_req(&out_array[i], arg_array, 1769 napids_to_list, 1); 1770 } 1771 1772 /* 1773 * post filter data which was actually requested 1774 */ 1775 if (out_array[i].req == 1) { 1776 do_post_filter(&out_array[i], post_filtp, &nsel); 1777 } 1778 } 1779 1780 sel_boards = config_calloc_check(nsel, sizeof (*sel_boards)); 1781 if (sel_boards == NULL) { 1782 ret = CFGA_LIB_ERROR; 1783 goto out; 1784 } 1785 1786 for (i = 0, j = 0; i < nlist; i++) { 1787 if (out_array[i].req == 1) { 1788 sel_boards[j] = out_array[i].ldatap; 1789 j++; 1790 } 1791 } 1792 1793 assert(j == nsel); 1794 1795 /* 1796 * Print headings even if no list entries - Bug or feature ? 1797 */ 1798 if (!noheadings && prnt_list != NULL) { 1799 if ((ret = print_fields(nprcols, prnt_list, 1, 0, 1800 delimp, NULL, fp)) != CFGA_OK) { 1801 goto out; 1802 } 1803 if (ncols2 != 0) { 1804 if ((ret = print_fields(nprcols, prnt_list, 1, 1805 1, delimp, NULL, fp)) != CFGA_OK) { 1806 goto out; 1807 } 1808 } 1809 } 1810 1811 if (nsel != 0) { 1812 if (sort_list != NULL && nsel > 1) { 1813 qsort(sel_boards, nsel, sizeof (sel_boards[0]), 1814 ldata_compare); 1815 } 1816 1817 if (prnt_list != NULL) { 1818 for (i = 0; i < nsel; i++) { 1819 if ((ret = print_fields(nprcols, 1820 prnt_list, 0, 0, delimp, sel_boards[i], fp)) 1821 != CFGA_OK) 1822 goto out; 1823 if (ncols2 != 0) { 1824 if ((ret = print_fields( 1825 nprcols, prnt_list, 0, 1, delimp, 1826 sel_boards[i], fp)) != CFGA_OK) 1827 goto out; 1828 } 1829 } 1830 } 1831 } 1832 /* 1833 * Go thru the argument list and notify user about args 1834 * which did not have a match 1835 */ 1836 report_no_response(arg_array, napids_to_list); 1837 ret = CFGA_OK; 1838 /*FALLTHRU*/ 1839 out: 1840 S_FREE(sel_boards); 1841 S_FREE(arg_array); 1842 S_FREE(out_array); 1843 1844 S_FREE(sort_list); 1845 S_FREE(prnt_list); 1846 1847 return (ret); 1848 } 1849 1850 1851 /* 1852 * Mark all user inputs which got a response 1853 */ 1854 static void 1855 arg_got_resp(ap_arg_t *inp, ap_out_t *out_array, int nouts, int dyn_exp) 1856 { 1857 int i; 1858 cfga_ap_types_t type; 1859 1860 1861 if (nouts == 0) { 1862 return; 1863 } 1864 1865 type = find_arg_type(inp->arg); 1866 1867 /* 1868 * Go through list of output stats and check if argument 1869 * produced that output 1870 */ 1871 for (i = 0; i < nouts; i++) { 1872 if (type == PHYSICAL_AP_ID) { 1873 if (config_ap_id_cmp(out_array[i].ldatap->ap_phys_id, 1874 inp->arg) == 0) { 1875 break; 1876 } 1877 } else if (type == LOGICAL_AP_ID) { 1878 if (config_ap_id_cmp(out_array[i].ldatap->ap_log_id, 1879 inp->arg) == 0) { 1880 break; 1881 } 1882 } else if (type == AP_TYPE) { 1883 /* 1884 * An AP_TYPE argument cannot generate dynamic 1885 * attachment point stats unless dynamic expansion was 1886 * requested by user. 1887 */ 1888 if (!dyn_exp && get_dyn(out_array[i].ldatap->ap_log_id) 1889 != NULL) { 1890 continue; 1891 } 1892 1893 if (strncmp(out_array[i].ldatap->ap_log_id, inp->arg, 1894 strlen(inp->arg)) == 0) { 1895 break; 1896 } 1897 } else { 1898 return; 1899 } 1900 } 1901 1902 if (i < nouts) { 1903 inp->resp = 1; 1904 } 1905 } 1906 1907 /* Mark all stat data which were requested by user */ 1908 static void 1909 out_was_req(ap_out_t *outp, ap_arg_t *in_array, int nargs, int no_dyn) 1910 { 1911 int i; 1912 cfga_ap_types_t type = UNKNOWN_AP; 1913 char physid[MAXPATHLEN], logid[MAXPATHLEN]; 1914 1915 1916 /* If no user args, all output is acceptable */ 1917 if (nargs == 0) { 1918 outp->req = 1; 1919 return; 1920 } 1921 1922 1923 (void) snprintf(physid, sizeof (physid), "%s", 1924 outp->ldatap->ap_phys_id); 1925 (void) snprintf(logid, sizeof (logid), "%s", outp->ldatap->ap_log_id); 1926 1927 /* 1928 * Do comparison with or without dynamic component as requested by 1929 * user. 1930 */ 1931 if (no_dyn) { 1932 /* Remove the dynamic component */ 1933 remove_dyn(physid); 1934 remove_dyn(logid); 1935 } 1936 1937 for (i = 0; i < nargs; i++) { 1938 type = find_arg_type(in_array[i].arg); 1939 if (type == PHYSICAL_AP_ID) { 1940 1941 if (config_ap_id_cmp(in_array[i].arg, physid) == 0) { 1942 break; 1943 } 1944 } else if (type == LOGICAL_AP_ID) { 1945 1946 if (config_ap_id_cmp(in_array[i].arg, logid) == 0) { 1947 break; 1948 } 1949 } else if (type == AP_TYPE) { 1950 /* 1951 * Aptypes cannot generate dynamic attachment 1952 * points unless dynamic expansion is specified. 1953 * in which case this routine would be called a 1954 * 2nd time with the no_dyn flag set and there 1955 * would be no dynamic ap_ids. 1956 */ 1957 if (get_dyn(logid) != NULL) { 1958 continue; 1959 } 1960 1961 if (strncmp(in_array[i].arg, logid, 1962 strlen(in_array[i].arg)) == 0) { 1963 break; 1964 } 1965 } else { 1966 continue; 1967 } 1968 } 1969 1970 if (i < nargs) { 1971 /* Ok, this output was requested */ 1972 outp->req = 1; 1973 } 1974 1975 } 1976 1977 static void 1978 do_post_filter(ap_out_t *outp, post_filter_t *post_filtp, int *nselp) 1979 { 1980 int i; 1981 1982 if (outp->req != 1) { 1983 return; 1984 } 1985 1986 /* 1987 * For fields without filtering (CFGA_MATCH_NOFILTER), 1988 * compare always returns 0 (success) 1989 */ 1990 for (i = 0; i < N_FIELDS; i++) { 1991 /* 1992 * Note: Order is important for partial match (via strncmp). 1993 * The first argument for compare must be the filter. 1994 */ 1995 if (all_fields[i].compare(&post_filtp->ldata, outp->ldatap, 1996 post_filtp->match_type_p[i])) { 1997 outp->req = 0; /* Blocked by filter */ 1998 return; 1999 } 2000 } 2001 2002 /* 2003 * Passed through filter 2004 */ 2005 (*nselp)++; 2006 } 2007 2008 static void 2009 report_no_response(ap_arg_t *arg_array, int nargs) 2010 { 2011 int i; 2012 2013 if (nargs == 0) { 2014 return; 2015 } 2016 2017 2018 /* 2019 * nop if no user arguments 2020 */ 2021 for (i = 0; i < nargs; i++) { 2022 if (arg_array[i].resp == 0) { 2023 (void) fprintf(stderr, 2024 gettext("%s: No matching library found\n"), 2025 arg_array[i].arg); 2026 } 2027 } 2028 } 2029 2030 /* 2031 * ldata_compare - compare two attachment point list data structures. 2032 */ 2033 static int 2034 ldata_compare( 2035 const void *vb1, 2036 const void *vb2) 2037 { 2038 int i; 2039 int res = -1; 2040 cfga_list_data_t *b1, *b2; 2041 2042 2043 b1 = *(cfga_list_data_t **)vb1; 2044 b2 = *(cfga_list_data_t **)vb2; 2045 2046 for (i = 0; i < nsort_list; i++) { 2047 res = (*(sort_list[i].fld->compare))(b1, b2, CFGA_MATCH_ORDER); 2048 if (res != 0) { 2049 if (sort_list[i].reverse) 2050 res = -res; 2051 break; 2052 } 2053 } 2054 2055 return (res); 2056 } 2057 2058 /* 2059 * count_fields - Count the number of fields, using supplied delimiter. 2060 */ 2061 static int 2062 count_fields(char *fspec, char delim) 2063 { 2064 char *cp = NULL; 2065 int n; 2066 2067 if (fspec == 0 || *fspec == '\0') 2068 return (0); 2069 n = 1; 2070 for (cp = fspec; *cp != '\0'; cp++) 2071 if (*cp == delim) 2072 n++; 2073 return (n); 2074 } 2075 2076 /* 2077 * get_field 2078 * This function is not a re-implementation of strtok(). 2079 * There can be null fields - strtok() eats spans of delimiters. 2080 */ 2081 static char * 2082 get_field(char **fspp) 2083 { 2084 char *cp = NULL, *fld; 2085 2086 fld = *fspp; 2087 2088 if (fld != NULL && *fld == '\0') 2089 fld = NULL; 2090 2091 if (fld != NULL) { 2092 cp = strchr(*fspp, FDELIM); 2093 if (cp == NULL) { 2094 *fspp = NULL; 2095 } else { 2096 *cp = '\0'; 2097 *fspp = cp + 1; 2098 if (*fld == '\0') 2099 fld = NULL; 2100 } 2101 } 2102 return (fld); 2103 } 2104 2105 /* 2106 * process_fields - 2107 */ 2108 static int 2109 process_fields( 2110 int ncol, 2111 struct print_col *list, 2112 int line2, 2113 char *fmt) 2114 { 2115 struct print_col *pp = NULL; 2116 struct field_info *fldp = NULL; 2117 char *fmtx; 2118 char *fldn; 2119 int err; 2120 2121 err = 0; 2122 fmtx = fmt; 2123 for (pp = list; pp < &list[ncol]; pp++) { 2124 fldn = get_field(&fmtx); 2125 fldp = &null_field; 2126 if (fldn != NULL) { 2127 struct field_info *tfldp; 2128 2129 tfldp = find_field(fldn); 2130 if (tfldp != NULL) { 2131 fldp = tfldp; 2132 } else { 2133 (void) fprintf(stderr, gettext(unk_field), 2134 cmdname, fldn); 2135 err = 1; 2136 } 2137 } 2138 if (line2) { 2139 pp->line2 = fldp; 2140 if (fldp->width > pp->width) 2141 pp->width = fldp->width; 2142 } else { 2143 pp->line1 = fldp; 2144 pp->width = fldp->width; 2145 } 2146 } 2147 return (err); 2148 } 2149 2150 /* 2151 * process_sort_fields - 2152 */ 2153 static int 2154 process_sort_fields( 2155 int nsort, 2156 struct sort_el *list, 2157 char *fmt) 2158 { 2159 int i; 2160 int rev; 2161 struct field_info *fldp = NULL; 2162 char *fmtx; 2163 char *fldn; 2164 int err; 2165 2166 err = 0; 2167 fmtx = fmt; 2168 for (i = 0; i < nsort; i++) { 2169 fldn = get_field(&fmtx); 2170 fldp = &null_field; 2171 rev = 0; 2172 if (fldn != NULL) { 2173 struct field_info *tfldp = NULL; 2174 2175 if (*fldn == '-') { 2176 rev = 1; 2177 fldn++; 2178 } 2179 tfldp = find_field(fldn); 2180 if (tfldp != NULL) { 2181 fldp = tfldp; 2182 } else { 2183 (void) fprintf(stderr, gettext(unk_field), 2184 cmdname, fldn); 2185 err = 1; 2186 } 2187 } 2188 list[i].reverse = rev; 2189 list[i].fld = fldp; 2190 } 2191 return (err); 2192 } 2193 2194 /* 2195 * print_fields - 2196 */ 2197 static cfga_err_t 2198 print_fields( 2199 int ncol, 2200 struct print_col *list, 2201 int heading, 2202 int line2, 2203 char *delim, 2204 cfga_list_data_t *bdp, 2205 FILE *fp) 2206 { 2207 char *del = NULL; 2208 struct print_col *pp = NULL; 2209 struct field_info *fldp = NULL; 2210 static char *outline, *end; 2211 char *lp; 2212 2213 if (outline == NULL) { 2214 int out_len, delim_len; 2215 2216 delim_len = strlen(delim); 2217 out_len = 0; 2218 for (pp = list; pp < &list[ncol]; pp++) { 2219 out_len += pp->width; 2220 out_len += delim_len; 2221 } 2222 out_len -= delim_len; 2223 outline = config_calloc_check(out_len + 1, 1); 2224 if (outline == NULL) { 2225 return (CFGA_LIB_ERROR); 2226 } 2227 end = &outline[out_len + 1]; 2228 } 2229 2230 lp = outline; 2231 del = ""; 2232 for (pp = list; pp < &list[ncol]; pp++) { 2233 fldp = line2 ? pp->line2 : pp->line1; 2234 (void) snprintf(lp, end - lp, "%s", del); 2235 lp += strlen(lp); 2236 if (heading) { 2237 (void) snprintf(lp, end - lp, "%-*s", 2238 fldp->width, fldp->heading); 2239 } else { 2240 (*fldp->printfn)(bdp, fldp->width, lp); 2241 } 2242 lp += strlen(lp); 2243 del = delim; 2244 } 2245 2246 /* 2247 * Trim trailing spaces 2248 */ 2249 while (--lp >= outline && *lp == ' ') 2250 *lp = '\0'; 2251 (void) fprintf(fp, "%s\n", outline); 2252 return (CFGA_OK); 2253 } 2254 2255 /* 2256 * config_calloc_check - perform allocation, check result and 2257 * set error indicator 2258 */ 2259 static void * 2260 config_calloc_check( 2261 size_t nelem, 2262 size_t elsize) 2263 { 2264 void *p; 2265 static char alloc_fail[] = 2266 "%s: memory allocation failed (%d*%d bytes)\n"; 2267 2268 2269 p = calloc(nelem, elsize); 2270 if (p == NULL) { 2271 (void) fprintf(stderr, gettext(alloc_fail), cmdname, 2272 nelem, elsize); 2273 } 2274 return (p); 2275 } 2276 2277 /* 2278 * find_arg_type - determine if an argument is an ap_id or an ap_type. 2279 */ 2280 static cfga_ap_types_t 2281 find_arg_type(const char *ap_id) 2282 { 2283 struct stat sbuf; 2284 cfga_ap_types_t type; 2285 char *mkr = NULL, *cp; 2286 int size_ap = 0, size_mkr = 0, digit = 0, i = 0; 2287 char path[MAXPATHLEN]; 2288 char apbuf[MAXPATHLEN]; 2289 size_t len; 2290 2291 2292 /* 2293 * sanity checks 2294 */ 2295 if (ap_id == NULL || *ap_id == '\0') { 2296 return (UNKNOWN_AP); 2297 } 2298 2299 /* 2300 * Mask the dynamic component if any 2301 */ 2302 if ((cp = GET_DYN(ap_id)) != NULL) { 2303 len = cp - ap_id; 2304 } else { 2305 len = strlen(ap_id); 2306 } 2307 2308 if (len >= sizeof (apbuf)) { 2309 return (UNKNOWN_AP); 2310 } 2311 2312 (void) strncpy(apbuf, ap_id, len); 2313 apbuf[len] = '\0'; 2314 2315 /* 2316 * If it starts with a slash and is stat-able 2317 * its a physical. 2318 */ 2319 if (*apbuf == '/' && stat(apbuf, &sbuf) == 0) { 2320 return (PHYSICAL_AP_ID); 2321 } 2322 2323 /* 2324 * Is this a symlink in CFGA_DEV_DIR ? 2325 */ 2326 (void) snprintf(path, sizeof (path), "%s/%s", CFGA_DEV_DIR, apbuf); 2327 2328 if (lstat(path, &sbuf) == 0 && S_ISLNK(sbuf.st_mode) && 2329 stat(path, &sbuf) == 0) { 2330 return (LOGICAL_AP_ID); 2331 } 2332 2333 /* 2334 * Check for ":" which is always present in an ap_id but not maybe 2335 * present or absent in an ap_type. 2336 * We need to check that the characters right before the : are digits 2337 * since an ap_id is of the form <name><instance>:<specific ap name> 2338 */ 2339 if ((mkr = strchr(apbuf, ':')) == NULL) { 2340 type = AP_TYPE; 2341 } else { 2342 size_ap = strlen(apbuf); 2343 size_mkr = strlen(mkr); 2344 mkr = apbuf; 2345 2346 digit = 0; 2347 for (i = size_ap - size_mkr - 1; i > 0; i--) { 2348 if ((int)isdigit(mkr[i])) { 2349 digit++; 2350 break; 2351 } 2352 } 2353 if (digit == 0) { 2354 type = AP_TYPE; 2355 } else { 2356 type = LOGICAL_AP_ID; 2357 } 2358 } 2359 2360 return (type); 2361 } 2362 2363 2364 static char * 2365 get_dyn(const char *ap_id) 2366 { 2367 if (ap_id == NULL) { 2368 return (NULL); 2369 } 2370 2371 return (strstr(ap_id, CFGA_DYN_SEP)); 2372 } 2373 2374 /* 2375 * removes the dynamic component 2376 */ 2377 static void 2378 remove_dyn(char *ap_id) 2379 { 2380 char *cp; 2381 2382 if (ap_id == NULL) { 2383 return; 2384 } 2385 2386 cp = strstr(ap_id, CFGA_DYN_SEP); 2387 if (cp != NULL) { 2388 *cp = '\0'; 2389 } 2390 } 2391