1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #include <stdio.h> 27 #include <fcntl.h> 28 #include <ctype.h> 29 #include <string.h> 30 #include <stdlib.h> 31 #include <unistd.h> 32 #include <errno.h> 33 #include <limits.h> 34 #include <libintl.h> 35 #include <locale.h> 36 #include <sys/stat.h> 37 #include <sys/corectl.h> 38 #include <libproc.h> 39 #include <libscf.h> 40 #include <libscf_priv.h> 41 #include <assert.h> 42 43 #define E_SUCCESS 0 /* Exit status for success */ 44 #define E_ERROR 1 /* Exit status for error */ 45 #define E_USAGE 2 /* Exit status for usage error */ 46 47 static const char PATH_CONFIG[] = "/etc/coreadm.conf"; 48 static const char PATH_CONFIG_OLD[] = "/etc/coreadm.conf.old"; 49 50 #define COREADM_INST_NAME "system/coreadm:default" 51 #define COREADM_INST_FMRI \ 52 SCF_FMRI_SVC_PREFIX SCF_FMRI_SERVICE_PREFIX COREADM_INST_NAME 53 54 #define CONFIG_PARAMS "config_params" 55 #define GLOBAL_ENABLED "global_enabled" 56 #define PROCESS_ENABLED "process_enabled" 57 #define GLOBAL_SETID_ENABLED "global_setid_enabled" 58 #define PROCESS_SETID_ENABLED "process_setid_enabled" 59 #define GLOBAL_LOG_ENABLED "global_log_enabled" 60 #define GLOBAL_PATTERN "global_pattern" 61 #define GLOBAL_CONTENT "global_content" 62 #define INIT_PATTERN "init_pattern" 63 #define INIT_CONTENT "init_content" 64 65 static char *command; 66 static uint64_t options; 67 static int alloptions; 68 static char *glob_pattern; 69 static char gpattern[PATH_MAX]; 70 static core_content_t glob_content = CC_CONTENT_INVALID; 71 static char *init_pattern; 72 static char ipattern[PATH_MAX]; 73 static core_content_t init_content = CC_CONTENT_INVALID; 74 static char *proc_pattern; 75 static size_t proc_size; 76 static core_content_t proc_content = CC_CONTENT_INVALID; 77 78 static int report_settings(void); 79 static int do_processes(int, char **); 80 static int do_modify(boolean_t); 81 static int do_update(void); 82 static int do_legacy(void); 83 84 static scf_propvec_t prop_gpattern = { GLOBAL_PATTERN, NULL, SCF_TYPE_ASTRING }; 85 static scf_propvec_t prop_gcontent = { GLOBAL_CONTENT, NULL, SCF_TYPE_ASTRING }; 86 static scf_propvec_t prop_ipattern = { INIT_PATTERN, NULL, SCF_TYPE_ASTRING }; 87 static scf_propvec_t prop_icontent = { INIT_CONTENT, NULL, SCF_TYPE_ASTRING }; 88 static scf_propvec_t prop_option[] = { 89 { GLOBAL_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_GLOBAL_PATH }, 90 { PROCESS_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_PROCESS_PATH }, 91 { GLOBAL_SETID_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_GLOBAL_SETID }, 92 { PROCESS_SETID_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_PROCESS_SETID }, 93 { GLOBAL_LOG_ENABLED, NULL, SCF_TYPE_BOOLEAN, NULL, CC_GLOBAL_LOG }, 94 { NULL } 95 }; 96 #define MAX_PROPS (4 + (sizeof (prop_option) / sizeof (scf_propvec_t))) 97 98 static void 99 usage(void) 100 { 101 (void) fprintf(stderr, gettext( 102 "usage:\n")); 103 (void) fprintf(stderr, gettext( 104 " %s [ -g pattern ] [ -i pattern ] [ -G content ] [ -I content ]\n"), 105 command); 106 (void) fprintf(stderr, gettext( 107 " [ -e {global | process | global-setid | proc-setid | log} ]\n")); 108 (void) fprintf(stderr, gettext( 109 " [ -d {global | process | global-setid | proc-setid | log} ]\n")); 110 (void) fprintf(stderr, gettext( 111 " %s [ -p pattern ] [ -P content ] [ pid ... ]\n"), command); 112 exit(E_USAGE); 113 } 114 115 static int 116 perm(void) 117 { 118 (void) fprintf(stderr, gettext("%s: insufficient privileges to " 119 "exercise the -[GIgied] options\n"), command); 120 return (E_USAGE); 121 } 122 123 static int 124 parse_content(char *arg, core_content_t *content) 125 { 126 if (proc_str2content(arg, content) == 0) 127 return (0); 128 (void) fprintf(stderr, gettext("%s: invalid content string '%s'\n"), 129 command, arg); 130 return (1); 131 } 132 133 int 134 main(int argc, char **argv) 135 { 136 int flag; 137 int opt; 138 int modify; 139 int update = 0; 140 int legacy_update = 0; 141 int error = 0; 142 int npids; 143 char **pidlist; 144 145 char curpid[11]; 146 char *curpid_ptr = &curpid[0]; 147 148 (void) setlocale(LC_ALL, ""); 149 (void) textdomain(TEXT_DOMAIN); 150 151 /* command name (e.g., "coreadm") */ 152 if ((command = strrchr(argv[0], '/')) != NULL) 153 command++; 154 else 155 command = argv[0]; 156 157 while ((opt = getopt(argc, argv, "g:G:i:I:p:P:e:d:uU?")) != EOF) { 158 switch (opt) { 159 case 'g': 160 glob_pattern = optarg; 161 break; 162 case 'i': 163 init_pattern = optarg; 164 break; 165 case 'p': 166 proc_pattern = optarg; 167 proc_size = strlen(proc_pattern) + 1; 168 break; 169 case 'G': 170 error |= parse_content(optarg, &glob_content); 171 break; 172 case 'I': 173 error |= parse_content(optarg, &init_content); 174 break; 175 case 'P': 176 error |= parse_content(optarg, &proc_content); 177 break; 178 case 'e': 179 case 'd': 180 if (strcmp(optarg, "global") == 0) 181 flag = CC_GLOBAL_PATH; 182 else if (strcmp(optarg, "process") == 0) 183 flag = CC_PROCESS_PATH; 184 else if (strcmp(optarg, "global-setid") == 0) 185 flag = CC_GLOBAL_SETID; 186 else if (strcmp(optarg, "proc-setid") == 0) 187 flag = CC_PROCESS_SETID; 188 else if (strcmp(optarg, "log") == 0) 189 flag = CC_GLOBAL_LOG; 190 else { 191 flag = 0; 192 error = 1; 193 } 194 if (opt == 'e') 195 options |= flag; 196 else 197 options &= ~flag; 198 alloptions |= flag; 199 break; 200 case 'U': 201 update = 1; 202 break; 203 case 'u': 204 legacy_update = 1; 205 break; 206 case '?': 207 default: 208 error = 1; 209 break; 210 } 211 } 212 213 npids = argc - optind; 214 pidlist = argv + optind; 215 216 if (error) 217 usage(); 218 219 /* 220 * If 'modify' is true, we must modify the system settings 221 * and update the configuration file with the new parameters. 222 */ 223 modify = glob_pattern != NULL || glob_content != CC_CONTENT_INVALID || 224 init_pattern != NULL || init_content != CC_CONTENT_INVALID || 225 alloptions != 0; 226 227 if ((update || legacy_update) && (modify || proc_pattern != NULL || 228 proc_content != CC_CONTENT_INVALID || npids != 0)) { 229 (void) fprintf(stderr, 230 gettext("%s: the -u option must stand alone\n"), command); 231 usage(); 232 } 233 if (modify && 234 (proc_pattern != NULL || proc_content != CC_CONTENT_INVALID)) { 235 (void) fprintf(stderr, gettext( 236 "%s: -[GIgied] and -[Pp] options are mutually exclusive\n"), 237 command); 238 usage(); 239 } 240 if (modify && npids != 0) { 241 (void) fprintf(stderr, gettext( 242 "%s: -[GIgied] options cannot have a process-id list\n"), 243 command); 244 usage(); 245 } 246 if ((proc_pattern != NULL || proc_content != CC_CONTENT_INVALID) && 247 npids == 0) { 248 (void) sprintf(curpid, "%u", (uint_t)getppid()); 249 npids = 1; 250 pidlist = &curpid_ptr; 251 } 252 253 if (legacy_update) 254 return (do_legacy()); 255 if (update) 256 return (do_update()); 257 if (modify) 258 return (do_modify(B_FALSE)); 259 if (npids != 0) 260 return (do_processes(npids, pidlist)); 261 262 return (report_settings()); 263 } 264 265 static int 266 report_settings(void) 267 { 268 char content_str[PRCONTENTBUFSZ]; 269 270 if ((options = core_get_options()) == -1) { 271 perror("core_get_options()"); 272 return (E_ERROR); 273 } 274 if (core_get_global_path(gpattern, sizeof (gpattern)) != 0) { 275 perror("core_get_global_path()"); 276 return (E_ERROR); 277 } 278 if (core_get_default_path(ipattern, sizeof (ipattern)) != 0) { 279 perror("core_get_default_path()"); 280 return (E_ERROR); 281 } 282 if (core_get_global_content(&glob_content) != 0) { 283 perror("core_get_global_content()"); 284 return (E_ERROR); 285 } 286 if (core_get_default_content(&init_content) != 0) { 287 perror("core_get_default_content()"); 288 return (E_ERROR); 289 } 290 291 (void) printf(gettext(" global core file pattern: %s\n"), 292 gpattern); 293 (void) proc_content2str(glob_content, content_str, 294 sizeof (content_str)); 295 (void) printf(gettext(" global core file content: %s\n"), 296 content_str); 297 (void) printf(gettext(" init core file pattern: %s\n"), 298 ipattern); 299 (void) proc_content2str(init_content, content_str, 300 sizeof (content_str)); 301 (void) printf(gettext(" init core file content: %s\n"), 302 content_str); 303 (void) printf(gettext(" global core dumps: %s\n"), 304 (options & CC_GLOBAL_PATH)? "enabled" : "disabled"); 305 (void) printf(gettext(" per-process core dumps: %s\n"), 306 (options & CC_PROCESS_PATH)? "enabled" : "disabled"); 307 (void) printf(gettext(" global setid core dumps: %s\n"), 308 (options & CC_GLOBAL_SETID)? "enabled" : "disabled"); 309 (void) printf(gettext(" per-process setid core dumps: %s\n"), 310 (options & CC_PROCESS_SETID)? "enabled" : "disabled"); 311 (void) printf(gettext(" global core dump logging: %s\n"), 312 (options & CC_GLOBAL_LOG)? "enabled" : "disabled"); 313 return (E_SUCCESS); 314 } 315 316 static int 317 do_processes(int npids, char **pidlist) 318 { 319 char process_path[PATH_MAX]; 320 core_content_t content; 321 pid_t pid; 322 char *next; 323 int rc = E_SUCCESS; 324 char content_str[PRCONTENTBUFSZ]; 325 326 if (proc_pattern == NULL && proc_content == CC_CONTENT_INVALID) { 327 while (npids-- > 0) { 328 pid = strtol(*pidlist, &next, 10); 329 if (*next != '\0' || !isdigit(**pidlist)) { 330 (void) fprintf(stderr, 331 gettext("%s: invalid process-id\n"), 332 *pidlist); 333 rc = E_USAGE; 334 } else if (core_get_process_path(process_path, 335 sizeof (process_path), pid) != 0 || 336 core_get_process_content(&content, pid) != 0) { 337 perror(*pidlist); 338 rc = E_USAGE; 339 } else { 340 (void) proc_content2str(content, content_str, 341 sizeof (content_str)); 342 (void) printf(gettext("%s:\t%s\t%s\n"), 343 *pidlist, process_path, content_str); 344 } 345 pidlist++; 346 } 347 } else { 348 while (npids-- > 0) { 349 pid = strtol(*pidlist, &next, 10); 350 if (*next != '\0') { 351 (void) fprintf(stderr, 352 gettext("%s: invalid process-id\n"), 353 *pidlist); 354 rc = E_USAGE; 355 } else { 356 if (proc_pattern != NULL && 357 core_set_process_path(proc_pattern, 358 proc_size, pid) != 0) { 359 perror(*pidlist); 360 rc = E_USAGE; 361 } 362 363 if (proc_content != CC_CONTENT_INVALID && 364 core_set_process_content( 365 &proc_content, pid) != 0) { 366 perror(*pidlist); 367 rc = E_USAGE; 368 } 369 } 370 pidlist++; 371 } 372 } 373 374 return (rc); 375 } 376 377 static void 378 addprop(scf_propvec_t *props, int size, int count, scf_propvec_t *pv, void *ptr) 379 { 380 assert(count + 1 < size); 381 props[count] = *pv; 382 props[count].pv_ptr = ptr; 383 } 384 385 static boolean_t 386 is_online(const char *fmri) 387 { 388 char *state = smf_get_state(fmri); 389 boolean_t result = state != NULL && 390 strcmp(state, SCF_STATE_STRING_ONLINE) == 0; 391 392 free(state); 393 return (result); 394 } 395 396 /* 397 * The user has specified the -g, -G, -i, -I, -d, or -e options to 398 * modify the given configuration parameter. Perform the modification 399 * in the smf repository and then perform a smf_refresh_instance which 400 * will cause a coreadm -u to occur which will transfer ALL coreadm 401 * configuration information from the repository to the kernel. 402 */ 403 static int 404 do_modify(boolean_t method) 405 { 406 char gcontentstr[PRCONTENTBUFSZ]; 407 char icontentstr[PRCONTENTBUFSZ]; 408 scf_propvec_t *prop; 409 scf_propvec_t properties[MAX_PROPS + 1]; 410 int count = 0; 411 412 if (!method && !is_online(COREADM_INST_FMRI)) { 413 (void) fprintf(stderr, 414 gettext("%s: coreadm service not online\n"), command); 415 return (E_ERROR); 416 } 417 418 if (glob_pattern != NULL) 419 addprop(properties, MAX_PROPS, count++, &prop_gpattern, 420 glob_pattern); 421 422 if (glob_content != CC_CONTENT_INVALID) { 423 (void) proc_content2str(glob_content, gcontentstr, 424 sizeof (gcontentstr)); 425 addprop(properties, MAX_PROPS, count++, &prop_gcontent, 426 gcontentstr); 427 } 428 429 if (init_pattern != NULL) 430 addprop(properties, MAX_PROPS, count++, &prop_ipattern, 431 init_pattern); 432 433 if (init_content != CC_CONTENT_INVALID) { 434 (void) proc_content2str(init_content, icontentstr, 435 sizeof (icontentstr)); 436 addprop(properties, MAX_PROPS, count++, &prop_icontent, 437 icontentstr); 438 } 439 440 for (prop = prop_option; prop->pv_prop != NULL; prop++) 441 if ((alloptions & prop->pv_aux) != 0) 442 addprop(properties, MAX_PROPS, count++, prop, &options); 443 444 properties[count].pv_prop = NULL; 445 446 prop = NULL; 447 if (scf_write_propvec(COREADM_INST_FMRI, CONFIG_PARAMS, properties, 448 &prop) == SCF_FAILED) { 449 if (prop != NULL) { 450 (void) fprintf(stderr, gettext( 451 "%s: Unable to write property '%s': %s"), command, 452 prop->pv_prop, scf_strerror(scf_error())); 453 } else { 454 (void) fprintf(stderr, gettext( 455 "%s: Unable to write configuration: %s\n"), 456 command, scf_strerror(scf_error())); 457 } 458 return (E_ERROR); 459 } 460 461 if (smf_refresh_instance(COREADM_INST_FMRI) != 0) { 462 (void) fprintf(stderr, 463 gettext("%s: Unable to refresh %s: %s\n" 464 "Configuration stored but not made active.\n"), 465 command, COREADM_INST_FMRI, scf_strerror(scf_error())); 466 return (E_ERROR); 467 } 468 469 return (E_SUCCESS); 470 } 471 472 static const char * 473 write_kernel(void) 474 { 475 if (core_set_global_path(glob_pattern, strlen(glob_pattern) + 1) != 0) 476 return ("core_set_global_path()"); 477 478 if (core_set_global_content(&glob_content) != 0) 479 return ("core_set_global_content()"); 480 481 if (core_set_default_path(init_pattern, strlen(init_pattern) + 1) != 0) 482 return ("core_set_default_path()"); 483 484 if (core_set_default_content(&init_content) != 0) 485 return ("core_set_init_content()"); 486 487 if (core_set_options((int)options) != 0) 488 return ("core_set_options()"); 489 490 return (NULL); 491 } 492 493 /* 494 * BUFSIZE must be large enough to contain the longest path plus some more. 495 */ 496 #define BUFSIZE (PATH_MAX + 80) 497 498 static int 499 yes(char *name, char *value, int line) 500 { 501 if (strcmp(value, "yes") == 0) 502 return (1); 503 if (strcmp(value, "no") == 0) 504 return (0); 505 (void) fprintf(stderr, gettext( 506 "\"%s\", line %d: warning: value must be yes or no: %s=%s\n"), 507 PATH_CONFIG, line, name, value); 508 return (0); 509 } 510 511 static int 512 read_legacy(void) 513 { 514 FILE *fp; 515 int line; 516 char buf[BUFSIZE]; 517 char name[BUFSIZE], value[BUFSIZE]; 518 int n, len; 519 520 /* defaults */ 521 alloptions = CC_OPTIONS; 522 options = CC_PROCESS_PATH; 523 gpattern[0] = '\0'; 524 (void) strcpy(ipattern, "core"); 525 glob_content = init_content = CC_CONTENT_DEFAULT; 526 527 glob_pattern = gpattern; 528 init_pattern = ipattern; 529 530 if ((fp = fopen(PATH_CONFIG, "r")) == NULL) 531 return (0); 532 533 for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) { 534 /* 535 * Skip comment lines and empty lines. 536 */ 537 if (buf[0] == '#' || buf[0] == '\n') 538 continue; 539 /* 540 * Look for "name=value", with optional whitespace on either 541 * side, terminated by a newline, and consuming the whole line. 542 */ 543 /* LINTED - unbounded string specifier */ 544 n = sscanf(buf, " %[^=]=%s \n%n", name, value, &len); 545 if (n >= 1 && name[0] != '\0' && 546 (n == 1 || len == strlen(buf))) { 547 if (n == 1) 548 value[0] = '\0'; 549 if (strcmp(name, "COREADM_GLOB_PATTERN") == 0) { 550 (void) strcpy(gpattern, value); 551 continue; 552 } 553 if (strcmp(name, "COREADM_GLOB_CONTENT") == 0) { 554 (void) proc_str2content(value, &glob_content); 555 continue; 556 } 557 if (strcmp(name, "COREADM_INIT_PATTERN") == 0) { 558 (void) strcpy(ipattern, value); 559 continue; 560 } 561 if (strcmp(name, "COREADM_INIT_CONTENT") == 0) { 562 (void) proc_str2content(value, &init_content); 563 continue; 564 } 565 if (strcmp(name, "COREADM_GLOB_ENABLED") == 0) { 566 if (yes(name, value, line)) 567 options |= CC_GLOBAL_PATH; 568 continue; 569 } 570 if (strcmp(name, "COREADM_PROC_ENABLED") == 0) { 571 if (yes(name, value, line)) 572 options |= CC_PROCESS_PATH; 573 else 574 options &= ~CC_PROCESS_PATH; 575 continue; 576 } 577 if (strcmp(name, "COREADM_GLOB_SETID_ENABLED") == 0) { 578 if (yes(name, value, line)) 579 options |= CC_GLOBAL_SETID; 580 continue; 581 } 582 if (strcmp(name, "COREADM_PROC_SETID_ENABLED") == 0) { 583 if (yes(name, value, line)) 584 options |= CC_PROCESS_SETID; 585 continue; 586 } 587 if (strcmp(name, "COREADM_GLOB_LOG_ENABLED") == 0) { 588 if (yes(name, value, line)) 589 options |= CC_GLOBAL_LOG; 590 continue; 591 } 592 (void) fprintf(stderr, gettext( 593 "\"%s\", line %d: warning: invalid token: %s\n"), 594 PATH_CONFIG, line, name); 595 } else { 596 (void) fprintf(stderr, 597 gettext("\"%s\", line %d: syntax error\n"), 598 PATH_CONFIG, line); 599 } 600 } 601 (void) fclose(fp); 602 603 return (1); 604 } 605 606 /* 607 * Loads and applies the coreadm configuration stored in the default 608 * coreadm instance. As this option is (only) used from within an SMF 609 * service method, this function must return an SMF_EXIT_* exit status 610 * to its caller. 611 */ 612 static int 613 do_update(void) 614 { 615 char *gcstr, *icstr; 616 scf_propvec_t properties[MAX_PROPS + 1]; 617 scf_propvec_t *prop; 618 int count = 0; 619 const char *errstr; 620 621 if (read_legacy()) { 622 if ((errstr = write_kernel()) != NULL) 623 goto error; 624 625 if (do_modify(B_TRUE) != 0 || 626 rename(PATH_CONFIG, PATH_CONFIG_OLD) != 0) { 627 (void) fprintf(stderr, gettext( 628 "%s: failed to import legacy configuration.\n"), 629 command); 630 return (SMF_EXIT_ERR_FATAL); 631 } 632 return (SMF_EXIT_OK); 633 } 634 635 addprop(properties, MAX_PROPS, count++, &prop_gpattern, &glob_pattern); 636 addprop(properties, MAX_PROPS, count++, &prop_gcontent, &gcstr); 637 addprop(properties, MAX_PROPS, count++, &prop_ipattern, &init_pattern); 638 addprop(properties, MAX_PROPS, count++, &prop_icontent, &icstr); 639 for (prop = prop_option; prop->pv_prop != NULL; prop++) 640 addprop(properties, MAX_PROPS, count++, prop, &options); 641 properties[count].pv_prop = NULL; 642 643 alloptions = CC_OPTIONS; 644 if (scf_read_propvec(COREADM_INST_FMRI, CONFIG_PARAMS, B_TRUE, 645 properties, &prop) == SCF_FAILED) { 646 if (prop != NULL) { 647 (void) fprintf(stderr, gettext( 648 "%s: configuration property '%s' not found.\n"), 649 command, prop->pv_prop); 650 } else { 651 (void) fprintf(stderr, gettext( 652 "%s: unable to read configuration: %s\n"), 653 command, scf_strerror(scf_error())); 654 } 655 return (SMF_EXIT_ERR_FATAL); 656 } 657 658 (void) proc_str2content(gcstr, &glob_content); 659 (void) proc_str2content(icstr, &init_content); 660 661 errstr = write_kernel(); 662 scf_clean_propvec(properties); 663 if (errstr == NULL) 664 return (SMF_EXIT_OK); 665 666 error: 667 if (errno == EPERM) { 668 (void) perm(); 669 return (SMF_EXIT_ERR_PERM); 670 } 671 perror(errstr); 672 return (SMF_EXIT_ERR_FATAL); 673 } 674 675 static int do_legacy() 676 { 677 const char *errstr; 678 679 if (read_legacy() && (errstr = write_kernel()) != NULL) { 680 if (errno == EPERM) 681 return (perm()); 682 perror(errstr); 683 return (E_ERROR); 684 } 685 686 return (E_SUCCESS); 687 } 688