/src/common/pipecmd.c

https://code.google.com/ · C · 325 lines · 218 code · 56 blank · 51 comment · 38 complexity · e99a5823a84fd0e8e19868c80d5a9d29 MD5 · raw file

  1. /*****************************************************************************\
  2. * $Id$
  3. *****************************************************************************
  4. * Copyright (C) 2007 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. #if HAVE_CONFIG_H
  27. #include "config.h"
  28. #endif
  29. #include <sys/wait.h>
  30. #include <sys/socket.h>
  31. #include <signal.h>
  32. #include <errno.h>
  33. #include <unistd.h>
  34. #include <stdio.h>
  35. #include "src/common/xmalloc.h"
  36. #include "src/common/xstring.h"
  37. #include "src/common/err.h"
  38. #include "src/common/list.h"
  39. #include "src/common/split.h"
  40. #include "src/pdsh/dsh.h"
  41. #include "src/pdsh/mod.h"
  42. #include "src/common/pipecmd.h"
  43. struct pipe_info_struct {
  44. pid_t pid; /* pid of child command */
  45. char *path; /* path to executable */
  46. char *cmd; /* basename of path */
  47. char *target; /* Target host of this instance */
  48. char *username; /* Username for this instance */
  49. char **args; /* Cmd arguments */
  50. int rank; /* Rank 1-N of this instance */
  51. int fd; /* stdin/out fd */
  52. int efd; /* stderr fd */
  53. };
  54. static int _pipecmd (char *path, char *args[], int *fd2p, pid_t *ppid);
  55. pipecmd_t pipe_info_create (const char *path, const char *target,
  56. const char *user, int rank)
  57. {
  58. struct pipe_info_struct *e = Malloc (sizeof (*e));
  59. e->path = Strdup (path);
  60. e->cmd = Strdup (xbasename (e->path));
  61. e->target = Strdup (target);
  62. e->username = Strdup (user);
  63. e->rank = rank;
  64. e->pid = (pid_t) 0;
  65. e->args = NULL;
  66. e->fd = -1;
  67. e->efd = -1;
  68. return (e);
  69. }
  70. static void pipe_info_destroy (struct pipe_info_struct *e)
  71. {
  72. if (e == NULL)
  73. return;
  74. Free ((void **) &e->path);
  75. Free ((void **) &e->cmd);
  76. Free ((void **) &e->target);
  77. Free ((void **) &e->username);
  78. Free ((void **) &e);
  79. }
  80. static char * pipecmd_format_arg (pipecmd_t e, const char *arg)
  81. {
  82. char buf [64];
  83. const char *q, *p;
  84. char *str = NULL;
  85. q = p = arg;
  86. while (*p != '\0') {
  87. if (*p == '%') {
  88. p++;
  89. switch (*p) {
  90. case 'h' : /* '%n' => target name */
  91. xstrcat (&str, e->target);
  92. break;
  93. case 'u':
  94. xstrcat (&str, e->username);
  95. break;
  96. case 'n':
  97. snprintf (buf, sizeof (buf) - 1, "%d", e->rank);
  98. xstrcat (&str, buf);
  99. break;
  100. case '%':
  101. xstrcatchar (&str, '%');
  102. break;
  103. default:
  104. xstrcatchar (&str, '%');
  105. xstrcatchar (&str, *p);
  106. break;
  107. }
  108. }
  109. else
  110. xstrcatchar (&str, *p);
  111. p++;
  112. }
  113. return (str);
  114. }
  115. /*
  116. * Loop through argv expanding any format characters
  117. */
  118. static char ** cmd_args_create (pipecmd_t e, const char **argv)
  119. {
  120. int i = 0;
  121. int n = 0;
  122. char **args = NULL;
  123. while (argv[n])
  124. n++;
  125. args = Malloc ((n + 2) * sizeof (char *));
  126. /*
  127. * Cmd should be args[0]:
  128. */
  129. args [0] = Strdup (e->cmd);
  130. for (i = 1; i < n+1; i++)
  131. args[i] = pipecmd_format_arg (e, argv[i-1]);
  132. args[i] = NULL;
  133. return (args);
  134. }
  135. static void cmd_args_destroy (char **args)
  136. {
  137. int i = 0;
  138. if (args == NULL)
  139. return;
  140. while (args[i])
  141. Free ((void **) &args[i++]);
  142. Free ((void **) &args);
  143. }
  144. void pipecmd_destroy (pipecmd_t p)
  145. {
  146. cmd_args_destroy (p->args);
  147. pipe_info_destroy (p);
  148. return;
  149. }
  150. pipecmd_t pipecmd (const char *path, const char **args, const char *target,
  151. const char *user, int rank)
  152. {
  153. pipecmd_t p = pipe_info_create (path, target, user, rank);
  154. p->args = cmd_args_create (p, args);
  155. if (!(p->fd = _pipecmd (p->path, p->args, &p->efd, &p->pid))) {
  156. err ("%p: exec cmd %s failed for host %S\n", path, target);
  157. pipecmd_destroy (p);
  158. return (NULL);
  159. }
  160. return (p);
  161. }
  162. int pipecmd_stdoutfd (pipecmd_t p)
  163. {
  164. if (p == NULL)
  165. return (-1);
  166. return (p->fd);
  167. }
  168. int pipecmd_stderrfd (pipecmd_t p)
  169. {
  170. if (p == NULL)
  171. return (-1);
  172. return (p->efd);
  173. }
  174. int pipecmd_signal (pipecmd_t p, int signo)
  175. {
  176. char *cmd;
  177. if (p == NULL)
  178. return (-1);
  179. cmd = xbasename (p->path);
  180. err ("sending signal %d to %s [%s] pid %d\n", signo, p->target, cmd,
  181. p->pid);
  182. return (kill (p->pid, signo));
  183. }
  184. int pipecmd_wait (pipecmd_t p, int *pstatus)
  185. {
  186. int status = 0;
  187. if (p == NULL)
  188. return (-1);
  189. if (waitpid (p->pid, &status, 0) < 0)
  190. err ("%p: %S: %s pid %ld: waitpid: %m\n", p->target,
  191. xbasename (p->path), p->pid);
  192. if (status != 0)
  193. err ("%p: %S: %s exited with exit code %d\n",
  194. p->target, xbasename (p->path), WEXITSTATUS (status));
  195. if (pstatus)
  196. *pstatus = status;
  197. return (0);
  198. }
  199. const char * pipecmd_target (pipecmd_t p)
  200. {
  201. return (p->target);
  202. }
  203. static void closeall (int fd)
  204. {
  205. int fdlimit = sysconf (_SC_OPEN_MAX);
  206. while (fd < fdlimit)
  207. close (fd++);
  208. return;
  209. }
  210. static int _pipecmd (char *path, char *args[], int *fd2p, pid_t *ppid)
  211. {
  212. int sp[2], esp[2];
  213. /*
  214. * Get socketpair for stdin/out
  215. */
  216. if (socketpair (AF_UNIX, SOCK_STREAM, 0, sp) < 0) {
  217. err ("%p: pipecmd: socketpair: %m\n");
  218. return (-1);
  219. }
  220. if (fd2p && socketpair (AF_UNIX, SOCK_STREAM, 0, esp) < 0) {
  221. err ("%p: pipecmd: socketpair: %m\n");
  222. return (-1);
  223. }
  224. if ((*ppid = fork ()) < 0) {
  225. err ("%p: pipecmd: fork: %m\n");
  226. return (-1);
  227. }
  228. if (*ppid == 0) {
  229. /*
  230. * Child. We use sp[1] for stdin/out, and close sp[0]
  231. */
  232. (void) close (sp[0]);
  233. if ((dup2 (sp[1], 0) < 0) || (dup2 (0, 1) < 0)) {
  234. err ("%p: pipecmd (in child): dup2: %m");
  235. _exit (255);
  236. }
  237. /*
  238. * Dup seperate stderr socketpair if fd2p was passed in.
  239. * Otherwise dup stdin/out onto stderr.
  240. */
  241. if (dup2 ((fd2p ? esp[1] : 0), 2) < 0) {
  242. err ("%p: pipecmd (in child): dup2: %m");
  243. _exit (255);
  244. }
  245. if (fd2p)
  246. (void) close (esp[0]);
  247. /* Try to close all stray file descriptors before
  248. * invocation of cmd to ensure that cmd stdin is closed
  249. * when pdsh/pdcp close their end of the socketpair.
  250. */
  251. closeall (3);
  252. setsid ();
  253. execvp (path, args);
  254. err ("%p: execvp %s failed: %m\n", path);
  255. _exit (255);
  256. }
  257. /*
  258. * Parent continues
  259. */
  260. (void) close (sp[1]);
  261. if (fd2p) {
  262. (void) close (esp[1]);
  263. *fd2p = esp[0];
  264. }
  265. return (sp[0]);
  266. }
  267. /*
  268. * vi: ts=4 sw=4 expandtab
  269. */