XRootD
XrdOucProg.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O u c P r o g . c c */
4 /* */
5 /* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
6 /* Produced by Andrew Hanushevsky for Stanford University under contract */
7 /* DE-AC02-76-SFO0515 with the Deprtment of Energy */
8 /* */
9 /* This file is part of the XRootD software suite. */
10 /* */
11 /* XRootD is free software: you can redistribute it and/or modify it under */
12 /* the terms of the GNU Lesser General Public License as published by the */
13 /* Free Software Foundation, either version 3 of the License, or (at your */
14 /* option) any later version. */
15 /* */
16 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
17 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
18 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
19 /* License for more details. */
20 /* */
21 /* You should have received a copy of the GNU Lesser General Public License */
22 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
23 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
24 /* */
25 /* The copyright holder's institutional names and contributor's names may not */
26 /* be used to endorse or promote products derived from this software without */
27 /* specific prior written permission of the institution or contributor. */
28 /******************************************************************************/
29 
30 #include <cerrno>
31 #include <cstdio>
32 #include <cstring>
33 #ifndef WIN32
34 #include <unistd.h>
35 #include <sys/types.h>
36 #include <sys/wait.h>
37 #else
38 #include <sys/types.h>
39 #include "XrdSys/XrdWin32.hh"
40 #endif
41 
42 #include "XrdOuc/XrdOucEnv.hh"
43 #include "XrdOuc/XrdOucProg.hh"
44 #include "XrdOuc/XrdOucStream.hh"
45 #include "XrdOuc/XrdOucUtils.hh"
46 #include "XrdSys/XrdSysError.hh"
47 #include "XrdSys/XrdSysPthread.hh"
48 
49 /******************************************************************************/
50 /* D e s t r u c t o r */
51 /******************************************************************************/
52 
54 {
55  Reset();
56  if (myStream) delete myStream;
57 }
58 
59 /******************************************************************************/
60 /* F e e d */
61 /******************************************************************************/
62 
63 int XrdOucProg::Feed(const char *data[], const int dlen[])
64 {
65  static XrdSysMutex feedMutex;
66  XrdSysMutexHelper feedHelper;
67  int rc;
68 
69 // Make sure we have a stream
70 //
71  if (!myStream) return EPIPE;
72  feedHelper.Lock(&feedMutex);
73 
74 // Check if this command is still running
75 //
76  if (!myStream->isAlive() && !Restart())
77  {if (eDest) eDest->Emsg("Prog" "Unable to restart", Arg[0]);
78  return EPIPE;
79  }
80 
81 // Send the line to the program
82 //
83  if (!myStream->Put((const char **)data, (const int *)dlen)) return 0;
84  if (eDest)
85  eDest->Emsg("Prog", myStream->LastError(), "feed", Arg[0]);
86  if ((rc = Restart()))
87  {if (eDest) eDest->Emsg("Prog", rc, "restart", Arg[0]);
88  return EPIPE;
89  }
90  if (!myStream->Put((const char **)data, (const int *)dlen)) return 0;
91  if (eDest)
92  eDest->Emsg("Prog", myStream->LastError(), "refeed", Arg[0]);
93  return EPIPE;
94 }
95 
96 /******************************************************************************/
97 /* R u n */
98 /******************************************************************************/
99 
100 #define runWithVec(strmP, theRC)\
101  const char *argV[4]; int argC = 0;\
102  if (arg1) argV[argC++] = arg1;\
103  if (arg2) argV[argC++] = arg2;\
104  if (arg3) argV[argC++] = arg3;\
105  if (arg4) argV[argC++] = arg4;\
106  theRC = Run(strmP, argV, argC)
107 
108 int XrdOucProg::Run(XrdOucStream *Sp, const char *argV[], int argC,
109  const char *envV[]) const
110 {
111  char **myArgs;
112  int rc, totArgs = numArgs + argC;
113 
114 // If we have no program, return an error
115 //
116  if (!ArgBuff)
117  {if (eDest) eDest->Emsg("Run", "No program specified");
118  return -ENOEXEC;
119  }
120 
121 // Copy arguments to stack storage (we could avoid this but not with the stack).
122 //
123  myArgs = (char**)alloca(sizeof(char *) * (totArgs + 1));
124  if (numArgs) memcpy(myArgs, Arg, sizeof(char*)*numArgs);
125  if (argC) memcpy(&myArgs[numArgs], argV, sizeof(char*)*argC);
126  myArgs[totArgs] = 0;
127 
128 // If this is a local process then just execute it inline on this thread
129 //
130  if (myProc) return (*myProc)(Sp, myArgs, totArgs);
131 
132 // Execute the command, possibly setting an environment.
133 //
134  if (envV)
135  {XrdOucEnv progEnv, *oldEnv = Sp->SetEnv(&progEnv);
136  progEnv.PutPtr("XrdEnvars**", (void *)envV);
137  rc = Sp->Exec(myArgs, 1, theEFD);
138  Sp->SetEnv(oldEnv);
139  } else rc = Sp->Exec(myArgs, 1, theEFD);
140 
141 // Diagnose any errors
142 //
143  if (rc)
144  {rc = Sp->LastError();
145  if (eDest) eDest->Emsg("Run", rc, "execute", Arg[0]);
146  return -rc;
147  }
148 
149 // All done, caller will drain output
150 //
151  return 0;
152 }
153 
154 /******************************************************************************/
155 
156 int XrdOucProg::Run(const char *argV[], int argC, const char *envV[]) const
157 {
158  XrdOucStream cmd;
159  char *lp;
160  int rc;
161 
162 // Execute the command
163 //
164  rc = Run(&cmd, argV, argC, envV);
165  if (rc) return rc;
166 
167 // Drain all output
168 //
169  while((lp = cmd.GetLine()))
170  if (eDest && *lp) eDest->Emsg("Run", lp);
171 
172 // All done
173 //
174  return RunDone(cmd);
175 }
176 
177 /******************************************************************************/
178 
179 int XrdOucProg::Run(XrdOucStream *Sp, const char *arg1, const char *arg2,
180  const char *arg3, const char *arg4) const
181 {
182  int rc;
183 
184 // Execute the command
185 //
186  runWithVec(Sp, rc);
187  return rc;
188 }
189 
190 /******************************************************************************/
191 
192 int XrdOucProg::Run(const char *arg1, const char *arg2,
193  const char *arg3, const char *arg4) const
194 {
195  XrdOucStream cmd;
196  char *lp;
197  int rc;
198 
199 // Execute the command
200 //
201  runWithVec(&cmd, rc);
202  if (rc) return rc;
203 
204 // Drain all output
205 //
206  while((lp = cmd.GetLine()))
207  if (eDest && *lp) eDest->Emsg("Run", lp);
208 
209 // All done
210 //
211  return RunDone(cmd);
212 }
213 
214 /******************************************************************************/
215 
216 int XrdOucProg::Run(char *outBuff, int outBsz,
217  const char *arg1, const char *arg2,
218  const char *arg3, const char *arg4) const
219 {
220  XrdOucStream cmd;
221  char *lp, *tp;
222  int n, rc;
223 
224 // Execute the command
225 //
226  runWithVec(&cmd, rc);
227  if (rc) return rc;
228 
229 // Drain the first line to the output buffer
230 //
231  if (outBuff && outBsz > 0)
232  {if ((lp = cmd.GetLine()))
233  {while (*lp && *lp == ' ') lp++;
234  if ((n = strlen(lp)))
235  {tp = lp+n-1;
236  while(*tp-- == ' ') n--;
237  if (n >= outBsz) n = outBsz-1;
238  strncpy(outBuff, lp, n); outBuff += n;
239  }
240  }
241  *outBuff = 0;
242  }
243 
244 // Drain remaining output
245 //
246  while((lp = cmd.GetLine())) {}
247 
248 // All done
249 //
250  return RunDone(cmd);
251 }
252 
253 /******************************************************************************/
254 /* R u n D o n e */
255 /******************************************************************************/
256 
258 {
259  int rc;
260 
261 // If this is an inline program then just return 0. There is no external process
262 // and the return code was returned at the time the inline process was run.
263 //
264  if (myProc) return 0;
265 
266 // Drain the command
267 //
268  rc = cmd.Drain();
269 
270 // Determine ending status
271 //
272  if (WIFSIGNALED(rc))
273  {if (eDest)
274  {char buff[16];
275  sprintf(buff, "%d", WTERMSIG(rc));
276  eDest->Emsg("Run", Arg[0], "killed by signal", buff);
277  }
278  return -EPIPE;
279  }
280  if (WIFEXITED(rc))
281  {rc = WEXITSTATUS(rc);
282  if (rc && eDest)
283  {char buff[16];
284  sprintf(buff, "%d", rc);
285  eDest->Emsg("Run", Arg[0], "ended with status", buff);
286  }
287  return -rc;
288  }
289  return 0; // We'll assume all went well here
290 }
291 
292 /******************************************************************************/
293 /* S e t u p */
294 /******************************************************************************/
295 
296 int XrdOucProg::Setup(const char *prog, XrdSysError *errP,
297  int (*Proc)(XrdOucStream *, char **, int))
298 {
299  static const int maxArgs = 65;
300  char *argV[maxArgs];
301  int rc;
302 
303 // Prepare to handle the program
304 //
305  Reset();
306  if (!errP) errP = eDest;
307  myProc = 0;
308  ArgBuff = strdup(prog);
309 
310 // Construct the argv array based on passed command line.
311 //
312  rc = XrdOucUtils::argList(ArgBuff, argV, maxArgs);
313  if (rc <= 0)
314  {if (errP)
315  {if (!rc || !argV[0])
316  {const char *pgm = (Proc ? "procedure" : "program");
317  errP->Emsg("Run", pgm, "name not specified.");
318  } else errP->Emsg("Run", rc, "set up", argV[0]);
319  }
320  return (rc ? rc : -EINVAL);
321  }
322 
323 // Record the arguments including the phamtom null pointer. We must have
324 // atleast one, the program or procedure name.
325 //
326  numArgs = rc;
327  Arg = new char*[rc+1];
328  memcpy(Arg, argV, sizeof(char *) * (rc+1));
329 
330 // If this is a local process then just record its address
331 //
332  if ((myProc = Proc)) return 0;
333 
334 // Make sure the program is really executable
335 //
336  if (access(Arg[0], X_OK))
337  {rc = errno;
338  if (errP) errP->Emsg("Run", rc, "set up", Arg[0]);
339  Reset();
340  return rc;
341  }
342  return 0;
343 }
344 
345 /******************************************************************************/
346 /* S t a r t */
347 /******************************************************************************/
348 
350 {
351 
352 // Create a stream for this command (it is an eror if we are already started)
353 //
354  if (myStream) return EBUSY;
355  if (!(myStream = new XrdOucStream(eDest))) return ENOMEM;
356 
357 // Execute the command and let it linger
358 //
359  theEFD = 0;
360  return Run(myStream);
361 }
362 
363 /******************************************************************************/
364 /* P r i v a t e M e t h o d s */
365 /******************************************************************************/
366 /******************************************************************************/
367 /* R e s e t */
368 /******************************************************************************/
369 
370 void XrdOucProg::Reset()
371 {
372 
373  if (ArgBuff) {free(ArgBuff); ArgBuff = 0;}
374  if (numArgs) delete [] Arg;
375  Arg = &ArgBuff;
376  numArgs = 0;
377 }
378 
379 /******************************************************************************/
380 /* R e s t a r t */
381 /******************************************************************************/
382 
383 int XrdOucProg::Restart()
384 {
385  myStream->Close();
386  return Run(myStream);
387 }
#define runWithVec(strmP, theRC)
Definition: XrdOucProg.cc:100
int access(const char *path, int amode)
void PutPtr(const char *varname, void *value)
Definition: XrdOucEnv.cc:316
int Start(void)
Definition: XrdOucProg.cc:349
int RunDone(XrdOucStream &cmd) const
Definition: XrdOucProg.cc:257
int Run(XrdOucStream *Sp, const char *argV[], int argc=0, const char *envV[]=0) const
Definition: XrdOucProg.cc:108
int Feed(const char *data[], const int dlen[])
Definition: XrdOucProg.cc:63
int Setup(const char *prog, XrdSysError *errP=0, int(*Proc)(XrdOucStream *, char **, int)=0)
Definition: XrdOucProg.cc:296
char * GetLine()
int Put(const char *data, const int dlen)
int Exec(const char *, int inrd=0, int efd=0)
void Close(int hold=0)
XrdOucEnv * SetEnv(XrdOucEnv *newEnv)
static int argList(char *args, char **argV, int argC)
Definition: XrdOucUtils.cc:124
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
void Lock(XrdSysMutex *Mutex)