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 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
31 #include <libdevinfo.h>
32 static char sf_cmt
[] = "# Statefile\t\tPath\n";
35 static char as_cmt
[] =
36 "# Auto-Shutdown\t\tIdle(min)\tStart/Finish(hh:mm)\tBehavior\n";
42 * cpr and pm combined permission/update status
44 prmup_t cpr_status
= { 0, OKUP
, "cpr" };
45 prmup_t pm_status
= { 0, OKUP
, "pm" };
49 * For config file parsing to work correctly/efficiently, this table
50 * needs to be sorted by .keyword and any longer string like "device"
51 * must appear before a substring like "dev".
53 static cinfo_t conftab
[] = {
54 "S3-support", S3sup
, &pm_status
, NULL
, 2, 0, 1,
55 "autoS3", autoS3
, &pm_status
, NULL
, 2, 0, 1,
56 "autopm", autopm
, &pm_status
, NULL
, 2, 0, 1,
57 "autoshutdown", autosd
, &cpr_status
, as_cmt
, 5, 0, 1,
58 "cpu-threshold", cputhr
, &pm_status
, NULL
, 2, 0, 1,
59 "cpu_deep_idle", cpuidle
, &pm_status
, NULL
, 2, 0, 1,
60 "cpupm", cpupm
, &pm_status
, NULL
, 2, 1, 1,
61 "device-dependency-property",
62 ddprop
, &pm_status
, NULL
, 3, 1, 1,
63 "device-dependency", devdep
, &pm_status
, NULL
, 3, 1, 1,
64 "device-thresholds", devthr
, &pm_status
, NULL
, 3, 1, 1,
65 "diskreads", dreads
, &cpr_status
, NULL
, 2, 0, 1,
66 "idlecheck", idlechk
, &cpr_status
, NULL
, 2, 0, 0,
67 "loadaverage", loadavg
, &cpr_status
, NULL
, 2, 0, 1,
68 "nfsreqs", nfsreq
, &cpr_status
, NULL
, 2, 0, 1,
70 "statefile", sfpath
, &cpr_status
, sf_cmt
, 2, 0, 0,
72 "system-threshold", systhr
, &pm_status
, NULL
, 2, 0, 1,
73 "ttychars", tchars
, &cpr_status
, NULL
, 2, 0, 1,
74 NULL
, NULL
, NULL
, NULL
, 0, 0, 0,
79 * Set cpr/pm permission from default file info.
82 set_perm(char *defstr
, char *user
, int *perm
, int cons
)
87 * /etc/default/power entries are:
88 * all (all users + root)
90 * <user1[, user2...> (list users + root)
91 * console-owner (console onwer + root)
92 * Any error in reading/parsing the file limits the
93 * access requirement to root.
95 dinfo
= defread(defstr
);
96 mesg(MDEBUG
, "set_perm: \"%s\", value \"%s\"\n",
97 defstr
, dinfo
? dinfo
: "NULL");
100 else if (strcmp(dinfo
, "all") == 0)
102 else if (strcmp(dinfo
, "console-owner") == 0)
104 else if (user
!= NULL
&&
105 (*dinfo
== '<') && (tk
= strrchr(++dinfo
, '>'))) {
106 /* Scan dinfo for a matching user. */
107 for (*tk
= '\0'; (tk
= strtok(dinfo
, ", ")) != NULL
;
109 mesg(MDEBUG
, "match_user: cmp (\"%s\", \"%s\")\n",
111 if (strcmp(tk
, user
) == 0) {
121 * Lookup cpr/pm user permissions in "/etc/default/power".
131 if ((ruid
= getuid()) == 0) {
132 cpr_status
.perm
= pm_status
.perm
= 1;
134 } else if ((pent
= getpwuid(ruid
)) != NULL
) {
135 user
= pent
->pw_name
;
140 if (defopen("/etc/default/power") == -1)
142 if (stat("/dev/console", &stbuf
) == -1)
145 cons_perm
= (ruid
== stbuf
.st_uid
);
147 set_perm("PMCHANGEPERM=", user
, &pm_status
.perm
, cons_perm
);
148 set_perm("CPRCHANGEPERM=", user
, &cpr_status
.perm
, cons_perm
);
150 (void) defopen(NULL
);
156 * Lookup energystar-v[23] property and set estar_vers.
159 lookup_estar_vers(void)
161 char es_prop
[] = "energystar-v?", *fmt
= "%s init/access error\n";
168 if ((node
= di_init("/", DINFOPROP
)) == DI_NODE_NIL
) {
169 mesg(MERR
, fmt
, "di_init");
171 } else if ((ph
= di_prom_init()) == DI_PROM_HANDLE_NIL
) {
172 mesg(MERR
, fmt
, "di_prom_init");
176 last
= strlen(es_prop
) - 1;
177 for (ch
= ESTAR_V2
; ch
<= ESTAR_V3
; ch
++) {
179 if (di_prom_prop_lookup_bytes(ph
, node
,
180 es_prop
, &prop_data
) == 0) {
181 mesg(MDEBUG
, "get_estar_vers: %s prop found\n",
194 * limit open() to the real user
197 pmc_open(char *name
, int oflag
)
203 if (seteuid(ruid
) == -1)
204 mesg(MEXIT
, "cannot reset euid to %d, %s\n",
205 ruid
, strerror(errno
));
206 fd
= open(name
, oflag
);
207 (void) seteuid(euid
);
213 * Alloc space and read a config file; caller needs to free the space.
216 get_conf_data(char *name
)
224 if ((fd
= pmc_open(name
, O_RDONLY
)) == -1)
225 mesg(MEXIT
, "cannot open %s\n", name
);
226 else if (fstat(fd
, &stbuf
) == -1)
227 mesg(MEXIT
, "cannot stat %s\n", name
);
228 size
= (size_t)stbuf
.st_size
;
229 def_src
= (stbuf
.st_ino
== def_info
.st_ino
&&
230 stbuf
.st_dev
== def_info
.st_dev
);
231 if ((buf
= malloc(size
+ 1)) == NULL
)
232 mesg(MEXIT
, "cannot allocate %u for \"%s\"\n", size
+ 1, name
);
233 nread
= read(fd
, buf
, size
);
235 if (nread
!= (ssize_t
)size
)
236 mesg(MEXIT
, "read error, expect %u, got %d, file \"%s\"\n",
238 *(buf
+ size
) = '\0';
244 * Add an arg to line_args, adding space if needed.
247 newarg(char *arg
, int index
)
252 if ((index
+ 1) > alcnt
) {
254 size
= alcnt
* sizeof (*line_args
);
255 if ((line_args
= realloc(line_args
, size
)) == NULL
)
256 mesg(MEXIT
, "cannot alloc %u for line args\n", size
);
258 *(line_args
+ index
) = arg
;
263 * Convert blank-delimited words into an arg vector and return
264 * the arg count; character strings get null-terminated in place.
267 build_args(char *cline
, char *tail
)
270 char **vec
, *arg
, *cp
;
274 * Search logic: look for "\\\n" as a continuation marker,
275 * treat any other "\\*" as ordinary arg data, scan until a
276 * white-space delimiter is found, and if the arg has length,
277 * null-terminate and save arg to line_args. The scan includes
278 * tail so the last arg is found without any special-case code.
280 for (arg
= cp
= cline
; cp
<= tail
; cp
++) {
282 if (*(cp
+ 1) && *(cp
+ 1) != '\n') {
286 } else if (strchr(" \t\n", *cp
) == NULL
)
297 mesg(MDEBUG
, "\nline %d, found %d args:\n", lineno
, cnt
);
298 for (vec
= line_args
; *vec
; vec
++)
299 mesg(MDEBUG
, " \"%s\"\n", *vec
);
307 * Match leading keyword from a conf line and
308 * return a reference to a config info struct.
313 cinfo_t
*cip
, *info
= NULL
;
318 * Scan the config table for a matching keyword; since the table
319 * is sorted by keyword strings, a few optimizations can be done:
320 * first compare only the first byte of the keyword, skip any
321 * table string that starts with a lower ASCII value, compare the
322 * full string only when the first byte matches, and stop checking
323 * if the table string starts with a higher ASCII value.
325 keyword
= LINEARG(0);
326 for (cip
= conftab
; cip
->keyword
; cip
++) {
327 chr_diff
= (int)(*cip
->keyword
- *keyword
);
329 mesg(MDEBUG
, "line %d, ('%c' - '%c') = %d\n",
330 lineno
, *cip
->keyword
, *line
, chr_diff
);
334 else if (chr_diff
== 0) {
335 if (strcmp(keyword
, cip
->keyword
) == 0) {
347 * Find the end of a [possibly continued] conf line
348 * and record the real/lf-delimited line count at *lcnt.
351 find_line_end(char *line
, int *lcnt
)
357 while ((lf
= strchr(next
, '\n')) != NULL
) {
359 if (lf
== line
|| (*(lf
- 1) != '\\') || *(lf
+ 1) == '\0')
368 * Parse the named conf file and for each conf line
369 * call the action routine or conftab handler routine.
372 parse_conf_file(char *name
, vact_t action
, boolean_t first_parse
)
374 char *file_buf
, *cline
, *line
, *lend
;
381 * Do the "implied default" for autoS3, but only before we
382 * start parsing the first conf file.
385 (void) S3_helper("S3-support-enable", "S3-support-disable",
386 PM_ENABLE_S3
, PM_DISABLE_S3
, "S3-support", "default",
390 file_buf
= get_conf_data(name
);
391 mesg(MDEBUG
, "\nnow parsing \"%s\"...\n", name
);
395 while ((lend
= find_line_end(line
, &linc
)) != NULL
) {
397 * Each line should start with valid data
398 * but leading white-space can be ignored
400 while (line
< lend
) {
401 if (*line
!= ' ' && *line
!= '\t')
407 * Copy line into allocated space and null-terminate
408 * without the trailing line-feed.
410 if ((llen
= (lend
- line
)) != 0) {
411 if ((cline
= malloc(llen
+ 1)) == NULL
)
412 mesg(MEXIT
, "cannot alloc %u bytes "
413 "for line copy\n", llen
);
414 (void) memcpy(cline
, line
, llen
);
415 *(cline
+ llen
) = '\0';
420 * For blank and comment lines: possibly show a debug
421 * message and otherwise ignore them. For other lines:
422 * parse into an arg vector and try to match the first
423 * arg with conftab keywords. When a match is found,
424 * check for exact or minimum arg count, and call the
425 * action or handler routine; if handler does not return
426 * OKUP, set the referenced update value to NOUP so that
427 * later CPR or PM updates are skipped.
430 mesg(MDEBUG
, "\nline %d, blank...\n", lineno
);
431 else if (*line
== '#')
432 mesg(MDEBUG
, "\nline %d, comment...\n", lineno
);
433 else if ((cnt
= build_args(cline
, cline
+ llen
)) != 0) {
434 if ((cip
= get_cinfo()) == NULL
) {
435 mesg(MEXIT
, "unrecognized keyword \"%s\"\n",
437 } else if (cnt
!= cip
->argc
&&
438 (cip
->any
== 0 || cnt
< cip
->argc
)) {
439 mesg(MEXIT
, "found %d args, expect %d%s\n",
440 cnt
, cip
->argc
, cip
->any
? "+" : "");
442 (*action
)(line
, llen
+ 1, cip
);
443 else if (cip
->status
->perm
&& (def_src
|| cip
->alt
)) {
444 if ((*cip
->handler
)() != OKUP
)
445 cip
->status
->update
= NOUP
;
448 "==> handler skipped: %s_perm %d, "
449 "def_src %d, alt %d\n", cip
->status
->set
,
450 cip
->status
->perm
, def_src
, cip
->alt
);
464 int ret
= ioctl(pm_fd
, PM_GET_PM_STATE
, NULL
);
466 mesg(MDEBUG
, "Cannot get PM state: %s\n",
470 case PM_SYSTEM_PM_ENABLED
:
471 mesg(MDEBUG
, "Autopm Enabled\n");
473 case PM_SYSTEM_PM_DISABLED
:
474 mesg(MDEBUG
, "Autopm Disabled\n");
477 ret
= ioctl(pm_fd
, PM_GET_S3_SUPPORT_STATE
, NULL
);
479 mesg(MDEBUG
, "Cannot get PM state: %s\n",
483 case PM_S3_SUPPORT_ENABLED
:
484 mesg(MDEBUG
, "S3 support Enabled\n");
486 case PM_S3_SUPPORT_DISABLED
:
487 mesg(MDEBUG
, "S3 support Disabled\n");
490 ret
= ioctl(pm_fd
, PM_GET_AUTOS3_STATE
, NULL
);
492 mesg(MDEBUG
, "Cannot get PM state: %s\n",
496 case PM_AUTOS3_ENABLED
:
497 mesg(MDEBUG
, "AutoS3 Enabled\n");
499 case PM_AUTOS3_DISABLED
:
500 mesg(MDEBUG
, "AutoS3 Disabled\n");