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