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#include <stdio.h>
32#include <errno.h>
33#include <string.h>
34#include <stdlib.h>
35#include <unistd.h>
36#include <signal.h>
37#include <wait.h>
38#include <sys/types.h>
39#include <sys/stat.h>
40#include <fcntl.h>
41#include <pwd.h>
42#include <grp.h>
43#include <note.h>
44#include "pkglib.h"
45#include "pkglibmsgs.h"
46#include "pkglocale.h"
47
48/* global environment inherited by this process */
49extern char	**environ;
50
51/* dstream.c */
52extern int	ds_curpartcnt;
53extern int	ds_close(int pkgendflg);
54
55/*
56 * global internal (private) variables
57 */
58
59/* received signal count - bumped with hooked signals are caught */
60
61static int	sig_received = 0;
62
63/*
64 * Name:	sig_trap
65 * Description:	hooked up to signal counts number of signals received
66 * Arguments:	a_signo - [RO, *RO] - (int)
67 *			Integer representing the signal received; see signal(3c)
68 * Returns:	<void>
69 */
70
71static void
72sig_trap(int a_signo)
73{
74	_NOTE(ARGUNUSED(a_signo));
75	sig_received++;
76}
77
78/*
79 * Name:	pkgexecv
80 * Description:	Asynchronously execute a package command in a separate process
81 *		and return results - the subprocess MUST arm it's own SIGINT
82 *		and SIGHUP signals and must return a standard package command
83 *		exit code (see returns below)
84 *		Only another package command (such as pkginstall, pkgremove,
85 *		etc.) may be called via this interface. No files are closed
86 *		because open files are passed across to certain commands using
87 *		either implicit agreements between the two (yuk!) or by using
88 *		the '-p' option which passes a string of digits, some of which
89 *		represent open file descriptors passed through this interface!
90 * Arguments:	filein - [RO, *RO] - (char *)
91 *			Pointer to string representing the name of the file to
92 *			use for the package commands's stdin
93 *			== (char *)NULL or == "" - the current stdin
94 *			is used for the new package command process
95 *		fileout - [RO, *RO] - (char *)
96 *			Pointer to string representing the name of the file to
97 *			use for the package commands's stdout and stderr
98 *			== (char *)NULL or == "" - the current stdout/stderr
99 *			is used for the new package command process
100 *		uname - [RO, *RO] - (char *)
101 *			Pointer to string representing the user name to execute
102 *			the package command as - the user name is looked up
103 *			using the ncgrpw:cpwnam() interface
104 *			== (char *)NULL or == "" - the user name of the current
105 *			process is used for the new package command process
106 *		gname - [RO, *RO] - (char *)
107 *			Pointer to string representing the group name to execute
108 *			the package command as - the group name is looked up
109 *			using the ncgrpw:cgrnam() interface
110 *			== (char *)NULL or == "" - the group name of the current
111 *			process is used for the new package command process
112 *		arg - [RO, *RO] - (char **)
113 *			Pointer to array of character pointers representing the
114 *			arguments to pass to the package command - the array is
115 *			terminated with a pointer to (char *)NULL
116 * Returns:	int
117 *			== 99 - exec() of package command failed
118 *			== -1 - fork failed or other fatal error during
119 *				execution of the package command
120 *			otherwise - exit code from package command:
121 *			0 - successful
122 *			1 - package operation failed (fatal error)
123 *			2 - non-fatal error (warning)
124 *			3 - operation interrupted (including SIGINT/SIGHUP)
125 *			4 - admin settings prevented operation
126 *			5 - administration required and -n was specified
127 *			IN addition:
128 *			10 is added to the return code if reboot after the
129 *				installation of all packages is required
130 *			20 is added to the return code if immediate reboot
131 *				after installation of this package is required
132 */
133
134int
135pkgexecv(char *filein, char *fileout, char *uname, char *gname, char *arg[])
136{
137	int			exit_no;
138	int			n;
139	int			status;
140	pid_t			pid;
141	pid_t			waitstat;
142	struct group		*grp;
143	struct passwd		*pwp;
144	struct sigaction	nact;
145	struct sigaction	oact;
146	void			(*funcSighup)();
147	void			(*funcSigint)();
148
149	/* flush standard i/o before creating new process */
150
151	(void) fflush(stdout);
152	(void) fflush(stderr);
153
154	/*
155	 * hold SIGINT/SIGHUP signals and reset signal received counter;
156	 * after the vfork() the parent and child need to setup their respective
157	 * interrupt handling and release the hold on the signals
158	 */
159
160	(void) sighold(SIGINT);
161	(void) sighold(SIGHUP);
162
163	sig_received = 0;
164
165	/*
166	 * create new process to execute command in;
167	 * vfork() is being used to avoid duplicating the parents
168	 * memory space - this means that the child process may
169	 * not modify any of the parents memory including the
170	 * standard i/o descriptors - all the child can do is
171	 * adjust interrupts and open files as a prelude to a
172	 * call to exec().
173	 */
174
175	pid = vfork();
176
177	if (pid < 0) {
178		/*
179		 * *************************************************************
180		 * fork failed!
181		 * *************************************************************
182		 */
183
184		progerr(pkg_gt(ERR_FORK_FAILED), errno, strerror(errno));
185
186		/* release hold on signals */
187
188		(void) sigrelse(SIGHUP);
189		(void) sigrelse(SIGINT);
190
191		return (-1);
192	}
193
194	if (pid > 0) {
195		/*
196		 * *************************************************************
197		 * This is the forking (parent) process
198		 * *************************************************************
199		 */
200
201		/* close datastream if any portion read */
202
203		if (ds_curpartcnt >= 0) {
204			if (ds_close(0) != 0) {
205				/* kill child process */
206
207				(void) sigsend(P_PID, pid, SIGKILL);
208
209				/* release hold on signals */
210
211				(void) sigrelse(SIGHUP);
212				(void) sigrelse(SIGINT);
213
214				return (-1);
215			}
216		}
217
218		/*
219		 * setup signal handlers for SIGINT and SIGHUP and release hold
220		 */
221
222		/* hook SIGINT to sig_trap() */
223
224		nact.sa_handler = sig_trap;
225		nact.sa_flags = SA_RESTART;
226		(void) sigemptyset(&nact.sa_mask);
227
228		if (sigaction(SIGINT, &nact, &oact) < 0) {
229			funcSigint = SIG_DFL;
230		} else {
231			funcSigint = oact.sa_handler;
232		}
233
234		/* hook SIGHUP to sig_trap() */
235
236		nact.sa_handler = sig_trap;
237		nact.sa_flags = SA_RESTART;
238		(void) sigemptyset(&nact.sa_mask);
239
240		if (sigaction(SIGHUP, &nact, &oact) < 0) {
241			funcSighup = SIG_DFL;
242		} else {
243			funcSighup = oact.sa_handler;
244		}
245
246		/* release hold on signals */
247
248		(void) sigrelse(SIGHUP);
249		(void) sigrelse(SIGINT);
250
251		/*
252		 * wait for the process to exit, reap child exit status
253		 */
254
255		for (;;) {
256			status = 0;
257			waitstat = waitpid(pid, &status, 0);
258			if (waitstat < 0) {
259				/* waitpid returned error */
260				if (errno == EAGAIN) {
261					/* try again */
262					continue;
263				}
264				if (errno == EINTR) {
265					continue;
266				}
267				/* error from waitpid: bail */
268				break;
269			} else if (waitstat == pid) {
270				/* child exit status available */
271				break;
272			}
273		}
274
275		/*
276		 * reset signal handlers
277		 */
278
279		/* reset SIGINT */
280
281		nact.sa_handler = funcSigint;
282		nact.sa_flags = SA_RESTART;
283		(void) sigemptyset(&nact.sa_mask);
284
285		(void) sigaction(SIGINT, &nact, (struct sigaction *)NULL);
286
287		/* reset SIGHUP */
288
289		nact.sa_handler = funcSighup;
290		nact.sa_flags = SA_RESTART;
291		(void) sigemptyset(&nact.sa_mask);
292
293		(void) sigaction(SIGHUP, &nact, (struct sigaction *)NULL);
294
295		/* error if child process does not match */
296
297		if (waitstat != pid) {
298			progerr(pkg_gt(ERR_WAIT_FAILED), pid, status,
299			    errno, strerror(errno));
300			return (-1);
301		}
302
303		/*
304		 * determine final exit code:
305		 * - if signal received, then return interrupted (3)
306		 * - if child exit status is available, return exit child status
307		 * - otherwise return error (-1)
308		 */
309
310		if (sig_received != 0) {
311			exit_no = 3;	/* interrupted */
312		} else if (WIFEXITED(status)) {
313			exit_no = WEXITSTATUS(status);
314		} else {
315			exit_no = -1;	/* exec() or other process error */
316		}
317
318		return (exit_no);
319	}
320
321	/*
322	 * *********************************************************************
323	 * This is the forked (child) process
324	 * *********************************************************************
325	 */
326
327	/* reset all signals to default */
328
329	for (n = 0; n < NSIG; n++) {
330		(void) sigset(n, SIG_DFL);
331	}
332
333	/* release hold on signals held by parent before fork() */
334
335	(void) sigrelse(SIGHUP);
336	(void) sigrelse(SIGINT);
337
338	/*
339	 * The caller wants to have stdin connected to filein.
340	 */
341
342	if (filein && *filein) {
343		/*
344		 * If input is supposed to be connected to /dev/tty
345		 */
346		if (strncmp(filein, "/dev/tty", 8) == 0) {
347			/*
348			 * If stdin is connected to a tty device.
349			 */
350			if (isatty(STDIN_FILENO)) {
351				/*
352				 * Reopen it to /dev/tty.
353				 */
354				n = open(filein, O_RDONLY);
355				if (n >= 0) {
356					(void) dup2(n, STDIN_FILENO);
357				}
358			}
359		} else {
360			/*
361			 * If we did not want to be connected to /dev/tty, we
362			 * connect input to the requested file no questions.
363			 */
364			n = open(filein, O_RDONLY);
365			if (n >= 0) {
366				(void) dup2(n, STDIN_FILENO);
367			}
368		}
369	}
370
371	/*
372	 * The caller wants to have stdout and stderr connected to fileout.
373	 * If "fileout" is "/dev/tty" then reconnect stdout to "/dev/tty"
374	 * only if /dev/tty is not already associated with "a tty".
375	 */
376
377	if (fileout && *fileout) {
378		/*
379		 * If output is supposed to be connected to /dev/tty
380		 */
381		if (strncmp(fileout, "/dev/tty", 8) == 0) {
382			/*
383			 * If stdout is connected to a tty device.
384			 */
385			if (isatty(STDOUT_FILENO)) {
386				/*
387				 * Reopen it to /dev/tty if /dev/tty available.
388				 */
389				n = open(fileout, O_WRONLY);
390				if (n >= 0) {
391					/*
392					 * /dev/tty is available - close the
393					 * current standard output stream, and
394					 * reopen it on /dev/tty
395					 */
396					(void) dup2(n, STDOUT_FILENO);
397				}
398			}
399			/*
400			 * not connected to tty device - probably redirect to
401			 * file - preserve existing output device
402			 */
403		} else {
404			/*
405			 * If we did not want to be connected to /dev/tty, we
406			 * connect output to the requested file no questions.
407			 */
408			/* LINTED O_CREAT without O_EXCL specified in call to */
409			n = open(fileout, O_WRONLY|O_CREAT|O_APPEND, 0666);
410			if (n >= 0) {
411				(void) dup2(n, STDOUT_FILENO);
412			}
413		}
414
415		/*
416		 * Dup stderr from stdout.
417		 */
418
419		(void) dup2(STDOUT_FILENO, STDERR_FILENO);
420	}
421
422	/*
423	 * do NOT close all file descriptors except stdio
424	 * file descriptors are passed in to some subcommands
425	 * (see dstream:ds_getinfo() and dstream:ds_putinfo())
426	 */
427
428	/* set group/user i.d. if requested */
429
430	if (gname && *gname && (grp = cgrnam(gname)) != NULL) {
431		if (setgid(grp->gr_gid) == -1) {
432			progerr(pkg_gt(ERR_SETGID), grp->gr_gid);
433		}
434	}
435	if (uname && *uname && (pwp = cpwnam(uname)) != NULL) {
436		if (setuid(pwp->pw_uid) == -1) {
437			progerr(pkg_gt(ERR_SETUID), pwp->pw_uid);
438		}
439	}
440
441	/* execute target executable */
442
443	(void) execve(arg[0], arg, environ);
444	progerr(pkg_gt(ERR_EX_FAIL), arg[0], errno);
445	_exit(99);
446	/*NOTREACHED*/
447}
448