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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include "pmconfig.h" 29 #include <sys/mkdev.h> 30 #include <sys/syslog.h> 31 #include <sys/openpromio.h> 32 #include <sys/mnttab.h> 33 #include <syslog.h> 34 #include <stdlib.h> 35 36 37 #define STRCPYLIM(dst, src, str) strcpy_limit(dst, src, sizeof (dst), str) 38 #define LASTBYTE(str) (str + strlen(str) - 1) 39 40 static char nerr_fmt[] = "number is out of range (%s)\n"; 41 static char alloc_fmt[] = "cannot allocate space for \"%s\", %s\n"; 42 static char set_thresh_fmt[] = "error setting threshold(s) for \"%s\", %s\n"; 43 static char bad_thresh_fmt[] = "bad threshold(s)\n"; 44 static char stat_fmt[] = "cannot stat \"%s\", %s\n"; 45 static char always_on[] = "always-on"; 46 47 /* 48 * When lines in a config file (usually "/etc/power.conf") start with 49 * a recognized keyword, a "handler" routine is called for specific 50 * CPR or PM -related action(s). Each routine returns a status code 51 * indicating whether all tasks were successful; if any errors occured, 52 * future CPR or PM updates are skipped. Following are the handler 53 * routines for all keywords: 54 */ 55 56 57 /* 58 * Check for valid cpupm behavior and communicate it to the kernel. 59 */ 60 int 61 cpupm(void) 62 { 63 struct btoc { 64 char *behavior; 65 int cmd; 66 int Errno; 67 }; 68 static struct btoc blist[] = { 69 "disable", PM_STOP_CPUPM, EINVAL, 70 "enable", PM_START_CPUPM, EBUSY, 71 NULL, 0, 0 72 }; 73 struct btoc *bp; 74 char *behavior; 75 76 for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) { 77 if (strcmp(behavior, bp->behavior) == 0) 78 break; 79 } 80 if (bp->cmd == 0) { 81 mesg(MERR, "invalid cpupm behavior \"%s\"\n", behavior); 82 return (NOUP); 83 } 84 if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) { 85 mesg(MERR, "cpupm %s failed, %s\n", 86 behavior, strerror(errno)); 87 return (NOUP); 88 } 89 return (OKUP); 90 } 91 92 93 /* 94 * Check for valid autopm behavior and save after ioctl success. 95 */ 96 int 97 autopm(void) 98 { 99 struct btoc { 100 char *behavior; 101 int cmd, Errno, isdef; 102 }; 103 static struct btoc blist[] = { 104 "default", PM_START_PM, EBUSY, 1, 105 "disable", PM_STOP_PM, EINVAL, 0, 106 "enable", PM_START_PM, EBUSY, 0, 107 NULL, 0, 0, 0, 108 }; 109 struct btoc *bp; 110 char *behavior; 111 112 for (behavior = LINEARG(1), bp = blist; bp->cmd; bp++) { 113 if (strcmp(behavior, bp->behavior) == 0) 114 break; 115 } 116 if (bp->cmd == 0) { 117 mesg(MERR, "invalid autopm behavior \"%s\"\n", behavior); 118 return (NOUP); 119 } 120 121 /* 122 * for "default" behavior, do not enable autopm if not ESTAR_V3 123 */ 124 if (!bp->isdef || (estar_vers == ESTAR_V3)) { 125 if (ioctl(pm_fd, bp->cmd, NULL) == -1 && errno != bp->Errno) { 126 mesg(MERR, "autopm %s failed, %s\n", 127 behavior, strerror(errno)); 128 return (NOUP); 129 } 130 } 131 (void) strcpy(new_cc.apm_behavior, behavior); 132 return (OKUP); 133 } 134 135 136 static int 137 gethm(char *src, int *hour, int *min) 138 { 139 if (sscanf(src, "%d:%d", hour, min) != 2) { 140 mesg(MERR, "bad time format (%s)\n", src); 141 return (-1); 142 } 143 return (0); 144 } 145 146 147 static void 148 strcpy_limit(char *dst, char *src, size_t limit, char *info) 149 { 150 if (strlcpy(dst, src, limit) >= limit) 151 mesg(MEXIT, "%s is too long (%s)\n", info, src); 152 } 153 154 155 /* 156 * Convert autoshutdown idle and start/finish times; 157 * check and record autoshutdown behavior. 158 */ 159 int 160 autosd(void) 161 { 162 char **bp, *behavior; 163 char *unrec = gettext("unrecognized autoshutdown behavior"); 164 static char *blist[] = { 165 "autowakeup", "default", "noshutdown", 166 "shutdown", "unconfigured", NULL 167 }; 168 169 new_cc.as_idle = atoi(LINEARG(1)); 170 if (gethm(LINEARG(2), &new_cc.as_sh, &new_cc.as_sm) || 171 gethm(LINEARG(3), &new_cc.as_fh, &new_cc.as_fm)) 172 return (NOUP); 173 mesg(MDEBUG, "idle %d, start %d:%02d, finish %d:%02d\n", 174 new_cc.as_idle, new_cc.as_sh, new_cc.as_sm, 175 new_cc.as_fh, new_cc.as_fm); 176 177 for (behavior = LINEARG(4), bp = blist; *bp; bp++) { 178 if (strcmp(behavior, *bp) == 0) 179 break; 180 } 181 if (*bp == NULL) { 182 mesg(MERR, "%s: \"%s\"\n", unrec, behavior); 183 return (NOUP); 184 } 185 STRCPYLIM(new_cc.as_behavior, *bp, unrec); 186 return (OKUP); 187 } 188 189 190 /* 191 * Check for a real device and try to resolve to a full path. 192 * The orig/resolved path may be modified into a prom pathname, 193 * and an allocated copy of the result is stored at *destp; 194 * the caller will need to free that space. Returns 1 for any 195 * error, otherwise 0; also sets *errp after an alloc error. 196 */ 197 static int 198 devpath(char **destp, char *src, int *errp) 199 { 200 struct stat stbuf; 201 char buf[PATH_MAX]; 202 char *cp, *dstr; 203 int devok, dcs = 0; 204 size_t len; 205 206 /* 207 * When there's a real device, try to resolve the path 208 * and trim the leading "/devices" component. 209 */ 210 if ((devok = (stat(src, &stbuf) == 0 && stbuf.st_rdev)) != 0) { 211 if (realpath(src, buf) == NULL) { 212 mesg(MERR, "realpath cannot resolve \"%s\"\n", 213 src, strerror(errno)); 214 return (1); 215 } 216 src = buf; 217 dstr = "/devices"; 218 len = strlen(dstr); 219 dcs = (strncmp(src, dstr, len) == 0); 220 if (dcs) 221 src += len; 222 } else 223 mesg(MDEBUG, stat_fmt, src, strerror(errno)); 224 225 /* 226 * When the path has ":anything", display an error for 227 * a non-device or truncate a resolved+modifed path. 228 */ 229 if (cp = strchr(src, ':')) { 230 if (devok == 0) { 231 mesg(MERR, "physical path may not contain " 232 "a minor string (%s)\n", src); 233 return (1); 234 } else if (dcs) 235 *cp = '\0'; 236 } 237 238 if ((*destp = strdup(src)) == NULL) { 239 *errp = NOUP; 240 mesg(MERR, alloc_fmt, src, strerror(errno)); 241 } 242 return (*destp == NULL); 243 } 244 245 246 /* 247 * Call pm ioctl request(s) to set property/device dependencies. 248 */ 249 static int 250 dev_dep_common(int isprop) 251 { 252 int cmd, argn, upval = OKUP; 253 char *src, *first, **destp; 254 pm_req_t pmreq; 255 256 bzero(&pmreq, sizeof (pmreq)); 257 src = LINEARG(1); 258 if (isprop) { 259 cmd = PM_ADD_DEPENDENT_PROPERTY; 260 first = NULL; 261 pmreq.pmreq_kept = src; 262 } else { 263 cmd = PM_ADD_DEPENDENT; 264 if (devpath(&first, src, &upval)) 265 return (upval); 266 pmreq.pmreq_kept = first; 267 } 268 destp = &pmreq.pmreq_keeper; 269 270 /* 271 * Now loop through any dependents. 272 */ 273 for (argn = 2; (src = LINEARG(argn)) != NULL; argn++) { 274 if (devpath(destp, src, &upval)) { 275 if (upval != OKUP) 276 return (upval); 277 break; 278 } 279 if ((upval = ioctl(pm_fd, cmd, &pmreq)) == -1) { 280 mesg(MDEBUG, "pm ioctl, cmd %d, errno %d\n" 281 "kept \"%s\", keeper \"%s\"\n", 282 cmd, errno, pmreq.pmreq_kept, pmreq.pmreq_keeper); 283 mesg(MERR, "cannot set \"%s\" dependency " 284 "for \"%s\", %s\n", pmreq.pmreq_keeper, 285 pmreq.pmreq_kept, strerror(errno)); 286 } 287 free(*destp); 288 *destp = NULL; 289 if (upval != OKUP) 290 break; 291 } 292 293 free(first); 294 return (upval); 295 } 296 297 298 int 299 ddprop(void) 300 { 301 return (dev_dep_common(1)); 302 } 303 304 305 int 306 devdep(void) 307 { 308 return (dev_dep_common(0)); 309 } 310 311 312 /* 313 * Convert a numeric string (with a possible trailing scaling byte) 314 * into an integer. Returns a converted value and *nerrp unchanged, 315 * or 0 with *nerrp set to 1 for a conversion error. 316 */ 317 static int 318 get_scaled_value(char *str, int *nerrp) 319 { 320 longlong_t svalue = 0, factor = 1; 321 char *sp; 322 323 errno = 0; 324 svalue = strtol(str, &sp, 0); 325 if (errno || (*str != '-' && (*str < '0' || *str > '9'))) 326 *nerrp = 1; 327 else if (sp && *sp != '\0') { 328 if (*sp == 'h') 329 factor = 3600; 330 else if (*sp == 'm') 331 factor = 60; 332 else if (*sp != 's') 333 *nerrp = 1; 334 } 335 /* any bytes following sp are ignored */ 336 337 if (*nerrp == 0) { 338 svalue *= factor; 339 if (svalue < INT_MIN || svalue > INT_MAX) 340 *nerrp = 1; 341 } 342 if (*nerrp) 343 mesg(MERR, nerr_fmt, str); 344 mesg(MDEBUG, "got scaled value %d\n", (int)svalue); 345 return ((int)svalue); 346 } 347 348 349 /* 350 * Increment the count of threshold values, 351 * reallocate *vlistp and append another element. 352 * Returns 1 on error, otherwise 0. 353 */ 354 static int 355 vlist_append(int **vlistp, int *vcntp, int value) 356 { 357 (*vcntp)++; 358 if (*vlistp = realloc(*vlistp, *vcntp * sizeof (**vlistp))) 359 *(*vlistp + *vcntp - 1) = value; 360 else 361 mesg(MERR, alloc_fmt, "threshold list", strerror(errno)); 362 return (*vlistp == NULL); 363 } 364 365 366 /* 367 * Convert a single threshold string or paren groups of thresh's as 368 * described below. All thresh's are saved to an allocated list at 369 * *vlistp; the caller will need to free that space. On return: 370 * *vcntp is the count of the vlist array, and vlist is either 371 * a single thresh or N groups of thresh's with a trailing zero: 372 * (cnt_1 thr_1a thr_1b [...]) ... (cnt_N thr_Na thr_Nb [...]) 0. 373 * Returns 0 when all conversions were OK, and 1 for any syntax, 374 * conversion, or alloc error. 375 */ 376 static int 377 get_thresh(int **vlistp, int *vcntp) 378 { 379 int argn, value, gci, grp_cnt = 0, paren = 0, nerr = 0; 380 char *rp, *src; 381 382 for (argn = 2; (src = LINEARG(argn)) != NULL; argn++) { 383 if (*src == LPAREN) { 384 gci = *vcntp; 385 if (nerr = vlist_append(vlistp, vcntp, 0)) 386 break; 387 paren = 1; 388 src++; 389 } 390 if (*(rp = LASTBYTE(src)) == RPAREN) { 391 if (paren) { 392 grp_cnt = *vcntp - gci; 393 *(*vlistp + gci) = grp_cnt; 394 paren = 0; 395 *rp = '\0'; 396 } else { 397 nerr = 1; 398 break; 399 } 400 } 401 402 value = get_scaled_value(src, &nerr); 403 if (nerr || (nerr = vlist_append(vlistp, vcntp, value))) 404 break; 405 } 406 407 if (nerr == 0 && grp_cnt) 408 nerr = vlist_append(vlistp, vcntp, 0); 409 return (nerr); 410 } 411 412 413 /* 414 * Set device thresholds from (3) formats: 415 * path "always-on" 416 * path time-spec: [0-9]+[{h,m,s}] 417 * path (ts1 ts2 ...)+ 418 */ 419 int 420 devthr(void) 421 { 422 int cmd, upval = OKUP, nthresh = 0, *vlist = NULL; 423 pm_req_t pmreq; 424 425 bzero(&pmreq, sizeof (pmreq)); 426 if (devpath(&pmreq.physpath, LINEARG(1), &upval)) 427 return (upval); 428 429 if (strcmp(LINEARG(2), always_on) == 0) { 430 cmd = PM_SET_DEVICE_THRESHOLD; 431 pmreq.value = INT_MAX; 432 } else if (get_thresh(&vlist, &nthresh)) { 433 mesg(MERR, bad_thresh_fmt); 434 upval = NOUP; 435 } else if (nthresh == 1) { 436 pmreq.value = *vlist; 437 cmd = PM_SET_DEVICE_THRESHOLD; 438 } else { 439 pmreq.data = vlist; 440 pmreq.datasize = (nthresh * sizeof (*vlist)); 441 cmd = PM_SET_COMPONENT_THRESHOLDS; 442 } 443 444 if (upval != NOUP && (upval = ioctl(pm_fd, cmd, &pmreq)) == -1) 445 mesg(MERR, set_thresh_fmt, pmreq.physpath, strerror(errno)); 446 447 free(vlist); 448 free(pmreq.physpath); 449 return (upval); 450 } 451 452 453 static int 454 scan_int(char *src, int *dst) 455 { 456 long lval; 457 458 errno = 0; 459 460 lval = strtol(LINEARG(1), NULL, 0); 461 if (errno || lval > INT_MAX || lval < 0) { 462 mesg(MERR, nerr_fmt, src); 463 return (NOUP); 464 } 465 466 *dst = (int)lval; 467 return (OKUP); 468 } 469 470 static int 471 scan_float(char *src, float *dst) 472 { 473 float fval; 474 475 errno = 0; 476 477 fval = strtof(src, NULL); 478 if (errno || fval < 0.0) { 479 mesg(MERR, nerr_fmt, src); 480 return (NOUP); 481 } 482 483 *dst = fval; 484 return (OKUP); 485 } 486 487 488 int 489 dreads(void) 490 { 491 return (scan_int(LINEARG(1), &new_cc.diskreads_thold)); 492 } 493 494 495 /* 496 * Set pathname for idlecheck; 497 * an overflowed pathname is treated as a fatal error. 498 */ 499 int 500 idlechk(void) 501 { 502 STRCPYLIM(new_cc.idlecheck_path, LINEARG(1), "idle path"); 503 return (OKUP); 504 } 505 506 507 int 508 loadavg(void) 509 { 510 return (scan_float(LINEARG(1), &new_cc.loadaverage_thold)); 511 } 512 513 514 int 515 nfsreq(void) 516 { 517 return (scan_int(LINEARG(1), &new_cc.nfsreqs_thold)); 518 } 519 520 521 #ifdef sparc 522 static char open_fmt[] = "cannot open \"%s\", %s\n"; 523 524 /* 525 * Verify the filesystem type for a regular statefile is "ufs" 526 * or verify a block device is not in use as a mounted filesytem. 527 * Returns 1 if any error, otherwise 0. 528 */ 529 static int 530 check_mount(char *sfile, dev_t sfdev, int ufs) 531 { 532 char *src, *err_fmt = NULL, *mnttab = MNTTAB; 533 int rgent, match = 0; 534 struct extmnttab ent; 535 FILE *fp; 536 537 if ((fp = fopen(mnttab, "r")) == NULL) { 538 mesg(MERR, open_fmt, mnttab, strerror(errno)); 539 return (1); 540 } 541 542 /* 543 * Search for a matching dev_t; 544 * ignore non-ufs filesystems for a regular statefile. 545 */ 546 while ((rgent = getextmntent(fp, &ent, sizeof (ent))) != -1) { 547 if (rgent > 0) { 548 mesg(MERR, "error reading \"%s\"\n", mnttab); 549 (void) fclose(fp); 550 return (1); 551 } else if (ufs && strcmp(ent.mnt_fstype, "ufs")) 552 continue; 553 else if (makedev(ent.mnt_major, ent.mnt_minor) == sfdev) { 554 match = 1; 555 break; 556 } 557 } 558 (void) fclose(fp); 559 560 /* 561 * No match is needed for a block device statefile, 562 * a match is needed for a regular statefile. 563 */ 564 if (match == 0) { 565 if (new_cc.cf_type == CFT_SPEC) 566 STRCPYLIM(new_cc.cf_devfs, sfile, "block statefile"); 567 else 568 err_fmt = "cannot find ufs mount point for \"%s\"\n"; 569 } else if (new_cc.cf_type == CFT_UFS) { 570 STRCPYLIM(new_cc.cf_fs, ent.mnt_mountp, "mnt entry"); 571 STRCPYLIM(new_cc.cf_devfs, ent.mnt_special, "mnt special"); 572 while (*(sfile + 1) == '/') sfile++; 573 src = sfile + strlen(ent.mnt_mountp); 574 while (*src == '/') src++; 575 STRCPYLIM(new_cc.cf_path, src, "statefile path"); 576 } else 577 err_fmt = "statefile device \"%s\" is a mounted filesystem\n"; 578 if (err_fmt) 579 mesg(MERR, err_fmt, sfile); 580 return (err_fmt != NULL); 581 } 582 583 584 /* 585 * Convert a Unix device to a prom device and save on success, 586 * log any ioctl/conversion error. 587 */ 588 static int 589 utop(void) 590 { 591 union obpbuf { 592 char buf[OBP_MAXPATHLEN + sizeof (uint_t)]; 593 struct openpromio oppio; 594 }; 595 union obpbuf oppbuf; 596 struct openpromio *opp; 597 char *promdev = "/dev/openprom"; 598 int fd, upval; 599 600 if ((fd = open(promdev, O_RDONLY)) == -1) { 601 mesg(MERR, open_fmt, promdev, strerror(errno)); 602 return (NOUP); 603 } 604 605 opp = &oppbuf.oppio; 606 opp->oprom_size = OBP_MAXPATHLEN; 607 strcpy_limit(opp->oprom_array, new_cc.cf_devfs, 608 OBP_MAXPATHLEN, "statefile device"); 609 upval = ioctl(fd, OPROMDEV2PROMNAME, opp); 610 (void) close(fd); 611 if (upval == OKUP) 612 STRCPYLIM(new_cc.cf_dev_prom, opp->oprom_array, "prom device"); 613 else { 614 openlog("pmconfig", 0, LOG_DAEMON); 615 syslog(LOG_NOTICE, 616 gettext("cannot convert \"%s\" to prom device"), 617 new_cc.cf_devfs); 618 closelog(); 619 } 620 621 return (upval); 622 } 623 624 625 /* 626 * Check for a valid statefile pathname, inode and mount status. 627 */ 628 int 629 sfpath(void) 630 { 631 static int statefile; 632 char *err_fmt = NULL; 633 char *sfile, *sp, ch; 634 struct stat stbuf; 635 int dir = 0; 636 dev_t dev; 637 638 if (statefile) { 639 mesg(MERR, "ignored redundant statefile entry\n"); 640 return (OKUP); 641 } else if (ua_err) { 642 if (ua_err != ENOTSUP) 643 mesg(MERR, "uadmin(A_FREEZE, A_CHECK, 0): %s\n", 644 strerror(ua_err)); 645 return (NOUP); 646 } 647 648 /* 649 * Check for an absolute path and trim any trailing '/'. 650 */ 651 sfile = LINEARG(1); 652 if (*sfile != '/') { 653 mesg(MERR, "statefile requires an absolute path\n"); 654 return (NOUP); 655 } 656 for (sp = sfile + strlen(sfile) - 1; sp > sfile && *sp == '/'; sp--) 657 *sp = '\0'; 658 659 /* 660 * If the statefile doesn't exist, the leading path must be a dir. 661 */ 662 if (stat(sfile, &stbuf) == -1) { 663 if (errno == ENOENT) { 664 dir = 1; 665 if ((sp = strrchr(sfile, '/')) == sfile) 666 sp++; 667 ch = *sp; 668 *sp = '\0'; 669 if (stat(sfile, &stbuf) == -1) 670 err_fmt = stat_fmt; 671 *sp = ch; 672 } else 673 err_fmt = stat_fmt; 674 if (err_fmt) { 675 mesg(MERR, err_fmt, sfile, strerror(errno)); 676 return (NOUP); 677 } 678 } 679 680 /* 681 * Check for regular/dir/block types, set cf_type and dev. 682 */ 683 if (S_ISREG(stbuf.st_mode) || (dir && S_ISDIR(stbuf.st_mode))) { 684 new_cc.cf_type = CFT_UFS; 685 dev = stbuf.st_dev; 686 } else if (S_ISBLK(stbuf.st_mode)) { 687 if (minor(stbuf.st_rdev) != 2) { 688 new_cc.cf_type = CFT_SPEC; 689 dev = stbuf.st_rdev; 690 } else 691 err_fmt = "statefile device cannot be slice 2 (%s)\n" 692 "would clobber the disk label and boot-block\n"; 693 } else 694 err_fmt = "bad file type for \"%s\"\n" 695 "statefile must be a regular file or block device\n"; 696 if (err_fmt) { 697 mesg(MERR, err_fmt, sfile); 698 return (NOUP); 699 } 700 701 if (check_mount(sfile, dev, (new_cc.cf_type == CFT_UFS)) || utop()) 702 return (NOUP); 703 new_cc.cf_magic = CPR_CONFIG_MAGIC; 704 statefile = 1; 705 return (OKUP); 706 } 707 #endif /* sparc */ 708 709 710 /* 711 * Common function to set a system or cpu threshold. 712 */ 713 static int 714 cmnthr(int req) 715 { 716 int value, nerr = 0, upval = OKUP; 717 char *thresh = LINEARG(1); 718 719 if (strcmp(thresh, always_on) == 0) 720 value = INT_MAX; 721 else if ((value = get_scaled_value(thresh, &nerr)) < 0 || nerr) { 722 mesg(MERR, "%s must be a positive value\n", LINEARG(0)); 723 upval = NOUP; 724 } 725 if (upval == OKUP) 726 (void) ioctl(pm_fd, req, value); 727 return (upval); 728 } 729 730 731 /* 732 * Try setting system threshold. 733 */ 734 int 735 systhr(void) 736 { 737 return (cmnthr(PM_SET_SYSTEM_THRESHOLD)); 738 } 739 740 741 /* 742 * Try setting cpu threshold. 743 */ 744 int 745 cputhr(void) 746 { 747 return (cmnthr(PM_SET_CPU_THRESHOLD)); 748 } 749 750 751 int 752 tchars(void) 753 { 754 return (scan_int(LINEARG(1), &new_cc.ttychars_thold)); 755 } 756