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