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.
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.
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]
22 * Copyright 2015 Gary Mills
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 #include <sys/mkdev.h>
29 #include <sys/syslog.h>
30 #include <sys/openpromio.h>
31 #include <sys/mnttab.h>
33 #include <sys/efi_partition.h>
38 #include <sys/smbios.h>
42 #define STRCPYLIM(dst, src, str) strcpy_limit(dst, src, sizeof (dst), str)
43 #define LASTBYTE(str) (str + strlen(str) - 1)
45 static char nerr_fmt
[] = "number is out of range (%s)\n";
46 static char alloc_fmt
[] = "cannot allocate space for \"%s\", %s\n";
47 static char set_thresh_fmt
[] = "error setting threshold(s) for \"%s\", %s\n";
48 static char bad_thresh_fmt
[] = "bad threshold(s)\n";
49 static char stat_fmt
[] = "cannot stat \"%s\", %s\n";
50 static char always_on
[] = "always-on";
52 #define PM_DEFAULT_ALGORITHM -1
54 * When lines in a config file (usually "/etc/power.conf") start with
55 * a recognized keyword, a "handler" routine is called for specific
56 * CPR or PM -related action(s). Each routine returns a status code
57 * indicating whether all tasks were successful; if any errors occured,
58 * future CPR or PM updates are skipped. Following are the handler
59 * routines for all keywords:
63 static char pm_cmd_string
[32];
71 req
.data
= (void *)pm_cmd_string
;
72 req
.datasize
= sizeof (pm_cmd_string
);
74 if (ioctl(pm_fd
, PM_GET_CMD_NAME
, &req
) < 0) {
75 perror(gettext("PM_GET_CMD_NAME failed:"));
78 return (pm_cmd_string
);
82 isonlist(char *listname
, const char *man
, const char *prod
)
87 sl
.pms_listname
= listname
;
88 sl
.pms_manufacturer
= (char *)man
;
89 sl
.pms_product
= (char *)prod
;
90 ret
= ioctl(pm_fd
, PM_SEARCH_LIST
, &sl
);
91 mesg(MDEBUG
, "PM_SEARCH_LIST %s for %s,%s returns %d\n",
92 listname
, man
, prod
, ret
);
97 do_ioctl(int ioctl_cmd
, char *keyword
, char *behavior
, int suppress
)
99 mesg(MDEBUG
, "doing ioctl %s for %s ", pm_map(ioctl_cmd
), keyword
);
100 if (ioctl(pm_fd
, ioctl_cmd
, NULL
) == -1) {
101 int suppressed
= suppress
== -1 || suppress
== errno
;
103 mesg(MERR
, "%s %s failed, %s\n", keyword
, behavior
,
107 mesg(MDEBUG
, "%s %s failed, %s\n", keyword
, behavior
,
112 mesg(MDEBUG
, "succeeded\n");
117 * Check for valid cpupm behavior and communicate it to the kernel.
129 static struct bmtoc bmlist
[] = {
130 "disable", "\0", PM_STOP_CPUPM
, EINVAL
,
131 "enable", "poll-mode", PM_START_CPUPM_POLL
, EBUSY
,
132 "enable", "event-mode", PM_START_CPUPM_EV
, EBUSY
,
133 "enable", "\0", PM_START_CPUPM
, EBUSY
,
140 behavior
= LINEARG(1);
141 if ((mode
= LINEARG(2)) == NULL
)
144 for (bp
= bmlist
; bp
->cmd
; bp
++) {
145 if (strcmp(behavior
, bp
->behavior
) == 0 &&
146 strcmp(mode
, bp
->mode
) == 0) {
151 if (LINEARG(2) == NULL
) {
152 mesg(MERR
, "invalid cpupm behavior \"%s\"\n", behavior
);
154 mesg(MERR
, "invalid cpupm behavior \"%s %s\"\n",
159 if (ioctl(pm_fd
, bp
->cmd
, NULL
) == -1 && errno
!= bp
->Errno
) {
160 mesg(MERR
, "cpupm %s failed, %s\n",
161 behavior
, strerror(errno
));
168 * Check for valid cpu_deep_idle option and communicate it to the kernel.
178 static struct btoc blist
[] = {
179 "disable", PM_DISABLE_CPU_DEEP_IDLE
, EINVAL
,
180 "enable", PM_ENABLE_CPU_DEEP_IDLE
, EBUSY
,
181 "default", PM_DEFAULT_CPU_DEEP_IDLE
, EBUSY
,
187 for (behavior
= LINEARG(1), bp
= blist
; bp
->cmd
; bp
++) {
188 if (strcmp(behavior
, bp
->behavior
) == 0)
192 mesg(MERR
, "invalid cpu_deep_idle behavior \"%s\"\n", behavior
);
195 if (ioctl(pm_fd
, bp
->cmd
, NULL
) == -1 && errno
!= bp
->Errno
) {
196 mesg(MERR
, "cpu_deep_idle %s failed, %s\n",
197 behavior
, strerror(errno
));
204 * Two decisions are identical except for the list names and ioctl commands
205 * inputs: whitelist, blacklist, yes, no
206 * if (! ("S3" kstat exists))
208 * if (SystemInformation.Manufacturer == "Sun Microsystems" &&
209 * (Pref_PM_Profile == Workstation || Pref_PM_Profile == Desktop)) {
210 * if (platform on blacklist)
214 * if (platform on whitelist)
221 S3_helper(char *whitelist
, char *blacklist
, int yes
, int no
, char *keyword
,
222 char *behavior
, int *didyes
, int suppress
)
224 int oflags
= SMB_O_NOCKSUM
| SMB_O_NOVERS
;
233 int preferred_pm_profile
= 0;
234 char yesstr
[32], nostr
[32]; /* DEBUG */
238 (void) strncpy(yesstr
, pm_map(yes
), sizeof (yesstr
));
239 (void) strncpy(nostr
, pm_map(no
), sizeof (nostr
));
240 mesg(MDEBUG
, "S3_helper(%s, %s, %s, %s, %s, %s)\n", whitelist
,
241 blacklist
, yesstr
, nostr
, keyword
, behavior
);
242 if ((kc
= kstat_open()) == NULL
) {
243 mesg(MDEBUG
, "kstat_open failed\n");
246 ksp
= kstat_lookup(kc
, "acpi", -1, "acpi");
248 mesg(MDEBUG
, "kstat_lookup 'acpi', -1, 'acpi' failed\n");
249 (void) kstat_close(kc
);
252 (void) kstat_read(kc
, ksp
, NULL
);
253 dp
= kstat_data_lookup(ksp
, "S3");
254 if (dp
== NULL
|| dp
->value
.l
== 0) {
255 mesg(MDEBUG
, "kstat_data_lookup 'S3' fails\n");
257 mesg(MDEBUG
, "value.l %lx\n", dp
->value
.l
);
258 (void) kstat_close(kc
);
259 return (do_ioctl(no
, keyword
, behavior
, suppress
));
261 mesg(MDEBUG
, "kstat indicates S3 support (%lx)\n", dp
->value
.l
);
263 if (!whitelist_only
) {
265 * We still have an ACPI ksp, search it again for
266 * 'preferred_pm_profile' (needs to be valid if we don't
267 * aren't only using a whitelist).
269 dp
= kstat_data_lookup(ksp
, "preferred_pm_profile");
271 mesg(MDEBUG
, "kstat_data_lookup 'ppmp fails\n");
272 (void) kstat_close(kc
);
273 return (do_ioctl(no
, keyword
, behavior
, suppress
));
275 mesg(MDEBUG
, "kstat indicates preferred_pm_profile is %lx\n",
277 preferred_pm_profile
= dp
->value
.l
;
279 (void) kstat_close(kc
);
281 if ((shp
= smbios_open(NULL
,
282 SMB_VERSION
, oflags
, &ret
)) == NULL
) {
283 /* we promised not to complain */
284 /* we bail leaving it to the kernel default */
285 mesg(MDEBUG
, "smbios_open failed %d\n", errno
);
288 if ((id
= smbios_info_system(shp
, &sys
)) == SMB_ERR
) {
289 mesg(MDEBUG
, "smbios_info_system failed %d\n", errno
);
293 if (smbios_info_common(shp
, id
, &info
) == SMB_ERR
) {
294 mesg(MDEBUG
, "smbios_info_common failed %d\n", errno
);
298 mesg(MDEBUG
, "Manufacturer: %s\n", info
.smbi_manufacturer
);
299 mesg(MDEBUG
, "Product: %s\n", info
.smbi_product
);
302 if (!whitelist_only
) {
303 #define PPP_DESKTOP 1
304 #define PPP_WORKSTATION 3
305 if (strcmp(info
.smbi_manufacturer
, "Sun Microsystems") == 0 &&
306 (preferred_pm_profile
== PPP_DESKTOP
||
307 preferred_pm_profile
== PPP_WORKSTATION
)) {
308 if (isonlist(blacklist
,
309 info
.smbi_manufacturer
, info
.smbi_product
)) {
310 return (do_ioctl(no
, keyword
, behavior
,
313 ret
= do_ioctl(yes
, keyword
, behavior
,
315 *didyes
= (ret
== OKUP
);
320 if (isonlist(whitelist
,
321 info
.smbi_manufacturer
, info
.smbi_product
)) {
322 ret
= do_ioctl(yes
, keyword
, behavior
, suppress
);
323 *didyes
= (ret
== OKUP
);
326 return (do_ioctl(no
, keyword
, behavior
, suppress
));
331 S3sup(void) /* S3-support keyword handler */
337 static struct btoc blist
[] = {
338 "default", PM_DEFAULT_ALGORITHM
,
339 "enable", PM_ENABLE_S3
,
340 "disable", PM_DISABLE_S3
,
347 for (behavior
= LINEARG(1), bp
= blist
; bp
->cmd
; bp
++) {
348 if (strcmp(behavior
, bp
->behavior
) == 0)
352 mesg(MERR
, "invalid S3-support behavior \"%s\"\n", behavior
);
361 return (do_ioctl(bp
->cmd
, "S3-support", behavior
, EBUSY
));
363 case PM_DEFAULT_ALGORITHM
:
365 * we suppress errors in the "default" case because we
366 * already did an invisible default call, so we know we'll
369 return (S3_helper("S3-support-enable", "S3-support-disable",
370 PM_ENABLE_S3
, PM_DISABLE_S3
, "S3-support", behavior
,
374 mesg(MERR
, "S3-support %s failed, %s\n", behavior
,
381 * Check for valid autoS3 behavior and save after ioctl success.
390 static struct btoc blist
[] = {
391 "default", PM_DEFAULT_ALGORITHM
,
392 "disable", PM_STOP_AUTOS3
,
393 "enable", PM_START_AUTOS3
,
400 for (behavior
= LINEARG(1), bp
= blist
; bp
->cmd
; bp
++) {
401 if (strcmp(behavior
, bp
->behavior
) == 0)
405 mesg(MERR
, "invalid autoS3 behavior \"%s\"\n", behavior
);
411 mesg(MERR
, "autoS3 %s failed, %s\n",
412 behavior
, strerror(errno
));
413 mesg(MDEBUG
, "unknown command\n", bp
->cmd
);
417 case PM_START_AUTOS3
:
418 return (do_ioctl(bp
->cmd
, "autoS3", behavior
, EBUSY
));
420 case PM_DEFAULT_ALGORITHM
:
421 return (S3_helper("S3-autoenable", "S3-autodisable",
422 PM_START_AUTOS3
, PM_STOP_AUTOS3
, "autoS3", behavior
,
429 * Check for valid autopm behavior and save after ioctl success.
436 int cmd
, Errno
, isdef
;
438 static struct btoc blist
[] = {
439 "default", PM_START_PM
, -1, 1,
440 "disable", PM_STOP_PM
, EINVAL
, 0,
441 "enable", PM_START_PM
, EBUSY
, 0,
447 for (behavior
= LINEARG(1), bp
= blist
; bp
->cmd
; bp
++) {
448 if (strcmp(behavior
, bp
->behavior
) == 0)
452 mesg(MERR
, "invalid autopm behavior \"%s\"\n", behavior
);
457 * for "default" behavior, do not enable autopm if not ESTAR_V3
460 if (!bp
->isdef
|| (estar_vers
== ESTAR_V3
)) {
461 if (ioctl(pm_fd
, bp
->cmd
, NULL
) == -1 && errno
!= bp
->Errno
) {
462 mesg(MERR
, "autopm %s failed, %s\n",
463 behavior
, strerror(errno
));
467 (void) strcpy(new_cc
.apm_behavior
, behavior
);
472 if (ioctl(pm_fd
, bp
->cmd
, NULL
) == -1 && errno
!= bp
->Errno
) {
473 mesg(MERR
, "autopm %s failed, %s\n",
474 behavior
, strerror(errno
));
477 mesg(MDEBUG
, "autopm %s succeeded\n", behavior
);
482 int ret
= S3_helper("autopm-enable", "autopm-disable",
483 PM_START_PM
, PM_STOP_PM
, "autopm", behavior
, &didenable
,
486 /* tell powerd to attach all devices */
487 new_cc
.is_autopm_default
= 1;
488 (void) strcpy(new_cc
.apm_behavior
, behavior
);
497 gethm(char *src
, int *hour
, int *min
)
499 if (sscanf(src
, "%d:%d", hour
, min
) != 2) {
500 mesg(MERR
, "bad time format (%s)\n", src
);
508 strcpy_limit(char *dst
, char *src
, size_t limit
, char *info
)
510 if (strlcpy(dst
, src
, limit
) >= limit
)
511 mesg(MEXIT
, "%s is too long (%s)\n", info
, src
);
516 * Convert autoshutdown idle and start/finish times;
517 * check and record autoshutdown behavior.
522 char **bp
, *behavior
;
523 char *unrec
= gettext("unrecognized autoshutdown behavior");
524 static char *blist
[] = {
525 "autowakeup", "default", "noshutdown",
526 "shutdown", "unconfigured", NULL
529 new_cc
.as_idle
= atoi(LINEARG(1));
530 if (gethm(LINEARG(2), &new_cc
.as_sh
, &new_cc
.as_sm
) ||
531 gethm(LINEARG(3), &new_cc
.as_fh
, &new_cc
.as_fm
))
533 mesg(MDEBUG
, "idle %d, start %d:%02d, finish %d:%02d\n",
534 new_cc
.as_idle
, new_cc
.as_sh
, new_cc
.as_sm
,
535 new_cc
.as_fh
, new_cc
.as_fm
);
537 for (behavior
= LINEARG(4), bp
= blist
; *bp
; bp
++) {
538 if (strcmp(behavior
, *bp
) == 0)
542 mesg(MERR
, "%s: \"%s\"\n", unrec
, behavior
);
545 STRCPYLIM(new_cc
.as_behavior
, *bp
, unrec
);
551 * Check for a real device and try to resolve to a full path.
552 * The orig/resolved path may be modified into a prom pathname,
553 * and an allocated copy of the result is stored at *destp;
554 * the caller will need to free that space. Returns 1 for any
555 * error, otherwise 0; also sets *errp after an alloc error.
558 devpath(char **destp
, char *src
, int *errp
)
567 * When there's a real device, try to resolve the path
568 * and trim the leading "/devices" component.
570 if ((devok
= (stat(src
, &stbuf
) == 0 && stbuf
.st_rdev
)) != 0) {
571 if (realpath(src
, buf
) == NULL
) {
572 mesg(MERR
, "realpath cannot resolve \"%s\"\n",
573 src
, strerror(errno
));
579 dcs
= (strncmp(src
, dstr
, len
) == 0);
583 mesg(MDEBUG
, stat_fmt
, src
, strerror(errno
));
586 * When the path has ":anything", display an error for
587 * a non-device or truncate a resolved+modifed path.
589 if ((cp
= strchr(src
, ':')) != NULL
) {
591 mesg(MERR
, "physical path may not contain "
592 "a minor string (%s)\n", src
);
598 if ((*destp
= strdup(src
)) == NULL
) {
600 mesg(MERR
, alloc_fmt
, src
, strerror(errno
));
602 return (*destp
== NULL
);
607 * Call pm ioctl request(s) to set property/device dependencies.
610 dev_dep_common(int isprop
)
612 int cmd
, argn
, upval
= OKUP
;
613 char *src
, *first
, **destp
;
616 bzero(&pmreq
, sizeof (pmreq
));
619 cmd
= PM_ADD_DEPENDENT_PROPERTY
;
621 pmreq
.pmreq_kept
= src
;
623 cmd
= PM_ADD_DEPENDENT
;
624 if (devpath(&first
, src
, &upval
))
626 pmreq
.pmreq_kept
= first
;
628 destp
= &pmreq
.pmreq_keeper
;
631 * Now loop through any dependents.
633 for (argn
= 2; (src
= LINEARG(argn
)) != NULL
; argn
++) {
634 if (devpath(destp
, src
, &upval
)) {
639 if ((upval
= ioctl(pm_fd
, cmd
, &pmreq
)) == -1) {
640 mesg(MDEBUG
, "pm ioctl, cmd %d, errno %d\n"
641 "kept \"%s\", keeper \"%s\"\n",
642 cmd
, errno
, pmreq
.pmreq_kept
, pmreq
.pmreq_keeper
);
643 mesg(MERR
, "cannot set \"%s\" dependency "
644 "for \"%s\", %s\n", pmreq
.pmreq_keeper
,
645 pmreq
.pmreq_kept
, strerror(errno
));
661 return (dev_dep_common(1));
668 return (dev_dep_common(0));
673 * Convert a numeric string (with a possible trailing scaling byte)
674 * into an integer. Returns a converted value and *nerrp unchanged,
675 * or 0 with *nerrp set to 1 for a conversion error.
678 get_scaled_value(char *str
, int *nerrp
)
680 longlong_t svalue
= 0, factor
= 1;
684 svalue
= strtol(str
, &sp
, 0);
685 if (errno
|| (*str
!= '-' && (*str
< '0' || *str
> '9')))
687 else if (sp
&& *sp
!= '\0') {
695 /* any bytes following sp are ignored */
699 if (svalue
< INT_MIN
|| svalue
> INT_MAX
)
703 mesg(MERR
, nerr_fmt
, str
);
704 mesg(MDEBUG
, "got scaled value %d\n", (int)svalue
);
705 return ((int)svalue
);
710 * Increment the count of threshold values,
711 * reallocate *vlistp and append another element.
712 * Returns 1 on error, otherwise 0.
715 vlist_append(int **vlistp
, int *vcntp
, int value
)
718 if ((*vlistp
= realloc(*vlistp
, *vcntp
* sizeof (**vlistp
))) != NULL
)
719 *(*vlistp
+ *vcntp
- 1) = value
;
721 mesg(MERR
, alloc_fmt
, "threshold list", strerror(errno
));
722 return (*vlistp
== NULL
);
727 * Convert a single threshold string or paren groups of thresh's as
728 * described below. All thresh's are saved to an allocated list at
729 * *vlistp; the caller will need to free that space. On return:
730 * *vcntp is the count of the vlist array, and vlist is either
731 * a single thresh or N groups of thresh's with a trailing zero:
732 * (cnt_1 thr_1a thr_1b [...]) ... (cnt_N thr_Na thr_Nb [...]) 0.
733 * Returns 0 when all conversions were OK, and 1 for any syntax,
734 * conversion, or alloc error.
737 get_thresh(int **vlistp
, int *vcntp
)
739 int argn
, value
, gci
= 0, grp_cnt
= 0, paren
= 0, nerr
= 0;
742 for (argn
= 2; (src
= LINEARG(argn
)) != NULL
; argn
++) {
743 if (*src
== LPAREN
) {
745 if ((nerr
= vlist_append(vlistp
, vcntp
, 0)) != 0)
750 if (*(rp
= LASTBYTE(src
)) == RPAREN
) {
752 grp_cnt
= *vcntp
- gci
;
753 *(*vlistp
+ gci
) = grp_cnt
;
762 value
= get_scaled_value(src
, &nerr
);
763 if (nerr
|| (nerr
= vlist_append(vlistp
, vcntp
, value
)))
767 if (nerr
== 0 && grp_cnt
)
768 nerr
= vlist_append(vlistp
, vcntp
, 0);
774 * Set device thresholds from (3) formats:
776 * path time-spec: [0-9]+[{h,m,s}]
777 * path (ts1 ts2 ...)+
782 int cmd
, upval
= OKUP
, nthresh
= 0, *vlist
= NULL
;
785 bzero(&pmreq
, sizeof (pmreq
));
786 if (devpath(&pmreq
.physpath
, LINEARG(1), &upval
))
789 if (strcmp(LINEARG(2), always_on
) == 0) {
790 cmd
= PM_SET_DEVICE_THRESHOLD
;
791 pmreq
.value
= INT_MAX
;
792 } else if (get_thresh(&vlist
, &nthresh
)) {
793 mesg(MERR
, bad_thresh_fmt
);
795 } else if (nthresh
== 1) {
796 pmreq
.value
= *vlist
;
797 cmd
= PM_SET_DEVICE_THRESHOLD
;
800 pmreq
.datasize
= (nthresh
* sizeof (*vlist
));
801 cmd
= PM_SET_COMPONENT_THRESHOLDS
;
804 if (upval
!= NOUP
&& (upval
= ioctl(pm_fd
, cmd
, &pmreq
)) == -1)
805 mesg(MERR
, set_thresh_fmt
, pmreq
.physpath
, strerror(errno
));
808 free(pmreq
.physpath
);
814 scan_int(char *src
, int *dst
)
820 lval
= strtol(LINEARG(1), NULL
, 0);
821 if (errno
|| lval
> INT_MAX
|| lval
< 0) {
822 mesg(MERR
, nerr_fmt
, src
);
831 scan_float(char *src
, float *dst
)
837 fval
= strtof(src
, NULL
);
838 if (errno
|| fval
< 0.0) {
839 mesg(MERR
, nerr_fmt
, src
);
851 return (scan_int(LINEARG(1), &new_cc
.diskreads_thold
));
856 * Set pathname for idlecheck;
857 * an overflowed pathname is treated as a fatal error.
862 STRCPYLIM(new_cc
.idlecheck_path
, LINEARG(1), "idle path");
870 return (scan_float(LINEARG(1), &new_cc
.loadaverage_thold
));
877 return (scan_int(LINEARG(1), &new_cc
.nfsreqs_thold
));
881 static char open_fmt
[] = "cannot open \"%s\", %s\n";
884 * Verify the filesystem type for a regular statefile is "ufs"
885 * or verify a block device is not in use as a mounted filesytem.
886 * Returns 1 if any error, otherwise 0.
889 check_mount(char *sfile
, dev_t sfdev
, int ufs
)
891 char *src
, *err_fmt
= NULL
, *mnttab
= MNTTAB
;
892 int rgent
, match
= 0;
893 struct mnttab zroot
= { 0 };
895 struct extmnttab ent
;
898 if ((fp
= fopen(mnttab
, "r")) == NULL
) {
899 mesg(MERR
, open_fmt
, mnttab
, strerror(errno
));
904 zroot
.mnt_mountp
= "/";
905 zroot
.mnt_fstype
= "zfs";
906 if (getmntany(fp
, &entry
, &zroot
) == 0) {
907 err_fmt
= "ufs statefile with zfs root is not"
909 mesg(MERR
, err_fmt
, sfile
);
916 * Search for a matching dev_t;
917 * ignore non-ufs filesystems for a regular statefile.
919 while ((rgent
= getextmntent(fp
, &ent
, sizeof (ent
))) != -1) {
921 mesg(MERR
, "error reading \"%s\"\n", mnttab
);
924 } else if (ufs
&& strcmp(ent
.mnt_fstype
, "ufs"))
926 else if (makedev(ent
.mnt_major
, ent
.mnt_minor
) == sfdev
) {
933 * No match is needed for a block device statefile,
934 * a match is needed for a regular statefile.
937 if (new_cc
.cf_type
!= CFT_UFS
)
938 STRCPYLIM(new_cc
.cf_devfs
, sfile
, "block statefile");
940 err_fmt
= "cannot find ufs mount point for \"%s\"\n";
941 } else if (new_cc
.cf_type
== CFT_UFS
) {
942 STRCPYLIM(new_cc
.cf_fs
, ent
.mnt_mountp
, "mnt entry");
943 STRCPYLIM(new_cc
.cf_devfs
, ent
.mnt_special
, "mnt special");
944 while (*(sfile
+ 1) == '/') sfile
++;
945 src
= sfile
+ strlen(ent
.mnt_mountp
);
946 while (*src
== '/') src
++;
947 STRCPYLIM(new_cc
.cf_path
, src
, "statefile path");
949 err_fmt
= "statefile device \"%s\" is a mounted filesystem\n";
952 mesg(MERR
, err_fmt
, sfile
);
953 return (err_fmt
!= NULL
);
958 * Convert a Unix device to a prom device and save on success,
959 * log any ioctl/conversion error.
962 utop(char *fs_name
, char *prom_name
)
965 char buf
[OBP_MAXPATHLEN
+ sizeof (uint_t
)];
966 struct openpromio oppio
;
969 struct openpromio
*opp
;
970 char *promdev
= "/dev/openprom";
973 if ((fd
= open(promdev
, O_RDONLY
)) == -1) {
974 mesg(MERR
, open_fmt
, promdev
, strerror(errno
));
979 opp
->oprom_size
= OBP_MAXPATHLEN
;
980 strcpy_limit(opp
->oprom_array
, fs_name
,
981 OBP_MAXPATHLEN
, "statefile device");
982 upval
= ioctl(fd
, OPROMDEV2PROMNAME
, opp
);
985 strcpy_limit(prom_name
, opp
->oprom_array
, OBP_MAXPATHLEN
,
988 openlog("pmconfig", 0, LOG_DAEMON
);
990 gettext("cannot convert \"%s\" to prom device"),
999 * given the path to a zvol, return the cXtYdZ name
1000 * returns < 0 on error, 0 if it isn't a zvol, > 1 on success
1003 ztop(char *arg
, char *diskname
)
1005 zpool_handle_t
*zpool_handle
;
1006 nvlist_t
*config
, *nvroot
;
1009 libzfs_handle_t
*lzfs
;
1012 char pool_name
[MAXPATHLEN
];
1014 if (strncmp(arg
, "/dev/zvol/dsk/", 14)) {
1018 (void) strncpy(pool_name
, arg
, MAXPATHLEN
);
1019 if ((p
= strchr(pool_name
, '/')) != NULL
)
1021 STRCPYLIM(new_cc
.cf_fs
, p
+ 1, "statefile path");
1023 if ((lzfs
= libzfs_init()) == NULL
) {
1024 mesg(MERR
, "failed to initialize ZFS library\n");
1027 if ((zpool_handle
= zpool_open(lzfs
, pool_name
)) == NULL
) {
1028 mesg(MERR
, "couldn't open pool '%s'\n", pool_name
);
1032 config
= zpool_get_config(zpool_handle
, NULL
);
1033 if (nvlist_lookup_nvlist(config
, ZPOOL_CONFIG_VDEV_TREE
,
1035 zpool_close(zpool_handle
);
1039 verify(nvlist_lookup_nvlist_array(nvroot
, ZPOOL_CONFIG_CHILDREN
,
1040 &child
, &children
) == 0);
1041 if (children
!= 1) {
1042 mesg(MERR
, "expected one vdev, got %d\n", children
);
1043 zpool_close(zpool_handle
);
1047 vname
= zpool_vdev_name(lzfs
, zpool_handle
, child
[0], B_FALSE
);
1048 if (vname
== NULL
) {
1049 mesg(MERR
, "couldn't determine vdev name\n");
1050 zpool_close(zpool_handle
);
1054 (void) strcpy(diskname
, "/dev/dsk/");
1055 (void) strcat(diskname
, vname
);
1057 zpool_close(zpool_handle
);
1063 * returns NULL if the slice is good (e.g. does not start at block
1064 * zero, or a string describing the error if it doesn't
1067 is_good_slice(char *sfile
, char **err
)
1072 char rdskname
[MAXPATHLEN
];
1076 /* convert from dsk to rdsk */
1077 STRCPYLIM(rdskname
, sfile
, "disk name");
1078 x
= strstr(rdskname
, "dsk/");
1079 y
= strstr(sfile
, "dsk/");
1082 (void) strcpy(x
, y
);
1085 if ((fd
= open(rdskname
, O_RDONLY
)) == -1) {
1086 *err
= "could not open '%s'\n";
1087 } else if ((rc
= read_vtoc(fd
, &vtoc
)) >= 0) {
1089 * we got a slice number; now check the block
1090 * number where the slice starts
1092 if (vtoc
.v_part
[rc
].p_start
< 2)
1093 *err
= "using '%s' would clobber the disk label\n";
1095 return (*err
? B_FALSE
: B_TRUE
);
1096 } else if ((rc
== VT_ENOTSUP
) &&
1097 (efi_alloc_and_read(fd
, &gpt
)) >= 0) {
1098 /* EFI slices don't clobber the disk label */
1103 *err
= "could not read partition table from '%s'\n";
1108 * Check for a valid statefile pathname, inode and mount status.
1113 static int statefile
;
1114 char *err_fmt
= NULL
;
1115 char *sfile
, *sp
, ch
;
1122 mesg(MERR
, "ignored redundant statefile entry\n");
1124 } else if (ua_err
) {
1125 if (ua_err
!= ENOTSUP
)
1126 mesg(MERR
, "uadmin(A_FREEZE, A_CHECK, 0): %s\n",
1132 * Check for an absolute path and trim any trailing '/'.
1135 if (*sfile
!= '/') {
1136 mesg(MERR
, "statefile requires an absolute path\n");
1139 for (sp
= sfile
+ strlen(sfile
) - 1; sp
> sfile
&& *sp
== '/'; sp
--)
1143 * If the statefile doesn't exist, the leading path must be a dir.
1145 if (stat(sfile
, &stbuf
) == -1) {
1146 if (errno
== ENOENT
) {
1148 if ((sp
= strrchr(sfile
, '/')) == sfile
)
1152 if (stat(sfile
, &stbuf
) == -1)
1158 mesg(MERR
, err_fmt
, sfile
, strerror(errno
));
1164 * Check for regular/dir/block types, set cf_type and dev.
1166 if (S_ISREG(stbuf
.st_mode
) || (dir
&& S_ISDIR(stbuf
.st_mode
))) {
1167 new_cc
.cf_type
= CFT_UFS
;
1169 } else if (S_ISBLK(stbuf
.st_mode
)) {
1170 if (is_good_slice(sfile
, &err_fmt
)) {
1171 switch (ztop(sfile
, diskname
)) {
1173 new_cc
.cf_type
= CFT_ZVOL
;
1176 new_cc
.cf_type
= CFT_SPEC
;
1182 dev
= stbuf
.st_rdev
;
1185 err_fmt
= "bad file type for \"%s\"\n"
1186 "statefile must be a regular file or block device\n";
1188 mesg(MERR
, err_fmt
, sfile
);
1191 if (check_mount(sfile
, dev
, (new_cc
.cf_type
== CFT_UFS
)))
1193 if (new_cc
.cf_type
== CFT_ZVOL
) {
1194 if (utop(diskname
, new_cc
.cf_dev_prom
))
1196 } else if (utop(new_cc
.cf_devfs
, new_cc
.cf_dev_prom
)) {
1199 new_cc
.cf_magic
= CPR_CONFIG_MAGIC
;
1207 * Common function to set a system or cpu threshold.
1212 int value
, nerr
= 0, upval
= OKUP
;
1213 char *thresh
= LINEARG(1);
1215 if (strcmp(thresh
, always_on
) == 0)
1217 else if ((value
= get_scaled_value(thresh
, &nerr
)) < 0 || nerr
) {
1218 mesg(MERR
, "%s must be a positive value\n", LINEARG(0));
1222 (void) ioctl(pm_fd
, req
, value
);
1228 * Try setting system threshold.
1233 return (cmnthr(PM_SET_SYSTEM_THRESHOLD
));
1238 * Try setting cpu threshold.
1243 return (cmnthr(PM_SET_CPU_THRESHOLD
));
1250 return (scan_int(LINEARG(1), &new_cc
.ttychars_thold
));