PageRenderTime 54ms CodeModel.GetById 30ms app.highlight 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/pdsh/xpopen.c

https://code.google.com/
C | 264 lines | 137 code | 47 blank | 80 comment | 46 complexity | 0683f3210d2cba87a77c234d226e8699 MD5 | raw file
  1/*****************************************************************************\
  2 *  $Id$
  3 *****************************************************************************
  4 *  Copyright (C) 2001-2006 The Regents of the University of California.
  5 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  6 *  Written by Jim Garlick <garlick@llnl.gov>.
  7 *  UCRL-CODE-2003-005.
  8 *  
  9 *  This file is part of Pdsh, a parallel remote shell program.
 10 *  For details, see <http://www.llnl.gov/linux/pdsh/>.
 11 *  
 12 *  Pdsh is free software; you can redistribute it and/or modify it under
 13 *  the terms of the GNU General Public License as published by the Free
 14 *  Software Foundation; either version 2 of the License, or (at your option)
 15 *  any later version.
 16 *  
 17 *  Pdsh is distributed in the hope that it will be useful, but WITHOUT ANY
 18 *  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 19 *  FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 20 *  details.
 21 *  
 22 *  You should have received a copy of the GNU General Public License along
 23 *  with Pdsh; if not, write to the Free Software Foundation, Inc.,
 24 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 25\*****************************************************************************/
 26
 27#if     HAVE_CONFIG_H
 28#include "config.h"
 29#endif
 30
 31#include <stdio.h>
 32#include <limits.h>             /* ARG_MAX */
 33
 34#if	HAVE_UNISTD_H
 35#include <unistd.h>             /* for R_OK, access() */
 36# if defined(_SC_ARG_MAX)
 37#  if defined(ARG_MAX)
 38#     undef ARG_MAX
 39#  endif
 40#  define ARG_MAX sysconf (_SC_ARG_MAX)
 41# endif
 42#endif
 43
 44#include <sys/wait.h>           /* waitpid() */
 45#include <string.h>             /* strcmp() */
 46#include <stdlib.h>
 47#include <errno.h>
 48
 49#include "src/common/xmalloc.h"
 50#include "src/common/xstring.h"
 51#include "src/common/err.h"
 52#include "src/common/list.h"
 53
 54#define QUOTE '\"'
 55#define SPACE ' '
 56#define TAB   '\t'
 57#define NWLN  '\n'
 58
 59extern int errno;
 60
 61static struct pid {
 62    struct pid *next;
 63    FILE *fp;
 64    pid_t pid;
 65} *pidlist;
 66
 67/* forward declaration */
 68static void _parse_command_with_quotes(char *av[], int maxargs, char *str);
 69
 70/* 
 71 * xpopen(): a safer popen for pdsh.
 72 * We bypass shell by doing a fork and exec'ing the cmd directly.
 73 * also, set euid back to original user. This avoids passing possibly
 74 * user supplied arguments to a suid shell.
 75 *
 76 * xpopen returns NULL if the fork or pipe calls fail, or if it cannot
 77 * allocate memory.
 78 *
 79 * Since a shell is not invoked on the command string, you cannot pass
 80 * things that normally the shell would handle. The only thing xpopen()
 81 * attempts to deal with are double quoted strings.
 82 *
 83 * the returned stream must be closed by xpclose (see below)
 84 * 
 85 * cmd (IN) 	cmd to run, as in popen
 86 * mode(IN)	"r" for reading, "w" (or anything else) for writing
 87 * OUT		FILE * to output/input stream of child process
 88 */
 89FILE *xpopen(char *cmd, char *mode)
 90{
 91    struct pid *cur;
 92    int fds[2], read, fd;
 93    pid_t pid;
 94    char *av[ARG_MAX + 1];
 95    int maxfd = sysconf(_SC_OPEN_MAX);
 96    
 97    _parse_command_with_quotes(av, ARG_MAX, cmd);
 98
 99    if ((*mode != 'r' && *mode != 'w') || mode[1] != '\0') {
100        errno = EINVAL;
101        return (NULL);
102    }
103
104    cur = Malloc(sizeof(struct pid));
105
106    read = (*mode == 'r');
107
108    if (pipe(fds) < 0) {
109        close(fds[0]);
110        close(fds[1]);
111        Free((void **) &cur);
112        errx("%p: unable to dup stdout\n");
113    }
114
115    switch (pid = fork()) {
116    case -1:                   /* Error. */
117        close(fds[0]);
118        close(fds[1]);
119        Free((void **) &cur);
120        return (NULL);
121
122    case 0:                    /* child */
123
124        close(fds[read ? 0 : 1]);
125        dup2(fds[read ? 1 : 0], read ? STDOUT_FILENO : STDIN_FILENO);
126
127        for (fd = STDERR_FILENO + 1; fd < maxfd; fd++)
128            close(fd);
129
130        setgid(getgid());
131        setuid(getuid());
132
133        do {
134            if (access(av[0], F_OK) != 0 && errno != EINTR) {
135                fprintf(stderr, "%s: not found\n", av[0]);
136                fflush(stderr);
137            }
138        } while (errno == EINTR);
139
140        execv(av[0], av);
141
142        exit(errno);
143    }                           /* switch() */
144
145    close(fds[read ? 1 : 0]);
146
147    /* insert child pid into pidlist */
148    cur->fp = fdopen(fds[read ? 0 : 1], mode);
149    cur->pid = pid;
150    cur->next = pidlist;
151    pidlist = cur;
152
153    return (cur->fp);
154
155}
156
157/*
158 * xpclose(): close stream opened with xpopen. reap proper child and
159 *            return exit status.
160 *
161 * f (IN)	file stream as returned by xpopen()
162 *
163 * (OUT)	-1 on error (was f opened by xpopen?) 
164 *              otherwise, exit status of child as modified by 
165 *              WEXITSTATUS macro (see waitpid(3))
166 *              This is different from pclose, which returns the 
167 *              unmodified status from waitpid.
168 */
169int xpclose(FILE * f)
170{
171    int status;
172    pid_t pid;
173    struct pid *cur, *last;
174
175    fclose(f);
176
177    for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
178        if (f == cur->fp)
179            break;
180
181    if (cur == NULL)
182        return (-1);
183
184    do {
185        pid = waitpid(cur->pid, &status, 0);
186    } while (pid == -1 && errno == EINTR);
187
188    if (last == NULL)
189        pidlist = cur->next;
190    else
191        last->next = cur->next;
192
193    Free((void **) &cur);
194
195    return (WIFEXITED(status) != 0) ? WEXITSTATUS(status) : -1;
196}
197
198/* parse_commaned_with_quotes(): 
199 * helper function for xpopen
200 *
201 * provides simple string parsing with support for double quoted arguments,
202 * and that is all. This is probably too simple to do what you want.
203 * Only makes a minimal effort to complain when there's no matching quote.
204 *
205 * argv(IN) container for resulting arguments, strings enclosed in "" are
206 *          treated as a single arg.
207 * maxn(IN) max number of arguments
208 * str(IN)	string to parse
209 *
210 */
211static void _parse_command_with_quotes(char *argv[], int maxn, char *str)
212{
213    int i = 0;
214    char *c, *lc;
215
216    c = lc = str;
217
218    while (*c != '\0' && i < maxn) {
219        switch (*c) {
220        case QUOTE:
221            lc = ++c;
222            /* find matching quote */
223            while (*c != '\0' && *c != QUOTE)
224                c++;
225
226            if (*c == '\0')
227                errx("%P: Unmatched `%c' in xpopen\n", *lc);
228
229            /* nullify quote */
230            *c = '\0';
231
232            /* push token onto argument vector */
233            if (strlen(lc) > 0)
234                argv[i++] = lc;
235
236            /* move c past null */
237            lc = ++c;
238
239            break;
240        case SPACE:
241        case TAB:
242        case NWLN:
243            /* nullify and push token onto list */
244            *c = '\0';
245            if (lc != NULL && strlen(lc) > 0)
246                argv[i++] = lc;
247
248            lc = ++c;
249            break;
250        default:
251            c++;
252        }
253    }
254
255    /* hit a null. push last token onto list, if such a one exists */
256    if (strlen(lc) > 0 && i < maxn)
257        argv[i++] = lc;
258
259    argv[i] = NULL;
260}
261
262/*
263 * vi:tabstop=4 shiftwidth=4 expandtab
264 */