/src/modules/sshcmd.c
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 */