PageRenderTime 46ms CodeModel.GetById 11ms app.highlight 29ms RepoModel.GetById 2ms app.codeStats 0ms

/src/modules/sshcmd.c

https://code.google.com/
C | 408 lines | 236 code | 67 blank | 105 comment | 37 complexity | e80cfb351a05ddb963aea699d89c03c0 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/*
 28 * This is an rcmd() replacement originally by Chris Siebenmann 
 29 * <cks@utcc.utoronto.ca>.  There was no copyright information on the original.
 30 * If this finds its way back to the original author please let me know if
 31 * you would like this header block changed...
 32 * 
 33 * Brought in to pdsh from USC rdist -jg
 34 * Changes:
 35 * - added fd2p arg handling
 36 * - changed name, func prototype, and added sshcmd() and rshcmd() wrappers
 37 * - use 'err' for output
 38 * - unset DISPLAY and call setsid() so ssh won't hang prompting for passphrase
 39 */
 40
 41#if     HAVE_CONFIG_H
 42#include "config.h"
 43#endif
 44
 45
 46#include <sys/wait.h>
 47#include <signal.h>
 48#include <errno.h>
 49#include <netdb.h>
 50#include <sys/types.h>
 51#include <stdlib.h>             /* putenv */
 52#if	HAVE_UNISTD_H
 53#include <unistd.h>
 54#endif
 55#if	HAVE_PTHREAD_H
 56#include <pthread.h>
 57#endif
 58#include <string.h>             /* memset */
 59
 60#include <stddef.h>
 61#include <sys/socket.h> 
 62#include <sys/wait.h>
 63
 64#include "src/common/xmalloc.h"
 65#include "src/common/xstring.h"
 66#include "src/common/err.h"
 67#include "src/common/list.h"
 68#include "src/common/split.h"
 69#include "src/common/pipecmd.h"
 70#include "src/pdsh/dsh.h"
 71#include "src/pdsh/mod.h"
 72
 73#if STATIC_MODULES
 74#  define pdsh_module_info sshcmd_module_info
 75#  define pdsh_module_priority sshcmd_module_priority
 76#endif    
 77
 78#define DEFAULT_SSH_ARGS "-2 -a -x %h"
 79
 80int pdsh_module_priority = DEFAULT_MODULE_PRIORITY;
 81
 82    
 83static int mod_ssh_postop(opt_t *opt);
 84static int mod_ssh_exit (void);
 85
 86static int sshcmd_init(opt_t *);
 87static int sshcmd_signal(int, void *arg, int);
 88static int sshcmd(char *, char *, char *, char *, char *, int, int *, void **);
 89static int sshcmd_destroy (pipecmd_t p);
 90static int sshcmd_args_init (void);
 91static int fixup_ssh_args (List ssh_args_list, int need_user);
 92
 93List ssh_args_list =     NULL;
 94
 95/*
 96 *  Export generic pdsh module operations:
 97 */
 98struct pdsh_module_operations sshcmd_module_ops = {
 99    (ModInitF)       NULL,
100    (ModExitF)       mod_ssh_exit,
101    (ModReadWcollF)  NULL,
102    (ModPostOpF)     mod_ssh_postop
103};
104
105/*
106 *  Export rcmd module operations
107 */
108struct pdsh_rcmd_operations sshcmd_rcmd_ops = {
109    (RcmdInitF)    sshcmd_init,
110    (RcmdSigF)     sshcmd_signal,
111    (RcmdF)        sshcmd,
112    (RcmdDestroyF) sshcmd_destroy
113};
114
115/* 
116 * Export module options
117 */
118struct pdsh_module_option sshcmd_module_options[] = 
119 { 
120   PDSH_OPT_TABLE_END
121 };
122
123/* 
124 * Sshcmd module info 
125 */
126struct pdsh_module pdsh_module_info = {
127  "rcmd",
128  "ssh",
129  "Jim Garlick <garlick@llnl.gov>",
130  "ssh based rcmd connect method",
131  DSH | PCP, 
132
133  &sshcmd_module_ops,
134  &sshcmd_rcmd_ops,
135  &sshcmd_module_options[0],
136};
137
138static char **ssh_argv_create (List arg_list, const char **remote_argv)
139{
140    int n;
141    char *arg;
142    char **argv;
143    const char **p;
144    ListIterator i;
145
146    n = 0;
147    for (p = remote_argv; *p; p++)
148        n++;
149
150    n += list_count (arg_list) + 2;
151    argv = (char **) Malloc (n * sizeof (char *));
152    memset (argv, 0, n);
153
154    n = 0;
155    i = list_iterator_create (arg_list);
156    while ((arg = list_next (i))) 
157        argv[n++] = Strdup (arg);
158    list_iterator_destroy (i);
159
160    /* Append remote_argv to standard list of args */
161    for (p = remote_argv; *p; p++)
162        argv[n++] = Strdup (*p);
163
164    return (argv);
165}
166
167static void ssh_argv_destroy (char **args)
168{
169    int i = 0;
170    while (args[i])
171        Free ((void **) &args[i++]);
172    Free ((void **) &args);
173}
174
175static int ssh_args_prepend_timeout (int timeout)
176{
177#if SSH_HAS_CONNECT_TIMEOUT
178    char buf[64];
179
180    if (timeout <= 0)
181        return (0);
182
183    snprintf (buf, 64, SSH_CONNECT_TIMEOUT_OPTION, timeout);
184    list_prepend (ssh_args_list, Strdup (buf));
185#endif
186    return (0);
187}
188
189static int mod_ssh_postop(opt_t *opt)
190{
191    sshcmd_args_init ();
192    ssh_args_prepend_timeout (opt->connect_timeout);
193
194    /*
195     *  Append PATH=...; to ssh args if DSHPATH was set
196     */
197    if (opt->dshpath)
198        list_append (ssh_args_list, Strdup (opt->dshpath));
199
200    return 0;
201}
202
203static int sshcmd_init(opt_t * opt)
204{
205    /*
206     * Drop privileges if running setuid root
207     */
208    if ((geteuid() == 0) && (getuid() != 0))
209        setuid (getuid ());
210
211    /*
212     *  Do not resolve hostnames in pdsh when using ssh
213     */
214    if (rcmd_opt_set (RCMD_OPT_RESOLVE_HOSTS, 0) < 0)
215        errx ("%p: sshcmd_init: rcmd_opt_set: %m\n");
216
217    return 0;
218}
219
220static void free_f (void *x)
221{
222    Free ((void **) &x);
223}
224
225static List ssh_args_list_copy (List args)
226{
227    List copy;
228    ListIterator i = list_iterator_create (args);
229    const char *arg;
230
231    copy = list_create ((ListDelF) free_f);
232    while ((arg = list_next (i)))
233        list_append (copy, Strdup (arg));
234    list_iterator_destroy (i);
235
236    return (copy);
237}
238
239static int mod_ssh_exit (void)
240{
241    if (ssh_args_list)
242        list_destroy (ssh_args_list);
243
244    return 0;
245}
246
247/*
248 * SSH doesn't support signal forwarding, at least the way pdsh uses it
249 *  at this time. Instead we always send SIGTERM which seems to have the
250 *  desired effect of killing off ssh most of the time.
251 */
252static int sshcmd_signal(int fd, void *arg, int signum)
253{
254    /*
255     *  Always send SIGTERM. SIGINT doesn't seem to get forwarded by ssh, and
256     *    really termination of the connection is probably the desired result.
257     */
258    err ("sending SIGTERM to ssh %s\n", pipecmd_target ((pipecmd_t) arg));
259    return (pipecmd_signal ((pipecmd_t) arg, SIGTERM));
260}
261
262static int
263sshcmd(char *ahost, char *addr, char *luser, char *ruser, char *cmd,
264         int rank, int *fd2p, void **arg)
265{
266    pipecmd_t p = NULL;
267    const char **remote_argv = pdsh_remote_argv ();
268    const char *alt_argv[] = { cmd, NULL };
269    char **ssh_args;
270    List args_list;
271
272    /*
273     *  If running as pdcp/rpdcp, then the dsh code has rewritten
274     *   the cmd to invoke pdcp server on remote nodes. Therefore
275     *    avoid using pdsh_remote_argv() directly, instead use cmd:
276     */
277    if (pdsh_personality() == PCP)
278        remote_argv = alt_argv;
279    /*
280     *  In interactive dsh mode, pdsh_remote_argv() will be empty
281     *   so we can't use it.
282     */
283    if (!remote_argv || !*remote_argv)
284        remote_argv = alt_argv;
285
286    /*
287     *  Create a copy of ssh_args_list to customize for this target
288     */
289    args_list = ssh_args_list_copy (ssh_args_list);
290
291    if (strcmp (luser, ruser) != 0)
292        fixup_ssh_args (args_list, 1);
293    else
294        fixup_ssh_args (args_list, 0);
295
296    ssh_args = ssh_argv_create (args_list, remote_argv);
297
298    list_destroy (args_list);
299
300    if (!(p = pipecmd ("ssh", (const char **) ssh_args, ahost, ruser, rank)))
301        goto out;
302
303    if (fd2p)
304        *fd2p = pipecmd_stderrfd (p);
305
306    *arg = (void *) p;
307
308out:
309    ssh_argv_destroy (ssh_args);
310    return (p ? pipecmd_stdoutfd (p) : -1);
311}
312
313static int 
314sshcmd_destroy (pipecmd_t p)
315{
316    int status = 0;
317
318    if (pipecmd_wait (p, &status) < 0)
319        err ("%p: %S: wait on ssh cmd: %m\n", pipecmd_target (p));  
320
321    pipecmd_destroy (p);
322
323    return WEXITSTATUS (status);
324}
325
326/*
327 *  Check string argument [arg] for parameter substitution sequence [s].
328 *    If [s] is found in [arg] then also check that [s] is not preceeded
329 *    by '%' which would have the effect of escaping the parameter
330 *    substitution.
331 */
332static int arg_has_parameter (const char *arg, const char *s)
333{
334    char *p;
335    if ((p = strstr (arg, s)) && ((p == arg) || (*(p-1) != '%')))
336        return 1;
337    return 0;
338}
339
340/*
341 *  Scan the current ssh_args_list for presence of %u and %h.
342 *   If they are not present, assume we need to append them to the
343 *   ssh args.
344 */
345static int fixup_ssh_args (List ssh_args_list, int need_user)
346{
347    ListIterator i = list_iterator_create (ssh_args_list);
348    char *arg;
349    int got_user = 0;
350    int got_host = 0;
351
352    while ((arg = list_next (i))) {
353        if (need_user && arg_has_parameter (arg, "%u"))
354            got_user = 1;
355        if (arg_has_parameter (arg, "%h"))
356            got_host = 1;
357    }
358
359    if (need_user && !got_user) {
360        if (got_host) {
361            /*
362             *  Ensure "%lu" is not inserted after "%h":
363             */
364            list_iterator_reset (i);
365            arg = list_find (i, (ListFindF) arg_has_parameter, "%h");
366            list_insert (i, Strdup ("-l%u"));
367        }
368        else
369            list_append (ssh_args_list, Strdup ("-l%u"));
370    }
371
372    /*
373     *  Always append "%h" to end of args
374     */
375     if (!got_host)
376         list_append (ssh_args_list, Strdup ("%h"));
377
378     list_iterator_destroy (i);
379     return (0);
380}
381
382static int sshcmd_args_init (void)
383{
384    char *val = NULL;
385    char *str = NULL;
386
387    /*
388     *  Place SSH_ARGS_APPEND at the beggining of the args string
389     *   because %h must always come last...
390     */
391    if ((val = getenv ("PDSH_SSH_ARGS_APPEND"))) {
392        str = Strdup (val);
393        xstrcatchar (&str, ' ');
394    }
395
396    if (!(val = getenv ("PDSH_SSH_ARGS")))
397        val = DEFAULT_SSH_ARGS;
398    xstrcat (&str, val);
399
400    ssh_args_list = list_split (" ", str);
401    Free ((void **) &str);
402
403    return (0);
404}
405
406/*
407 * vi:tabstop=4 shiftwidth=4 expandtab
408 */