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/*
23 * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28/* All Rights Reserved */
29
30
31/*
32 * System includes
33 */
34
35#include <stdio.h>
36#include <string.h>
37#include <signal.h>
38#include <stdlib.h>
39#include <unistd.h>
40#include <sys/utsname.h>
41#include <locale.h>
42#include <libintl.h>
43
44/*
45 * consolidation pkg command library includes
46 */
47
48#include <pkglib.h>
49
50/*
51 * local pkg command library includes
52 */
53
54#include "install.h"
55#include "libadm.h"
56#include "libinst.h"
57#include "messages.h"
58
59#define	MAILCMD	"/usr/bin/mail"
60
61/* lockinst.c */
62extern void	unlockinst(void);
63
64/* mntinfo.c */
65extern int	unmount_client(void);
66
67extern char	*msgtext;
68extern char	*pkginst;
69
70extern int	started;
71extern int	dreboot;	/* != 0 if reboot required after installation */
72extern int	failflag;	/* != 0 if fatal error has occurred (1) */
73extern int	ireboot;	/* != 0 if immediate reboot required */
74extern int	warnflag;	/* != 0 if non-fatal error has occurred (2) */
75
76extern struct admin	adm;
77
78/*
79 * exported functions
80 */
81
82void			quit(int retcode);
83void			quitSetSilentExit(boolean_t a_silentExit);
84void			quitSetZoneName(char *a_zoneName);
85sighdlrFunc_t		*quitGetTrapHandler(void);
86
87/*
88 * forward declarations
89 */
90
91static void		mailmsg(int retcode);
92static void		quitmsg(int retcode);
93static void		trap(int signo);
94
95static char		*zoneName = (char *)NULL;
96static boolean_t	silentExit = B_FALSE;
97static int		includeZonename = 0;
98static int		trapEntered = 0;
99
100/*
101 * *****************************************************************************
102 * global external (public) functions
103 * *****************************************************************************
104 */
105
106/*
107 * Name:	quitGetTrapHandler
108 * Description:	return address of this modules "signal trap" handler
109 * Arguments:	void
110 * Returns:	sighdlrFunc_t
111 *			The address of the trap handler that can be passed to
112 *			the signal() type system calls
113 */
114
115sighdlrFunc_t *
116quitGetTrapHandler()
117{
118	return (&trap);
119}
120
121/*
122 * Name:	quitSetZoneName
123 * Description:	set the zone name the program is running in
124 * Arguments:	a_zoneName - pointer to string representing the name of the zone
125 *			that the program is running in
126 * Returns:	void
127 */
128
129void
130quitSetZoneName(char *a_zoneName)
131{
132	zoneName = a_zoneName;
133	if ((zoneName == (char *)NULL || *zoneName == '\0')) {
134		includeZonename = 0;
135	} else {
136		includeZonename = 1;
137	}
138}
139
140/*
141 * Name:	quitSetSilentExit
142 * Description:	set the "silent exit" flag - if silent exit is TRUE, then
143 *		no messages are output by quit() when it is called
144 * Arguments:	a_silentExit - indicates whether or not silent exit is set
145 * Returns:	void
146 */
147
148void
149quitSetSilentExit(boolean_t a_silentExit)
150{
151	silentExit = a_silentExit;
152}
153
154/*
155 * Name:	quit
156 * Description:	cleanup and exit
157 * Arguments:	a_retcode - the code to use to determine final exit status;
158 *			if this is NOT "99" and if a "ckreturnFunc" is
159 *			set, then that function is called with a_retcode
160 *			to set the final exit status.
161 *		Valid values are:
162 *		0 - success
163 *		1 - package operation failed (fatal error)
164 *		2 - non-fatal error (warning)
165 *		3 - user selected quit (operation interrupted)
166 *		4 - admin settings prevented operation
167 *		5 - interaction required and -n (non-interactive) specified
168 *		"10" is added to indicate "immediate reboot required"
169 *		"20" is be added to indicate "reboot after install required"
170 *		99 - do not interpret the code - just exit "99"
171 * Returns:	<<this function does not return - calls exit()>>
172 */
173
174void
175quit(int retcode)
176{
177	/* disable interrupts */
178
179	(void) signal(SIGINT, SIG_IGN);
180	(void) signal(SIGHUP, SIG_IGN);
181
182	/* process return code if not quit(99) */
183
184	if (retcode != 99) {
185		if ((retcode % 10) == 0) {
186			if (failflag) {
187				retcode += 1;
188			} else if (warnflag) {
189				retcode += 2;
190			}
191		}
192
193		if (ireboot) {
194			retcode = (retcode % 10) + 20;
195		}
196
197		if (dreboot) {
198			retcode = (retcode % 10) + 10;
199		}
200	}
201
202	/*
203	 * In the event that this quit() was called prior to completion of
204	 * the task, do an unlockinst() just in case.
205	 */
206	unlockinst();
207
208	/* unmount the mounts that are our responsibility. */
209	(void) unmount_client();
210
211	/* send mail to appropriate user list */
212	mailmsg(retcode);
213
214	/* display message about this installation */
215	quitmsg(retcode);
216
217	/* final exit debugging message */
218
219	echoDebug(DBG_EXIT_WITH_CODE, retcode);
220
221	exit(retcode);
222	/*NOTREACHED*/
223}
224
225/*
226 * *****************************************************************************
227 * static internal (private) functions
228 * *****************************************************************************
229 */
230
231static void
232quitmsg(int retcode)
233{
234	if (silentExit == B_TRUE) {
235		return;
236	}
237
238	(void) putc('\n', stderr);
239
240	/* if there is no pkgname, no message to report */
241	if (pkginst != (char *)NULL) {
242		ptext(stderr, qreason(3, retcode, 0, includeZonename),
243			pkginst, zoneName);
244	}
245
246	if (retcode && !started) {
247		ptext(stderr, MSG_NOCHANGE);
248	}
249}
250
251static void
252mailmsg(int retcode)
253{
254	struct utsname utsbuf;
255	FILE	*pp;
256	char	*cmd;
257	size_t	len;
258
259	if (silentExit == B_TRUE) {
260		return;
261	}
262
263	if (!started || (adm.mail == NULL))
264		return;
265
266	len = strlen(adm.mail) + sizeof (MAILCMD) + 2;
267	cmd = calloc(len, sizeof (char));
268	if (cmd == NULL) {
269		logerr(WRN_NOMAIL);
270		return;
271	}
272
273	(void) snprintf(cmd, len, "%s %s", MAILCMD, adm.mail);
274	if ((pp = popen(cmd, "w")) == NULL) {
275		logerr(WRN_NOMAIL);
276		return;
277	}
278
279	if (msgtext) {
280		ptext(pp, gettext(msgtext));
281	}
282
283	(void) strcpy(utsbuf.nodename, gettext("(unknown)"));
284	(void) uname(&utsbuf);
285	ptext(pp, qreason(4, retcode, 0, includeZonename), pkginst,
286		utsbuf.nodename, zoneName);
287
288	if (pclose(pp)) {
289		logerr(WRN_FLMAIL);
290	}
291}
292
293/*
294 * Name:	trap
295 * Description:	signal handler connected via quitGetTrapHandler()
296 * Arguments:	signo - [RO, *RO] - (int)
297 *			Integer representing the signal that caused the trap
298 *			to this function to occur
299 * Returns:	<< NONE >>
300 * NOTE:	This function exits the program after doing mandatory cleanup.
301 * NOTE:	Even though quit() should NOT return, there is a call to _exit()
302 *		put after each call to quit() just in case quit() ever returned
303 *		by mistake.
304 */
305
306static void
307trap(int signo)
308{
309	/* prevent reentrance */
310
311	if (trapEntered++ != 0) {
312		return;
313	}
314
315	if ((signo == SIGINT) || (signo == SIGHUP)) {
316		quit(3);
317		_exit(3);
318	}
319	quit(1);
320	_exit(1);
321}
322