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