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 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 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