PageRenderTime 18ms CodeModel.GetById 1ms app.highlight 12ms RepoModel.GetById 1ms app.codeStats 1ms

/src/common/pipecmd.c

https://code.google.com/
C | 325 lines | 218 code | 56 blank | 51 comment | 38 complexity | e99a5823a84fd0e8e19868c80d5a9d29 MD5 | raw file
  1/*****************************************************************************\
  2 *  $Id$
  3 *****************************************************************************
  4 *  Copyright (C) 2007 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 <sys/wait.h>
 32#include <sys/socket.h>
 33#include <signal.h>
 34#include <errno.h>
 35#include <unistd.h>
 36#include <stdio.h>
 37
 38#include "src/common/xmalloc.h"
 39#include "src/common/xstring.h"
 40#include "src/common/err.h"
 41#include "src/common/list.h"
 42#include "src/common/split.h"
 43#include "src/pdsh/dsh.h"
 44#include "src/pdsh/mod.h"
 45
 46#include "src/common/pipecmd.h"
 47
 48struct pipe_info_struct {
 49    pid_t pid;              /* pid of child command             */
 50    char *path;             /* path to executable               */
 51    char *cmd;              /* basename of path                 */
 52    char *target;           /* Target host of this instance     */
 53    char *username;         /* Username for this instance       */
 54    char **args;            /* Cmd arguments                    */
 55    int rank;               /* Rank 1-N of this instance        */
 56    int fd;                 /* stdin/out fd                     */
 57    int efd;                /* stderr fd                        */
 58};
 59
 60static int _pipecmd (char *path, char *args[], int *fd2p, pid_t *ppid);
 61
 62pipecmd_t pipe_info_create (const char *path, const char *target, 
 63        const char *user, int rank)
 64{
 65    struct pipe_info_struct *e = Malloc (sizeof (*e));
 66
 67    e->path = Strdup (path);
 68    e->cmd = Strdup (xbasename (e->path));
 69    e->target = Strdup (target);
 70    e->username = Strdup (user);
 71    e->rank = rank;
 72
 73    e->pid = (pid_t) 0;
 74    e->args = NULL;
 75    e->fd = -1;
 76    e->efd = -1;
 77
 78    return (e);
 79}
 80
 81static void pipe_info_destroy (struct pipe_info_struct *e)
 82{
 83    if (e == NULL)
 84        return;
 85
 86    Free ((void **) &e->path);
 87    Free ((void **) &e->cmd);
 88    Free ((void **) &e->target);
 89    Free ((void **) &e->username);
 90    Free ((void **) &e);
 91}
 92
 93static char * pipecmd_format_arg (pipecmd_t e, const char *arg)
 94{
 95    char buf [64];
 96    const char *q, *p;
 97    char *str = NULL;
 98
 99    q = p = arg;
100
101    while (*p != '\0') {
102        if (*p == '%') {
103            p++;
104            switch (*p) {
105                case 'h' : /* '%n' => target name */
106                    xstrcat (&str, e->target);
107                    break;
108                case 'u':
109                    xstrcat (&str, e->username);
110                    break;
111                case 'n':
112                    snprintf (buf, sizeof (buf) - 1, "%d", e->rank);
113                    xstrcat (&str, buf);
114                    break;
115                case '%':
116                    xstrcatchar (&str, '%');
117                    break;
118                default:
119                    xstrcatchar (&str, '%');
120                    xstrcatchar (&str, *p);
121                    break;
122            }
123        }
124        else 
125            xstrcatchar (&str, *p);
126        p++;
127    }
128
129    return (str);
130}
131
132/*
133 *  Loop through argv expanding any format characters
134 */
135static char ** cmd_args_create (pipecmd_t e, const char **argv)
136{
137    int i = 0;
138    int n = 0;
139    char **args = NULL;
140
141    while (argv[n])
142        n++;
143
144    args = Malloc ((n + 2) * sizeof (char *));
145    /*
146     *  Cmd should be args[0]:
147     */
148    args [0] = Strdup (e->cmd);
149    for (i = 1; i < n+1; i++) 
150        args[i] = pipecmd_format_arg (e, argv[i-1]);
151
152    args[i] = NULL;
153
154    return (args);
155}
156
157static void cmd_args_destroy (char **args)
158{
159    int i = 0;
160
161    if (args == NULL)
162        return;
163
164    while (args[i])
165        Free ((void **) &args[i++]);
166
167    Free ((void **) &args);
168}
169
170void pipecmd_destroy (pipecmd_t p)
171{
172    cmd_args_destroy (p->args);
173    pipe_info_destroy (p);
174    return;
175}
176
177pipecmd_t pipecmd (const char *path, const char **args, const char *target,
178        const char *user, int rank)
179{
180    pipecmd_t p = pipe_info_create (path, target, user, rank);
181    p->args = cmd_args_create (p, args);
182
183    if (!(p->fd = _pipecmd (p->path, p->args, &p->efd, &p->pid))) {
184        err ("%p: exec cmd %s failed for host %S\n", path, target);
185        pipecmd_destroy (p);
186        return (NULL);
187    }
188    return (p);
189}
190
191int pipecmd_stdoutfd (pipecmd_t p)
192{
193    if (p == NULL)
194        return (-1);
195    return (p->fd);
196}
197
198int pipecmd_stderrfd (pipecmd_t p)
199{
200    if (p == NULL)
201        return (-1);
202    return (p->efd);
203}
204
205int pipecmd_signal (pipecmd_t p, int signo)
206{
207    char *cmd;
208
209    if (p == NULL)
210        return (-1);
211   
212    cmd =  xbasename (p->path);
213    err ("sending signal %d to %s [%s] pid %d\n", signo, p->target, cmd,
214            p->pid);
215
216    return (kill (p->pid, signo));
217}
218
219int pipecmd_wait (pipecmd_t p, int *pstatus)
220{
221    int status = 0;
222
223    if (p == NULL)
224        return (-1);
225
226    if (waitpid (p->pid, &status, 0) < 0)
227        err ("%p: %S: %s pid %ld: waitpid: %m\n", p->target, 
228                xbasename (p->path), p->pid);
229
230    if (status != 0)
231        err ("%p: %S: %s exited with exit code %d\n",
232                p->target, xbasename (p->path), WEXITSTATUS (status));
233
234    if (pstatus)
235        *pstatus = status;
236
237    return (0);
238}
239
240const char * pipecmd_target (pipecmd_t p)
241{
242    return (p->target);
243}
244
245
246static void closeall (int fd)
247{
248    int fdlimit = sysconf (_SC_OPEN_MAX);
249
250    while (fd < fdlimit)
251        close (fd++);
252    return;
253}
254
255static int _pipecmd (char *path, char *args[], int *fd2p, pid_t *ppid)
256{
257    int sp[2], esp[2];
258
259    /*
260     *  Get socketpair for stdin/out
261     */
262    if (socketpair (AF_UNIX, SOCK_STREAM, 0, sp) < 0) {
263        err ("%p: pipecmd: socketpair: %m\n");
264        return (-1);
265    }
266
267    if (fd2p && socketpair (AF_UNIX, SOCK_STREAM, 0, esp) < 0) {
268        err ("%p: pipecmd: socketpair: %m\n");
269        return (-1);
270    }
271
272    if ((*ppid = fork ()) < 0) {
273        err ("%p: pipecmd: fork: %m\n");
274        return (-1);
275    }
276
277    if (*ppid == 0) {
278        /*
279         *  Child. We use sp[1] for stdin/out, and close sp[0]
280         */
281        (void) close (sp[0]);
282        if ((dup2 (sp[1], 0) < 0) || (dup2 (0, 1) < 0)) {
283            err ("%p: pipecmd (in child): dup2: %m");
284            _exit (255);
285        }
286
287        /*
288         *  Dup seperate stderr socketpair if fd2p was passed in.
289         *   Otherwise dup stdin/out onto stderr.
290         */
291        if (dup2 ((fd2p ? esp[1] : 0), 2) < 0) {
292                err ("%p: pipecmd (in child): dup2: %m");
293                _exit (255);
294        }
295        if (fd2p)
296            (void) close (esp[0]);
297
298        /*  Try to close all stray file descriptors before
299         *   invocation of cmd to ensure that cmd stdin is closed
300         *   when pdsh/pdcp close their end of the socketpair.
301         */
302        closeall (3);
303
304        setsid ();
305        execvp (path, args);
306        err ("%p: execvp %s failed: %m\n", path);
307        _exit (255);
308    }
309
310    /*
311     * Parent continues
312     */
313    (void) close (sp[1]);
314    if (fd2p) {
315        (void) close (esp[1]);
316        *fd2p = esp[0];
317    }
318
319    return (sp[0]);
320}
321
322
323/*
324 * vi: ts=4 sw=4 expandtab
325 */