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