/* * Copyright (c) 1988, 1993, 1994 * The Regents of the University of California. All rights reserved. * * This code is derived from software written by Ken Arnold and * published in UNIX Review, Vol. 6, No. 8. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef lint static char sccsid[] = "@(#)popen.c 8.3 (Berkeley) 4/6/94"; #endif /* not lint */ #ifdef HAVE_CONFIG_H #include #endif #include #ifdef HAVE_SYS_WAIT_H #include #endif #include #include #include #include #include #include /* Include glob.h last, because it may define "const" which breaks system headers on some platforms. */ #include #include "extern.h" #include "buildin_ls.h" /* see man 3p optind */ //extern int optind; /* unistd.h */ /* * Special version of popen which avoids call to shell. This ensures noone * may create a pipe to a hidden program as a side effect of a list or dir * command. */ #define MAX_ARGC 100 #define MAX_GARGC 1000 struct file_pid { FILE *file; pid_t pid; struct file_pid *next; }; /* A linked list associating ftpd_popen'd FILEs with pids. */ struct file_pid *file_pids = 0; /* do escaping of blanks suitable for ftpd_popen() */ char *ftpd_popen_escape(const char *s) { const char *p; char *r; char *d; size_t n = 0; for (p = s; *p != '\0'; p++) { if (*p == '\\' || *p == ' ' || *p == '\t' || *p == '\n') n++; n++; } r = (char *)calloc(1, n+1); if (!r) return 0; d = r; for (p = s; *p != '\0'; p++) { if (*p == '\\' || *p == ' ' || *p == '\t' || *p == '\n') *d++ = '\\'; *d++ = *p; } *d = '\0'; return r; } /* get next token seperated by (not escaped) blanks and remove escaping from token */ static char *get_token(char **s) { char *p, *t, *d; int escaped = 0; p = *s; if (!p) return 0; // skip white while(*p == ' ' || *p == '\t' || *p == '\n') p++; t = p; d = p; for(; *p != '\0'; p++) { if (escaped) { *d++ = *p; escaped = 0; } else { if (*p == '\\') escaped = 1; else { if (*p == ' ' || *p == '\t' || *p == '\n') break; *d++ = *p; } } } if (*p != '\0') *s = p+1; else *s = 0; *d = '\0'; return (*t != '\0') ? t : 0; } /* start program. blanks in arguments should be escaped by ftpd_popen_escape() */ FILE * ftpd_popen(char *program, const char *type_) { char *cp; FILE *iop; struct file_pid *fpid; int argc, gargc, pdes[2], pid; char **pop, *argv[MAX_ARGC], *gargv[MAX_GARGC]; Log(("ftpd_popen program=%s type_=%s", program, type_)); if (*type_ != 'r' && *type_ != 'w' || type_[1]) return (NULL); if (pipe(pdes) < 0) return (NULL); /* break up string into pieces */ for (argc = 0, cp = program; argc < MAX_ARGC - 1; argc++) if (!(argv[argc] = get_token(&cp))) break; argv[MAX_ARGC - 1] = NULL; /* glob each piece */ gargv[0] = argv[0]; for (gargc = argc = 1; argc < MAX_ARGC - 1 && argv[argc] && gargc < MAX_GARGC-1 ; argc++) { glob_t gl; int flags = GLOB_NOCHECK; #ifdef GLOB_BRACE flags |= GLOB_BRACE; #endif #ifdef GLOB_QUOTE flags |= GLOB_QUOTE; #endif #if 0 && defined(GLOB_TILDE) flags |= GLOB_TILDE; #endif if (pattern_too_complex(argv[argc])) { gargv[gargc++] = strdup(argv[argc]); } else { memset(&gl, 0, sizeof(gl)); if (glob(argv[argc], flags, NULL, &gl)) gargv[gargc++] = strdup(argv[argc]); else for (pop = gl.gl_pathv; *pop && gargc < MAX_GARGC-1; pop++) gargv[gargc++] = strdup(*pop); globfree(&gl); } } gargv[gargc] = NULL; iop = NULL; switch(pid = fork()) { case -1: /* error */ (void)close(pdes[0]); (void)close(pdes[1]); goto pfree; /* NOTREACHED */ case 0: /* child */ if (*type_ == 'r') { if (pdes[1] != STDOUT_FILENO) { dup2(pdes[1], STDOUT_FILENO); (void)close(pdes[1]); } dup2(STDOUT_FILENO, STDERR_FILENO); /* stderr too! */ (void)close(pdes[0]); } else { if (pdes[0] != STDIN_FILENO) { dup2(pdes[0], STDIN_FILENO); (void)close(pdes[0]); } (void)close(pdes[1]); } // #ifdef WITH_LIBLS Log(("popen args argc=%d", argc)); { int i; for (i = 0; gargv[i] != 0; i++) { Log((" '%s'", gargv[i])); } } #if 1 // FRITZBOX /* mvo: should this be a config-option? */ if(strcmp(gargv[0], "/bin/ls") == 0) { optind = 0; exit(buildin_ls(gargc, gargv)); } #else execv(gargv[0], gargv); #endif _exit(1); } /* parent; assume fdopen can't fail... */ if (*type_ == 'r') { iop = fdopen(pdes[0], type_); (void)close(pdes[1]); } else { iop = fdopen(pdes[1], type_); (void)close(pdes[0]); } fpid = (struct file_pid *) calloc (1, sizeof (struct file_pid)); if (fpid) { fpid->file = iop; fpid->pid = pid; fpid->next = file_pids; file_pids = fpid; } pfree: for (argc = 1; gargv[argc] != NULL; argc++) free(gargv[argc]); return (iop); } int ftpd_pclose(FILE *iop) { struct file_pid *fpid = file_pids, *prev_fpid = 0; int status; #ifdef HAVE_SIGACTION sigset_t sigs, osigs; #else int omask; #endif pid_t pid; /* * pclose returns -1 if stream is not associated with a * `popened' command, or, if already `pclosed'. */ while (fpid && fpid->file != iop) { prev_fpid = fpid; fpid = fpid->next; } if (! fpid) return -1; if (prev_fpid) prev_fpid->next = fpid->next; else file_pids = fpid->next; (void)fclose(iop); #ifdef HAVE_SIGACTION sigemptyset(&sigs); sigaddset(&sigs, SIGINT); sigaddset(&sigs, SIGQUIT); sigaddset(&sigs, SIGHUP); sigprocmask(SIG_BLOCK, &sigs, &osigs); #else omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP)); #endif while ((pid = waitpid(fpid->pid, &status, 0)) < 0 && errno == EINTR) continue; free (fpid); #ifdef HAVE_SIGACTION sigprocmask(SIG_SETMASK, &osigs, 0); #else (void)sigsetmask(omask); #endif if (pid < 0) return (pid); if (WIFEXITED(status)) return (WEXITSTATUS(status)); return (1); }