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