XRootD
Loading...
Searching...
No Matches
XrdOucStream.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d O u c S t r e a m . 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 <cctype>
31#include <fcntl.h>
32#include <cstdlib>
33#include <cstring>
34#include <cstdio>
35#ifndef WIN32
36#include <poll.h>
37#include <unistd.h>
38#include <strings.h>
39#if !defined(__linux__) && !defined(__CYGWIN__) && !defined(__GNU__) && !defined(__FreeBSD__)
40#include <sys/conf.h>
41#endif
42#include <sys/stat.h>
43#include <termios.h>
44#include <sys/types.h>
45#include <sys/wait.h>
46#else // WIN32
47#include "XrdSys/XrdWin32.hh"
48#include <process.h>
49#endif // WIN32
50
51#include <set>
52#include <string>
53
54#include "XrdOuc/XrdOucEnv.hh"
58#include "XrdOuc/XrdOucTList.hh"
59#include "XrdOuc/XrdOucUtils.hh"
60#include "XrdSys/XrdSysE2T.hh"
61#include "XrdSys/XrdSysFD.hh"
66
67/******************************************************************************/
68/* l o c a l d e f i n e s */
69/******************************************************************************/
70
71#define MaxARGC 64
72#define XrdOucStream_EOM 0x01
73#define XrdOucStream_BUSY 0x02
74#define XrdOucStream_ELIF 0x80
75
76#define XrdOucStream_CADD 0x010000
77#define XrdOucStream_CONT 0xff0000
78#define XrdOucStream_CMAX 0x0f0000
79
80#define Erq(p, a, b) Err(p, a, b, (char *)0)
81#define Err(p, a, b, c) (ecode=(Eroute ? Eroute->Emsg(#p, a, b, c) : a), -1)
82#define Erp(p, a, b, c) ecode=(Eroute ? Eroute->Emsg(#p, a, b, c) : a)
83
84// The following is used by child processes prior to exec() to avoid deadlocks
85//
86#define Erx(p, a, b) std::cerr <<#p <<": " <<XrdSysE2T(a) <<' ' <<b <<std::endl;
87
88/******************************************************************************/
89/* S t a t i c M e m b e r s & O b j e c t s */
90/******************************************************************************/
91
92// The following mutex is used to allow only one fork at a time so that
93// we do not leak file descriptors. It is a short-lived lock.
94//
95namespace {XrdSysMutex forkMutex;}
96
97XrdOucString *XrdOucStream::theCFG = 0;
98
99/******************************************************************************/
100/* L o c a l C l a s s e s */
101/******************************************************************************/
102
104 {char *myHost;
105 char *myName;
106 char *myExec;
107
108 std::set<std::string> *fcList;
109 std::set<std::string>::iterator itFC;
110
112 fcList(0) {}
113 ~StreamInfo() {if (fcList) delete fcList;}
114 };
115
116namespace
117{
118class contHandler
119{
120public:
121
122char *path;
123XrdOucTList *tlP;
124
125void Add(const char *sfx) {tlP = new XrdOucTList(sfx,(int)strlen(sfx),tlP);}
126
127 contHandler() : path(0), tlP(0) {}
128 ~contHandler() {XrdOucTList *tlN;
129 while(tlP) {tlN = tlP; tlP = tlP->next; delete tlN;}
130 if (path) free(path);
131 }
132};
133}
134
135/******************************************************************************/
136/* L o c a l F u n c t i o n s */
137/******************************************************************************/
138
139namespace
140{
141bool KeepFile(const char *fname, XrdOucTList *tlP)
142{
143 struct sfxList {const char *txt; int len;};
144 static sfxList sfx[] = {{".cfsaved", 8},
145 {".rpmsave", 8},
146 {".rpmnew", 7},
147 {".dpkg-old", 9},
148 {".dpkg-dist", 10},
149 {"~", 1}
150 };
151 static int sfxLNum = sizeof(sfx)/sizeof(struct sfxList);
152 int n;
153
154// We don't keep file that start with a dot
155//
156 if (*fname == '.') return false;
157 n = strlen(fname);
158
159// Process white list first, otherwise use the black list
160//
161 if (tlP)
162 {while(tlP)
163 {if (tlP->ival[0] < n && !strcmp(tlP->text, fname+n-tlP->ival[0]))
164 return true;
165 tlP = tlP->next;
166 }
167 return false;
168 }
169
170// Check all other suffixes we wish to avoid
171//
172 for (int i = 0; i < sfxLNum; i++)
173 {if (sfx[i].len < n && !strcmp(sfx[i].txt, fname+n-sfx[i].len))
174 return false;
175 }
176
177// This file can be kept
178//
179 return true;
180}
181}
182
183/******************************************************************************/
184/* o o u c _ S t r e a m C o n s t r u c t o r */
185/******************************************************************************/
186
187XrdOucStream::XrdOucStream(XrdSysError *erobj, const char *ifname,
188 XrdOucEnv *anEnv, const char *Pfx)
189{
190 char *cp;
191
192
193 if (ifname)
194 {myInst = strdup(ifname);
195 myInfo = new StreamInfo;
196 if (!(cp = index(myInst, ' '))) {cp = myInst; myInfo->myExec = 0;}
197 else {*cp = '\0'; cp++;
198 myInfo->myExec = (*myInst ? myInst : 0);
199 }
200 if ( (myInfo->myHost = index(cp, '@')))
201 {*(myInfo->myHost) = '\0';
202 myInfo->myHost++;
203 myInfo->myName = (*cp ? cp : 0);
204 } else {myInfo->myHost = cp; myInfo->myName = 0;}
205 } else {myInst = 0; myInfo = 0;}
206 myRsv1 = myRsv2 = 0;
207
208 FD = -1;
209 FE = -1;
210 bsize = 0;
211 buff = 0;
212 bnext = 0;
213 bleft = 0;
214 recp = 0;
215 token = 0;
216 flags = 0;
217 child = 0;
218 ecode = 0;
219 notabs = 0;
220 xcont = 1;
221 xline = 0;
222 Eroute = erobj;
223 myEnv = anEnv;
224 sawif = 0;
225 skpel = 0;
226 if (myEnv && Eroute)
227 {llBuff = (char *)malloc(llBsz);
228 llBcur = llBuff; llBok = 0; llBleft = llBsz; *llBuff = '\0';
229 Verbose= 1;
230 } else {
231 Verbose= 0;
232 llBuff = 0;
233 llBcur = 0;
234 llBleft= 0;
235 llBok = 0;
236 }
237 varVal = (myEnv ? new char[maxVLen+1] : 0);
238 llPrefix = Pfx;
239}
240
241/******************************************************************************/
242/* A t t a c h */
243/******************************************************************************/
244
245int XrdOucStream::AttachIO(int infd, int outfd, int bsz)
246{
247 if (Attach(infd, bsz)) return -1;
248 FE = outfd;
249 return 0;
250}
251
252int XrdOucStream::Attach(int FileDescriptor, int bsz)
253{
254
255 // Close the current stream. Close will handle unopened streams.
256 //
257 StreamInfo *saveInfo = myInfo; myInfo = 0;
258 Close();
259 myInfo = saveInfo;
260
261 // Allocate a new buffer for this stream
262 //
263 if (!bsz) buff = 0;
264 else if (!(buff = (char *)malloc(bsz+1)))
265 return Erq(Attach, errno, "allocate stream buffer");
266
267 // Initialize the stream
268 //
269 FD= FE = FileDescriptor;
270 bnext = buff;
271 bsize = bsz+1;
272 bleft = 0;
273 recp = 0;
274 token = 0;
275 flags = 0;
276 ecode = 0;
277 xcont = 1;
278 xline = 0;
279 sawif = 0;
280 skpel = 0;
281 if (llBuff)
282 {llBcur = llBuff; *llBuff = '\0'; llBleft = llBsz; llBok = 0;}
283 return 0;
284}
285
286/******************************************************************************/
287/* C a p t u r e */
288/******************************************************************************/
289
290void XrdOucStream::Capture(const char **cVec, bool linefeed)
291{
292// Make sure we can handle this
293//
294 if (theCFG && cVec && cVec[0])
295 {if (linefeed) theCFG->append("\n# ");
296 else theCFG->append("# ");
297 int i = 0;
298 while(cVec[i]) theCFG->append(cVec[i++]);
299 theCFG->append('\n');
300 }
301}
302
303/******************************************************************************/
304
306{
307 XrdOucString *oldCFG = theCFG;
308 theCFG = newCFG;
309 return oldCFG;
310}
311
312/******************************************************************************/
313
315{
316 return theCFG;
317}
318
319/******************************************************************************/
320/* C l o s e */
321/******************************************************************************/
322
324{
325
326 // Wait for any associated process on this stream
327 //
328 if (!hold && child) Drain();
329 else child = 0;
330
331 // Close the associated file descriptor if it was open
332 //
333 if (FD >= 0) close(FD);
334 if (FE >= 0 && FE != FD) close(FE);
335
336 // Release the buffer if it was allocated.
337 //
338 if (buff) free(buff);
339
340 // Clear all data values by attaching a dummy FD
341 //
342 FD = FE = -1;
343 buff = 0;
344
345 // Check if we should echo the last line
346 //
347 if (llBuff)
348 {if (Verbose && *llBuff && llBok > 1)
349 {if (Eroute) Eroute->Say(llPrefix, llBuff);
350 if (theCFG) add2CFG(llBuff);
351 }
352 llBok = 0;
353 }
354
355 // Delete any info object we have allocated
356 //
357 if (myInfo)
358 {delete myInfo;
359 myInfo = 0;
360 }
361}
362
363/******************************************************************************/
364/* D r a i n */
365/******************************************************************************/
366
368{
369 int Status = 0;
370
371 // Drain any outstanding processes (i.e., kill the process group)
372 //
373#ifndef WIN32
374 int retc;
375 if (child) {kill(-child, 9);
376 do {retc = waitpid(child, &Status, 0);}
377 while(retc > 0 || (retc == -1 && errno == EINTR));
378 child = 0;
379 }
380#else
381 if (child) {
382 TerminateProcess((HANDLE)child, 0);
383 child = 0;
384 }
385#endif
386 return Status;
387}
388
389/******************************************************************************/
390/* E c h o */
391/******************************************************************************/
392
394{
395 if (llBok > 1 && Verbose && llBuff)
396 {if (Eroute) Eroute->Say(llPrefix,llBuff);
397 if (theCFG) add2CFG(llBuff);
398 }
399 llBok = 0;
400}
401
402/******************************************************************************/
403/* E c h o O n l y */
404/******************************************************************************/
405
406void XrdOucStream::Echo(bool capture)
407{
408 if (llBok && Verbose && llBuff)
409 {if (Eroute) Eroute->Say(llPrefix,llBuff);
410 if (capture && theCFG) add2CFG(llBuff);
411 }
412 llBok = 0;
413}
414
415/******************************************************************************/
416/* E x e c */
417/******************************************************************************/
418
419int XrdOucStream::Exec(const char *theCmd, int inrd, int efd)
420{
421 int j;
422 char *cmd, *origcmd, *parm[MaxARGC];
423
424 if (!theCmd)
425 return EINVAL;
426
427 // Allocate a buffer for the command as we will be modifying it
428 //
429 origcmd = cmd = (char *)malloc(strlen(theCmd)+1);
430 strcpy(cmd, theCmd);
431
432 // Construct the argv array based on passed command line.
433 //
434 for (j = 0; j < MaxARGC-1 && *cmd; j++)
435 {while(*cmd == ' ') cmd++;
436 if (!(*cmd)) break;
437 parm[j] = cmd;
438 while(*cmd && *cmd != ' ') cmd++;
439 if (*cmd) {*cmd = '\0'; cmd++;}
440 }
441 parm[j] = (char *)0;
442
443 // Continue with normal processing
444 //
445 int ret = j > 0 ? Exec(parm, inrd, efd) : EINVAL;
446 free(origcmd);
447 return ret;
448}
449
450int XrdOucStream::Exec(char **parm, int inrd, int efd)
451{
452 int fildes[2], Child_in = -1, Child_out = -1, Child_log = -1;
453
454 // Create a pipe. Minimize file descriptor leaks.
455 //
456 if (inrd >= 0)
457 {if (pipe(fildes))
458 return Err(Exec, errno, "create input pipe for", parm[0]);
459 else {
460 fcntl(fildes[0], F_SETFD, FD_CLOEXEC);
461 Attach(fildes[0]); Child_out = fildes[1];
462 }
463
464 if (inrd)
465 {if (pipe(fildes))
466 return Err(Exec, errno, "create output pipe for", parm[0]);
467 else {
468 fcntl(fildes[1], F_SETFD, FD_CLOEXEC);
469 FE = fildes[1]; Child_in = fildes[0];
470 }
471 }
472 } else {Child_out = FD; Child_in = FE;}
473
474 // Handle the standard error file descriptor
475 //
476 if (!efd) Child_log = (Eroute ? dup(Eroute->logger()->originalFD()) : -1);
477 else if (efd > 0) Child_log = efd;
478 else if (efd == -2){Child_log = Child_out; Child_out = -1;}
479 else if (efd == -3) Child_log = Child_out;
480
481 // Fork a process first so we can pick up the next request. We also
482 // set the process group in case the child hasn't been able to do so.
483 // Make sure only one fork occurs at any one time (we are the only one).
484 //
485 forkMutex.Lock();
486 if ((child = fork()))
487 {if (child < 0)
488 {close(Child_in); close(Child_out); forkMutex.UnLock();
489 return Err(Exec, errno, "fork request process for", parm[0]);
490 }
491 close(Child_out);
492 if (inrd) close(Child_in );
493 if (!efd && Child_log >= 0) close(Child_log);
494 forkMutex.UnLock();
495 setpgid(child, child);
496 return 0;
497 }
498
499 /*****************************************************************/
500 /* C h i l d P r o c e s s */
501 /*****************************************************************/
502
503 // Redirect standard in if so requested
504 //
505 if (Child_in >= 0)
506 {if (inrd)
507 {if (dup2(Child_in, STDIN_FILENO) < 0)
508 {Erx(Exec, errno, "setting up standard in for " <<parm[0]);
509 _exit(255);
510 } else if (Child_in != Child_out) close(Child_in);
511 }
512 }
513
514 // Reassign the stream to be standard out to capture all of the output.
515 //
516 if (Child_out >= 0)
517 {if (dup2(Child_out, STDOUT_FILENO) < 0)
518 {Erx(Exec, errno, "setting up standard out for " <<parm[0]);
519 _exit(255);
520 } else if (Child_out != Child_log) close(Child_out);
521 }
522
523 // Redirect stderr of the stream if we can to avoid keeping the logfile open
524 //
525 if (Child_log >= 0)
526 {if (dup2(Child_log, STDERR_FILENO) < 0)
527 {Erx(Exec, errno, "set up standard err for " <<parm[0]);
528 _exit(255);
529 } else close(Child_log);
530 }
531
532 // Check if we need to set any envornment variables
533 //
534 if (myEnv)
535 {char **envP;
536 int i = 0;
537 if ((envP = (char **)myEnv->GetPtr("XrdEnvars**")))
538 while(envP[i]) {putenv(envP[i]); i++;}
539 }
540
541 // Set our process group (the parent should have done this by now) then
542 // invoke the command never to return
543 //
544 setpgid(0,0);
545 execv(parm[0], parm);
546 Erx(Exec, errno, "executing " <<parm[0]);
547 _exit(255);
548}
549
550/******************************************************************************/
551/* G e t L i n e */
552/******************************************************************************/
553
555{
556 int bcnt, retc;
557 char *bp;
558
559// Check if end of message has been reached.
560//
561 if (flags & XrdOucStream_EOM) return (char *)NULL;
562
563// Find the next record in the buffer
564//
565 if (bleft > 0)
566 {recp = bnext; bcnt = bleft;
567 for (bp = bnext; bcnt--; bp++)
568 if (!*bp || *bp == '\n')
569 {if (!*bp) flags |= XrdOucStream_EOM;
570 *bp = '\0';
571 bnext = ++bp;
572 bleft = bcnt;
573 token = recp;
574 return recp;
575 }
576 else if (notabs && *bp == '\t') *bp = ' ';
577
578 // There is no next record, so move up data in the buffer.
579 //
580 strncpy(buff, bnext, bleft);
581 bnext = buff + bleft;
582 }
583 else bnext = buff;
584
585// Prepare to read in more data.
586//
587 bcnt = bsize - (bnext - buff) -1;
588 bp = bnext;
589
590// Read up to the maximum number of bytes. Stop reading should we see a
591// new-line character or a null byte -- the end of a record.
592//
593 recp = token = buff; // This will always be true at this point
594 while(bcnt)
595 {do { retc = read(FD, (void *)bp, (size_t)bcnt); }
596 while (retc < 0 && errno == EINTR);
597
598 if (retc < 0) {Erp(GetLine,errno,"read request",0); return (char *)0;}
599 if (!retc)
600 {*bp = '\0';
601 flags |= XrdOucStream_EOM;
602 bnext = ++bp;
603 bleft = 0;
604 return buff;
605 }
606
607 bcnt -= retc;
608 while(retc--)
609 if (!*bp || *bp == '\n')
610 {if (!*bp) flags |= XrdOucStream_EOM;
611 else *bp = '\0';
612 bnext = ++bp;
613 bleft = retc;
614 return buff;
615 } else {
616 if (notabs && *bp == '\t') *bp = ' ';
617 bp++;
618 }
619 }
620
621// All done, force an end of record.
622//
623 Erp(GetLine, EMSGSIZE, "read full message", 0);
624 buff[bsize-1] = '\0';
625 return buff;
626}
627
628/******************************************************************************/
629/* G e t T o k e n */
630/******************************************************************************/
631
632char *XrdOucStream::GetToken(int lowcase) {
633 char *tpoint;
634
635 // Verify that we have a token to return;
636 //
637 if (!token) return (char *)NULL;
638
639 // Skip to the first non-blank character.
640 //
641 while (*token && *token == ' ') token ++;
642 if (!*token) {token = 0; return 0;}
643 tpoint = token;
644
645 // Find the end of the token.
646 //
647 if (lowcase) while (*token && *token != ' ')
648 {*token = (char)tolower((int)*token); token++;}
649 else while (*token && *token != ' ') {token++;}
650 if (*token) {*token = '\0'; token++;}
651
652 // All done here.
653 //
654 return tpoint;
655}
656
657char *XrdOucStream::GetToken(char **rest, int lowcase)
658{
659 char *tpoint;
660
661 // Get the next token
662 //
663 if (!(tpoint = GetToken(lowcase))) return tpoint;
664
665 // Skip to the first non-blank character.
666 //
667 while (*token && *token == ' ') token ++;
668 if (rest) *rest = token;
669
670
671 // All done.
672 //
673 return tpoint;
674}
675
676/******************************************************************************/
677/* G e t F i r s t W o r d */
678/******************************************************************************/
679
681{
682 // If in the middle of a line, flush to the end of the line. Suppress
683 // variable substitution when doing this to avoid errors.
684 //
685 if (xline)
686 {XrdOucEnv *oldEnv = SetEnv(0);
687 while(GetWord(lowcase)) {}
688 SetEnv(oldEnv);
689 }
690 return GetWord(lowcase);
691}
692
693/******************************************************************************/
694/* G e t M y F i r s t W o r d */
695/******************************************************************************/
696
698{
699 char *var;
700 int skip2fi = 0;
701
702 Echo();
703
704 if (!myInst)
705 {if (!myEnv) return add2llB(GetFirstWord(lowcase), 1);
706 else {while((var = GetFirstWord(lowcase)) && !isSet(var)) {}
707 return add2llB(var, 1);
708 }
709 }
710
711 do {if (!(var = GetFirstWord(lowcase)))
712 {if (sawif && !ecode)
713 {ecode = EINVAL;
714 if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
715 }
716 return add2llB(var, 1);
717 }
718
719 add2llB(var, 1);
720
721 if (!strcmp("continue", var))
722 {if (!docont()) return 0;
723 continue;
724 }
725
726 if ( !strcmp("if", var)) var = doif();
727 if (var && !strcmp("else", var)) var = doelse();
728 if (var && !strcmp("fi", var))
729 {if (sawif) sawif = skpel = skip2fi = 0;
730 else {if (Eroute)
731 Eroute->Emsg("Stream", "No preceding 'if' for 'fi'.");
732 ecode = EINVAL;
733 }
734 continue;
735 }
736 if (var && (!myEnv || !isSet(var))) return add2llB(var, 1);
737 } while (1);
738
739 return 0;
740}
741
742/******************************************************************************/
743/* G e t W o r d */
744/******************************************************************************/
745
746char *XrdOucStream::GetWord(int lowcase)
747{
748 char *wp, *ep;
749
750 // A call means the first token was acceptable and we continuing to
751 // parse, hence the line is echoable.
752 //
753 if (llBok == 1) llBok = 2;
754
755 // If we have a token, return it
756 //
757 xline = 1;
758 while((wp = GetToken(lowcase)))
759 {if (!myEnv) return add2llB(wp);
760 if ((wp = vSubs(wp)) && *wp) return add2llB(wp);
761 }
762
763 // If no continuation allowed, return a null (but only once)
764 //
765 if (!xcont) {xcont = 1; xline = 0; return (char *)0;}
766
767 // Find the next non-blank non-comment line
768 //
769do {while(GetLine())
770 {// Get the first token (none if it is a blank line)
771 //
772 if (!(wp = GetToken(lowcase))) continue;
773
774 // If token starts with a pound sign, skip the line
775 //
776 if (*wp == '#') continue;
777
778 // Process continuations (last non-blank character is a back-slash)
779 //
780 ep = bnext-2;
781 while (ep >= buff && *ep == ' ') ep--;
782 if (ep < buff) continue;
783 if (*ep == '\\') {xcont = 1; *ep = '\0';}
784 else xcont = 0;
785 return add2llB((myEnv ? vSubs(wp) : wp));
786 }
787
788 if (myInfo && myInfo->fcList)
789 {if (myInfo->itFC == myInfo->fcList->end())
790 {bleft = 0;
791 flags |= XrdOucStream_EOM;
792 break;
793 }
794 const char *path = (*(myInfo->itFC)).c_str();
795 myInfo->itFC++;
796 if (!docontF(path)) break;
797 bleft = 0;
798 flags &= ~XrdOucStream_EOM;
799 } else break;
800 } while(true);
801
802 xline = 0;
803 return (char *)0;
804}
805
806/******************************************************************************/
807/* G e t R e s t */
808/******************************************************************************/
809
810int XrdOucStream::GetRest(char *theBuff, int Blen, int lowcase)
811{
812 char *tp, *myBuff = theBuff;
813 int tlen;
814
815// Get remaining tokens
816//
817 theBuff[0] = '\0';
818 while ((tp = GetWord(lowcase)))
819 {tlen = strlen(tp);
820 if (tlen+1 >= Blen) return 0;
821 if (myBuff != theBuff) {*myBuff++ = ' '; Blen--;}
822 strcpy(myBuff, tp);
823 Blen -= tlen; myBuff += tlen;
824 }
825
826// All done
827//
828 add2llB(0);
829 return 1;
830}
831
832/******************************************************************************/
833/* R e t T o k e n */
834/******************************************************************************/
835
837{
838 // Check if we can back up
839 //
840 if (!token || token == recp) return;
841
842 // Find the null byte for the token and remove it, if possible
843 //
844 while(*token && token != recp) token--;
845 if (token != recp)
846 {if (token+1 != bnext) *token = ' ';
847 token--;
848 while(*token && *token != ' ' && token != recp) token--;
849 if (token != recp) token++;
850 }
851
852 // If saving line, we must do the same for the saved line
853 //
854 if (llBuff)
855 while(llBcur != llBuff && *llBcur != ' ') {llBcur--; llBleft++;}
856}
857
858/******************************************************************************/
859/* P u t */
860/******************************************************************************/
861
862int XrdOucStream::Put(const char *data, const int dlen) {
863 int dcnt = dlen, retc;
864
865 if (flags & XrdOucStream_BUSY) {ecode = ETXTBSY; return -1;}
866
867 while(dcnt)
868 {do { retc = write(FE, (const void *)data, (size_t)dlen);}
869 while (retc < 0 && errno == EINTR);
870 if (retc >= 0) dcnt -= retc;
871 else {flags |= XrdOucStream_BUSY;
872 Erp(Put, errno, "write to stream", 0);
873 flags &= ~XrdOucStream_BUSY;
874 return -1;
875 }
876 }
877 return 0;
878}
879
880int XrdOucStream::Put(const char *datavec[], const int dlenvec[]) {
881 int i, retc, dlen;
882 const char *data;
883
884 if (flags & XrdOucStream_BUSY) {ecode = ETXTBSY; return -1;}
885
886 for (i = 0; datavec[i]; i++)
887 {data = datavec[i]; dlen = dlenvec[i];
888 while(dlen)
889 {do { retc = write(FE, (const void *)data, (size_t)dlen);}
890 while (retc < 0 && errno == EINTR);
891 if (retc >= 0) {data += retc; dlen -= retc;}
892 else {flags |= XrdOucStream_BUSY;
893 Erp(Put, errno, "write to stream",0);
894 flags &= ~XrdOucStream_BUSY;
895 return -1;
896 }
897 }
898 }
899 return 0;
900}
901
902/******************************************************************************/
903/* P u t L i n e */
904/******************************************************************************/
905
906int XrdOucStream::PutLine(const char *data, int dlen)
907{
908 static const int plSize = 2048;
909
910// Allocate a buffer if one is not allocated
911//
912 if (!buff)
913 {if (!(buff = (char *)malloc(plSize)))
914 return Erq(Attach, errno, "allocate stream buffer");
915 bsize = plSize;
916 }
917
918// Adjust dlen
919//
920 if (dlen <= 0) dlen = strlen(data);
921 if (dlen >= bsize) dlen = bsize-1;
922
923// Simply insert the line into the buffer, truncating if need be
924//
925 bnext = recp = token = buff; // This will always be true at this point
926 if (dlen <= 0)
927 {*buff = '\0';
928 flags |= XrdOucStream_EOM;
929 bleft = 0;
930 } else {
931 strncpy(buff, data, dlen);
932 *(buff+dlen) = 0;
933 bleft = dlen+1;
934 }
935// All done
936//
937 return 0;
938}
939
940/******************************************************************************/
941/* W a i t 4 D a t a */
942/******************************************************************************/
943
945{
946 struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0};
947 int retc;
948
949// Wait until we can actually read something
950//
951 do {retc = poll(&polltab, 1, msMax);} while(retc < 0 && errno == EINTR);
952 if (retc != 1) return (retc ? errno : -1);
953
954// Return correct value
955//
956 return (polltab.revents & (POLLIN|POLLRDNORM) ? 0 : EIO);
957}
958
959/******************************************************************************/
960/* P r i v a t e M e t h o d s */
961/******************************************************************************/
962/******************************************************************************/
963/* a d d 2 C F G */
964/******************************************************************************/
965
966void XrdOucStream::add2CFG(const char *data, bool isCMT)
967{
968 if (isCMT) theCFG->append("# ");
969 theCFG->append(data);
970 theCFG->append('\n');
971}
972
973/******************************************************************************/
974/* a d d 2 l l B */
975/******************************************************************************/
976
977char *XrdOucStream::add2llB(char *tok, int reset)
978{
979 int tlen;
980
981// Return if not saving data
982//
983 if (!llBuff) return tok;
984
985// Check if we should flush the previous line
986//
987 if (reset)
988 {llBok = 1;
989 llBcur = llBuff;
990 llBleft= llBsz;
991 *llBuff = '\0';
992 } else if (!llBok) return tok;
993 else {llBok = 2;
994 if (llBleft >= 2)
995 {*llBcur++ = ' '; *llBcur = '\0'; llBleft--;}
996 }
997
998// Add in the new token
999//
1000 if (tok)
1001 {tlen = strlen(tok);
1002 if (tlen < llBleft)
1003 {strcpy(llBcur, tok); llBcur += tlen; llBleft -= tlen;}
1004 }
1005 return tok;
1006}
1007
1008/******************************************************************************/
1009/* E c h o */
1010/******************************************************************************/
1011
1012bool XrdOucStream::Echo(int ec, const char *t1, const char *t2, const char *t3)
1013{
1014 if (Eroute)
1015 {if (t1) Eroute->Emsg("Stream", t1, t2, t3);
1016 if (llBok > 1 && Verbose && llBuff) Eroute->Say(llPrefix,llBuff);
1017 }
1018 ecode = ec;
1019 llBok = 0;
1020 return false;
1021}
1022
1023/******************************************************************************/
1024/* d o c o n t */
1025/******************************************************************************/
1026
1027bool XrdOucStream::docont()
1028{
1029 char *theWord;
1030
1031// A continue is not valid within the scope of an if
1032//
1033 if (sawif) return Echo(EINVAL, "'continue' invalid within 'if-fi'.");
1034
1035// Get the path (keep case), if none then ignore this continue
1036//
1037 theWord = GetWord();
1038 if (!theWord)
1039 {Echo();
1040 return true;
1041 }
1042
1043// Prepare to handle the directive
1044//
1045 contHandler cH;
1046 cH.path = strdup(theWord);
1047
1048// Grab additioal tokens which may be suffixes
1049//
1050 theWord = GetWord();
1051 while(theWord && *theWord == '*')
1052 {if (!*(theWord+1)) return Echo(EINVAL, "suffix missing after '*'.");
1053 cH.Add(theWord+1);
1054 theWord = GetWord();
1055 }
1056
1057// If we have a token, it better be an if
1058//
1059 if (theWord && strcmp(theWord, "if"))
1060 return Echo(EINVAL, "expecting 'if' but found", theWord);
1061
1062// Process the 'if'
1063//
1064 if (theWord && !XrdOucUtils::doIf(Eroute, *this, "continue directive",
1065 myInfo->myHost,myInfo->myName,myInfo->myExec))
1066 return true;
1067 Echo();
1068// if (Eroute) Eroute->Say(llPrefix, "continue ", path, " if true");
1069// if (Eroute) Eroute->Say(llPrefix, "continue ", bnext);
1070 return docont(cH.path, cH.tlP);
1071}
1072
1073/******************************************************************************/
1074
1075bool XrdOucStream::docont(const char *path, XrdOucTList *tlP)
1076{
1077 struct stat Stat;
1078 bool noentok;
1079
1080// A continue directive in the context of a continuation is illegal
1081//
1082 if ((myInfo && myInfo->fcList) || (flags & XrdOucStream_CONT) != 0)
1083 return Echo(EINVAL, "'continue' in a continuation is not allowed.");
1084
1085// Check if this file must exist (we also take care of empty paths)
1086//
1087 if ((noentok = (*path == '?')))
1088 {path++;
1089 if (!(*path)) return true;
1090 }
1091
1092// Check if this is a file or directory
1093//
1094 if (stat(path, &Stat))
1095 {if (errno == ENOENT && noentok) return true;
1096 if (Eroute)
1097 {Eroute->Emsg("Stream", errno, "open", path);
1098 ecode = ECANCELED;
1099 } else ecode = errno;
1100 return false;
1101 }
1102
1103// For directory continuation, there is much more to do (this can only happen
1104// once). Note that we used to allow a limited number of chained file
1105// continuations. No more, but we are still setup to easily do so.
1106//
1107 if ((Stat.st_mode & S_IFMT) == S_IFDIR)
1108 {if (!docontD(path, tlP)) return false;
1109 path = (*(myInfo->itFC)).c_str();
1110 myInfo->itFC++;
1111 } else flags |= XrdOucStream_CADD;
1112
1113// if ((flags & XrdOucStream_CONT) > XrdOucStream_CMAX)
1114// {if (Eroute)
1115// {Eroute->Emsg("Stream", EMLINK, "continue to", path);
1116// ecode = ECANCELED;
1117// } else ecode = EMLINK;
1118// return false;
1119// }
1120// }
1121
1122// Continue with the next file
1123//
1124 return docontF(path, noentok);
1125}
1126
1127/******************************************************************************/
1128/* d o c o n t D */
1129/******************************************************************************/
1130
1131bool XrdOucStream::docontD(const char *path, XrdOucTList *tlP)
1132{
1133 static const mode_t isXeq = S_IXUSR | S_IXGRP | S_IXOTH;
1134 XrdOucNSWalk nsWalk(Eroute, path, 0, XrdOucNSWalk::retFile);
1135 int rc;
1136
1137// Get all of the file entries in this directory
1138//
1139 XrdOucNSWalk::NSEnt *nsX, *nsP = nsWalk.Index(rc);
1140 if (rc)
1141 {if (Eroute) Eroute->Emsg("Stream", rc, "index config files in", path);
1142 ecode = ECANCELED;
1143 return false;
1144 }
1145
1146// Keep only files of interest
1147//
1148 myInfo->fcList = new std::set<std::string>;
1149 while((nsX = nsP))
1150 {nsP = nsP->Next;
1151 if ((nsX->Stat.st_mode & isXeq) == 0 && KeepFile(nsX->File, tlP))
1152 myInfo->fcList->insert(std::string(nsX->Path));
1153 delete nsX;
1154 }
1155
1156// Check if we have anything in the map
1157//
1158 if (myInfo->fcList->size() == 0)
1159 {delete myInfo->fcList;
1160 myInfo->fcList = 0;
1161 return false;
1162 }
1163
1164// All done
1165//
1166 myInfo->itFC = myInfo->fcList->begin();
1167 return true;
1168}
1169
1170/******************************************************************************/
1171/* c o n t F */
1172/******************************************************************************/
1173
1174bool XrdOucStream::docontF(const char *path, bool noentok)
1175{
1176 int cFD;
1177
1178// Open the file and handle any errors
1179//
1180 if ((cFD = XrdSysFD_Open(path, O_RDONLY)) < 0)
1181 {if (errno == ENOENT && noentok) return true;
1182 if (Eroute)
1183 {Eroute->Emsg("Stream", errno, "open", path);
1184 ecode = ECANCELED;
1185 } else ecode = errno;
1186 return false;
1187 }
1188
1189// Continue to the next file
1190//
1191 if (XrdSysFD_Dup2(cFD, FD) < 0)
1192 {if (Eroute)
1193 {Eroute->Emsg("Stream", ecode, "switch to", path);
1194 close(cFD);
1195 ecode = ECANCELED;
1196 } else ecode = errno;
1197 return false;
1198 }
1199
1200// Indicate we are switching to anther file
1201//
1202 if (Eroute) Eroute->Say("Config continuing with file ", path, " ...");
1203 bleft = 0;
1204 close(cFD);
1205 return true;
1206}
1207
1208/******************************************************************************/
1209/* d o e l s e */
1210/******************************************************************************/
1211
1212char *XrdOucStream::doelse()
1213{
1214 char *var;
1215
1216// An else must be preceeded by an if and not by a naked else
1217//
1218 if (!sawif || sawif == 2)
1219 {if (Eroute) Eroute->Emsg("Stream", "No preceding 'if' for 'else'.");
1220 ecode = EINVAL;
1221 return 0;
1222 }
1223
1224// If skipping all else caluses, skip all lines until we reach a fi
1225//
1226 if (skpel)
1227 {while((var = GetFirstWord()))
1228 {if (!strcmp("fi", var)) return var;}
1229 if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
1230 ecode = EINVAL;
1231 return 0;
1232 }
1233
1234// Elses are still possible then process one of them
1235//
1236 do {if (!(var = GetWord())) // A naked else will always succeed
1237 {sawif = 2;
1238 return 0;
1239 }
1240 if (strcmp("if", var)) // An else may only be followed by an if
1241 {Eroute->Emsg("Stream","'else",var,"' is invalid.");
1242 ecode = EINVAL;
1243 return 0;
1244 }
1245 sawif = 0;
1246 flags |= XrdOucStream_ELIF;
1247 var = doif();
1248 flags &= ~XrdOucStream_ELIF;
1249 } while(var && !strcmp("else", var));
1250 return var;
1251}
1252
1253/******************************************************************************/
1254/* d o i f */
1255/******************************************************************************/
1256
1257/* Function: doif
1258
1259 Purpose: To parse the directive: if [<hlist>] [exec <pgm>] [named <nlist>]
1260 fi
1261
1262 <hlist> Apply subsequent directives until the 'fi' if this host
1263 is one of the hosts in the blank separated list. Each
1264 host name may have a single asterisk somewhere in the
1265 name to indicate where arbitrry characters lie.
1266
1267 <pgm> Apply subsequent directives if this program is named <pgm>.
1268
1269 <nlist> Apply subsequent directives if this host instance name
1270 is in the list of blank separated names.
1271
1272 Notes: 1) At least one of hlist, pgm, or nlist must be specified.
1273 2) The combination of hlist, pgm, nlist must all be true.
1274
1275 Output: 0 upon success or !0 upon failure.
1276*/
1277
1278char *XrdOucStream::doif()
1279{
1280 char *var, ifLine[512];
1281 int rc;
1282
1283// Check if the previous if was properly closed
1284//
1285 if (sawif)
1286 {if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
1287 ecode = EINVAL;
1288 }
1289
1290// Save the line for context message should we get an error
1291//
1292 snprintf(ifLine, sizeof(ifLine), "%s", token);
1293
1294// Check if we should continue
1295//
1296 sawif = 1; skpel = 0;
1297 if ((rc = XrdOucUtils::doIf(Eroute,*this,"if directive",
1298 myInfo->myHost,myInfo->myName,myInfo->myExec)))
1299 {if (rc >= 0) skpel = 1;
1300 else {ecode = EINVAL;
1301 if(Eroute) Eroute->Say(llPrefix,
1302 (flags & XrdOucStream_ELIF ? "else " : 0),
1303 "if ", ifLine);
1304 }
1305 return 0;
1306 }
1307
1308// Skip all lines until we reach a fi or else
1309//
1310 while((var = GetFirstWord()))
1311 {if (!strcmp("fi", var)) return var;
1312 if (!strcmp("else", var)) return var;
1313 }
1314
1315// Make sure we have a fi
1316//
1317 if (!var)
1318 {if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
1319 ecode = EINVAL;
1320 }
1321 return 0;
1322}
1323
1324/******************************************************************************/
1325/* g e t V a l u e */
1326/******************************************************************************/
1327
1328int XrdOucStream::getValue(const char *path, char *vbuff, int vbsz)
1329{
1330 struct stat Stat;
1331 int n, rc = 0, vFD;
1332
1333// Make sure the file exists and it not too big
1334//
1335 if (stat(path, &Stat)) return errno;
1336 if (Stat.st_size >= vbsz) return EFBIG;
1337
1338// Open the file and read it in
1339//
1340 if ((vFD = XrdSysFD_Open(path, O_RDONLY)) < 0) return errno;
1341 if ((n = read(vFD, vbuff, vbsz-1)) >= 0) vbuff[n] = 0;
1342 else rc = errno;
1343
1344// All done
1345//
1346 close(vFD);
1347 return rc;
1348}
1349
1350/******************************************************************************/
1351/* i s S e t */
1352/******************************************************************************/
1353
1354int XrdOucStream::isSet(char *var)
1355{
1356 static const char *Mtxt1[2] = {"setenv", "set"};
1357 static const char *Mtxt2[2] = {"Setenv variable", "Set variable"};
1358 static const char *Mtxt3[2] = {"Variable", "Environmental variable"};
1359 char *tp, *vn, *vp, *pv, Vname[64] = "", ec, Nil = 0, sawIT = 0;
1360 int Set = 1;
1361 char valBuff[1024] = "";
1362
1363// Process set var = value | set -v | setenv = value
1364//
1365 if (!strcmp("setenv", var)) Set = 0;
1366 else if (strcmp("set", var)) return 0;
1367
1368// Now get the operand
1369//
1370 if (!(tp = GetToken()))
1371 return xMsg("Missing variable name after '",Mtxt1[Set],"'.");
1372
1373// Option flags only apply to set not setenv
1374//
1375 if (Set)
1376 {if (!strcmp(tp, "-q")) {if (llBuff) {free(llBuff); llBuff = 0;}; return 1;}
1377 if (!strcmp(tp, "-v") || !strcmp(tp, "-V"))
1378 {if (Eroute)
1379 {if (!llBuff) llBuff = (char *)malloc(llBsz);
1380 llBcur = llBuff; llBok = 0; llBleft = llBsz; *llBuff = '\0';
1381 Verbose = (strcmp(tp, "-V") ? 1 : 2);
1382 }
1383 return 1;
1384 }
1385 }
1386
1387// Next may be var= | var | var=val | var< | var<val
1388//
1389 if ((vp = index(tp, '=')) || (vp = index(tp, '<')))
1390 {sawIT = *vp; *vp = '\0'; vp++;}
1391 if (strlcpy(Vname, tp, sizeof(Vname)) >= sizeof(Vname))
1392 return xMsg(Mtxt2[Set],tp,"is too long.");
1393 if (!Set && !strncmp("XRD", Vname, 3))
1394 return xMsg("Setenv variable",tp,"may not start with 'XRD'.");
1395
1396// Verify that variable is only an alphanum
1397//
1398 tp = Vname;
1399 while (*tp && (*tp == '_' || isalnum(*tp))) tp++;
1400 if (*tp) return xMsg(Mtxt2[Set], Vname, "is non-alphanumeric");
1401
1402// Now look for the value
1403//
1404 if (sawIT) tp = vp;
1405 else if (!(tp = GetToken()) || (*tp != '=' && *tp != '<'))
1406 return xMsg("Missing '=' after", Mtxt1[Set], Vname);
1407 else {sawIT = *tp; tp++;}
1408 if (!*tp && !(tp = GetToken())) tp = (char *)"";
1409
1410// Handle reading value from a file
1411//
1412 if (sawIT == '<')
1413 {int rc;
1414 if (!*tp) return xMsg(Mtxt2[Set], Vname, "path to value not specified");
1415 if ((rc = getValue(tp, valBuff, sizeof(valBuff))))
1416 {char tbuff[512];
1417 snprintf(tbuff, sizeof(tbuff), "cannot be set via path %s; %s",
1418 tp, XrdSysE2T(rc));
1419 return xMsg(Mtxt2[Set], Vname, tbuff);
1420 }
1421 tp = valBuff;
1422 }
1423
1424// The value may be '$var', in which case we need to get it out of the env if
1425// this is a set or from our environment if this is a setenv
1426//
1427 if (*tp != '$') vp = tp;
1428 else {pv = tp+1;
1429 if (*pv == '(') ec = ')';
1430 else if (*pv == '{') ec = '}';
1431 else if (*pv == '[') ec = ']';
1432 else ec = 0;
1433 if (!ec) vn = tp+1;
1434 else {while(*pv && *pv != ec) pv++;
1435 if (*pv) *pv = '\0';
1436 else ec = 0;
1437 vn = tp+2;
1438 }
1439 if (!*vn) {*pv = ec; return xMsg("Variable", tp, "is malformed.");}
1440 if (!(vp = (Set ? getenv(vn) : myEnv->Get(vn))))
1441 {if (ec != ']')
1442 {xMsg(Mtxt3[Set],vn,"is undefined."); *pv = ec; return 1;}
1443 vp = &Nil;
1444 }
1445 *pv = ec;
1446 }
1447
1448// Make sure the value is not too long
1449//
1450 if ((int)strlen(vp) > maxVLen)
1451 return xMsg(Mtxt3[Set], Vname, "value is too long.");
1452
1453// Set the value
1454//
1455 if (Verbose == 2 && Eroute)
1456 if (!(pv = (Set ? myEnv->Get(Vname) : getenv(Vname))) || strcmp(vp, pv))
1457 {char vbuff[1024];
1458 strcpy(vbuff, Mtxt1[Set]); strcat(vbuff, " "); strcat(vbuff, Vname);
1459 Eroute->Say(vbuff, " = ", vp);
1460 }
1461 if (Set) myEnv->Put(Vname, vp);
1462 else if (!(pv = getenv(Vname)) || strcmp(vp,pv))
1463 XrdOucEnv::Export(Vname, vp);
1464 return 1;
1465}
1466
1467/******************************************************************************/
1468/* v S u b s */
1469/******************************************************************************/
1470
1471char *XrdOucStream::vSubs(char *Var)
1472{
1473 char *vp, *sp, *dp, *vnp, ec, bkp, valbuff[maxVLen], Nil = 0;
1474 int n;
1475
1476// Check for substitution
1477//
1478 if (!Var) return Var;
1479 sp = Var; dp = valbuff; n = maxVLen-1; *varVal = '\0';
1480
1481 while(*sp && n > 0)
1482 {if (*sp == '\\') {*dp++ = *(sp+1); sp +=2; n--; continue;}
1483 if (*sp != '$'
1484 || (!isalnum(*(sp+1)) && !index("({[", *(sp+1))))
1485 {*dp++ = *sp++; n--; continue;}
1486 sp++; vnp = sp;
1487 if (*sp == '(') ec = ')';
1488 else if (*sp == '{') ec = '}';
1489 else if (*sp == '[') ec = ']';
1490 else ec = 0;
1491 if (ec) {sp++; vnp++;}
1492 while(isalnum(*sp)) sp++;
1493 if (ec && *sp != ec)
1494 {xMsg("Variable", vnp-2, "is malformed."); return varVal;}
1495 bkp = *sp; *sp = '\0';
1496 if (!(vp = myEnv->Get(vnp)))
1497 {if (ec != ']') xMsg("Variable", vnp, "is undefined.");
1498 vp = &Nil;
1499 }
1500 while(n && *vp) {*dp++ = *vp++; n--;}
1501 if (*vp) break;
1502 if (ec) sp++;
1503 else *sp = bkp;
1504 }
1505
1506 if (*sp) xMsg("Substituted text too long using", Var);
1507 else {*dp = '\0'; strcpy(varVal, valbuff);}
1508 return varVal;
1509}
1510
1511/******************************************************************************/
1512/* x M s g */
1513/******************************************************************************/
1514
1515int XrdOucStream::xMsg(const char *txt1, const char *txt2, const char *txt3)
1516{
1517 if (Eroute) Eroute->Emsg("Stream", txt1, txt2, txt3);
1518 ecode = EINVAL;
1519 return 1;
1520}
struct stat Stat
Definition XrdCks.cc:49
#define Err(p, a, b, c)
#define XrdOucStream_EOM
#define XrdOucStream_BUSY
#define XrdOucStream_ELIF
#define XrdOucStream_CADD
#define Erx(p, a, b)
#define Erp(p, a, b, c)
#define XrdOucStream_CONT
#define MaxARGC
#define Erq(p, a, b)
int fcntl(int fd, int cmd,...)
#define close(a)
Definition XrdPosix.hh:48
#define write(a, b, c)
Definition XrdPosix.hh:115
#define stat(a, b)
Definition XrdPosix.hh:101
#define read(a, b, c)
Definition XrdPosix.hh:82
const char * XrdSysE2T(int errcode)
Definition XrdSysE2T.cc:104
size_t strlcpy(char *dst, const char *src, size_t sz)
char * Get(const char *varname)
Definition XrdOucEnv.hh:69
static int Export(const char *Var, const char *Val)
Definition XrdOucEnv.cc:170
void * GetPtr(const char *varname)
Definition XrdOucEnv.cc:263
void Put(const char *varname, const char *value)
Definition XrdOucEnv.hh:85
static const int retFile
XrdOucEnv * SetEnv(XrdOucEnv *newEnv)
XrdOucStream(XrdSysError *erobj=0, const char *ifname=0, XrdOucEnv *anEnv=0, const char *Pfx=0)
char * GetMyFirstWord(int lowcase=0)
int PutLine(const char *data, int dlen=0)
static XrdOucString * Capture()
char * GetLine()
char * GetFirstWord(int lowcase=0)
char * GetWord(int lowcase=0)
int Attach(int FileDescriptor, int bsz=2047)
int AttachIO(int infd, int outfd, int bsz=2047)
int Put(const char *data, const int dlen)
int Exec(const char *, int inrd=0, int efd=0)
int Wait4Data(int msMax=-1)
void Close(int hold=0)
char * GetToken(int lowcase=0)
int GetRest(char *theBuf, int Blen, int lowcase=0)
void append(const int i)
XrdOucTList * next
static int doIf(XrdSysError *eDest, XrdOucStream &Config, const char *what, const char *hname, const char *nname, const char *pname)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
XrdSysLogger * logger(XrdSysLogger *lp=0)
std::set< std::string > * fcList
std::set< std::string >::iterator itFC
struct NSEnt * Next