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