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 2009 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 32 #include <stdio.h> 33 #include <errno.h> 34 #include <string.h> 35 #include <strings.h> 36 #include <signal.h> 37 #include <fcntl.h> 38 #include <stdlib.h> 39 #include <unistd.h> 40 #include <wait.h> 41 #include <sys/types.h> 42 #include "pkglib.h" 43 #include "pkglocale.h" 44 #include "pkglibmsgs.h" 45 46 #ifndef _STDARG_H 47 #include "stdarg.h" 48 #endif 49 50 /* 51 * Private definitions 52 */ 53 54 /* Maximum number of arguments to pkg_ExecCmdList */ 55 56 #define MAX_EXEC_CMD_ARGS 100 57 58 /* Size of buffer increments when reading from pipe */ 59 60 #define PIPE_BUFFER_INCREMENT 256 61 62 static char errfile[L_tmpnam+1]; 63 64 /* 65 * Public Methods 66 */ 67 68 69 void 70 rpterr(void) 71 { 72 FILE *fp; 73 int c; 74 75 if (errfile[0]) { 76 if (fp = fopen(errfile, "r")) { 77 while ((c = getc(fp)) != EOF) 78 (void) putc(c, stderr); 79 (void) fclose(fp); 80 } 81 (void) unlink(errfile); 82 errfile[0] = '\0'; 83 } 84 } 85 86 void 87 ecleanup(void) 88 { 89 if (errfile[0]) { 90 (void) unlink(errfile); 91 errfile[0] = '\0'; 92 } 93 } 94 95 int 96 esystem(char *cmd, int ifd, int ofd) 97 { 98 char *perrfile; 99 int status = 0; 100 pid_t pid; 101 102 perrfile = tmpnam(NULL); 103 if (perrfile == NULL) { 104 progerr( 105 pkg_gt("unable to create temp error file, errno=%d"), 106 errno); 107 return (-1); 108 } 109 (void) strlcpy(errfile, perrfile, sizeof (errfile)); 110 111 /* flush standard i/o before creating new process */ 112 113 (void) fflush(stderr); 114 (void) fflush(stdout); 115 116 /* 117 * create new process to execute command in; 118 * vfork() is being used to avoid duplicating the parents 119 * memory space - this means that the child process may 120 * not modify any of the parents memory including the 121 * standard i/o descriptors - all the child can do is 122 * adjust interrupts and open files as a prelude to a 123 * call to exec(). 124 */ 125 126 pid = vfork(); 127 if (pid == 0) { 128 /* 129 * this is the child process 130 */ 131 int i; 132 133 /* reset any signals to default */ 134 135 for (i = 0; i < NSIG; i++) { 136 (void) sigset(i, SIG_DFL); 137 } 138 139 if (ifd > 0) { 140 (void) dup2(ifd, STDIN_FILENO); 141 } 142 143 if (ofd >= 0 && ofd != STDOUT_FILENO) { 144 (void) dup2(ofd, STDOUT_FILENO); 145 } 146 147 i = open(errfile, O_WRONLY|O_CREAT|O_TRUNC, 0666); 148 if (i >= 0) { 149 (void) dup2(i, STDERR_FILENO); 150 } 151 152 /* Close all open files except standard i/o */ 153 154 closefrom(3); 155 156 /* execute target executable */ 157 158 (void) execl("/sbin/sh", "/sbin/sh", "-c", cmd, NULL); 159 progerr(pkg_gt("exec of <%s> failed, errno=%d"), cmd, errno); 160 _exit(99); 161 } else if (pid < 0) { 162 /* fork failed! */ 163 164 logerr(pkg_gt("bad vfork(), errno=%d"), errno); 165 return (-1); 166 } 167 168 /* 169 * this is the parent process 170 */ 171 172 (void) sighold(SIGINT); 173 pid = waitpid(pid, &status, 0); 174 (void) sigrelse(SIGINT); 175 176 if (pid < 0) { 177 return (-1); /* probably interrupted */ 178 } 179 180 switch (status & 0177) { 181 case 0: 182 case 0177: 183 status = status >> 8; 184 /*FALLTHROUGH*/ 185 186 default: 187 /* terminated by a signal */ 188 status = status & 0177; 189 } 190 191 if (status == 0) { 192 ecleanup(); 193 } 194 195 return (status); 196 } 197 198 FILE * 199 epopen(char *cmd, char *mode) 200 { 201 char *buffer, *perrfile; 202 FILE *pp; 203 size_t len; 204 size_t alen; 205 206 if (errfile[0]) { 207 /* cleanup previous errfile */ 208 (void) unlink(errfile); 209 } 210 211 perrfile = tmpnam(NULL); 212 if (perrfile == NULL) { 213 progerr( 214 pkg_gt("unable to create temp error file, errno=%d"), 215 errno); 216 return ((FILE *)0); 217 } 218 219 if (strlcpy(errfile, perrfile, sizeof (errfile)) > sizeof (errfile)) { 220 progerr(pkg_gt("file name max length %d; name is too long: %s"), 221 sizeof (errfile), perrfile); 222 return (NULL); 223 } 224 225 len = strlen(cmd)+6+strlen(errfile); 226 buffer = (char *)calloc(len, sizeof (char)); 227 if (buffer == NULL) { 228 progerr(pkg_gt("no memory in epopen(), errno=%d"), errno); 229 return (NULL); 230 } 231 232 if (strchr(cmd, '|')) { 233 alen = snprintf(buffer, len, "(%s) 2>%s", cmd, errfile); 234 } else { 235 alen = snprintf(buffer, len, "%s 2>%s", cmd, errfile); 236 } 237 238 if (alen > len) { 239 progerr(pkg_gt("command max length %d; cmd is too long: %s"), 240 len, cmd); 241 return (NULL); 242 } 243 244 pp = popen(buffer, mode); 245 246 free(buffer); 247 return (pp); 248 } 249 250 int 251 epclose(FILE *pp) 252 { 253 int n; 254 255 n = pclose(pp); 256 if (n == 0) 257 ecleanup(); 258 return (n); 259 } 260 261 /* 262 * Name: e_ExecCmdArray 263 * Synopsis: Execute Unix command and return results 264 * Description: Execute a Unix command and return results and status 265 * Arguments: 266 * r_status - [RO, *RW] - (int *) 267 * Return (exit) status from Unix command: 268 * == -1 : child terminated with a signal 269 * != -1 : lower 8-bit value child passed to exit() 270 * r_results - [RO, *RW] - (char **) 271 * Any output generated by the Unix command to stdout 272 * and to stderr 273 * == (char *)NULL if no output generated 274 * a_inputFile - [RO, *RO] - (char *) 275 * Pointer to character string representing file to be 276 * used as "standard input" for the command. 277 * == (char *)NULL to use "/dev/null" as standard input 278 * a_cmd - [RO, *RO] - (char *) 279 * Pointer to character string representing the full path 280 * of the Unix command to execute 281 * char **a_args - [RO, *RO] - (char **) 282 * List of character strings representing the arguments 283 * to be passed to the Unix command. The list must be 284 * terminated with an element that is (char *)NULL 285 * Returns: int 286 * == 0 - Command executed 287 * Look at r_status for results of Unix command 288 * != 0 - problems executing command 289 * r_status and r_results have no meaning; 290 * r_status will be -1 291 * r_results will be NULL 292 * NOTE: Any results returned is placed in new storage for the 293 * calling method. The caller must use 'free' to dispose 294 * of the storage once the results are no longer needed. 295 * NOTE: If 0 is returned, 'r_status' must be queried to 296 * determine the results of the Unix command. 297 * NOTE: The system "errno" value from immediately after waitpid() call 298 * is preserved for the calling method to use to determine 299 * the system reason why the operation failed. 300 */ 301 302 int 303 e_ExecCmdArray(int *r_status, char **r_results, 304 char *a_inputFile, char *a_cmd, char **a_args) 305 { 306 char *buffer; 307 int bufferIndex; 308 int bufferSize; 309 int ipipe[2] = {0, 0}; 310 pid_t pid; 311 pid_t resultPid; 312 int status; 313 int lerrno; 314 int stdinfile = -1; 315 316 /* reset return results buffer pointer */ 317 318 if (r_results != (char **)NULL) { 319 *r_results = (char *)NULL; 320 } 321 322 *r_status = -1; 323 324 /* 325 * See if command exists 326 */ 327 328 if (access(a_cmd, F_OK|X_OK) != 0) { 329 return (-1); 330 } 331 332 /* 333 * See if input file exists 334 */ 335 336 if (a_inputFile != (char *)NULL) { 337 stdinfile = open(a_inputFile, O_RDONLY); 338 } else { 339 stdinfile = open("/dev/null", O_RDONLY); /* stdin = /dev/null */ 340 } 341 342 if (stdinfile < 0) { 343 return (-1); 344 } 345 346 /* 347 * Create a pipe to be used to capture the command output 348 */ 349 350 if (pipe(ipipe) != 0) { 351 (void) close(stdinfile); 352 return (-1); 353 } 354 355 356 bufferSize = PIPE_BUFFER_INCREMENT; 357 bufferIndex = 0; 358 buffer = calloc(1, bufferSize); 359 if (buffer == (char *)NULL) { 360 (void) close(stdinfile); 361 return (-1); 362 } 363 364 /* flush standard i/o before creating new process */ 365 366 (void) fflush(stderr); 367 (void) fflush(stdout); 368 369 /* 370 * create new process to execute command in; 371 * vfork() is being used to avoid duplicating the parents 372 * memory space - this means that the child process may 373 * not modify any of the parents memory including the 374 * standard i/o descriptors - all the child can do is 375 * adjust interrupts and open files as a prelude to a 376 * call to exec(). 377 */ 378 379 pid = vfork(); 380 381 if (pid == 0) { 382 /* 383 * This is the forked (child) process ====================== 384 */ 385 386 int i; 387 388 /* reset any signals to default */ 389 390 for (i = 0; i < NSIG; i++) { 391 (void) sigset(i, SIG_DFL); 392 } 393 394 /* assign stdin, stdout, stderr as appropriate */ 395 396 (void) dup2(stdinfile, STDIN_FILENO); 397 (void) close(ipipe[0]); /* close out pipe reader side */ 398 (void) dup2(ipipe[1], STDOUT_FILENO); 399 (void) dup2(ipipe[1], STDERR_FILENO); 400 401 /* Close all open files except standard i/o */ 402 403 closefrom(3); 404 405 /* execute target executable */ 406 407 (void) execvp(a_cmd, a_args); 408 perror(a_cmd); /* Emit error msg - ends up in callers buffer */ 409 _exit(0x00FE); 410 } 411 412 /* 413 * This is the forking (parent) process ==================== 414 */ 415 416 (void) close(stdinfile); 417 (void) close(ipipe[1]); /* Close write side of pipe */ 418 419 /* 420 * Spin reading data from the child into the buffer - when the read eofs 421 * the child has exited 422 */ 423 424 for (;;) { 425 ssize_t bytesRead; 426 427 /* read as much child data as there is available buffer space */ 428 429 bytesRead = read(ipipe[0], buffer + bufferIndex, 430 bufferSize - bufferIndex); 431 432 /* break out of read loop if end-of-file encountered */ 433 434 if (bytesRead == 0) { 435 break; 436 } 437 438 /* if error, continue if recoverable, else break out of loop */ 439 440 if (bytesRead == -1) { 441 /* try again: EAGAIN - insufficient resources */ 442 443 if (errno == EAGAIN) { 444 continue; 445 } 446 447 /* try again: EINTR - interrupted system call */ 448 449 if (errno == EINTR) { 450 continue; 451 } 452 453 /* break out of loop - error not recoverable */ 454 break; 455 } 456 457 /* at least 1 byte read: expand buffer if at end */ 458 459 bufferIndex += bytesRead; 460 if (bufferIndex >= bufferSize) { 461 buffer = realloc(buffer, 462 bufferSize += PIPE_BUFFER_INCREMENT); 463 (void) memset(buffer + bufferIndex, 0, 464 bufferSize - bufferIndex); 465 } 466 } 467 468 (void) close(ipipe[0]); /* Close read side of pipe */ 469 470 /* Get subprocess exit status */ 471 472 for (;;) { 473 resultPid = waitpid(pid, &status, 0L); 474 lerrno = (resultPid == -1 ? errno : 0); 475 476 /* break loop if child process status reaped */ 477 478 if (resultPid != -1) { 479 break; 480 } 481 482 /* break loop if not interrupted out of waitpid */ 483 484 if (errno != EINTR) { 485 break; 486 } 487 } 488 489 /* 490 * If the child process terminated due to a call to exit(), then 491 * set results equal to the 8-bit exit status of the child process; 492 * otherwise, set the exit status to "-1" indicating that the child 493 * exited via a signal. 494 */ 495 496 *r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1; 497 498 /* return appropriate output */ 499 500 if (!*buffer) { 501 /* No contents in output buffer - discard */ 502 free(buffer); 503 } else if (r_results == (char **)NULL) { 504 /* Not requested to return results - discard */ 505 free(buffer); 506 } else { 507 /* have output and request to return: pass to calling method */ 508 *r_results = buffer; 509 } 510 511 errno = lerrno; 512 return (resultPid == -1 ? -1 : 0); 513 } 514 515 /* 516 * Name: e_ExecCmdList 517 * Synopsis: Execute Unix command and return results 518 * Description: Execute a Unix command and return results and status 519 * Arguments: 520 * r_status - [RO, *RW] - (int *) 521 * Return (exit) status from Unix command 522 * r_results - [RO, *RW] - (char **) 523 * Any output generated by the Unix command to stdout 524 * and to stderr 525 * == (char *)NULL if no output generated 526 * a_inputFile - [RO, *RO] - (char *) 527 * Pointer to character string representing file to be 528 * used as "standard input" for the command. 529 * == (char *)NULL to use "/dev/null" as standard input 530 * a_cmd - [RO, *RO] - (char *) 531 * Pointer to character string representing the full path 532 * of the Unix command to execute 533 * ... - [RO] (?) 534 * Zero or more arguments to the Unix command 535 * The argument list must be ended with (void *)NULL 536 * Returns: int 537 * == 0 - Command executed 538 * Look at r_status for results of Unix command 539 * != 0 - problems executing command 540 * r_status and r_results have no meaning 541 * NOTE: Any results returned is placed in new storage for the 542 * calling method. The caller must use 'free' to dispose 543 * of the storage once the results are no longer needed. 544 * NOTE: If LU_SUCCESS is returned, 'r_status' must be queried to 545 * determine the results of the Unix command. 546 */ 547 548 int 549 e_ExecCmdList(int *r_status, char **r_results, 550 char *a_inputFile, char *a_cmd, ...) 551 { 552 va_list ap; /* references variable argument list */ 553 char *array[MAX_EXEC_CMD_ARGS+1]; 554 int argno = 0; 555 556 /* 557 * Create argument array for exec system call 558 */ 559 560 bzero(array, sizeof (array)); 561 562 va_start(ap, a_cmd); /* Begin variable argument processing */ 563 564 for (argno = 0; argno < MAX_EXEC_CMD_ARGS; argno++) { 565 array[argno] = va_arg(ap, char *); 566 if (array[argno] == (char *)NULL) { 567 break; 568 } 569 } 570 571 va_end(ap); 572 return (e_ExecCmdArray(r_status, r_results, a_inputFile, a_cmd, array)); 573 } 574