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 */
49 extern char **environ;
50
51 /* dstream.c */
52 extern int ds_curpartcnt;
53 extern 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
61 static 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
71 static void
sig_trap(int a_signo)72 sig_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
134 int
pkgexecv(char * filein,char * fileout,char * uname,char * gname,char * arg[])135 pkgexecv(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