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