1 /* 2 * Copyright (c) 1988, 1993, 1994 3 * The Regents of the University of California. All rights reserved. 4 * 5 * This code is derived from software written by Ken Arnold and 6 * published in UNIX Review, Vol. 6, No. 8. 7 * 8 * %sccs.include.redist.c% 9 * 10 */ 11 12 #ifndef lint 13 static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 04/06/94"; 14 #endif /* not lint */ 15 16 #include <sys/types.h> 17 #include <sys/wait.h> 18 19 #include <errno.h> 20 #include <glob.h> 21 #include <signal.h> 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include "extern.h" 28 29 /* 30 * Special version of popen which avoids call to shell. This ensures noone 31 * may create a pipe to a hidden program as a side effect of a list or dir 32 * command. 33 */ 34 static int *pids; 35 static int fds; 36 37 FILE * 38 ftpd_popen(program, type) 39 char *program, *type; 40 { 41 char *cp; 42 FILE *iop; 43 int argc, gargc, pdes[2], pid; 44 char **pop, *argv[100], *gargv[1000]; 45 46 if (*type != 'r' && *type != 'w' || type[1]) 47 return (NULL); 48 49 if (!pids) { 50 if ((fds = getdtablesize()) <= 0) 51 return (NULL); 52 if ((pids = (int *)malloc((u_int)(fds * sizeof(int)))) == NULL) 53 return (NULL); 54 memset(pids, 0, fds * sizeof(int)); 55 } 56 if (pipe(pdes) < 0) 57 return (NULL); 58 59 /* break up string into pieces */ 60 for (argc = 0, cp = program;; cp = NULL) 61 if (!(argv[argc++] = strtok(cp, " \t\n"))) 62 break; 63 64 /* glob each piece */ 65 gargv[0] = argv[0]; 66 for (gargc = argc = 1; argv[argc]; argc++) { 67 glob_t gl; 68 int flags = GLOB_BRACE|GLOB_NOCHECK|GLOB_QUOTE|GLOB_TILDE; 69 70 memset(&gl, 0, sizeof(gl)); 71 if (glob(argv[argc], flags, NULL, &gl)) 72 gargv[gargc++] = strdup(argv[argc]); 73 else 74 for (pop = gl.gl_pathv; *pop; pop++) 75 gargv[gargc++] = strdup(*pop); 76 globfree(&gl); 77 } 78 gargv[gargc] = NULL; 79 80 iop = NULL; 81 switch(pid = vfork()) { 82 case -1: /* error */ 83 (void)close(pdes[0]); 84 (void)close(pdes[1]); 85 goto pfree; 86 /* NOTREACHED */ 87 case 0: /* child */ 88 if (*type == 'r') { 89 if (pdes[1] != STDOUT_FILENO) { 90 dup2(pdes[1], STDOUT_FILENO); 91 (void)close(pdes[1]); 92 } 93 dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */ 94 (void)close(pdes[0]); 95 } else { 96 if (pdes[0] != STDIN_FILENO) { 97 dup2(pdes[0], STDIN_FILENO); 98 (void)close(pdes[0]); 99 } 100 (void)close(pdes[1]); 101 } 102 execv(gargv[0], gargv); 103 _exit(1); 104 } 105 /* parent; assume fdopen can't fail... */ 106 if (*type == 'r') { 107 iop = fdopen(pdes[0], type); 108 (void)close(pdes[1]); 109 } else { 110 iop = fdopen(pdes[1], type); 111 (void)close(pdes[0]); 112 } 113 pids[fileno(iop)] = pid; 114 115 pfree: for (argc = 1; gargv[argc] != NULL; argc++) 116 free(gargv[argc]); 117 118 return (iop); 119 } 120 121 int 122 ftpd_pclose(iop) 123 FILE *iop; 124 { 125 int fdes, omask, status; 126 pid_t pid; 127 128 /* 129 * pclose returns -1 if stream is not associated with a 130 * `popened' command, or, if already `pclosed'. 131 */ 132 if (pids == 0 || pids[fdes = fileno(iop)] == 0) 133 return (-1); 134 (void)fclose(iop); 135 omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); 136 while ((pid = waitpid(pids[fdes], &status, 0)) < 0 && errno == EINTR) 137 continue; 138 (void)sigsetmask(omask); 139 pids[fdes] = 0; 140 if (pid < 0) 141 return (pid); 142 if (WIFEXITED(status)) 143 return (WEXITSTATUS(status)); 144 return (1); 145 } 146