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 2004 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 #include <stdio.h> 30 #include <fcntl.h> 31 #include <ctype.h> 32 #include <string.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <errno.h> 36 #include <limits.h> 37 #include <libintl.h> 38 #include <locale.h> 39 #include <sys/stat.h> 40 #include <sys/corectl.h> 41 #include <libproc.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 #define CF_OWNER 0 /* Uid 0 (root) */ 49 #define CF_GROUP 1 /* Gid 1 (other) */ 50 #define CF_PERM (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) /* Mode 0644 */ 51 52 static char *command; 53 static char *glob_pattern; 54 static size_t glob_size; 55 static core_content_t glob_content = CC_CONTENT_INVALID; 56 static char *init_pattern; 57 static size_t init_size; 58 static core_content_t init_content = CC_CONTENT_INVALID; 59 static char *proc_pattern; 60 static size_t proc_size; 61 static core_content_t proc_content = CC_CONTENT_INVALID; 62 static int enable; 63 static int disable; 64 65 static int report_settings(void); 66 static int do_processes(int, char **); 67 static int do_modify(void); 68 static int do_update(void); 69 static int write_config(int); 70 71 static void 72 usage(void) 73 { 74 (void) fprintf(stderr, gettext( 75 "usage:\n")); 76 (void) fprintf(stderr, gettext( 77 " %s [ -g pattern ] [ -i pattern ] [ -G content ] [ -I content ]\n"), 78 command); 79 (void) fprintf(stderr, gettext( 80 " [ -e {global | process | global-setid | proc-setid | log} ]\n")); 81 (void) fprintf(stderr, gettext( 82 " [ -d {global | process | global-setid | proc-setid | log} ]\n")); 83 (void) fprintf(stderr, gettext( 84 " %s [ -p pattern ] [ -P content ] [ pid ... ]\n"), command); 85 (void) fprintf(stderr, gettext( 86 " %s -u\n"), command); 87 exit(E_USAGE); 88 } 89 90 static int 91 perm(void) 92 { 93 (void) fprintf(stderr, gettext("%s: insufficient privileges to " 94 "exercise the -[GIgiedu] options\n"), command); 95 return (E_USAGE); 96 } 97 98 int 99 main(int argc, char **argv) 100 { 101 int flag; 102 int opt; 103 int modify; 104 int update = 0; 105 int error = 0; 106 int npids; 107 char **pidlist; 108 109 char curpid[11]; 110 char *curpid_ptr = &curpid[0]; 111 112 (void) setlocale(LC_ALL, ""); 113 (void) textdomain(TEXT_DOMAIN); 114 115 /* command name (e.g., "coreadm") */ 116 if ((command = strrchr(argv[0], '/')) != NULL) 117 command++; 118 else 119 command = argv[0]; 120 121 while ((opt = getopt(argc, argv, "g:G:i:I:p:P:e:d:u?")) != EOF) { 122 switch (opt) { 123 case 'g': 124 glob_pattern = optarg; 125 glob_size = strlen(glob_pattern) + 1; 126 break; 127 case 'i': 128 init_pattern = optarg; 129 init_size = strlen(init_pattern) + 1; 130 break; 131 case 'p': 132 proc_pattern = optarg; 133 proc_size = strlen(proc_pattern) + 1; 134 break; 135 case 'G': 136 if (proc_str2content(optarg, &glob_content) != 0) { 137 (void) fprintf(stderr, gettext("invalid " 138 "content string '%s'\n"), optarg); 139 error = 1; 140 } 141 break; 142 case 'I': 143 if (proc_str2content(optarg, &init_content) != 0) { 144 (void) fprintf(stderr, gettext("invalid " 145 "content string '%s'\n"), optarg); 146 error = 1; 147 } 148 break; 149 case 'P': 150 if (proc_str2content(optarg, &proc_content) != 0) { 151 (void) fprintf(stderr, gettext("invalid " 152 "content string '%s'\n"), optarg); 153 error = 1; 154 } 155 break; 156 case 'e': 157 case 'd': 158 if (strcmp(optarg, "global") == 0) 159 flag = CC_GLOBAL_PATH; 160 else if (strcmp(optarg, "process") == 0) 161 flag = CC_PROCESS_PATH; 162 else if (strcmp(optarg, "global-setid") == 0) 163 flag = CC_GLOBAL_SETID; 164 else if (strcmp(optarg, "proc-setid") == 0) 165 flag = CC_PROCESS_SETID; 166 else if (strcmp(optarg, "log") == 0) 167 flag = CC_GLOBAL_LOG; 168 else { 169 flag = 0; 170 error = 1; 171 } 172 if (opt == 'e') { 173 enable |= flag; 174 disable &= ~flag; 175 } else { 176 disable |= flag; 177 enable &= ~flag; 178 } 179 break; 180 case 'u': 181 update = 1; 182 break; 183 case '?': 184 default: 185 error = 1; 186 break; 187 } 188 } 189 190 npids = argc - optind; 191 pidlist = argv + optind; 192 193 if (error) 194 usage(); 195 196 /* 197 * If 'modify' is true, we must modify the system settings 198 * and update the configuration file with the new parameters. 199 */ 200 modify = glob_pattern != NULL || glob_content != CC_CONTENT_INVALID || 201 init_pattern != NULL || init_content != CC_CONTENT_INVALID || 202 (enable | disable) != 0; 203 204 if (update && (modify || proc_pattern != NULL || 205 proc_content != CC_CONTENT_INVALID || npids != 0)) { 206 (void) fprintf(stderr, 207 gettext( 208 "%s: the -u option must stand alone\n"), 209 command); 210 usage(); 211 } 212 if (modify && 213 (proc_pattern != NULL || proc_content != CC_CONTENT_INVALID)) { 214 (void) fprintf(stderr, 215 gettext( 216 "%s: -[GIgied] and -[Pp] options are mutually exclusive\n"), 217 command); 218 usage(); 219 } 220 if (modify && npids != 0) { 221 (void) fprintf(stderr, 222 gettext( 223 "%s: -[GIgied] options cannot have a process-id list\n"), 224 command); 225 usage(); 226 } 227 if ((proc_pattern != NULL || proc_content != CC_CONTENT_INVALID) && 228 npids == 0) { 229 (void) sprintf(curpid, "%u", (uint_t)getppid()); 230 npids = 1; 231 pidlist = &curpid_ptr; 232 } 233 234 if (update) 235 return (do_update()); 236 if (modify) 237 return (do_modify()); 238 if (npids != 0) 239 return (do_processes(npids, pidlist)); 240 241 return (report_settings()); 242 } 243 244 static int 245 report_settings(void) 246 { 247 int options; 248 char global_path[PATH_MAX]; 249 char init_path[PATH_MAX]; 250 core_content_t gcontent, icontent; 251 char content_str[80]; 252 253 if ((options = core_get_options()) == -1) { 254 perror("core_get_options()"); 255 return (E_ERROR); 256 } 257 if (core_get_global_path(global_path, sizeof (global_path)) != 0) { 258 perror("core_get_global_path()"); 259 return (E_ERROR); 260 } 261 if (core_get_default_path(init_path, sizeof (init_path)) != 0) { 262 perror("core_get_default_path()"); 263 return (E_ERROR); 264 } 265 if (core_get_global_content(&gcontent) != 0) { 266 perror("core_get_global_content()"); 267 return (E_ERROR); 268 } 269 if (core_get_default_content(&icontent) != 0) { 270 perror("core_get_default_content()"); 271 return (E_ERROR); 272 } 273 (void) printf(gettext(" global core file pattern: %s\n"), 274 global_path); 275 (void) proc_content2str(gcontent, content_str, sizeof (content_str)); 276 (void) printf(gettext(" global core file content: %s\n"), 277 content_str); 278 (void) printf(gettext(" init core file pattern: %s\n"), 279 init_path); 280 (void) proc_content2str(icontent, content_str, sizeof (content_str)); 281 (void) printf(gettext(" init core file content: %s\n"), 282 content_str); 283 (void) printf(gettext(" global core dumps: %s\n"), 284 (options & CC_GLOBAL_PATH)? "enabled" : "disabled"); 285 (void) printf(gettext(" per-process core dumps: %s\n"), 286 (options & CC_PROCESS_PATH)? "enabled" : "disabled"); 287 (void) printf(gettext(" global setid core dumps: %s\n"), 288 (options & CC_GLOBAL_SETID)? "enabled" : "disabled"); 289 (void) printf(gettext(" per-process setid core dumps: %s\n"), 290 (options & CC_PROCESS_SETID)? "enabled" : "disabled"); 291 (void) printf(gettext(" global core dump logging: %s\n"), 292 (options & CC_GLOBAL_LOG)? "enabled" : "disabled"); 293 return (E_SUCCESS); 294 } 295 296 static int 297 do_processes(int npids, char **pidlist) 298 { 299 char process_path[PATH_MAX]; 300 core_content_t content; 301 pid_t pid; 302 char *next; 303 int rc = E_SUCCESS; 304 char content_str[80]; 305 306 if (proc_pattern == NULL && proc_content == CC_CONTENT_INVALID) { 307 while (npids-- > 0) { 308 pid = strtol(*pidlist, &next, 10); 309 if (*next != '\0' || !isdigit(**pidlist)) { 310 (void) fprintf(stderr, 311 gettext("%s: invalid process-id\n"), 312 *pidlist); 313 rc = E_USAGE; 314 } else if (core_get_process_path(process_path, 315 sizeof (process_path), pid) != 0 || 316 core_get_process_content(&content, pid) != 0) { 317 perror(*pidlist); 318 rc = E_USAGE; 319 } else { 320 (void) proc_content2str(content, content_str, 321 sizeof (content_str)); 322 (void) printf(gettext("%s:\t%s\t%s\n"), 323 *pidlist, process_path, content_str); 324 } 325 pidlist++; 326 } 327 } else { 328 while (npids-- > 0) { 329 pid = strtol(*pidlist, &next, 10); 330 if (*next != '\0') { 331 (void) fprintf(stderr, 332 gettext("%s: invalid process-id\n"), 333 *pidlist); 334 rc = E_USAGE; 335 } else { 336 if (proc_pattern != NULL && 337 core_set_process_path(proc_pattern, 338 proc_size, pid) != 0) { 339 perror(*pidlist); 340 rc = E_USAGE; 341 } 342 343 if (proc_content != CC_CONTENT_INVALID && 344 core_set_process_content( 345 &proc_content, pid) != 0) { 346 perror(*pidlist); 347 rc = E_USAGE; 348 } 349 } 350 pidlist++; 351 } 352 } 353 354 return (rc); 355 } 356 357 static int 358 do_modify(void) 359 { 360 int options; 361 362 if ((options = core_get_options()) == -1) { 363 perror("core_get_options()"); 364 return (E_ERROR); 365 } 366 options |= enable; 367 options &= ~disable; 368 if (core_set_options(options) != 0) { 369 if (errno == EPERM) 370 return (perm()); 371 perror("core_set_options()"); 372 return (E_ERROR); 373 } 374 if (glob_pattern != NULL && 375 core_set_global_path(glob_pattern, glob_size) != 0) { 376 if (errno == EPERM) 377 return (perm()); 378 perror("core_set_global_path()"); 379 return (E_ERROR); 380 } 381 if (glob_content != CC_CONTENT_INVALID && 382 core_set_global_content(&glob_content) != 0) { 383 if (errno == EPERM) 384 return (perm()); 385 perror("core_set_global_content()"); 386 return (E_ERROR); 387 } 388 if (init_pattern != NULL && 389 core_set_default_path(init_pattern, init_size) != 0) { 390 if (errno == EPERM) 391 return (perm()); 392 perror("core_set_default_path()"); 393 return (E_ERROR); 394 } 395 if (init_content != CC_CONTENT_INVALID && 396 core_set_default_content(&init_content) != 0) { 397 if (errno == EPERM) 398 return (perm()); 399 perror("core_set_default_content()"); 400 return (E_ERROR); 401 } 402 return (write_config(0)); 403 } 404 405 /* 406 * BUFSIZE must be large enough to contain the longest path plus some more. 407 */ 408 #define BUFSIZE (PATH_MAX + 80) 409 410 static int 411 yes(char *name, char *value, int line) 412 { 413 if (strcmp(value, "yes") == 0) 414 return (1); 415 if (strcmp(value, "no") == 0) 416 return (0); 417 (void) fprintf(stderr, 418 gettext( 419 "\"%s\", line %d: warning: value must be yes or no: %s=%s\n"), 420 PATH_CONFIG, line, name, value); 421 return (0); 422 } 423 424 static int 425 do_update(void) 426 { 427 FILE *fp; 428 int line; 429 int options; 430 char gpattern[PATH_MAX]; 431 char ipattern[PATH_MAX]; 432 core_content_t gcontent, icontent; 433 char buf[BUFSIZE]; 434 char name[BUFSIZE], value[BUFSIZE]; 435 int n; 436 int len; 437 438 /* defaults */ 439 options = CC_PROCESS_PATH; 440 gpattern[0] = '\0'; 441 (void) strcpy(ipattern, "core"); 442 gcontent = icontent = CC_CONTENT_DEFAULT; 443 444 if ((fp = fopen(PATH_CONFIG, "r")) == NULL) { 445 /* 446 * No config file, just accept the current settings. 447 */ 448 return (write_config(1)); 449 } 450 451 for (line = 1; fgets(buf, sizeof (buf), fp) != NULL; line++) { 452 /* 453 * Skip comment lines and empty lines. 454 */ 455 if (buf[0] == '#' || buf[0] == '\n') 456 continue; 457 /* 458 * Look for "name=value", with optional whitespace on either 459 * side, terminated by a newline, and consuming the whole line. 460 */ 461 /* LINTED - unbounded string specifier */ 462 n = sscanf(buf, " %[^=]=%s \n%n", name, value, &len); 463 if (n >= 1 && name[0] != '\0' && 464 (n == 1 || len == strlen(buf))) { 465 if (n == 1) 466 value[0] = '\0'; 467 if (strcmp(name, "COREADM_GLOB_PATTERN") == 0) { 468 (void) strcpy(gpattern, value); 469 continue; 470 } 471 if (strcmp(name, "COREADM_GLOB_CONTENT") == 0) { 472 (void) proc_str2content(value, &gcontent); 473 continue; 474 } 475 if (strcmp(name, "COREADM_INIT_PATTERN") == 0) { 476 (void) strcpy(ipattern, value); 477 continue; 478 } 479 if (strcmp(name, "COREADM_INIT_CONTENT") == 0) { 480 (void) proc_str2content(value, &icontent); 481 continue; 482 } 483 if (strcmp(name, "COREADM_GLOB_ENABLED") == 0) { 484 if (yes(name, value, line)) 485 options |= CC_GLOBAL_PATH; 486 continue; 487 } 488 if (strcmp(name, "COREADM_PROC_ENABLED") == 0) { 489 if (yes(name, value, line)) 490 options |= CC_PROCESS_PATH; 491 else 492 options &= ~CC_PROCESS_PATH; 493 continue; 494 } 495 if (strcmp(name, "COREADM_GLOB_SETID_ENABLED") == 0) { 496 if (yes(name, value, line)) 497 options |= CC_GLOBAL_SETID; 498 continue; 499 } 500 if (strcmp(name, "COREADM_PROC_SETID_ENABLED") == 0) { 501 if (yes(name, value, line)) 502 options |= CC_PROCESS_SETID; 503 continue; 504 } 505 if (strcmp(name, "COREADM_GLOB_LOG_ENABLED") == 0) { 506 if (yes(name, value, line)) 507 options |= CC_GLOBAL_LOG; 508 continue; 509 } 510 (void) fprintf(stderr, 511 gettext( 512 "\"%s\", line %d: warning: invalid token: %s\n"), 513 PATH_CONFIG, line, name); 514 } else { 515 (void) fprintf(stderr, 516 gettext("\"%s\", line %d: syntax error\n"), 517 PATH_CONFIG, line); 518 } 519 } 520 (void) fclose(fp); 521 if (core_set_options(options) != 0) { 522 if (errno == EPERM) 523 return (perm()); 524 perror("core_set_options()"); 525 return (E_ERROR); 526 } 527 if (core_set_global_path(gpattern, strlen(gpattern) + 1) != 0) { 528 if (errno == EPERM) 529 return (perm()); 530 perror("core_set_global_path()"); 531 return (E_ERROR); 532 } 533 if (core_set_default_path(ipattern, strlen(ipattern) + 1) != 0) { 534 if (errno == EPERM) 535 return (perm()); 536 perror("core_set_default_path()"); 537 return (E_ERROR); 538 } 539 if (core_set_global_content(&gcontent) != 0) { 540 if (errno == EPERM) 541 return (perm()); 542 perror("core_set_global_content()"); 543 return (E_ERROR); 544 } 545 if (core_set_default_content(&icontent) != 0) { 546 if (errno == EPERM) 547 return (perm()); 548 perror("core_set_default_content()"); 549 return (E_ERROR); 550 } 551 return (write_config(1)); 552 } 553 554 static int 555 write_config(int justtry) 556 { 557 int fd; 558 FILE *fp; 559 int options; 560 char global_path[PATH_MAX]; 561 char init_path[PATH_MAX]; 562 core_content_t gcontent, icontent; 563 char content_str[PRCONTENTBUFSZ]; 564 565 if ((fd = open(PATH_CONFIG, O_WRONLY | O_CREAT | O_TRUNC, 566 CF_PERM)) == -1) { 567 /* 568 * If we're updating the kernel settings from the contents 569 * of the config file, it's not essential that we rewrite 570 * that file. 571 */ 572 if (justtry) 573 return (E_SUCCESS); 574 575 if (errno == EACCES) { 576 (void) fprintf(stderr, gettext("%s: insufficient " 577 "privileges to update %s\n"), command, PATH_CONFIG); 578 return (E_SUCCESS); 579 } 580 581 (void) fprintf(stderr, gettext("failed to open %s: %s\n"), 582 PATH_CONFIG, strerror(errno)); 583 return (E_ERROR); 584 } 585 if ((options = core_get_options()) == -1) { 586 perror("core_get_options()"); 587 goto err; 588 } 589 if (core_get_global_path(global_path, sizeof (global_path)) != 0) { 590 perror("core_get_global_path()"); 591 goto err; 592 } 593 if (core_get_default_path(init_path, sizeof (init_path)) != 0) { 594 perror("core_get_default_path()"); 595 goto err; 596 } 597 if (core_get_global_content(&gcontent) != 0) { 598 perror("core_get_global_content()"); 599 goto err; 600 } 601 if (core_get_default_content(&icontent) != 0) { 602 perror("core_get_default_content()"); 603 goto err; 604 } 605 if ((fp = fdopen(fd, "w")) == NULL) { 606 (void) fprintf(stderr, 607 gettext("failed to open stream for %s: %s\n"), 608 PATH_CONFIG, strerror(errno)); 609 goto err; 610 } 611 (void) fputs( 612 "#\n" 613 "# coreadm.conf\n" 614 "#\n" 615 "# Parameters for system core file configuration.\n" 616 "# Do NOT edit this file by hand -- use coreadm(1) instead.\n" 617 "#\n", 618 fp); 619 620 (void) fprintf(fp, "COREADM_GLOB_PATTERN=%s\n", global_path); 621 (void) proc_content2str(gcontent, content_str, sizeof (content_str)); 622 (void) fprintf(fp, "COREADM_GLOB_CONTENT=%s\n", content_str); 623 (void) fprintf(fp, "COREADM_INIT_PATTERN=%s\n", init_path); 624 (void) proc_content2str(icontent, content_str, sizeof (content_str)); 625 (void) fprintf(fp, "COREADM_INIT_CONTENT=%s\n", content_str); 626 627 (void) fprintf(fp, "COREADM_GLOB_ENABLED=%s\n", 628 (options & CC_GLOBAL_PATH)? "yes" : "no"); 629 (void) fprintf(fp, "COREADM_PROC_ENABLED=%s\n", 630 (options & CC_PROCESS_PATH)? "yes" : "no"); 631 (void) fprintf(fp, "COREADM_GLOB_SETID_ENABLED=%s\n", 632 (options & CC_GLOBAL_SETID)? "yes" : "no"); 633 (void) fprintf(fp, "COREADM_PROC_SETID_ENABLED=%s\n", 634 (options & CC_PROCESS_SETID)? "yes" : "no"); 635 (void) fprintf(fp, "COREADM_GLOB_LOG_ENABLED=%s\n", 636 (options & CC_GLOBAL_LOG)? "yes" : "no"); 637 638 (void) fflush(fp); 639 (void) fsync(fd); 640 (void) fchmod(fd, CF_PERM); 641 (void) fchown(fd, CF_OWNER, CF_GROUP); 642 (void) fclose(fp); 643 644 return (0); 645 646 err: 647 (void) close(fd); 648 return (E_ERROR); 649 } 650