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.
27 * "pmconfig" performs a mixture of Energy-Star configuration tasks
28 * for both CheckPoint-Resume and Power-Management services.
29 * Tasks include parsing a config file (usually "/etc/power.conf"),
30 * updating CPR and PM config files, and setting various PM options
31 * via ioctl requests. From the mix, pmconfig should have a more
32 * generalized name similar to "estarconfig".
35 * "-r" reset CPR and PM options to default and exit.
36 * "-f file" specify an alternate config file; this is a
37 * private/non-advertised option used by "dtpower".
48 #define MCCPY_FIELD(dst, src, field) \
49 (void) memccpy(&dst.field, &src.field, 0, sizeof (dst.field) - 1)
52 static char conf_header
[] =
54 "# Copyright 1996-2002 Sun Microsystems, Inc. All rights reserved.\n"
55 "# Use is subject to license terms.\n"
57 "#pragma ident \"@(#)power.conf 2.1 02/03/04 SMI\"\n"
59 "# Power Management Configuration File\n"
63 static char *cpr_conf
= CPR_CONFIG
;
64 static char tmp_conf
[] = "/etc/.tmp.conf.XXXXXX";
65 static char orig_conf
[] = "/etc/power.conf-Orig";
66 static char default_conf
[] = "/etc/power.conf";
67 static char *power_conf
= default_conf
;
68 static pid_t powerd_pid
;
69 static prmup_t
*checkup
;
72 char estar_vers
= ESTAR_VNONE
;
76 static struct cprconfig disk_cc
;
77 struct cprconfig new_cc
;
79 static int fflag
, rflag
;
84 * Until we get more graphics driver support, we only enable autopm,
85 * S3 support and autoS3 by default on X86 systems that are on our whitelist.
87 int whitelist_only
= 1;
96 if (access(tmp_conf
, F_OK
) == 0)
97 (void) unlink(tmp_conf
);
102 * Multi-purpose message output routine; also exits when
103 * (status == MEXIT), other status is non-fatal.
107 mesg(int code
, char *fmt
, ...)
112 * debug is checked once here, avoiding N duplicate checks
113 * before each MDEBUG caller and unnecessary text dupduplication.
117 * If debug is not enabled, skip a debug message;
118 * lead with the program name for an error message,
119 * and follow with a filename and line number if an
120 * error occurs while parsing a conf file.
124 (void) fprintf(stderr
, "%s: ", prog
);
126 (void) fprintf(stderr
,
127 "\"%s\" line %d, ", power_conf
, lineno
);
130 va_start(vargs
, fmt
);
131 (void) vfprintf(stderr
, gettext(fmt
), vargs
);
144 (void) fprintf(stderr
, gettext("Usage: %s [-r]\n"), prog
);
150 * Lookup estar version, check if uadmin() service is supported,
151 * and read cpr_config info from disk.
162 if (estar_vers
== ESTAR_V2
)
163 new_cc
.is_cpr_default
= 1;
164 else if (estar_vers
== ESTAR_V3
)
165 new_cc
.is_autopm_default
= 1;
167 if (uadmin(A_FREEZE
, AD_CHECK
, 0) == 0)
168 new_cc
.is_cpr_capable
= 1;
172 if ((fd
= open("/dev/tod", O_RDONLY
)) != -1) {
173 new_cc
.is_autowakeup_capable
= 1;
179 * Read in the cpr conf file. If any open or read error occurs,
180 * display an error message only for a non-root user. The file
181 * may not exist on a newly installed system.
183 err_fmt
= "%s %s; please rerun %s as root\n";
184 if ((fd
= open(cpr_conf
, O_RDONLY
)) == -1) {
186 mesg(MEXIT
, err_fmt
, gettext("cannot open"),
189 nread
= read(fd
, &disk_cc
, sizeof (disk_cc
));
191 if (nread
!= (ssize_t
)sizeof (disk_cc
)) {
193 mesg(MEXIT
, err_fmt
, cpr_conf
,
194 gettext("file corrupted"), prog
);
196 (void) unlink(cpr_conf
);
197 bzero(&disk_cc
, sizeof (disk_cc
));
205 * Unconfigure and reset PM, device is left open for later use.
210 char *err_fmt
= NULL
;
212 if ((pm_fd
= open("/dev/pm", O_RDWR
)) == -1)
213 err_fmt
= "cannot open \"/dev/pm\": %s\n";
214 else if (ioctl(pm_fd
, PM_RESET_PM
, 0) == -1)
215 err_fmt
= "cannot reset pm state: %s\n";
217 mesg(MEXIT
, err_fmt
, strerror(errno
));
227 if ((fd
= open(PIDPATH
, O_RDONLY
)) == -1)
229 bzero(pidstr
, sizeof (pidstr
));
230 if (read(fd
, pidstr
, sizeof (pidstr
)) > 0) {
231 powerd_pid
= atoi(pidstr
);
232 mesg(MDEBUG
, "got powerd pid %ld\n", powerd_pid
);
239 * Write revised cprconfig struct to disk based on perms;
240 * returns 1 if any error, otherwise 0.
243 update_cprconfig(void)
245 struct cprconfig
*wrt_cc
= &new_cc
;
246 char *err_fmt
= NULL
;
250 /* For "pmconfig -r" case, copy select cpr-related fields. */
251 new_cc
.cf_magic
= disk_cc
.cf_magic
;
252 new_cc
.cf_type
= disk_cc
.cf_type
;
253 MCCPY_FIELD(new_cc
, disk_cc
, cf_path
);
254 MCCPY_FIELD(new_cc
, disk_cc
, cf_fs
);
255 MCCPY_FIELD(new_cc
, disk_cc
, cf_devfs
);
256 MCCPY_FIELD(new_cc
, disk_cc
, cf_dev_prom
);
259 if (!pm_status
.perm
) {
260 if (cpr_status
.update
== NOUP
)
262 /* save new struct data with old autopm setting */
263 MCCPY_FIELD(new_cc
, disk_cc
, apm_behavior
);
264 } else if (!cpr_status
.perm
) {
265 if (pm_status
.update
== NOUP
)
267 /* save original struct with new autopm setting */
268 MCCPY_FIELD(disk_cc
, new_cc
, apm_behavior
);
270 } else if (cpr_status
.update
== NOUP
|| pm_status
.update
== NOUP
)
273 if ((fd
= open(cpr_conf
, O_CREAT
| O_TRUNC
| O_WRONLY
, 0644)) == -1)
274 err_fmt
= "cannot open/create \"%s\", %s\n";
275 else if (write(fd
, wrt_cc
, sizeof (*wrt_cc
)) != sizeof (*wrt_cc
))
276 err_fmt
= "error writing \"%s\", %s\n";
278 mesg(MERR
, err_fmt
, cpr_conf
, strerror(errno
));
281 return (err_fmt
!= NULL
);
286 * Signal old powerd when there's a valid pid, or start a new one;
287 * returns 1 if any error, otherwise 0.
292 char *powerd
= "/usr/lib/power/powerd";
296 if (powerd_pid
> 0) {
297 if (sigsend(P_PID
, powerd_pid
, SIGHUP
) == 0)
299 else if (errno
!= ESRCH
) {
300 mesg(MERR
, "cannot deliver hangup to powerd\n");
305 if ((pid
= fork()) == NOPID
)
307 else if (pid
== P_MYPID
) {
308 (void) setreuid(0, 0);
309 (void) setregid(0, 0);
310 (void) setgroups(0, NULL
);
312 (void) execle(powerd
, powerd
, "-d", NULL
, NULL
);
314 (void) execle(powerd
, powerd
, NULL
, NULL
);
318 wp
= waitpid(pid
, &status
, 0);
319 } while (wp
== -1 && errno
== EINTR
);
323 mesg(MERR
, "could not start %s\n", powerd
);
324 return (wp
== -1 || status
!= 0);
331 static char *args
[] = { "/usr/bin/cp", default_conf
, orig_conf
, NULL
};
335 if (stat(orig_conf
, &stbuf
) == 0 && stbuf
.st_size
)
340 else if (pid
== P_MYPID
) {
341 (void) execve(args
[0], args
, NULL
);
344 (void) waitpid(pid
, NULL
, 0);
349 tmp_write(void *buf
, size_t len
)
351 if (write(tmp_fd
, buf
, len
) != (ssize_t
)len
)
352 mesg(MEXIT
, "error writing tmp file, %s\n", strerror(errno
));
357 tmp_save_line(char *line
, size_t len
, cinfo_t
*cip
)
360 tmp_write(cip
->cmt
, strlen(cip
->cmt
));
361 tmp_write(line
, len
);
366 * Filter conf lines and write them to the tmp file.
369 filter(char *line
, size_t len
, cinfo_t
*cip
)
374 * Lines from an alt conf file are selected when either:
375 * cip is NULL (keyword not matched, probably an old-style device),
376 * OR: it's both OK to accept the conf line (alt) AND either:
377 * preference is not set (NULL checkup) OR the cpr/pm preference
378 * (checkup) matches conftab status.
380 selected
= (cip
== NULL
|| (cip
->alt
&&
381 (checkup
== NULL
|| checkup
== cip
->status
)));
382 mesg(MDEBUG
, "filter: set \"%s\", selected %d\n",
383 cip
? cip
->status
->set
: "none", selected
);
385 tmp_save_line(line
, len
, cip
);
390 * Set checkup for conf line selection and parse a conf file with filtering.
391 * When pref is NULL, filter selects all conf lines from the new conf file;
392 * otherwise filter selects only cpr or pm related lines from the new or
393 * default conf file based on cpr or pm perm.
396 conf_scanner(prmup_t
*pref
)
398 mesg(MDEBUG
, "\nscanning set is %s\n", pref
? pref
->set
: "both");
400 parse_conf_file((pref
== NULL
|| pref
->perm
)
401 ? power_conf
: default_conf
, filter
, B_FALSE
);
406 * Search for any non-alt entries, call the handler routine,
407 * and write entries to the tmp file.
410 search(char *line
, size_t len
, cinfo_t
*cip
)
414 skip
= (cip
== NULL
|| cip
->alt
);
415 mesg(MDEBUG
, "search: %s\n", skip
? "skipped" : "retained");
418 if (cip
->status
->perm
)
419 (void) (*cip
->handler
)();
420 tmp_save_line(line
, len
, cip
);
425 * When perm and update status are OK, write a new conf file
426 * and rename to default_conf with the original attributes;
427 * returns 1 if any error, otherwise 0.
432 char *name
, *err_str
= NULL
;
435 if ((cpr_status
.perm
&& cpr_status
.update
!= OKUP
) ||
436 (pm_status
.perm
&& pm_status
.update
!= OKUP
)) {
437 mesg(MDEBUG
, "\nconf not written, "
438 "(cpr perm %d update %d), (pm perm %d update %d)\n",
439 cpr_status
.perm
, cpr_status
.update
,
440 pm_status
.perm
, pm_status
.update
);
445 if ((tmp_fd
= mkstemp(tmp_conf
)) == -1) {
446 mesg(MERR
, "cannot open/create tmp file \"%s\"\n", tmp_conf
);
449 tmp_write(conf_header
, sizeof (conf_header
) - 1);
452 * When both perms are set, save selected lines from the new file;
453 * otherwise save selected subsets from the new and default files.
455 if (cpr_status
.perm
&& pm_status
.perm
)
458 conf_scanner(&cpr_status
);
459 conf_scanner(&pm_status
);
463 * "dtpower" will craft an alt conf file with modified content from
464 * /etc/power.conf, but any alt conf file is not a trusted source;
465 * since some alt conf lines may be skipped, the trusted source is
466 * searched for those lines to retain their functionality.
468 parse_conf_file(default_conf
, search
, B_FALSE
);
470 (void) close(tmp_fd
);
472 if (stat(name
= default_conf
, &stbuf
) == -1)
474 else if (chmod(name
= tmp_conf
, stbuf
.st_mode
) == -1)
476 else if (chown(tmp_conf
, stbuf
.st_uid
, stbuf
.st_gid
) == -1)
478 else if (rename(tmp_conf
, default_conf
) == -1)
481 mesg(MDEBUG
, "\n\"%s\" renamed to \"%s\"\n",
482 tmp_conf
, default_conf
);
484 mesg(MERR
, "cannot %s \"%s\", %s\n",
485 err_str
, name
, strerror(errno
));
487 return (err_str
!= NULL
);
493 main(int cnt
, char **vec
)
497 (void) setlocale(LC_ALL
, "");
498 (void) textdomain(TEXT_DOMAIN
);
500 for (prog
= *vec
++; *vec
&& **vec
== '-'; vec
++) {
501 if (strlen(*vec
) > 2)
503 switch (*(*vec
+ 1)) {
509 if ((power_conf
= *++vec
) == NULL
)
530 mesg(MDEBUG
, "ruid %d, perms: cpr %d, pm %d\n",
531 ruid
, cpr_status
.perm
, pm_status
.perm
);
533 if ((!cpr_status
.perm
&& !pm_status
.perm
) ||
534 (rflag
&& !(cpr_status
.perm
&& pm_status
.perm
)))
535 mesg(MEXIT
, "%s\n", strerror(EACCES
));
536 if (rflag
== 0 && access(power_conf
, R_OK
))
537 mesg(MEXIT
, "\"%s\" is not readable\n", power_conf
);
546 return (update_cprconfig() || restart_powerd());
547 if (stat(default_conf
, &def_info
) == -1)
548 mesg(MEXIT
, "cannot stat %s, %s\n", default_conf
,
550 new_cc
.loadaverage_thold
= DFLT_THOLD
;
551 parse_conf_file(power_conf
, NULL
, B_TRUE
);
558 rval
= (update_cprconfig() || restart_powerd());