8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libpkg / common / runcmd.c
blob3be8d36ee2c66ae341193d67008f69a7c8666d50
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
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"
46 #ifndef _STDARG_H
47 #include "stdarg.h"
48 #endif
51 * Private definitions
54 /* Maximum number of arguments to pkg_ExecCmdList */
56 #define MAX_EXEC_CMD_ARGS 100
58 /* Size of buffer increments when reading from pipe */
60 #define PIPE_BUFFER_INCREMENT 256
62 static char errfile[L_tmpnam+1];
65 * Public Methods
69 void
70 rpterr(void)
72 FILE *fp;
73 int c;
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);
81 (void) unlink(errfile);
82 errfile[0] = '\0';
86 void
87 ecleanup(void)
89 if (errfile[0]) {
90 (void) unlink(errfile);
91 errfile[0] = NULL;
95 int
96 esystem(char *cmd, int ifd, int ofd)
98 char *perrfile;
99 int status = 0;
100 pid_t pid;
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);
109 (void) strlcpy(errfile, perrfile, sizeof (errfile));
111 /* flush standard i/o before creating new process */
113 (void) fflush(stderr);
114 (void) fflush(stdout);
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().
126 pid = vfork();
127 if (pid == 0) {
129 * this is the child process
131 int i;
133 /* reset any signals to default */
135 for (i = 0; i < NSIG; i++) {
136 (void) sigset(i, SIG_DFL);
139 if (ifd > 0) {
140 (void) dup2(ifd, STDIN_FILENO);
143 if (ofd >= 0 && ofd != STDOUT_FILENO) {
144 (void) dup2(ofd, STDOUT_FILENO);
147 i = open(errfile, O_WRONLY|O_CREAT|O_TRUNC, 0666);
148 if (i >= 0) {
149 (void) dup2(i, STDERR_FILENO);
152 /* Close all open files except standard i/o */
154 closefrom(3);
156 /* execute target executable */
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! */
164 logerr(pkg_gt("bad vfork(), errno=%d"), errno);
165 return (-1);
169 * this is the parent process
172 (void) sighold(SIGINT);
173 pid = waitpid(pid, &status, 0);
174 (void) sigrelse(SIGINT);
176 if (pid < 0) {
177 return (-1); /* probably interrupted */
180 switch (status & 0177) {
181 case 0:
182 case 0177:
183 status = status >> 8;
184 /*FALLTHROUGH*/
186 default:
187 /* terminated by a signal */
188 status = status & 0177;
191 if (status == 0) {
192 ecleanup();
195 return (status);
198 FILE *
199 epopen(char *cmd, char *mode)
201 char *buffer, *perrfile;
202 FILE *pp;
203 size_t len;
204 size_t alen;
206 if (errfile[0]) {
207 /* cleanup previous errfile */
208 (void) unlink(errfile);
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);
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 ((FILE *)0);
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 ((FILE *)0);
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);
238 if (alen > len) {
239 progerr(pkg_gt("command max length %d; cmd is too long: %s"),
240 len, cmd);
241 return ((FILE *)0);
244 pp = popen(buffer, mode);
246 free(buffer);
247 return (pp);
251 epclose(FILE *pp)
253 int n;
255 n = pclose(pp);
256 if (n == 0)
257 ecleanup();
258 return (n);
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.
303 e_ExecCmdArray(int *r_status, char **r_results,
304 char *a_inputFile, char *a_cmd, char **a_args)
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;
316 /* reset return results buffer pointer */
318 if (r_results != (char **)NULL) {
319 *r_results = (char *)NULL;
322 *r_status = -1;
325 * See if command exists
328 if (access(a_cmd, F_OK|X_OK) != 0) {
329 return (-1);
333 * See if input file exists
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 */
342 if (stdinfile < 0) {
343 return (-1);
347 * Create a pipe to be used to capture the command output
350 if (pipe(ipipe) != 0) {
351 (void) close(stdinfile);
352 return (-1);
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);
364 /* flush standard i/o before creating new process */
366 (void) fflush(stderr);
367 (void) fflush(stdout);
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().
379 pid = vfork();
381 if (pid == 0) {
383 * This is the forked (child) process ======================
386 int i;
388 /* reset any signals to default */
390 for (i = 0; i < NSIG; i++) {
391 (void) sigset(i, SIG_DFL);
394 /* assign stdin, stdout, stderr as appropriate */
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);
401 /* Close all open files except standard i/o */
403 closefrom(3);
405 /* execute target executable */
407 (void) execvp(a_cmd, a_args);
408 perror(a_cmd); /* Emit error msg - ends up in callers buffer */
409 _exit(0x00FE);
413 * This is the forking (parent) process ====================
416 (void) close(stdinfile);
417 (void) close(ipipe[1]); /* Close write side of pipe */
420 * Spin reading data from the child into the buffer - when the read eofs
421 * the child has exited
424 for (;;) {
425 ssize_t bytesRead;
427 /* read as much child data as there is available buffer space */
429 bytesRead = read(ipipe[0], buffer + bufferIndex,
430 bufferSize - bufferIndex);
432 /* break out of read loop if end-of-file encountered */
434 if (bytesRead == 0) {
435 break;
438 /* if error, continue if recoverable, else break out of loop */
440 if (bytesRead == -1) {
441 /* try again: EAGAIN - insufficient resources */
443 if (errno == EAGAIN) {
444 continue;
447 /* try again: EINTR - interrupted system call */
449 if (errno == EINTR) {
450 continue;
453 /* break out of loop - error not recoverable */
454 break;
457 /* at least 1 byte read: expand buffer if at end */
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);
468 (void) close(ipipe[0]); /* Close read side of pipe */
470 /* Get subprocess exit status */
472 for (;;) {
473 resultPid = waitpid(pid, &status, 0L);
474 lerrno = (resultPid == -1 ? errno : 0);
476 /* break loop if child process status reaped */
478 if (resultPid != -1) {
479 break;
482 /* break loop if not interrupted out of waitpid */
484 if (errno != EINTR) {
485 break;
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.
496 *r_status = WIFEXITED(status) ? WEXITSTATUS(status) : -1;
498 /* return appropriate output */
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;
511 errno = lerrno;
512 return (resultPid == -1 ? -1 : 0);
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.
549 e_ExecCmdList(int *r_status, char **r_results,
550 char *a_inputFile, char *a_cmd, ...)
552 va_list ap; /* references variable argument list */
553 char *array[MAX_EXEC_CMD_ARGS+1];
554 int argno = 0;
557 * Create argument array for exec system call
560 bzero(array, sizeof (array));
562 va_start(ap, a_cmd); /* Begin variable argument processing */
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;
571 va_end(ap);
572 return (e_ExecCmdArray(r_status, r_results, a_inputFile,
573 a_cmd, array));