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
rpterr(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
ecleanup(void)87 ecleanup(void)
88 {
89 if (errfile[0]) {
90 (void) unlink(errfile);
91 errfile[0] = NULL;
92 }
93 }
94
95 int
esystem(char * cmd,int ifd,int ofd)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 *
epopen(char * cmd,char * mode)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 ((FILE *)0);
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 ((FILE *)0);
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 ((FILE *)0);
242 }
243
244 pp = popen(buffer, mode);
245
246 free(buffer);
247 return (pp);
248 }
249
250 int
epclose(FILE * pp)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
e_ExecCmdArray(int * r_status,char ** r_results,char * a_inputFile,char * a_cmd,char ** a_args)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
e_ExecCmdList(int * r_status,char ** r_results,char * a_inputFile,char * a_cmd,...)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,
573 a_cmd, array));
574 }
575