/src/modules/xrcmd.c

https://code.google.com/ · C · 350 lines · 223 code · 27 blank · 100 comment · 45 complexity · 55fdaa8fd5adf12223e0c8b09af183c1 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 code is based on the BSD rcmd.c with MT safety added, and the
  28. * interface changed. Original UC regents header included below.
  29. */
  30. /*
  31. * Copyright (c) 1983, 1993, 1994
  32. * The Regents of the University of California. All rights reserved.
  33. *
  34. * Redistribution and use in source and binary forms, with or without
  35. * modification, are permitted provided that the following conditions
  36. * are met:
  37. * 1. Redistributions of source code must retain the above copyright
  38. * notice, this list of conditions and the following disclaimer.
  39. * 2. Redistributions in binary form must reproduce the above copyright
  40. * notice, this list of conditions and the following disclaimer in the
  41. * documentation and/or other materials provided with the distribution.
  42. * 3. All advertising materials mentioning features or use of this software
  43. * must display the following acknowledgement:
  44. * This product includes software developed by the University of
  45. * California, Berkeley and its contributors.
  46. * 4. Neither the name of the University nor the names of its contributors
  47. * may be used to endorse or promote products derived from this software
  48. * without specific prior written permission.
  49. *
  50. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  51. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  52. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  53. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  54. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  55. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  56. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  57. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  58. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  59. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  60. * SUCH DAMAGE.
  61. */
  62. /*
  63. * Changes:
  64. * - MT save
  65. * - changed functional interface.
  66. */
  67. #if defined(LIBC_SCCS) && !defined(lint)
  68. static char sccsid[] = "@(#)rcmd.c 8.3 (Berkeley) 3/26/94";
  69. #endif /* LIBC_SCCS and not lint */
  70. #if HAVE_CONFIG_H
  71. #include "config.h"
  72. #endif
  73. #include <sys/param.h>
  74. #include <sys/types.h>
  75. #include <sys/time.h>
  76. #include <sys/socket.h>
  77. #include <sys/stat.h>
  78. #include <stdio.h>
  79. #if HAVE_PTHREAD_H
  80. #include <pthread.h>
  81. #endif
  82. #include <netinet/in.h>
  83. #include <arpa/inet.h>
  84. #include <signal.h>
  85. #if HAVE_FCNTL_H
  86. #include <fcntl.h>
  87. #endif
  88. #include <netdb.h>
  89. #if HAVE_UNISTD_H
  90. #include <unistd.h>
  91. #endif
  92. #include <pwd.h>
  93. #include <errno.h>
  94. #include <ctype.h>
  95. #include <string.h>
  96. #if HAVE_STRINGS_H
  97. #include <strings.h> /* AIX FD_SET calls bzero */
  98. #endif
  99. #include "src/common/err.h"
  100. #include "src/common/list.h"
  101. #include "src/common/xpoll.h"
  102. #include "src/pdsh/dsh.h"
  103. #include "src/pdsh/mod.h"
  104. #include "src/pdsh/privsep.h"
  105. #define RSH_PORT 514
  106. #if STATIC_MODULES
  107. # define pdsh_module_info xrcmd_module_info
  108. # define pdsh_module_priority xrcmd_module_priority
  109. #endif
  110. int pdsh_module_priority = DEFAULT_MODULE_PRIORITY;
  111. static int xrcmd_init(opt_t *);
  112. static int xrcmd_signal(int, void *, int);
  113. static int xrcmd(char *, char *, char *, char *, char *, int, int *, void **);
  114. /*
  115. * Export pdsh module operations structure
  116. */
  117. struct pdsh_module_operations xrcmd_module_ops = {
  118. (ModInitF) NULL,
  119. (ModExitF) NULL,
  120. (ModReadWcollF) NULL,
  121. (ModPostOpF) NULL,
  122. };
  123. /*
  124. * Export rcmd module operations
  125. */
  126. struct pdsh_rcmd_operations xrcmd_rcmd_ops = {
  127. (RcmdInitF) xrcmd_init,
  128. (RcmdSigF) xrcmd_signal,
  129. (RcmdF) xrcmd,
  130. };
  131. /*
  132. * Export module options
  133. */
  134. struct pdsh_module_option xrcmd_module_options[] =
  135. {
  136. PDSH_OPT_TABLE_END
  137. };
  138. /*
  139. * Xrcmd module info
  140. */
  141. struct pdsh_module pdsh_module_info = {
  142. "rcmd",
  143. "rsh",
  144. "Jim Garlick <garlick@llnl.gov>",
  145. "BSD rcmd connect method",
  146. DSH | PCP,
  147. &xrcmd_module_ops,
  148. &xrcmd_rcmd_ops,
  149. &xrcmd_module_options[0],
  150. };
  151. static int xrcmd_init(opt_t * opt)
  152. {
  153. /* not implemented */
  154. return 0;
  155. }
  156. /*
  157. * Use rcmd backchannel to propagate signals.
  158. * efd (IN) file descriptor connected socket (-1 if not used)
  159. * signum (IN) signal number to send
  160. */
  161. static int xrcmd_signal(int efd, void *arg, int signum)
  162. {
  163. char c;
  164. if (efd >= 0) {
  165. /* set non-blocking mode for write - just take our best shot */
  166. if (fcntl(efd, F_SETFL, O_NONBLOCK) < 0)
  167. err("%p: fcntl: %m\n");
  168. c = (char) signum;
  169. write(efd, &c, 1);
  170. }
  171. return 0;
  172. }
  173. /*
  174. * The rcmd call itself.
  175. * ahost (IN) remote hostname
  176. * addr (IN) 4 byte internet address
  177. * locuser (IN) local username
  178. * remuser (IN) remote username
  179. * cmd (IN) command to execute
  180. * rank (IN) MPI rank for this process
  181. * fd2p (IN/OUT) if non-NULL, open stderr backchannel on this fd
  182. * s (RETURN) socket for stdout/sdin or -1 on failure
  183. */
  184. static int
  185. xrcmd(char *ahost, char *addr, char *locuser, char *remuser,
  186. char *cmd, int rank, int *fd2p, void **arg)
  187. {
  188. struct sockaddr_in sin, from;
  189. sigset_t oldset, blockme;
  190. pid_t pid;
  191. int s, lport, timo, rv;
  192. char c;
  193. struct xpollfd xpfds[2];
  194. memset (xpfds, 0, sizeof (xpfds));
  195. pid = getpid();
  196. sigemptyset(&blockme);
  197. sigaddset(&blockme, SIGURG);
  198. pthread_sigmask(SIG_BLOCK, &blockme, &oldset);
  199. for (timo = 1, lport = IPPORT_RESERVED - 1;;) {
  200. s = privsep_rresvport(&lport);
  201. if (s < 0) {
  202. if (errno == EAGAIN)
  203. err("%p: %S: rcmd: socket: all ports in use\n", ahost);
  204. else
  205. err("%p: %S: rcmd: socket: %m\n", ahost);
  206. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  207. return (-1);
  208. }
  209. fcntl(s, F_SETOWN, pid);
  210. memset (&sin, 0, sizeof (sin));
  211. sin.sin_family = AF_INET;
  212. memcpy(&sin.sin_addr, addr, IP_ADDR_LEN);
  213. sin.sin_port = htons(RSH_PORT);
  214. rv = connect(s, (struct sockaddr *) &sin, sizeof(sin));
  215. if (rv >= 0)
  216. break;
  217. (void) close(s);
  218. if (errno == EADDRINUSE) {
  219. lport--;
  220. continue;
  221. }
  222. if (errno == ECONNREFUSED && timo <= 16) {
  223. (void) sleep(timo);
  224. timo *= 2;
  225. continue;
  226. }
  227. if (errno == EINTR)
  228. err("%p: %S: connect: timed out\n", ahost);
  229. else
  230. err("%p: %S: connect: %m\n", ahost);
  231. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  232. return (-1);
  233. }
  234. lport--;
  235. if (fd2p == 0) {
  236. write(s, "", 1);
  237. lport = 0;
  238. } else {
  239. char num[8];
  240. int s2 = privsep_rresvport(&lport), s3;
  241. socklen_t len = sizeof(from); /* arg to accept */
  242. if (s2 < 0)
  243. goto bad;
  244. listen(s2, 1);
  245. snprintf(num, sizeof(num), "%d", lport);
  246. if (write(s, num, strlen(num) + 1) != strlen(num) + 1) {
  247. err("%p: %S: rcmd: write (setting up stderr): %m\n", ahost);
  248. (void) close(s2);
  249. goto bad;
  250. }
  251. errno = 0;
  252. xpfds[0].fd = s;
  253. xpfds[1].fd = s2;
  254. xpfds[0].events = xpfds[1].events = XPOLLREAD;
  255. if (((rv = xpoll(xpfds, 2, -1)) < 0) || rv != 1 || (xpfds[0].revents > 0)) {
  256. if (errno != 0)
  257. err("%p: %S: rcmd: xpoll (setting up stderr): %m\n", ahost);
  258. else
  259. err("%p: %S: rcmd: xpoll: protocol failure in circuit setup\n", ahost);
  260. (void) close(s2);
  261. goto bad;
  262. }
  263. s3 = accept(s2, (struct sockaddr *) &from, &len);
  264. (void) close(s2);
  265. if (s3 < 0) {
  266. err("%p: %S: rcmd: accept: %m\n", ahost);
  267. lport = 0;
  268. goto bad;
  269. }
  270. *fd2p = s3;
  271. from.sin_port = ntohs((u_short) from.sin_port);
  272. if (from.sin_family != AF_INET ||
  273. from.sin_port >= IPPORT_RESERVED ||
  274. from.sin_port < IPPORT_RESERVED / 2) {
  275. err("%p: %S: socket: protocol failure in circuit setup\n",
  276. ahost);
  277. goto bad2;
  278. }
  279. }
  280. (void) write(s, locuser, strlen(locuser) + 1);
  281. (void) write(s, remuser, strlen(remuser) + 1);
  282. (void) write(s, cmd, strlen(cmd) + 1);
  283. rv = read(s, &c, 1);
  284. if (rv < 0) {
  285. if (errno == EINTR)
  286. err("%p: %S: read: protocol failure: %s\n",
  287. ahost, "timed out");
  288. else
  289. err("%p: %S: read: protocol failure: %m\n", ahost);
  290. goto bad2;
  291. } else if (rv != 1) {
  292. err("%p: %S: read: protocol failure: %s\n",
  293. ahost, "invalid response");
  294. goto bad2;
  295. }
  296. if (c != 0) {
  297. /* retrieve error string from remote server */
  298. char tmpbuf[LINEBUFSIZE];
  299. char *p = tmpbuf;
  300. while (read(s, &c, 1) == 1) {
  301. *p++ = c;
  302. if (c == '\n')
  303. break;
  304. }
  305. if (c != '\n')
  306. *p++ = '\n';
  307. *p++ = '\0';
  308. err("%S: %s", ahost, tmpbuf);
  309. goto bad2;
  310. }
  311. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  312. return (s);
  313. bad2:
  314. if (lport)
  315. (void) close(*fd2p);
  316. bad:
  317. (void) close(s);
  318. pthread_sigmask(SIG_SETMASK, &oldset, NULL);
  319. return (-1);
  320. }
  321. /*
  322. * vi:tabstop=4 shiftwidth=4 expandtab
  323. */