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]
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 */
41 #include <sys/types.h>
43 #include "pkglocale.h"
44 #include "pkglibmsgs.h"
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];
76 if (fp
= fopen(errfile
, "r")) {
77 while ((c
= getc(fp
)) != EOF
)
78 (void) putc(c
, stderr
);
81 (void) unlink(errfile
);
90 (void) unlink(errfile
);
96 esystem(char *cmd
, int ifd
, int ofd
)
102 perrfile
= tmpnam(NULL
);
103 if (perrfile
== NULL
) {
105 pkg_gt("unable to create temp error file, errno=%d"),
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
129 * this is the child process
133 /* reset any signals to default */
135 for (i
= 0; i
< NSIG
; i
++) {
136 (void) sigset(i
, SIG_DFL
);
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);
149 (void) dup2(i
, STDERR_FILENO
);
152 /* Close all open files except standard i/o */
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
);
161 } else if (pid
< 0) {
164 logerr(pkg_gt("bad vfork(), errno=%d"), errno
);
169 * this is the parent process
172 (void) sighold(SIGINT
);
173 pid
= waitpid(pid
, &status
, 0);
174 (void) sigrelse(SIGINT
);
177 return (-1); /* probably interrupted */
180 switch (status
& 0177) {
183 status
= status
>> 8;
187 /* terminated by a signal */
188 status
= status
& 0177;
199 epopen(char *cmd
, char *mode
)
201 char *buffer
, *perrfile
;
207 /* cleanup previous errfile */
208 (void) unlink(errfile
);
211 perrfile
= tmpnam(NULL
);
212 if (perrfile
== NULL
) {
214 pkg_gt("unable to create temp error file, errno=%d"),
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
);
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
);
232 if (strchr(cmd
, '|')) {
233 alen
= snprintf(buffer
, len
, "(%s) 2>%s", cmd
, errfile
);
235 alen
= snprintf(buffer
, len
, "%s 2>%s", cmd
, errfile
);
239 progerr(pkg_gt("command max length %d; cmd is too long: %s"),
244 pp
= popen(buffer
, mode
);
262 * Name: e_ExecCmdArray
263 * Synopsis: Execute Unix command and return results
264 * Description: Execute a Unix command and return results and status
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
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
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
)
309 int ipipe
[2] = {0, 0};
316 /* reset return results buffer pointer */
318 if (r_results
!= (char **)NULL
) {
319 *r_results
= (char *)NULL
;
325 * See if command exists
328 if (access(a_cmd
, F_OK
|X_OK
) != 0) {
333 * See if input file exists
336 if (a_inputFile
!= (char *)NULL
) {
337 stdinfile
= open(a_inputFile
, O_RDONLY
);
339 stdinfile
= open("/dev/null", O_RDONLY
); /* stdin = /dev/null */
347 * Create a pipe to be used to capture the command output
350 if (pipe(ipipe
) != 0) {
351 (void) close(stdinfile
);
356 bufferSize
= PIPE_BUFFER_INCREMENT
;
358 buffer
= calloc(1, bufferSize
);
359 if (buffer
== (char *)NULL
) {
360 (void) close(stdinfile
);
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
383 * This is the forked (child) process ======================
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 */
405 /* execute target executable */
407 (void) execvp(a_cmd
, a_args
);
408 perror(a_cmd
); /* Emit error msg - ends up in callers buffer */
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
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) {
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
) {
447 /* try again: EINTR - interrupted system call */
449 if (errno
== EINTR
) {
453 /* break out of loop - error not recoverable */
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 */
473 resultPid
= waitpid(pid
, &status
, 0L);
474 lerrno
= (resultPid
== -1 ? errno
: 0);
476 /* break loop if child process status reaped */
478 if (resultPid
!= -1) {
482 /* break loop if not interrupted out of waitpid */
484 if (errno
!= EINTR
) {
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 */
501 /* No contents in output buffer - discard */
503 } else if (r_results
== (char **)NULL
) {
504 /* Not requested to return results - discard */
507 /* have output and request to return: pass to calling method */
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
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
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
534 * Zero or more arguments to the Unix command
535 * The argument list must be ended with (void *)NULL
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];
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
) {
572 return (e_ExecCmdArray(r_status
, r_results
, a_inputFile
,