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 bnext = stpncpy(buff, bnext, bleft);
581 }
582 else bnext = buff;
583
584// Prepare to read in more data.
585//
586 bcnt = bsize - (bnext - buff) -1;
587 bp = bnext;
588
589// Read up to the maximum number of bytes. Stop reading should we see a
590// new-line character or a null byte -- the end of a record.
591//
592 recp = token = buff; // This will always be true at this point
593 while(bcnt)
594 {do { retc = read(FD, (void *)bp, (size_t)bcnt); }
595 while (retc < 0 && errno == EINTR);
596
597 if (retc < 0) {Erp(GetLine,errno,"read request",0); return (char *)0;}
598 if (!retc)
599 {*bp = '\0';
600 flags |= XrdOucStream_EOM;
601 bnext = ++bp;
602 bleft = 0;
603 return buff;
604 }
605
606 bcnt -= retc;
607 while(retc--)
608 if (!*bp || *bp == '\n')
609 {if (!*bp) flags |= XrdOucStream_EOM;
610 else *bp = '\0';
611 bnext = ++bp;
612 bleft = retc;
613 return buff;
614 } else {
615 if (notabs && *bp == '\t') *bp = ' ';
616 bp++;
617 }
618 }
619
620// All done, force an end of record.
621//
622 Erp(GetLine, EMSGSIZE, "read full message", 0);
623 buff[bsize-1] = '\0';
624 return buff;
625}
626
627/******************************************************************************/
628/* G e t T o k e n */
629/******************************************************************************/
630
631char *XrdOucStream::GetToken(int lowcase) {
632 char *tpoint;
633
634 // Verify that we have a token to return;
635 //
636 if (!token) return (char *)NULL;
637
638 // Skip to the first non-blank character.
639 //
640 while (*token && *token == ' ') token ++;
641 if (!*token) {token = 0; return 0;}
642 tpoint = token;
643
644 // Find the end of the token.
645 //
646 if (lowcase) while (*token && *token != ' ')
647 {*token = (char)tolower((int)*token); token++;}
648 else while (*token && *token != ' ') {token++;}
649 if (*token) {*token = '\0'; token++;}
650
651 // All done here.
652 //
653 return tpoint;
654}
655
656char *XrdOucStream::GetToken(char **rest, int lowcase)
657{
658 char *tpoint;
659
660 // Get the next token
661 //
662 if (!(tpoint = GetToken(lowcase))) return tpoint;
663
664 // Skip to the first non-blank character.
665 //
666 while (*token && *token == ' ') token ++;
667 if (rest) *rest = token;
668
669
670 // All done.
671 //
672 return tpoint;
673}
674
675/******************************************************************************/
676/* G e t F i r s t W o r d */
677/******************************************************************************/
678
680{
681 // If in the middle of a line, flush to the end of the line. Suppress
682 // variable substitution when doing this to avoid errors.
683 //
684 if (xline)
685 {XrdOucEnv *oldEnv = SetEnv(0);
686 while(GetWord(lowcase)) {}
687 SetEnv(oldEnv);
688 }
689 return GetWord(lowcase);
690}
691
692/******************************************************************************/
693/* G e t M y F i r s t W o r d */
694/******************************************************************************/
695
697{
698 char *var;
699 int skip2fi = 0;
700
701 Echo();
702
703 if (!myInst)
704 {if (!myEnv) return add2llB(GetFirstWord(lowcase), 1);
705 else {while((var = GetFirstWord(lowcase)) && !isSet(var)) {}
706 return add2llB(var, 1);
707 }
708 }
709
710 do {if (!(var = GetFirstWord(lowcase)))
711 {if (sawif && !ecode)
712 {ecode = EINVAL;
713 if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
714 }
715 return add2llB(var, 1);
716 }
717
718 add2llB(var, 1);
719
720 if (!strcmp("continue", var))
721 {if (!docont()) return 0;
722 continue;
723 }
724
725 if ( !strcmp("if", var)) var = doif();
726 if (var && !strcmp("else", var)) var = doelse();
727 if (var && !strcmp("fi", var))
728 {if (sawif) sawif = skpel = skip2fi = 0;
729 else {if (Eroute)
730 Eroute->Emsg("Stream", "No preceding 'if' for 'fi'.");
731 ecode = EINVAL;
732 }
733 continue;
734 }
735 if (var && (!myEnv || !isSet(var))) return add2llB(var, 1);
736 } while (1);
737
738 return 0;
739}
740
741/******************************************************************************/
742/* G e t W o r d */
743/******************************************************************************/
744
745char *XrdOucStream::GetWord(int lowcase)
746{
747 char *wp, *ep;
748
749 // A call means the first token was acceptable and we continuing to
750 // parse, hence the line is echoable.
751 //
752 if (llBok == 1) llBok = 2;
753
754 // If we have a token, return it
755 //
756 xline = 1;
757 while((wp = GetToken(lowcase)))
758 {if (!myEnv) return add2llB(wp);
759 if ((wp = vSubs(wp)) && *wp) return add2llB(wp);
760 }
761
762 // If no continuation allowed, return a null (but only once)
763 //
764 if (!xcont) {xcont = 1; xline = 0; return (char *)0;}
765
766 // Find the next non-blank non-comment line
767 //
768do {while(GetLine())
769 {// Get the first token (none if it is a blank line)
770 //
771 if (!(wp = GetToken(lowcase))) continue;
772
773 // If token starts with a pound sign, skip the line
774 //
775 if (*wp == '#') continue;
776
777 // Process continuations (last non-blank character is a back-slash)
778 //
779 ep = bnext-2;
780 while (ep >= buff && *ep == ' ') ep--;
781 if (ep < buff) continue;
782 if (*ep == '\\') {xcont = 1; *ep = '\0';}
783 else xcont = 0;
784 return add2llB((myEnv ? vSubs(wp) : wp));
785 }
786
787 if (myInfo && myInfo->fcList)
788 {if (myInfo->itFC == myInfo->fcList->end())
789 {bleft = 0;
790 flags |= XrdOucStream_EOM;
791 break;
792 }
793 const char *path = (*(myInfo->itFC)).c_str();
794 myInfo->itFC++;
795 if (!docontF(path)) break;
796 bleft = 0;
797 flags &= ~XrdOucStream_EOM;
798 } else break;
799 } while(true);
800
801 xline = 0;
802 return (char *)0;
803}
804
805/******************************************************************************/
806/* G e t R e s t */
807/******************************************************************************/
808
809int XrdOucStream::GetRest(char *theBuff, int Blen, int lowcase)
810{
811 char *tp, *myBuff = theBuff;
812 int tlen;
813
814// Get remaining tokens
815//
816 theBuff[0] = '\0';
817 while ((tp = GetWord(lowcase)))
818 {tlen = strlen(tp);
819 if (tlen+1 >= Blen) return 0;
820 if (myBuff != theBuff) {*myBuff++ = ' '; Blen--;}
821 strcpy(myBuff, tp);
822 Blen -= tlen; myBuff += tlen;
823 }
824
825// All done
826//
827 add2llB(0);
828 return 1;
829}
830
831/******************************************************************************/
832/* R e t T o k e n */
833/******************************************************************************/
834
836{
837 // Check if we can back up
838 //
839 if (!token || token == recp) return;
840
841 // Find the null byte for the token and remove it, if possible
842 //
843 while(*token && token != recp) token--;
844 if (token != recp)
845 {if (token+1 != bnext) *token = ' ';
846 token--;
847 while(*token && *token != ' ' && token != recp) token--;
848 if (token != recp) token++;
849 }
850
851 // If saving line, we must do the same for the saved line
852 //
853 if (llBuff)
854 while(llBcur != llBuff && *llBcur != ' ') {llBcur--; llBleft++;}
855}
856
857/******************************************************************************/
858/* P u t */
859/******************************************************************************/
860
861int XrdOucStream::Put(const char *data, const int dlen) {
862 int dcnt = dlen, retc;
863
864 if (flags & XrdOucStream_BUSY) {ecode = ETXTBSY; return -1;}
865
866 while(dcnt)
867 {do { retc = write(FE, (const void *)data, (size_t)dlen);}
868 while (retc < 0 && errno == EINTR);
869 if (retc >= 0) dcnt -= retc;
870 else {flags |= XrdOucStream_BUSY;
871 Erp(Put, errno, "write to stream", 0);
872 flags &= ~XrdOucStream_BUSY;
873 return -1;
874 }
875 }
876 return 0;
877}
878
879int XrdOucStream::Put(const char *datavec[], const int dlenvec[]) {
880 int i, retc, dlen;
881 const char *data;
882
883 if (flags & XrdOucStream_BUSY) {ecode = ETXTBSY; return -1;}
884
885 for (i = 0; datavec[i]; i++)
886 {data = datavec[i]; dlen = dlenvec[i];
887 while(dlen)
888 {do { retc = write(FE, (const void *)data, (size_t)dlen);}
889 while (retc < 0 && errno == EINTR);
890 if (retc >= 0) {data += retc; dlen -= retc;}
891 else {flags |= XrdOucStream_BUSY;
892 Erp(Put, errno, "write to stream",0);
893 flags &= ~XrdOucStream_BUSY;
894 return -1;
895 }
896 }
897 }
898 return 0;
899}
900
901/******************************************************************************/
902/* P u t L i n e */
903/******************************************************************************/
904
905int XrdOucStream::PutLine(const char *data, int dlen)
906{
907 static const int plSize = 2048;
908
909// Allocate a buffer if one is not allocated
910//
911 if (!buff)
912 {if (!(buff = (char *)malloc(plSize)))
913 return Erq(Attach, errno, "allocate stream buffer");
914 bsize = plSize;
915 }
916
917// Adjust dlen
918//
919 if (dlen <= 0) dlen = strlen(data);
920 if (dlen >= bsize) dlen = bsize-1;
921
922// Simply insert the line into the buffer, truncating if need be
923//
924 bnext = recp = token = buff; // This will always be true at this point
925 if (dlen <= 0)
926 {*buff = '\0';
927 flags |= XrdOucStream_EOM;
928 bleft = 0;
929 } else {
930 strncpy(buff, data, dlen);
931 *(buff+dlen) = 0;
932 bleft = dlen+1;
933 }
934// All done
935//
936 return 0;
937}
938
939/******************************************************************************/
940/* W a i t 4 D a t a */
941/******************************************************************************/
942
944{
945 struct pollfd polltab = {FD, POLLIN|POLLRDNORM, 0};
946 int retc;
947
948// Wait until we can actually read something
949//
950 do {retc = poll(&polltab, 1, msMax);} while(retc < 0 && errno == EINTR);
951 if (retc != 1) return (retc ? errno : -1);
952
953// Return correct value
954//
955 return (polltab.revents & (POLLIN|POLLRDNORM) ? 0 : EIO);
956}
957
958/******************************************************************************/
959/* P r i v a t e M e t h o d s */
960/******************************************************************************/
961/******************************************************************************/
962/* a d d 2 C F G */
963/******************************************************************************/
964
965void XrdOucStream::add2CFG(const char *data, bool isCMT)
966{
967 if (isCMT) theCFG->append("# ");
968 theCFG->append(data);
969 theCFG->append('\n');
970}
971
972/******************************************************************************/
973/* a d d 2 l l B */
974/******************************************************************************/
975
976char *XrdOucStream::add2llB(char *tok, int reset)
977{
978 int tlen;
979
980// Return if not saving data
981//
982 if (!llBuff) return tok;
983
984// Check if we should flush the previous line
985//
986 if (reset)
987 {llBok = 1;
988 llBcur = llBuff;
989 llBleft= llBsz;
990 *llBuff = '\0';
991 } else if (!llBok) return tok;
992 else {llBok = 2;
993 if (llBleft >= 2)
994 {*llBcur++ = ' '; *llBcur = '\0'; llBleft--;}
995 }
996
997// Add in the new token
998//
999 if (tok)
1000 {tlen = strlen(tok);
1001 if (tlen < llBleft)
1002 {strcpy(llBcur, tok); llBcur += tlen; llBleft -= tlen;}
1003 }
1004 return tok;
1005}
1006
1007/******************************************************************************/
1008/* E c h o */
1009/******************************************************************************/
1010
1011bool XrdOucStream::Echo(int ec, const char *t1, const char *t2, const char *t3)
1012{
1013 if (Eroute)
1014 {if (t1) Eroute->Emsg("Stream", t1, t2, t3);
1015 if (llBok > 1 && Verbose && llBuff) Eroute->Say(llPrefix,llBuff);
1016 }
1017 ecode = ec;
1018 llBok = 0;
1019 return false;
1020}
1021
1022/******************************************************************************/
1023/* d o c o n t */
1024/******************************************************************************/
1025
1026bool XrdOucStream::docont()
1027{
1028 char *theWord;
1029
1030// A continue is not valid within the scope of an if
1031//
1032 if (sawif) return Echo(EINVAL, "'continue' invalid within 'if-fi'.");
1033
1034// Get the path (keep case), if none then ignore this continue
1035//
1036 theWord = GetWord();
1037 if (!theWord)
1038 {Echo();
1039 return true;
1040 }
1041
1042// Prepare to handle the directive
1043//
1044 contHandler cH;
1045 cH.path = strdup(theWord);
1046
1047// Grab additioal tokens which may be suffixes
1048//
1049 theWord = GetWord();
1050 while(theWord && *theWord == '*')
1051 {if (!*(theWord+1)) return Echo(EINVAL, "suffix missing after '*'.");
1052 cH.Add(theWord+1);
1053 theWord = GetWord();
1054 }
1055
1056// If we have a token, it better be an if
1057//
1058 if (theWord && strcmp(theWord, "if"))
1059 return Echo(EINVAL, "expecting 'if' but found", theWord);
1060
1061// Process the 'if'
1062//
1063 if (theWord && !XrdOucUtils::doIf(Eroute, *this, "continue directive",
1064 myInfo->myHost,myInfo->myName,myInfo->myExec))
1065 return true;
1066 Echo();
1067// if (Eroute) Eroute->Say(llPrefix, "continue ", path, " if true");
1068// if (Eroute) Eroute->Say(llPrefix, "continue ", bnext);
1069 return docont(cH.path, cH.tlP);
1070}
1071
1072/******************************************************************************/
1073
1074bool XrdOucStream::docont(const char *path, XrdOucTList *tlP)
1075{
1076 struct stat Stat;
1077 bool noentok;
1078
1079// A continue directive in the context of a continuation is illegal
1080//
1081 if ((myInfo && myInfo->fcList) || (flags & XrdOucStream_CONT) != 0)
1082 return Echo(EINVAL, "'continue' in a continuation is not allowed.");
1083
1084// Check if this file must exist (we also take care of empty paths)
1085//
1086 if ((noentok = (*path == '?')))
1087 {path++;
1088 if (!(*path)) return true;
1089 }
1090
1091// Check if this is a file or directory
1092//
1093 if (stat(path, &Stat))
1094 {if (errno == ENOENT && noentok) return true;
1095 if (Eroute)
1096 {Eroute->Emsg("Stream", errno, "open", path);
1097 ecode = ECANCELED;
1098 } else ecode = errno;
1099 return false;
1100 }
1101
1102// For directory continuation, there is much more to do (this can only happen
1103// once). Note that we used to allow a limited number of chained file
1104// continuations. No more, but we are still setup to easily do so.
1105//
1106 if ((Stat.st_mode & S_IFMT) == S_IFDIR)
1107 {if (!docontD(path, tlP)) return false;
1108 path = (*(myInfo->itFC)).c_str();
1109 myInfo->itFC++;
1110 } else flags |= XrdOucStream_CADD;
1111
1112// if ((flags & XrdOucStream_CONT) > XrdOucStream_CMAX)
1113// {if (Eroute)
1114// {Eroute->Emsg("Stream", EMLINK, "continue to", path);
1115// ecode = ECANCELED;
1116// } else ecode = EMLINK;
1117// return false;
1118// }
1119// }
1120
1121// Continue with the next file
1122//
1123 return docontF(path, noentok);
1124}
1125
1126/******************************************************************************/
1127/* d o c o n t D */
1128/******************************************************************************/
1129
1130bool XrdOucStream::docontD(const char *path, XrdOucTList *tlP)
1131{
1132 static const mode_t isXeq = S_IXUSR | S_IXGRP | S_IXOTH;
1133 XrdOucNSWalk nsWalk(Eroute, path, 0, XrdOucNSWalk::retFile);
1134 int rc;
1135
1136// Get all of the file entries in this directory
1137//
1138 XrdOucNSWalk::NSEnt *nsX, *nsP = nsWalk.Index(rc);
1139 if (rc)
1140 {if (Eroute) Eroute->Emsg("Stream", rc, "index config files in", path);
1141 ecode = ECANCELED;
1142 return false;
1143 }
1144
1145// Keep only files of interest
1146//
1147 myInfo->fcList = new std::set<std::string>;
1148 while((nsX = nsP))
1149 {nsP = nsP->Next;
1150 if ((nsX->Stat.st_mode & isXeq) == 0 && KeepFile(nsX->File, tlP))
1151 myInfo->fcList->insert(std::string(nsX->Path));
1152 delete nsX;
1153 }
1154
1155// Check if we have anything in the map
1156//
1157 if (myInfo->fcList->size() == 0)
1158 {delete myInfo->fcList;
1159 myInfo->fcList = 0;
1160 return false;
1161 }
1162
1163// All done
1164//
1165 myInfo->itFC = myInfo->fcList->begin();
1166 return true;
1167}
1168
1169/******************************************************************************/
1170/* c o n t F */
1171/******************************************************************************/
1172
1173bool XrdOucStream::docontF(const char *path, bool noentok)
1174{
1175 int cFD;
1176
1177// Open the file and handle any errors
1178//
1179 if ((cFD = XrdSysFD_Open(path, O_RDONLY)) < 0)
1180 {if (errno == ENOENT && noentok) return true;
1181 if (Eroute)
1182 {Eroute->Emsg("Stream", errno, "open", path);
1183 ecode = ECANCELED;
1184 } else ecode = errno;
1185 return false;
1186 }
1187
1188// Continue to the next file
1189//
1190 if (XrdSysFD_Dup2(cFD, FD) < 0)
1191 {if (Eroute)
1192 {Eroute->Emsg("Stream", ecode, "switch to", path);
1193 close(cFD);
1194 ecode = ECANCELED;
1195 } else ecode = errno;
1196 return false;
1197 }
1198
1199// Indicate we are switching to anther file
1200//
1201 if (Eroute) Eroute->Say("Config continuing with file ", path, " ...");
1202 bleft = 0;
1203 close(cFD);
1204 return true;
1205}
1206
1207/******************************************************************************/
1208/* d o e l s e */
1209/******************************************************************************/
1210
1211char *XrdOucStream::doelse()
1212{
1213 char *var;
1214
1215// An else must be preceeded by an if and not by a naked else
1216//
1217 if (!sawif || sawif == 2)
1218 {if (Eroute) Eroute->Emsg("Stream", "No preceding 'if' for 'else'.");
1219 ecode = EINVAL;
1220 return 0;
1221 }
1222
1223// If skipping all else caluses, skip all lines until we reach a fi
1224//
1225 if (skpel)
1226 {while((var = GetFirstWord()))
1227 {if (!strcmp("fi", var)) return var;}
1228 if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
1229 ecode = EINVAL;
1230 return 0;
1231 }
1232
1233// Elses are still possible then process one of them
1234//
1235 do {if (!(var = GetWord())) // A naked else will always succeed
1236 {sawif = 2;
1237 return 0;
1238 }
1239 if (strcmp("if", var)) // An else may only be followed by an if
1240 {Eroute->Emsg("Stream","'else",var,"' is invalid.");
1241 ecode = EINVAL;
1242 return 0;
1243 }
1244 sawif = 0;
1245 flags |= XrdOucStream_ELIF;
1246 var = doif();
1247 flags &= ~XrdOucStream_ELIF;
1248 } while(var && !strcmp("else", var));
1249 return var;
1250}
1251
1252/******************************************************************************/
1253/* d o i f */
1254/******************************************************************************/
1255
1256/* Function: doif
1257
1258 Purpose: To parse the directive: if [<hlist>] [exec <pgm>] [named <nlist>]
1259 fi
1260
1261 <hlist> Apply subsequent directives until the 'fi' if this host
1262 is one of the hosts in the blank separated list. Each
1263 host name may have a single asterisk somewhere in the
1264 name to indicate where arbitrry characters lie.
1265
1266 <pgm> Apply subsequent directives if this program is named <pgm>.
1267
1268 <nlist> Apply subsequent directives if this host instance name
1269 is in the list of blank separated names.
1270
1271 Notes: 1) At least one of hlist, pgm, or nlist must be specified.
1272 2) The combination of hlist, pgm, nlist must all be true.
1273
1274 Output: 0 upon success or !0 upon failure.
1275*/
1276
1277char *XrdOucStream::doif()
1278{
1279 char *var, ifLine[512];
1280 int rc;
1281
1282// Check if the previous if was properly closed
1283//
1284 if (sawif)
1285 {if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
1286 ecode = EINVAL;
1287 }
1288
1289// Save the line for context message should we get an error
1290//
1291 snprintf(ifLine, sizeof(ifLine), "%s", token);
1292
1293// Check if we should continue
1294//
1295 sawif = 1; skpel = 0;
1296 if ((rc = XrdOucUtils::doIf(Eroute,*this,"if directive",
1297 myInfo->myHost,myInfo->myName,myInfo->myExec)))
1298 {if (rc >= 0) skpel = 1;
1299 else {ecode = EINVAL;
1300 if(Eroute) Eroute->Say(llPrefix,
1301 (flags & XrdOucStream_ELIF ? "else " : 0),
1302 "if ", ifLine);
1303 }
1304 return 0;
1305 }
1306
1307// Skip all lines until we reach a fi or else
1308//
1309 while((var = GetFirstWord()))
1310 {if (!strcmp("fi", var)) return var;
1311 if (!strcmp("else", var)) return var;
1312 }
1313
1314// Make sure we have a fi
1315//
1316 if (!var)
1317 {if (Eroute) Eroute->Emsg("Stream", "Missing 'fi' for last 'if'.");
1318 ecode = EINVAL;
1319 }
1320 return 0;
1321}
1322
1323/******************************************************************************/
1324/* g e t V a l u e */
1325/******************************************************************************/
1326
1327int XrdOucStream::getValue(const char *path, char *vbuff, int vbsz)
1328{
1329 struct stat Stat;
1330 int n, rc = 0, vFD;
1331
1332// Make sure the file exists and it not too big
1333//
1334 if (stat(path, &Stat)) return errno;
1335 if (Stat.st_size >= vbsz) return EFBIG;
1336
1337// Open the file and read it in
1338//
1339 if ((vFD = XrdSysFD_Open(path, O_RDONLY)) < 0) return errno;
1340 if ((n = read(vFD, vbuff, vbsz-1)) >= 0) vbuff[n] = 0;
1341 else rc = errno;
1342
1343// All done
1344//
1345 close(vFD);
1346 return rc;
1347}
1348
1349/******************************************************************************/
1350/* i s S e t */
1351/******************************************************************************/
1352
1353int XrdOucStream::isSet(char *var)
1354{
1355 static const char *Mtxt1[2] = {"setenv", "set"};
1356 static const char *Mtxt2[2] = {"Setenv variable", "Set variable"};
1357 static const char *Mtxt3[2] = {"Variable", "Environmental variable"};
1358 char *tp, *vn, *vp, *pv, Vname[64] = "", ec, Nil = 0, sawIT = 0;
1359 int Set = 1;
1360 char valBuff[1024] = "";
1361
1362// Process set var = value | set -v | setenv = value
1363//
1364 if (!strcmp("setenv", var)) Set = 0;
1365 else if (strcmp("set", var)) return 0;
1366
1367// Now get the operand
1368//
1369 if (!(tp = GetToken()))
1370 return xMsg("Missing variable name after '",Mtxt1[Set],"'.");
1371
1372// Option flags only apply to set not setenv
1373//
1374 if (Set)
1375 {if (!strcmp(tp, "-q")) {if (llBuff) {free(llBuff); llBuff = 0;}; return 1;}
1376 if (!strcmp(tp, "-v") || !strcmp(tp, "-V"))
1377 {if (Eroute)
1378 {if (!llBuff) llBuff = (char *)malloc(llBsz);
1379 llBcur = llBuff; llBok = 0; llBleft = llBsz; *llBuff = '\0';
1380 Verbose = (strcmp(tp, "-V") ? 1 : 2);
1381 }
1382 return 1;
1383 }
1384 }
1385
1386// Next may be var= | var | var=val | var< | var<val
1387//
1388 if ((vp = index(tp, '=')) || (vp = index(tp, '<')))
1389 {sawIT = *vp; *vp = '\0'; vp++;}
1390 if (strlcpy(Vname, tp, sizeof(Vname)) >= sizeof(Vname))
1391 return xMsg(Mtxt2[Set],tp,"is too long.");
1392 if (!Set && !strncmp("XRD", Vname, 3))
1393 return xMsg("Setenv variable",tp,"may not start with 'XRD'.");
1394
1395// Verify that variable is only an alphanum
1396//
1397 tp = Vname;
1398 while (*tp && (*tp == '_' || isalnum(*tp))) tp++;
1399 if (*tp) return xMsg(Mtxt2[Set], Vname, "is non-alphanumeric");
1400
1401// Now look for the value
1402//
1403 if (sawIT) tp = vp;
1404 else if (!(tp = GetToken()) || (*tp != '=' && *tp != '<'))
1405 return xMsg("Missing '=' after", Mtxt1[Set], Vname);
1406 else {sawIT = *tp; tp++;}
1407 if (!*tp && !(tp = GetToken())) tp = (char *)"";
1408
1409// Handle reading value from a file
1410//
1411 if (sawIT == '<')
1412 {int rc;
1413 if (!*tp) return xMsg(Mtxt2[Set], Vname, "path to value not specified");
1414 if ((rc = getValue(tp, valBuff, sizeof(valBuff))))
1415 {char tbuff[512];
1416 snprintf(tbuff, sizeof(tbuff), "cannot be set via path %s; %s",
1417 tp, XrdSysE2T(rc));
1418 return xMsg(Mtxt2[Set], Vname, tbuff);
1419 }
1420 tp = valBuff;
1421 }
1422
1423// The value may be '$var', in which case we need to get it out of the env if
1424// this is a set or from our environment if this is a setenv
1425//
1426 if (*tp != '$') vp = tp;
1427 else {pv = tp+1;
1428 if (*pv == '(') ec = ')';
1429 else if (*pv == '{') ec = '}';
1430 else if (*pv == '[') ec = ']';
1431 else ec = 0;
1432 if (!ec) vn = tp+1;
1433 else {while(*pv && *pv != ec) pv++;
1434 if (*pv) *pv = '\0';
1435 else ec = 0;
1436 vn = tp+2;
1437 }
1438 if (!*vn) {*pv = ec; return xMsg("Variable", tp, "is malformed.");}
1439 if (!(vp = (Set ? getenv(vn) : myEnv->Get(vn))))
1440 {if (ec != ']')
1441 {xMsg(Mtxt3[Set],vn,"is undefined."); *pv = ec; return 1;}
1442 vp = &Nil;
1443 }
1444 *pv = ec;
1445 }
1446
1447// Make sure the value is not too long
1448//
1449 if ((int)strlen(vp) > maxVLen)
1450 return xMsg(Mtxt3[Set], Vname, "value is too long.");
1451
1452// Set the value
1453//
1454 if (Verbose == 2 && Eroute)
1455 if (!(pv = (Set ? myEnv->Get(Vname) : getenv(Vname))) || strcmp(vp, pv))
1456 {char vbuff[1024];
1457 strcpy(vbuff, Mtxt1[Set]); strcat(vbuff, " "); strcat(vbuff, Vname);
1458 Eroute->Say(vbuff, " = ", vp);
1459 }
1460 if (Set) myEnv->Put(Vname, vp);
1461 else if (!(pv = getenv(Vname)) || strcmp(vp,pv))
1462 XrdOucEnv::Export(Vname, vp);
1463 return 1;
1464}
1465
1466/******************************************************************************/
1467/* v S u b s */
1468/******************************************************************************/
1469
1470char *XrdOucStream::vSubs(char *Var)
1471{
1472 char *vp, *sp, *dp, *vnp, ec, bkp, valbuff[maxVLen], Nil = 0;
1473 int n;
1474
1475// Check for substitution
1476//
1477 if (!Var) return Var;
1478 sp = Var; dp = valbuff; n = maxVLen-1; *varVal = '\0';
1479
1480 while(*sp && n > 0)
1481 {if (*sp == '\\') {*dp++ = *(sp+1); sp +=2; n--; continue;}
1482 if (*sp != '$'
1483 || (!isalnum(*(sp+1)) && !index("({[", *(sp+1))))
1484 {*dp++ = *sp++; n--; continue;}
1485 sp++; vnp = sp;
1486 if (*sp == '(') ec = ')';
1487 else if (*sp == '{') ec = '}';
1488 else if (*sp == '[') ec = ']';
1489 else ec = 0;
1490 if (ec) {sp++; vnp++;}
1491 while(isalnum(*sp)) sp++;
1492 if (ec && *sp != ec)
1493 {xMsg("Variable", vnp-2, "is malformed."); return varVal;}
1494 bkp = *sp; *sp = '\0';
1495 if (!(vp = myEnv->Get(vnp)))
1496 {if (ec != ']') xMsg("Variable", vnp, "is undefined.");
1497 vp = &Nil;
1498 }
1499 while(n && *vp) {*dp++ = *vp++; n--;}
1500 if (*vp) break;
1501 if (ec) sp++;
1502 else *sp = bkp;
1503 }
1504
1505 if (*sp) xMsg("Substituted text too long using", Var);
1506 else {*dp = '\0'; strcpy(varVal, valbuff);}
1507 return varVal;
1508}
1509
1510/******************************************************************************/
1511/* x M s g */
1512/******************************************************************************/
1513
1514int XrdOucStream::xMsg(const char *txt1, const char *txt2, const char *txt3)
1515{
1516 if (Eroute) Eroute->Emsg("Stream", txt1, txt2, txt3);
1517 ecode = EINVAL;
1518 return 1;
1519}
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)
#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