PageRenderTime 51ms CodeModel.GetById 32ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/src/pdsh/main.c

https://code.google.com/
C | 430 lines | 273 code | 70 blank | 87 comment | 72 complexity | d44bc9982c059faaaac145cdd0163674 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 <signal.h>
 32#include <errno.h>
 33#include <sys/types.h>
 34#include <sys/param.h>
 35#if	HAVE_UNISTD_H
 36#include <unistd.h>             /* setuid */
 37#endif
 38#include <sys/wait.h>           /* wait */
 39#include <string.h>             /* strcmp */
 40#include <stdlib.h>             /* exit */
 41#include <stdio.h>
 42
 43#if	HAVE_READLINE
 44#include <readline/readline.h>
 45#include <readline/history.h>
 46#include <sys/types.h>
 47#include <sys/stat.h>
 48#include <fcntl.h>
 49#endif
 50
 51#include "src/common/err.h"
 52#include "src/common/xmalloc.h"
 53#include "src/common/xstring.h"
 54#include "dsh.h"
 55#include "opt.h"
 56#include "mod.h"
 57#include "pcp_client.h"
 58#include "pcp_server.h"
 59#include "privsep.h"
 60
 61extern const char *pdsh_module_dir;
 62
 63static void _interactive_dsh(opt_t *);
 64static int _pcp_remote_client (opt_t *);
 65static int _pcp_remote_server (opt_t *);
 66
 67int main(int argc, char *argv[])
 68{
 69    opt_t opt;
 70    int retval = 0;
 71    const char *m;
 72
 73
 74    /*
 75     * Initialize.
 76     */
 77    err_init(xbasename(argv[0]));       /* init err package */
 78
 79    /*
 80     *  If running setuid, fork a child to handle 
 81     *   all privileged operations and drop privs in this process.
 82     */
 83    privsep_init();
 84
 85    /*
 86     * Seed options with default values:
 87     */
 88    opt_default(&opt, argv[0]);
 89
 90    /*
 91     * Override defaults with environment
 92     */
 93    opt_env(&opt);
 94
 95    /*
 96     * Process any options that need to be handled early:
 97     */
 98    opt_args_early(&opt, argc, argv);
 99
100    /*
101     *  Load static or dynamic pdsh modules
102     */
103    mod_init();
104    /*
105     *  Allow module directory to be overridden, but not when
106     *   running as root or setuid. (This is mainly for testing...)
107     */
108    if (!(m = getenv ("PDSH_MODULE_DIR")) ||
109          getuid() == 0 ||
110          getuid() != geteuid())
111        m = pdsh_module_dir;
112    if (mod_load_modules(m, &opt) < 0)
113        errx("%p: Couldn't load any pdsh modules\n");
114
115    /*
116     * Handle options.
117     */
118    opt_args(&opt, argc, argv); /* override with command line           */
119
120    if (opt_verify(&opt)) {     /* verify options, print errors         */
121        /*
122         * Do the work.
123         */
124        if (opt.info_only)      /* display info only */
125            opt_list(&opt);
126        else if (pdsh_personality() == PCP && opt.pcp_server) 
127            retval = (_pcp_remote_server (&opt) < 0);
128        else if (pdsh_personality() == PCP && opt.pcp_client)
129            retval = (_pcp_remote_client (&opt) < 0);
130        else if (pdsh_personality() == PCP || opt.cmd != NULL)
131            retval = dsh(&opt); /* single dsh/pcp command */
132        else                    /* prompt loop */
133            _interactive_dsh(&opt);
134    } else {
135        retval = 1;
136    }
137
138    mod_exit(); 
139
140    /*
141     * Clean up.
142     */
143    privsep_fini();
144    opt_free(&opt);             /* free heap storage in opt struct */
145    err_cleanup();
146
147    return retval;
148}
149
150#if	HAVE_READLINE
151
152static int _history_file_create (char *path, size_t len)
153{
154    char *      home;
155    struct stat sbuf;
156    int         fd;
157    int         rc;
158    int         n;
159
160    memset (path, 0, len);
161
162    if (!(home = getenv ("HOME"))) {
163        err ("%p: Unable to read HOME env var\n");
164        return (-1);
165    }
166
167    n = snprintf (path, len, "%s/.pdsh", home);
168    if ((n < 0) || (n >= len)) {
169        err ("%p: Unable to open %s/.pdsh: path too long\n", home);
170        return (-1);
171    }
172
173    /*  Check for ~/.pdsh directory 
174     *    and create if it does not exist
175     */
176    if (lstat (path, &sbuf) < 0) {
177        if (errno == ENOENT) {
178            if (mkdir (path, 0700) < 0) {
179                err ("%p: Unable to create ~/.pdsh: %m\n");
180                return (-1);
181            }
182        } else {
183            err ("%p: Couldn't find ~/.pdsh: %m\n");
184            return (-1);
185        }
186    } else if (!(S_ISDIR (sbuf.st_mode))) {
187        err ("%p: ~/.pdsh exists but is not a directory\n");
188        return (-1);
189    }
190
191    /*  Now should have ~/.pdsh/,
192     *    create ~/.pdsh/history if it does not exist.
193     */
194    n = snprintf (path, len, "%s/.pdsh/history", home);
195    if ((n < 0) || (n >= len)) {
196        err ("%p: Unable to open %s/.pdsh/history: path too long\n", home);
197        return (-1);
198    }
199
200    if ((fd = open (path, O_CREAT, 00600)) < 0) {
201        err ("%p: Error: Unable to create history file \"%s\": %m\n", path);
202        return (-1);
203    }
204
205    close (fd);
206
207    if ((rc = read_history (path))) {
208        err ("%p: Warning: Unable to read history file \"%s\": %s\n", 
209                path, strerror (rc));
210        return (-1);
211    }
212
213    return (0);
214}
215
216static void _history_list (void)
217{
218    HIST_ENTRY **list;
219    int          i;
220
221    if (!(list = history_list ()))
222        return;
223
224    for (i = 0; list[i]; i++) {
225        out ("%p: %d: %s\n", i + history_base, list[i]->line);
226    }
227    return;
228}
229
230/* Warning: May be running setuid root! */
231static void _interactive_dsh(opt_t * opt)
232{
233    pid_t pid;
234    char prompt[64];
235    char history_filename[MAXPATHLEN];
236    char *cmd = NULL;
237    int got_history_file = 1;
238    int len;
239
240    snprintf(prompt, sizeof(prompt), "%s> ", opt->progname);
241
242    using_history ();
243
244    len = sizeof (history_filename);
245
246    if (_history_file_create (history_filename, len) < 0) {
247        got_history_file = 0;
248    } 
249
250    while ((cmd = readline(prompt)) != NULL) {
251        int   errnum;
252        char *expansion;
253
254        if ((errnum = history_expand (cmd, &expansion))) {
255            err ("%p: %s\n", expansion);
256        }
257
258        free (cmd);
259
260        if ((errnum < 0) || (errnum == 2)) {
261            free (expansion);
262            continue;
263        }
264
265        cmd = expansion;
266 
267        if (!strcmp(cmd, "history")) {
268            _history_list ();
269            continue;
270        }
271
272        add_history (cmd);
273
274        if (strlen(cmd) == 0) { /* empty line */
275            free(cmd);
276            continue;
277        }
278        if (!strcmp(cmd, "quit") || !strcmp(cmd, "exit")) {
279            free(cmd);          /* quit or exit */
280            break;
281        }
282
283        if ((strlen(cmd) != 0) && (got_history_file)) 
284            append_history (1, history_filename);
285
286        /* 
287         * fork dsh so we can ignore SIGINT in prompt loop 
288         */
289        switch (pid = fork()) {
290        case -1:               /* error */
291            errx("%p: fork: %m\n");
292        case 0:                /* child - run cmd */
293            opt->cmd = Strdup(cmd);
294            dsh(opt);
295            Free((void **) &opt->cmd);
296            exit(0);
297        default:               /* parent - wait */
298            while (waitpid(pid, NULL, 0) < 0) {
299                if (errno != EINTR)
300                    break;
301            }
302            break;
303        }
304
305        free (cmd);
306    }
307}
308
309#else
310
311static char *_getcmd(char *);
312static void _shell(uid_t, char *);
313
314/*
315 * Prompt for dsh commands, then execute them, prompt again, ...
316 * "quit", "exit", or ^D to get out.
317 *	opt (IN)	program options struct
318 */
319static void _interactive_dsh(opt_t * opt)
320{
321    pid_t pid;
322
323    signal(SIGINT, SIG_IGN);
324
325    while ((opt->cmd = _getcmd(opt->progname))) {
326        if (*opt->cmd == '\0') {        /* empty command */
327            Free((void **) &opt->cmd);
328            continue;
329        }
330        if (*opt->cmd == '!') { /* shell escape */
331            _shell(opt->luid, opt->cmd + 1);
332            Free((void **) &opt->cmd);
333            continue;
334        }
335        if (strcmp(opt->cmd, "quit") == 0       /* user exit */
336            || strcmp(opt->cmd, "exit") == 0) {
337            Free((void **) &opt->cmd);
338            break;
339        }
340
341        /* must fork dsh so we can ignore SIGINT in prompt loop */
342        switch (pid = fork()) {
343        case -1:
344            errx("%p: fork: %m\n");
345        case 0:
346            dsh(opt);           /* run command */
347            exit(0);
348        default:
349            while (waitpid(pid, NULL, 0) < 0 && errno == EINTR);
350        }
351        Free((void **) &opt->cmd);
352    }
353}
354
355/*
356 * Run a command that was shell-escaped from the dsh> prompt.  Run it as
357 * the real uid of the invoking user, so we must fork to maintain root 
358 * effective uid in the parent. 
359 *	uid (IN)	uid used to execute command
360 *	cmd (IN)	command and args
361 */
362static void _shell(uid_t uid, char *cmd)
363{
364    pid_t pid;
365
366    switch (pid = fork()) {
367    case -1:
368        errx("%p: fork: %m\n");
369    case 0:
370        setuid(uid);
371        system(cmd);
372        exit(0);
373    default:
374        waitpid(pid, NULL, 0);
375    }
376}
377
378/* 
379 * Prompt for a command and return it.  
380 *	prompt (IN)	string used to build prompt (e.g. program name)
381 */
382static char *_getcmd(char *prompt)
383{
384    char *cmd = NULL;
385    char buf[LINEBUFSIZE];
386
387    out("%s> ", prompt);
388    if (fgets(buf, LINEBUFSIZE, stdin) != NULL) {
389        buf[LINEBUFSIZE - 1] = '\0';
390        xstrcln(buf, NULL);
391        cmd = Strdup(buf);
392    }
393    return cmd;
394}
395
396#endif                          /* WITH_READLINE */
397
398static int _pcp_remote_server (opt_t *opt)
399{
400    struct pcp_server svr[1];
401
402    svr->infd =          STDIN_FILENO;
403    svr->outfd =         STDOUT_FILENO;
404    svr->preserve =      opt->preserve;
405    svr->target_is_dir = opt->target_is_directory;
406    svr->outfile =       opt->outfile_name;
407
408    return (pcp_server (svr));
409}
410
411static int _pcp_remote_client (opt_t *opt)
412{
413    struct pcp_client pcp[1];
414
415    pcp->infd =  STDIN_FILENO;
416    pcp->outfd = STDOUT_FILENO;
417
418    pcp->infiles = pcp_expand_dirs (opt->infile_names);
419
420    pcp->host =       opt->pcp_client_host;
421    pcp->preserve =   opt->preserve;
422    pcp->pcp_client = opt->pcp_client;
423
424    return (pcp_client (pcp));
425}
426
427
428/*
429 * vi:tabstop=4 shiftwidth=4 expandtab
430 */