xref: /illumos-gate/usr/src/cmd/power/sys-suspend.c (revision cbea7aca3fd7787405cbdbd93752998f03dfc25f)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  * Copyright 2012 Milan Jurik. All rights reserved.
25  * Copyright 2023 OmniOS Community Edition (OmniOSce) Association.
26  */
27 
28 /*
29  * This code has a lot in common with the original sys-suspend
30  * code.  Windowing facilities have been removed, and it has been
31  * updated to use more recent API's.
32  */
33 #include <stdio.h>
34 #include <fcntl.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <strings.h>
38 #include <unistd.h>
39 #include <libintl.h>
40 #include <locale.h>
41 #include <utility.h>
42 #include <signal.h>
43 #include <errno.h>
44 #include <setjmp.h>
45 #include <pwd.h>
46 #include <syslog.h>
47 #include <sys/types.h>
48 #include <sys/param.h>
49 #include <sys/utsname.h>
50 #include <sys/uadmin.h>
51 #include <auth_attr.h>
52 #include <auth_list.h>
53 #include <secdb.h>
54 #include <security/pam_appl.h>
55 #include <utmpx.h>
56 
57 /* For audit */
58 #include <bsm/adt.h>
59 #include <bsm/adt_event.h>
60 
61 #include <sys/wait.h>
62 #include <sys/stat.h>
63 #include <sys/pm.h>
64 #include <dirent.h>
65 #include <sys/cpr.h>
66 
67 /* STATICUSED */
68 struct utmpx	utmp;
69 #define	NMAX		(sizeof (utmp.ut_name))
70 
71 /*
72  * Authorizations used by Power Management
73  */
74 #define	AUTHNAME_SHUTDOWN	"solaris.system.shutdown"
75 #define	AUTHNAME_SUSPEND_RAM	"solaris.system.power.suspend.ram"
76 #define	AUTHNAME_SUSPEND_DISK	"solaris.system.power.suspend.disk"
77 
78 /* Platform specific definitions */
79 #ifdef i386
80 #define	AD_CHECK_SUSPEND	AD_CHECK_SUSPEND_TO_RAM
81 #define	AD_SUSPEND		AD_SUSPEND_TO_RAM
82 #define	ADT_FCN			ADT_UADMIN_FCN_AD_SUSPEND_TO_RAM
83 #define	AUTHNAME_SUSPEND	AUTHNAME_SUSPEND_RAM
84 #else
85 #define	AD_CHECK_SUSPEND	AD_CHECK_SUSPEND_TO_DISK
86 #define	AD_SUSPEND		AD_SUSPEND_TO_DISK
87 #define	ADT_FCN			ADT_UADMIN_FCN_AD_SUSPEND_TO_DISK
88 #define	AUTHNAME_SUSPEND	AUTHNAME_SUSPEND_DISK
89 #endif
90 
91 static	int		flags = 0;
92 static	int		no_tty = 0;
93 /*
94  * Flag definitions - could go in a header file, but there are just a few
95  */
96 #define	FORCE		0x001
97 #define	NO_WARN		0x002
98 #define	NO_XLOCK	0x004
99 #define	SHUTDOWN	0x008
100 #define	LOWPOWER	0x010
101 #define	TEST		0x800
102 
103 static	sigjmp_buf	jmp_stack;
104 static	char	user[NMAX + 1];
105 static	char	**argvl;
106 
107 
108 
109 /*
110  *  Forward Declarations.
111  */
112 static	void	pm_poweroff(void);
113 static	int	bringto_lowpower(void);
114 static	int	is_mou3(void);
115 static	void	suspend_error(int);
116 static	int	pm_check_suspend(void);
117 static	void	pm_suspend(void);
118 static	void	pm_do_auth(adt_session_data_t *);
119 
120 /*
121  *  External Declarations.
122  */
123 extern	int	pam_tty_conv(int, const struct pam_message **,
124     struct pam_response **, void *);
125 extern	char	*optarg;
126 
127 /*
128  * Audit related code.  I would also think that some of this could be
129  * in external code, as they could be useful of other apps.
130  */
131 /*
132  * Write audit event.  Could be useful in the PM library, so it is
133  * included here.  For the most part it is only used by the PAM code.
134  */
135 static void
pm_audit_event(adt_session_data_t * ah,au_event_t event_id,int status)136 pm_audit_event(adt_session_data_t *ah, au_event_t event_id, int status)
137 {
138 	adt_event_data_t	*event;
139 
140 
141 	if ((event = adt_alloc_event(ah, event_id)) == NULL) {
142 		return;
143 	}
144 
145 	(void) adt_put_event(event,
146 	    status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAILURE,
147 	    status == PAM_SUCCESS ? ADT_SUCCESS : ADT_FAIL_PAM + status);
148 
149 	adt_free_event(event);
150 }
151 
152 #define	RETRY_COUNT 15
153 static int
change_audit_file(void)154 change_audit_file(void)
155 {
156 	pid_t	pid;
157 
158 	if (!adt_audit_state(AUC_AUDITING)) {
159 		/* auditd not running, just return */
160 		return (0);
161 	}
162 
163 	if ((pid = fork()) == 0) {
164 		(void) execl("/usr/sbin/audit", "audit", "-n", NULL);
165 		(void) fprintf(stderr, gettext("error changing audit files: "
166 		    "%s\n"), strerror(errno));
167 		_exit(-1);
168 	} else if (pid == -1) {
169 		(void) fprintf(stderr, gettext("error changing audit files: "
170 		    "%s\n"), strerror(errno));
171 		return (-1);
172 	} else {
173 		pid_t	rc;
174 		int	retries = RETRY_COUNT;
175 
176 		/*
177 		 * Wait for audit(8) -n process to complete
178 		 *
179 		 */
180 		do {
181 			if ((rc = waitpid(pid, NULL, WNOHANG)) == pid) {
182 				return (0);
183 			} else if (rc == -1) {
184 				return (-1);
185 			} else {
186 				(void) sleep(1);
187 				retries--;
188 			}
189 
190 		} while (retries != 0);
191 	}
192 	return (-1);
193 }
194 
195 static void
wait_for_auqueue()196 wait_for_auqueue()
197 {
198 	au_stat_t	au_stat;
199 	int		retries = 10;
200 
201 	while (retries-- && auditon(A_GETSTAT, (caddr_t)&au_stat, 0) == 0) {
202 		if (au_stat.as_enqueue == au_stat.as_written) {
203 			break;
204 		}
205 		(void) sleep(1);
206 	}
207 }
208 
209 /* End of Audit-related code */
210 
211 /* ARGSUSED0 */
212 static void
alarm_handler(int sig)213 alarm_handler(int sig)
214 {
215 	siglongjmp(jmp_stack, 1);
216 }
217 
218 /*
219  * These are functions that would be candidates for moving to a library.
220  */
221 
222 /*
223  * pm_poweroff - similar to poweroff(8)
224  * This should do the same auditing as poweroff(8) would do when it
225  * becomes a libpower function.  Till then we use poweroff(8).
226  */
227 static void
pm_poweroff(void)228 pm_poweroff(void)
229 {
230 	if (chkauthattr(AUTHNAME_SHUTDOWN, user) != 1) {
231 		(void) printf(gettext("User %s does not have correct "
232 		    "authorizations to shutdown this machine.\n"), user);
233 		exit(1);
234 	}
235 	openlog("suspend", 0, LOG_DAEMON);
236 	syslog(LOG_NOTICE, "System is being shut down.");
237 	closelog();
238 
239 	/*
240 	 * Call poweroff(8) to shut down the system.
241 	 */
242 	(void) execl("/usr/sbin/poweroff", "poweroff", NULL);
243 
244 }
245 
246 /*
247  * pm_check_suspend() - Check to see if suspend is supported/enabled
248  * on this machine.
249  * Ultimately, we would prefer to get the "default" suspend type from
250  * a PM property or some other API, but for now, we know that STR is
251  * only available on x86 and STD is only available on Sparc.  It does
252  * make this function quite easy, though.
253  */
254 static int
pm_check_suspend(void)255 pm_check_suspend(void)
256 {
257 	/*
258 	 * Use the uadmin(2) "CHECK" command to see if suspend is supported
259 	 */
260 	return (uadmin(A_FREEZE, AD_CHECK_SUSPEND, 0));
261 }
262 
263 /*
264  * This entry point _should_ be the common entry to suspend.  It is in
265  * it's entirety here, but would be best moved to libpower when that
266  * is available.
267  */
268 static void
pm_suspend(void)269 pm_suspend(void)
270 {
271 	int			cprarg = AD_SUSPEND;
272 	enum adt_uadmin_fcn	fcn_id = ADT_FCN;
273 	au_event_t		event_id = ADT_uadmin_freeze;
274 	adt_event_data_t	*event = NULL; /* event to be generated */
275 	adt_session_data_t	*ah = NULL;  /* audit session handle */
276 
277 	/*
278 	 * Does the user have permission to use this command?
279 	 */
280 	if (chkauthattr(AUTHNAME_SUSPEND, user) != 1) {
281 		(void) printf(gettext("User %s does not have correct "
282 		    "authorizations to suspend this machine.\n"), user);
283 		exit(1);
284 	}
285 
286 	if (flags & LOWPOWER) {
287 		if (bringto_lowpower() == -1) {
288 			(void) printf(gettext("LowPower Failed\n"));
289 			exit(1);
290 		}
291 	} else if (flags & TEST) {
292 		/*
293 		 * Test mode, do checks as if a real suspend, but
294 		 * don't actually do the suspend.
295 		 */
296 		/* Check if suspend is supported */
297 		if (pm_check_suspend() == -1) {
298 			suspend_error(errno);
299 		}
300 
301 		(void) printf(gettext("TEST: Suspend would have been"
302 		    " performed\n"));
303 
304 	} else {
305 		/* Check if suspend is supported */
306 		if (pm_check_suspend() == -1) {
307 			suspend_error(errno);
308 		}
309 
310 		/*
311 		 * We are about to suspend this machine, try and
312 		 * lock the screen.  We don't really care if this
313 		 * succeeds or not, but that we actually tried. We
314 		 * also know that we have sufficient privileges to
315 		 * be here, so we lock the screen now, even if
316 		 * suspend actually fails.
317 		 * Note that garbage is sometimes displayed, and
318 		 * we don't really care about it, so we toss all
319 		 * text response.
320 		 * it would also be good if there were another option
321 		 * instead of launcing a file, as the disk might be
322 		 * spun down if we are suspending due to idle.
323 		 */
324 		if (!(flags & NO_XLOCK)) {
325 			(void) system("/usr/bin/xdg-screensaver lock "
326 			    " >/dev/null 2>&1");
327 		}
328 
329 		/* Time to do the actual deed!  */
330 		/*
331 		 * Before we actually suspend, we need to audit and
332 		 * "suspend" the audit files.
333 		 */
334 		/* set up audit session and event */
335 		if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) == 0) {
336 			if ((event = adt_alloc_event(ah, event_id)) != NULL) {
337 				event->adt_uadmin_freeze.fcn = fcn_id;
338 				event->adt_uadmin_freeze.mdep = NULL;
339 				if (adt_put_event(event, ADT_SUCCESS, 0) != 0) {
340 					(void) fprintf(stderr, gettext(
341 					    "%s: can't put audit event\n"),
342 					    argvl[0]);
343 				} else {
344 					wait_for_auqueue();
345 				}
346 			}
347 			(void) change_audit_file();
348 		} else {
349 			(void) fprintf(stderr, gettext(
350 			    "%s: can't start audit session\n"), argvl[0]);
351 		}
352 
353 		if (uadmin(A_FREEZE, cprarg, 0) != 0) {
354 			(void) printf(gettext("Suspend Failed\n"));
355 			if (flags & FORCE) {
356 				/*
357 				 * Note, that if we actually poweroff,
358 				 * that the poweroff function will handle
359 				 * that audit trail, and the resume
360 				 * trail is effectively done.
361 				 */
362 				pm_poweroff();
363 			} else {
364 				/* suspend_error() will exit. */
365 				suspend_error(errno);
366 				/*
367 				 * Audit the suspend failure and
368 				 * reuse the event, but don't create one
369 				 * if we don't already have one.
370 				 */
371 				if (event != NULL) {
372 					(void) adt_put_event(event,
373 					    ADT_FAILURE, 0);
374 				}
375 			}
376 		}
377 
378 		/*
379 		 * Write the thaw event.
380 		 */
381 		if (ah != NULL) {
382 			if ((event == NULL) &&
383 			    ((event = adt_alloc_event(ah, ADT_uadmin_thaw))
384 			    == NULL)) {
385 				(void) fprintf(stderr, gettext(
386 				    "%s: can't allocate thaw audit event\n"),
387 				    argvl[0]);
388 			} else {
389 				event->adt_uadmin_thaw.fcn = fcn_id;
390 				if (adt_put_event(event, ADT_SUCCESS, 0) != 0) {
391 					(void) fprintf(stderr, gettext(
392 					    "%s: can't put thaw audit event\n"),
393 					    argvl[0]);
394 				}
395 				(void) adt_free_event(event);
396 			}
397 		}
398 	}
399 	if ((no_tty ? 0 : 1) && !(flags & NO_XLOCK)) {
400 		pm_do_auth(ah);
401 	}
402 
403 	(void) adt_end_session(ah);
404 }
405 /* End of "library" functions */
406 
407 /*
408  * Print an appropriate error message and exit.
409  */
410 
411 static void
suspend_error(int error)412 suspend_error(int error)
413 {
414 	switch (error) {
415 	case EBUSY:
416 		(void) printf(gettext("suspend: "
417 		    "Suspend already in progress.\n\n"));
418 		exit(1);
419 		/*NOTREACHED*/
420 	case ENOMEM:
421 		/*FALLTHROUGH*/
422 	case ENOSPC:
423 		(void) printf(gettext("suspend: "
424 		    "Not enough resources to suspend.\n\n"));
425 		exit(1);
426 		/*NOTREACHED*/
427 	case ENOTSUP:
428 		(void) printf(gettext("suspend: "
429 		    "Suspend is not supported.\n\n"));
430 		exit(1);
431 		/*NOTREACHED*/
432 	case EPERM:
433 		(void) printf(gettext("suspend: "
434 		    "Not sufficient privileges.\n\n"));
435 		exit(1);
436 		/*NOTREACHED*/
437 	default:
438 		(void) printf(gettext("suspend: "
439 		    "unknown error.\n\n"));
440 		exit(1);
441 	}
442 
443 }
444 
445 /*
446  * refresh_dt() - Refresh screen when 'dtgreet' is running.
447  * This is here for compatibility reasons, and could be removed once
448  * dtgreet is no longer part of the system.
449  */
450 static int
refresh_dt()451 refresh_dt()
452 {
453 	int	status;
454 	struct stat	stat_buf;
455 
456 	/*
457 	 * If dtgreet exists, HUP it, otherwise just let screenlock
458 	 * do it's thing.
459 	 */
460 	if ((stat("/usr/dt/bin/dtgreet", &stat_buf) == 0) &&
461 	    (stat_buf.st_mode & S_IXUSR)) {
462 		switch (fork()) {
463 		case -1:
464 			break;
465 		case 0:
466 			(void) close(1);
467 			(void) execl("/usr/bin/pkill", "pkill",
468 			    "-HUP", "-u", "0", "-x", "dtgreet", NULL);
469 			break;
470 		default:
471 			(void) wait(&status);
472 		}
473 	}
474 
475 	return (0);
476 }
477 
478 #define	DT_TMP	"/var/dt/tmp"
479 
480 /*
481  * On enter, the "xauthority" string has the value "XAUTHORITY=".  On
482  * return, if a Xauthority file is found, concatenate it to this string,
483  * otherwise, return "xauthority" as it is.
484  */
485 static char *
get_xauthority(char * xauthority,size_t buflen)486 get_xauthority(char *xauthority, size_t buflen)
487 {
488 	pid_t uid;
489 	char *home_dir;
490 	struct passwd *pwd;
491 	char filepath[MAXPATHLEN];
492 	struct stat stat_buf;
493 	DIR *dirp;
494 	struct dirent *dp;
495 	char xauth[MAXPATHLEN] = "";
496 	time_t latest = 0;
497 
498 	uid = getuid();
499 
500 	/*
501 	 * Determine home directory of the user.
502 	 */
503 	if ((home_dir = getenv("HOME")) == NULL) {
504 		if ((pwd = getpwuid(uid)) == NULL) {
505 			(void) printf(gettext("Error: unable to get passwd "
506 			    "entry for user.\n"));
507 			exit(1);
508 		}
509 		home_dir = pwd->pw_dir;
510 	}
511 	if ((strlen(home_dir) + sizeof ("/.Xauthority")) >= MAXPATHLEN) {
512 		(void) printf(gettext("Error: path to home directory is too "
513 		    "long.\n"));
514 		exit(1);
515 	}
516 
517 	/*
518 	 * If there is a .Xauthority file in home directory, reference it.
519 	 */
520 	(void) snprintf(filepath, sizeof (filepath),
521 	    "%s/.Xauthority", home_dir);
522 	if (stat(filepath, &stat_buf) == 0) {
523 		(void) strlcpy(xauthority, filepath, buflen);
524 		return (xauthority);
525 	}
526 
527 	/*
528 	 * If Xsession can not access user's home directory, it creates the
529 	 * Xauthority file in "/var/dt/tmp" directory.  Since the exact
530 	 * name of the Xauthority is not known, search the directory and
531 	 * find the last changed file that starts with ".Xauth" and owned
532 	 * by the user.  Hopefully, that is the valid Xauthority file for
533 	 * the current X session.
534 	 */
535 	if ((dirp = opendir(DT_TMP)) == NULL)
536 		return (xauthority);
537 
538 	while ((dp = readdir(dirp)) != NULL) {
539 		if (strstr(dp->d_name, ".Xauth") != NULL) {
540 			(void) snprintf(filepath, sizeof (filepath),
541 			    "%s/%s", DT_TMP, dp->d_name);
542 			if (stat(filepath, &stat_buf) == -1)
543 				continue;
544 			if (stat_buf.st_uid != uid)
545 				continue;
546 			if (stat_buf.st_ctime > latest) {
547 				(void) strlcpy(xauth, filepath,
548 				    sizeof (xauth));
549 				latest = stat_buf.st_ctime;
550 			}
551 		}
552 	}
553 	(void) closedir(dirp);
554 
555 	(void) strlcpy(xauthority, xauth, buflen);
556 	return (xauthority);
557 }
558 
559 /*
560  * suspend can be called in following ways:
561  *	1. from daemon (powerd) for auto-shutdown.
562  *		a. there might be a OW/CDE environment
563  *		b. there might not be any windowing environment
564  *      2. by a user entered command.
565  *		a. the command can be entered from a cmdtool type OW/CDE tool
566  *		b. the command can be entered by a user logged in on a dumb
567  *		   terminal.
568  *			i) there might be a OW/CDE running on console
569  *			   and we have permission to talk to it.
570  *			ii) there is no OW/CDE running on console or we
571  *			   don't have permission to talk to it or console
572  *			   itself is the dumb terminal we have logged into.
573  *
574  * In main(), we decide on the correct case and call appropriate functions.
575  */
576 
577 int
main(int argc,char ** argv)578 main(int argc, char **argv)
579 {
580 	int		c;
581 	char		xauthority[MAXPATHLEN];
582 	struct passwd	*pw;
583 
584 	(void) signal(SIGHUP, SIG_IGN);
585 	(void) signal(SIGINT, SIG_IGN);
586 	(void) signal(SIGQUIT, SIG_IGN);
587 	(void) signal(SIGTSTP, SIG_IGN);
588 	(void) signal(SIGTTIN, SIG_IGN);
589 	(void) signal(SIGTTOU, SIG_IGN);
590 
591 	/*
592 	 * If suspend is invoked from a daemon (case 1 above), it
593 	 * will not have a working stdin, stdout and stderr. We need
594 	 * these to print proper error messages and possibly get user
595 	 * input. We attach them to console and hope that attachment
596 	 * works.
597 	 */
598 	if (ttyname(0) == NULL) {
599 		no_tty = 1;
600 		(void) dup2(open("/dev/console", O_RDONLY), 0);
601 		(void) dup2(open("/dev/console", O_WRONLY), 1);
602 		(void) dup2(open("/dev/console", O_WRONLY), 2);
603 	}
604 
605 	while ((c = getopt(argc, argv, "fnxhtd:")) != EOF) {
606 		switch (c) {
607 			case 'f':
608 				/*
609 				 * Force machine to poweroff if
610 				 * suspend fails
611 				 */
612 				flags |= FORCE;
613 				break;
614 			case 'n':
615 				/* No warning popups - Obsolete */
616 				flags |= NO_WARN;
617 				break;
618 			case 'x':
619 				/* Don't try to screenlock */
620 				flags |= NO_XLOCK;
621 				break;
622 			case 'h':
623 				/* Do a shutdown instead of suspend */
624 				flags |= SHUTDOWN;
625 				break;
626 			case 'd':
627 				/* Set the DISPLAY value in the environment */
628 				if (setenv("DISPLAY", optarg, 1) != 0) {
629 					(void) printf(gettext("Error: "
630 					    "unable to set DISPLAY "
631 					    "environment variable.\n"));
632 					return (1);
633 				}
634 				break;
635 			case 't':
636 				/* Test, don't actually do any operation */
637 				flags |= TEST;
638 				break;
639 			default:
640 				(void) printf(gettext("USAGE: suspend "
641 				    "[-fnxh] [-d <display>]\n"));
642 				return (1);
643 		}
644 	}
645 
646 	/*
647 	 * The action of pressing power key and power button on a MOU-3 machine
648 	 * causes suspend being invoked with SYSSUSPENDDODEFAULT
649 	 * enviromental variable set - indicating the default action is machine
650 	 * dependent: for MOU-3 type machine, "LowPower" mode is the default,
651 	 * for all the rest, "Suspend" is the default.  Existing suspend
652 	 * flags works the same.
653 	 */
654 	if (getenv("SYSSUSPENDDODEFAULT"))
655 		if (is_mou3())
656 			flags |= LOWPOWER;
657 
658 	if ((flags & FORCE) && (flags & LOWPOWER))
659 		flags &= ~LOWPOWER;
660 
661 	/*
662 	 * Flag "-h" overrides flag "-f".
663 	 */
664 	if ((flags & SHUTDOWN) && (flags & FORCE))
665 		flags &= ~(FORCE | LOWPOWER);
666 
667 	if (flags & FORCE)
668 		flags |= NO_WARN;
669 
670 	/*
671 	 * Check initally if the user has the authorizations to
672 	 * do either a suspend or shutdown.  pm_suspend() will also
673 	 * make this test, so we could defer till then, but if we
674 	 * do it now, we at least prevent a lot of unneeded setup.
675 	 */
676 	pw = getpwuid(getuid());
677 	(void) strncpy(user, pw->pw_name, NMAX);
678 
679 	if ((flags & (FORCE|SHUTDOWN)) &&
680 	    (chkauthattr(AUTHNAME_SHUTDOWN, pw->pw_name) != 1)) {
681 		(void) printf(gettext("User does not have correct "
682 		    "authorizations to shutdown the machine.\n"));
683 		exit(1);
684 	}
685 	if (!(flags & SHUTDOWN) &&
686 	    (chkauthattr(AUTHNAME_SUSPEND, pw->pw_name) != 1)) {
687 		(void) printf(gettext("User does not have correct "
688 		    "authorizations to suspend.\n"));
689 		exit(1);
690 	}
691 
692 	/*
693 	 * If we are only shutting down, there isn't much to do, just
694 	 * call pm_poweroff(), and let it do all the work.
695 	 */
696 	if (flags & SHUTDOWN) {
697 		/*
698 		 * pm_poweroff either powers off or exits,
699 		 * so there is no return.
700 		 */
701 		if (flags & TEST) {
702 			(void) printf("TEST: This machine would have "
703 			    "powered off\n");
704 			exit(1);
705 		} else {
706 			pm_poweroff();
707 		}
708 		/* NOTREACHED */
709 	}
710 
711 	/*
712 	 * If XAUTHORITY environment variable is not set, try to set
713 	 * one up.
714 	 */
715 	if (getenv("XAUTHORITY") == NULL)
716 		(void) setenv("XAUTHORITY",
717 		    get_xauthority(xauthority, MAXPATHLEN), 1);
718 
719 	/*
720 	 * In case of "suspend" being called from daemon "powerd",
721 	 * signal SIGALRM is blocked so use "sigset()" instead of "signal()".
722 	 */
723 	(void) sigset(SIGALRM, alarm_handler);
724 
725 	/* Call the "suspend" function to do the last of the work */
726 	pm_suspend();
727 
728 	if (refresh_dt() == -1) {
729 		(void) printf("%s: Failed to refresh screen.\n", argv[0]);
730 		return (1);
731 	}
732 	return (0);
733 }
734 
735 #include <sys/pm.h>
736 
737 /*
738  * Note that some of these functions are more relevant to Sparc platforms,
739  * but they do function properly on other platforms, they just don't do
740  * as much.
741  */
742 /*
743  * bringto_lowpower()
744  * This tells the PM framework to put the devices it controls in an idle
745  * state.  The framework only complains if a device that *must* be idle
746  * doesn't succeed in getting there.
747  */
748 static int
bringto_lowpower()749 bringto_lowpower()
750 {
751 	int	fd;
752 
753 	if ((fd = open("/dev/pm", O_RDWR)) < 0) {
754 		(void) printf(gettext("Can't open /dev/pm\n"));
755 		return (-1);
756 	}
757 
758 	if (ioctl(fd, PM_IDLE_DOWN, NULL) < 0) {
759 		(void) printf(gettext("Failed to bring system "
760 		    "to low power mode.\n"));
761 		(void) close(fd);
762 		return (-1);
763 	}
764 	(void) close(fd);
765 	return (0);
766 }
767 
768 #include <sys/cpr.h>
769 
770 /*
771  * Though this test is predominantly used on Sparc, it will run on other
772  * platforms, and might be usefull one day on those.
773  */
774 static int
is_mou3()775 is_mou3()
776 {
777 	struct cprconfig	cf;
778 	int			fd;
779 	int			found = 0;
780 
781 	if ((fd = open(CPR_CONFIG, O_RDONLY)) < 0) {
782 		(void) printf(gettext("Can't open /etc/.cpr_config file."));
783 		return (found);
784 	}
785 
786 	if (read(fd, (void *) &cf, sizeof (cf)) != sizeof (cf)) {
787 		(void) printf(gettext("Can't read /etc/.cpr_config file."));
788 	} else {
789 		found = cf.is_autopm_default;
790 	}
791 
792 	(void) close(fd);
793 	return (found);
794 }
795 
796 /*
797  * Reauthenticate the user on return from suspend.
798  * This is here and not in the PAM-specific file, as there are
799  * items specific to sys-suspend, and not generic to PAM.  This may
800  * become part of a future PM library.  The audit handle is passed,
801  * as the pm_suspend code actually starts an audit session, so it
802  * makes sense to just continue to use it.  If it were separated
803  * from the pm_suspend code, it will need to open a new session.
804  */
805 #define	DEF_ATTEMPTS	3
806 static void
pm_do_auth(adt_session_data_t * ah)807 pm_do_auth(adt_session_data_t *ah)
808 {
809 	pam_handle_t	*pm_pamh;
810 	int		err;
811 	int		pam_flag = 0;
812 	int		chpasswd_tries;
813 	struct pam_conv pam_conv = {pam_tty_conv, NULL};
814 
815 	if (user[0] == '\0')
816 		return;
817 
818 	if ((err = pam_start("sys-suspend", user, &pam_conv,
819 	    &pm_pamh)) != PAM_SUCCESS)
820 		return;
821 
822 	pam_flag = PAM_DISALLOW_NULL_AUTHTOK;
823 
824 	do {
825 		err = pam_authenticate(pm_pamh, pam_flag);
826 
827 		if (err == PAM_SUCCESS) {
828 			err = pam_acct_mgmt(pm_pamh, pam_flag);
829 
830 			if (err == PAM_NEW_AUTHTOK_REQD) {
831 				chpasswd_tries = 0;
832 
833 				do {
834 					err = pam_chauthtok(pm_pamh,
835 					    PAM_CHANGE_EXPIRED_AUTHTOK);
836 					chpasswd_tries++;
837 
838 				} while ((err == PAM_AUTHTOK_ERR ||
839 				    err == PAM_TRY_AGAIN) &&
840 				    chpasswd_tries < DEF_ATTEMPTS);
841 				pm_audit_event(ah, ADT_passwd, err);
842 			}
843 			err = pam_setcred(pm_pamh, PAM_REFRESH_CRED);
844 		}
845 		if (err != PAM_SUCCESS) {
846 			(void) fprintf(stdout, "%s\n",
847 			    pam_strerror(pm_pamh, err));
848 			pm_audit_event(ah, ADT_screenunlock, err);
849 		}
850 	} while (err != PAM_SUCCESS);
851 	pm_audit_event(ah, ADT_passwd, 0);
852 
853 	(void) pam_end(pm_pamh, err);
854 }
855