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