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