XRootD
Loading...
Searching...
No Matches
XrdXrootdJob.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d X r o o t d J o b . c c */
4/* */
5/* (c) 2006 by the Board of Trustees of the Leland Stanford, Jr., University */
6/* All Rights Reserved */
7/* Produced by Andrew Hanushevsky for Stanford University under contract */
8/* DE-AC02-76-SFO0515 with the Department of Energy */
9/* */
10/* This file is part of the XRootD software suite. */
11/* */
12/* XRootD is free software: you can redistribute it and/or modify it under */
13/* the terms of the GNU Lesser General Public License as published by the */
14/* Free Software Foundation, either version 3 of the License, or (at your */
15/* option) any later version. */
16/* */
17/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
18/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
19/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
20/* License for more details. */
21/* */
22/* You should have received a copy of the GNU Lesser General Public License */
23/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
24/* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
25/* */
26/* The copyright holder's institutional names and contributor's names may not */
27/* be used to endorse or promote products derived from this software without */
28/* specific prior written permission of the institution or contributor. */
29/******************************************************************************/
30
31#include <cstdio>
32#include <cstring>
33#include <netinet/in.h>
34#include <sys/uio.h>
35
36#include "Xrd/XrdLink.hh"
37#include "Xrd/XrdScheduler.hh"
38#include "XrdOuc/XrdOucProg.hh"
40#include "XrdSys/XrdSysError.hh"
47#include "XProtocol/XPtypes.hh"
48
49/******************************************************************************/
50/* L o c a l C l a s s e s */
51/******************************************************************************/
52
53class XrdXrootdJob2Do : public XrdJob
54{
55public:
56friend class XrdXrootdJob;
57
58void DoIt();
59
61
63
65 int jnum,
66 const char **args,
68 int opts);
70
71private:
72int addClient(XrdXrootdResponse *rp, int opts);
73void delClient(XrdXrootdResponse *rp);
74XrdOucTList *lstClient(void);
75int verClient(int dodel=0);
76void Redrive(void);
77void sendResult(char *lp, int caned=0, int erc=0);
78
79static const int maxClients = 8;
80struct {XrdLink *Link;
81 unsigned int Inst;
82 kXR_char streamid[2];
83 char isSync;
84 } Client[maxClients];
85
86 int numClients;
87
88 XrdOucStream jobStream; // -> Stream for job I/O
89 XrdXrootdJob *theJob; // -> Job description
90 char *theArgs[6]; // -> Program arguments (see XrdOucProg)
91 char *theResult; // -> The result
92 int JobNum; // Job Number
93 int JobRC; // Job kXR_ type return code
94 char JobMark;
95 char doRedrive;
96static const int argvnum = sizeof(theArgs)/sizeof(theArgs[0]);
97};
98
99/******************************************************************************/
100/* G l o b a l F u n c t i o n s */
101/******************************************************************************/
102
103namespace XrdXrootd
104{
105extern XrdSysError eLog;
106}
107using namespace XrdXrootd;
108
110
112{
113 return (item->Status == XrdXrootdJob2Do::Job_Waiting);
114}
115
116/******************************************************************************/
117/* C l a s s X r d X r o o t d J o b 2 D o */
118/******************************************************************************/
119/******************************************************************************/
120/* C o n s t r u c t o r */
121/******************************************************************************/
122
124 int jnum,
125 const char **args,
126 XrdXrootdResponse *resp,
127 int opts)
128 : XrdJob(job->JobName)
129{
130 int i;
131 for (i = 0; i < argvnum && args[i]; i++) theArgs[i] = strdup(args[i]);
132 for ( ; i < argvnum; i++) theArgs[i] = (char *)0;
133 theJob = job;
134 JobRC = 0;
135 JobNum = jnum;
136 JobMark = 0;
137 numClients = 0;
138 theResult = 0;
139 doRedrive = 0;
141 addClient(resp, opts);
142}
143
144/******************************************************************************/
145/* D e s t r u c t o r */
146/******************************************************************************/
147
149{
150 int i;
151
152 for (i = 0; i < numClients; i++)
153 if (!Client[i].isSync) {sendResult(0, 1); break;}
154
155 for (i = 0; i < argvnum; i++)
156 if (theArgs[i]) free(theArgs[i]);
157}
158
159/******************************************************************************/
160/* D o I t */
161/******************************************************************************/
162
163#define jobInfo theJob->JobName<<' '<<(theArgs[1] ? theArgs[1] : "")\
164 <<(theArgs[2] ? " " : "")<<(theArgs[2] ? theArgs[2] : "")
165
167{
168 static const char *TraceID = "jobXeq";
169 XrdXrootdJob2Do *jp = 0;
170 const char *endStat = " completed";
171 char *lp = 0;
172 int i, rc = 0;
173
174// While we were waiting to run we may have been cancelled. If we were not then
175// perform the actual function and get the result and send to any async clients
176//
177 if (Status != Job_Cancel)
178 {TRACE(REQ, "Job "<<jobInfo<<" started");
179 if ((rc = theJob->theProg->Run(&jobStream, theArgs[1], theArgs[2],
180 theArgs[3], theArgs[4])))
182 endStat= " failed";
183 lp = jobStream.GetLine();
184 theJob->myMutex.Lock();
185 }
186 else {lp = jobStream.GetLine();
187 rc = theJob->theProg->RunDone(jobStream);
188 theJob->myMutex.Lock();
189 if ((rc && rc != -EPIPE) || (rc == -EPIPE && (!lp || !(*lp))))
190 {Status = Job_Cancel; endStat = " failed";}
191 else if (Status != Job_Cancel)
192 {Status = Job_Done;
193 for (i = 0; i < numClients; i++)
194 if (!Client[i].isSync) {sendResult(lp); break;}
195 }
196 }
197 } else {
198 endStat = " cancelled";
199 theJob->myMutex.Lock();
200 }
201
202// Produce a trace record
203//
204 TRACE(REQ, "Job "<<jobInfo<<endStat);
205
206// If the number of jobs > than the max allowed, then redrive a waiting job
207// if in fact we represent a legitimate job slot (this could a phantom slot
208// due to ourselves being cancelled.
209//
210 if (doRedrive)
211 {if (theJob->numJobs > theJob->maxJobs) Redrive();
212 theJob->numJobs--;
213 }
214
215// If there are no polling clients left or we have been cancelled, then we
216// will delete ourselves and, if cancelled, send a notofication to everyone
217//
218 if (Status != Job_Cancel && numClients) theResult = lp;
219 else {if (Status == Job_Cancel) sendResult(lp, (rc ? -1 : 1), rc);
220 jp = theJob->JobTable.Remove(JobNum);
221 }
222
223// At this point we may need to delete ourselves. If so, jp will not be zero.
224// This must be the last action in this method.
225//
226 theJob->myMutex.UnLock();
227 if (jp) delete jp;
228}
229
230/******************************************************************************/
231/* P r i v a t e M e t h o d s */
232/******************************************************************************/
233/******************************************************************************/
234/* a d d C l i e n t */
235/******************************************************************************/
236
237int XrdXrootdJob2Do::addClient(XrdXrootdResponse *rp, int opts)
238{
239 XrdLink *lp = rp->theLink();
240 unsigned int Inst = lp->Inst();
241 int i;
242
243// Remove useless clients
244//
245 if (numClients >= maxClients) verClient();
246
247// See if we are already here
248//
249 for (i = 0; i < numClients; i++)
250 if (lp == Client[i].Link && Inst == Client[i].Inst) return 0;
251
252// Add the client if we can
253//
254 if (numClients >= maxClients) return -1;
255 Client[numClients].Link = lp;
256 Client[numClients].Inst = Inst;
257 if (opts & JOB_Sync) Client[numClients].isSync = 1;
258 else {rp->StreamID(Client[numClients].streamid);
259 Client[numClients].isSync = 0;
260 }
261 numClients++;
262 JobMark = 0;
263 return 1;
264}
265
266/******************************************************************************/
267/* d e l C l i e n t */
268/******************************************************************************/
269
270void XrdXrootdJob2Do::delClient(XrdXrootdResponse *rp)
271{
272 XrdLink *lp = rp->theLink();
273 unsigned int Inst = lp->Inst();
274 int i, j;
275
276// See if we are already here
277//
278 for (i = 0; i < numClients; i++)
279 if (lp == Client[i].Link && Inst == Client[i].Inst)
280 {for (j = i+1; j < numClients; j++) Client[i++] = Client[j];
281 numClients--;
282 break;
283 }
284}
285
286/******************************************************************************/
287/* l s t C l i e n t */
288/******************************************************************************/
289
290// Warning! The size of buff is large enough for the default number of clients
291// per job element.
292//
293XrdOucTList *XrdXrootdJob2Do::lstClient()
294{
295 char State, buff[4096], *bp = buff;
296 int bsz, i, k;
297
298// Get the state pf the job element
299//
300 switch(Status)
301 {case Job_Active: State = 'a'; break;
302 case Job_Cancel: State = 'c'; break;
303 case Job_Done: State = 'd'; break;
304 case Job_Waiting: State = 'w'; break;
305 default: State = 'u'; break;
306 };
307
308// Insert the header (reserve 8 characters for the trailer)
309//
310 bp = buff + sprintf(buff, "<s>%c</s><conn>", State);
311 bsz = sizeof(buff) - (bp - buff) - 8;
312
313// Remove all clients from a job whose network connection is no longer valid
314//
315 if (!numClients) bp++;
316 else for (i = 0; i < numClients; i++)
317 if (Client[i].Link && Client[i].Link->isInstance(Client[i].Inst))
318 {if ((k = strlcpy(bp, Client[i].Link->ID, bsz)) >= bsz
319 || (bsz -= k) < 1) {bp++; break;}
320 bp += k; *bp = ' '; bp++; bsz--;
321 }
322
323// Insert trailer
324//
325 if (*(bp-1) == ' ') bp--;
326 strcpy(bp, "</conn>");
327
328// Return the text
329//
330 return new XrdOucTList(buff, bp-buff+7);
331}
332
333/******************************************************************************/
334/* v e r C l i e n t */
335/******************************************************************************/
336
337int XrdXrootdJob2Do::verClient(int dodel) // Caller must have theJob->myMutex
338{
339 int i, j, k;
340
341// Remove all clients from a job whose network connection is no longer valid
342//
343 for (i = 0; i < numClients; i++)
344 if (!Client[i].Link->isInstance(Client[i].Inst))
345 {k = i;
346 for (j = i+1; j < numClients && j < maxClients; j++,k++) Client[k] = Client[j];
347 numClients--; i--;
348 }
349
350// If no more clients, delete ourselves if safe to do so (caller has lock)
351//
352 if (!numClients && dodel)
353 {XrdXrootdJob2Do *jp = theJob->JobTable.Remove(JobNum);
354 if (jp)
355 {if (jp->Status == XrdXrootdJob2Do::Job_Waiting) theJob->numJobs--;
356 delete jp;
357 return 0;
358 } else {
359 char ebuff[80];
360 snprintf(ebuff, sizeof(ebuff), "Unable to find %s job %d;",
361 theJob->JobName, JobNum);
362 eLog.Emsg("Job2Do", ebuff, "job slot disabled!");
363 }
364 }
365 return numClients;
366}
367
368/******************************************************************************/
369/* R e d r i v e */
370/******************************************************************************/
371
372void XrdXrootdJob2Do::Redrive() // Caller must have theJob->myMutex held
373{
374 XrdXrootdJob2Do *jp;
375 int Start = 0;
376
377// Find the first waiting job
378//
379
380 while ((jp = theJob->JobTable.Apply(XrdXrootdJobWaiting, (void *)0, Start)))
381 if (jp->verClient(jp->JobMark > 0)) break;
382 else Start = jp->JobNum+1;
383
384// Schedule this job if we really have one here
385//
386 if (jp)
387 {jp->Status = Job_Active; jp->doRedrive = 1;
388 theJob->Sched->Schedule((XrdJob *)jp);
389 }
390}
391
392/******************************************************************************/
393/* s e n d R e s u l t */
394/******************************************************************************/
395
396// Caller must have theJob->myMutex locked.
397
398void XrdXrootdJob2Do::sendResult(char *lp, int caned, int jrc)
399{
400 static const char *TraceID = "jobSendResult";
401 static const kXR_int32 Xcan = static_cast<kXR_int32>(htonl(kXR_Cancelled));
402 XrdXrootdReqID ReqID;
403 struct iovec jobVec[6];
404 XResponseType jobStat;
405 const char *trc, *tre;
406 kXR_int32 erc;
407 int j, i, dlen = 0, n = 1;
408
409// Format the message to be sent
410//
411 if (!caned && lp)
412 {jobStat = kXR_ok; trc = "ok";
413 if (theArgs[0])
414 { jobVec[n].iov_base = theArgs[0]; // 1
415 dlen = jobVec[n].iov_len = strlen(theArgs[0]); n++;
416 jobVec[n].iov_base = (char *)" "; // 2
417 dlen += jobVec[n].iov_len = 1; n++;
418 }
419 } else {
420 jobStat = kXR_error; trc = "error";
421 if (caned > 0) {erc = Xcan; lp = (char *)"Cancelled by admin.";}
422 else {erc = (jrc ? XProtocol::mapError(jrc) : kXR_ServerError);
423 erc = static_cast<kXR_int32>(htonl(erc));
424 if (!lp || !*lp) lp = (char *)"Program failed.";
425 }
426 jobVec[n].iov_base = (char *)&erc;
427 dlen = jobVec[n].iov_len = sizeof(erc); n++; // 3
428 }
429 jobVec[n].iov_base = lp; // 4
430 dlen += jobVec[n].iov_len = strlen(lp)+1; n++;
431
432// Send the response to each client waiting for it
433//
434 j = 0;
435 for (i = 0; i < numClients; i++)
436 {if (!Client[i].isSync)
437 {ReqID.setID(Client[i].streamid,
438 Client[i].Link->FDnum(), Client[i].Link->Inst());
439 tre = (XrdXrootdResponse::Send(ReqID, jobStat, jobVec, n, dlen) < 0
440 ? "skipped" : "sent");
441 TRACE(RSP, tre <<" async " <<trc <<" to " <<Client[i].Link->ID);
442 } else if (i != j) Client[j++] = Client[i];
443 }
444 numClients = j;
445}
446
447/******************************************************************************/
448/* C l a s s X r d X r o o t d J o b */
449/******************************************************************************/
450/******************************************************************************/
451/* C o n s t r u c t o r */
452/******************************************************************************/
453
455 XrdOucProg *pgm,
456 const char *jname,
457 int maxjobs)
458 : XrdJob("Job Scheduler"),
459 JobTable(maxjobs*3)
460{
461// Initialize the base member here
462//
463 Sched = schp;
464 theProg = pgm;
465 JobName = strdup(jname);
466 maxJobs = maxjobs;
467 numJobs = 0;
468
469// Schedule ourselves to run 15 minutes from now
470//
471 schp->Schedule((XrdJob *)this, time(0) + (reScan));
472}
473
474/******************************************************************************/
475/* D e s t r u c t o r */
476/******************************************************************************/
477
478// Note! There is no reliable way to delete this object because various
479// unsynchronized threads may be pending at various break points. Fortunately,
480// there really is no need to ever delete an object of this kind.
481
483{
484 if (JobName) free(JobName);
485 myMutex.Lock();
486 Sched->Cancel((XrdJob *)this);
487 myMutex.UnLock();
488}
489
490/******************************************************************************/
491/* C a n c e l */
492/******************************************************************************/
493
494int XrdXrootdJob::Cancel(const char *jkey, XrdXrootdResponse *resp)
495{
496 XrdXrootdJob2Do *jp = 0;
497 int i, jNum, jNext = 0, numcaned = 0;
498
499// Lock our data
500//
501 myMutex.Lock();
502
503// Cancel a specific job if a key was passed
504//
505 if (jkey)
506 {if ((jp = JobTable.Find(jkey)))
507 {numcaned = 1;
508 if (resp) {jp->delClient(resp);
509 if (!jp->numClients) CleanUp(jp);
510 }
511 else CleanUp(jp);
512 }
513 myMutex.UnLock();
514 return numcaned;
515 }
516
517// Delete multiple jobs
518//
519 while((jNum = JobTable.Next(jNext)) >= 0)
520 {jp = JobTable.Item(jNum);
521 if (resp)
522 {i = jp->numClients;
523 jp->delClient(resp);
524 if (i != jp->numClients) numcaned++;
525 if (!jp->numClients) CleanUp(jp);
526 } else {
527 CleanUp(jp);
528 numcaned++;
529 }
530 }
531
532// All done
533//
534 myMutex.UnLock();
535 return numcaned;
536}
537
538/******************************************************************************/
539/* D o I t */
540/******************************************************************************/
541
543{
544 int jNum, jNext = 0;
545 XrdXrootdJob2Do *jp;
546
547// Scan through all of the jobs looking for disconnected clients
548//
549 while((jNum = JobTable.Next(jNext)) >= 0)
550 {myMutex.Lock();
551 if ((jp = JobTable.Item(jNum)))
552 {if (jp->JobMark) {if (!jp->verClient()) CleanUp(jp);}
553 else jp->JobMark = 1;
554 }
555 myMutex.UnLock();
556 }
557
558// Schedule ourselves to run 15 minutes from now
559//
560 Sched->Schedule((XrdJob *)this, time(0) + (reScan));
561}
562
563/******************************************************************************/
564/* L i s t */
565/******************************************************************************/
566
567// Output: <job id="jname">%jobkey<s>%status</s><c>%clientid ...</c> ....</job>
568//
570{
571 char *jkey, buff[1024];
572 int tlen, jNum, jNext = 0;
573 XrdXrootdJob2Do *jp;
574 XrdOucTList *tF = 0, *tL = 0, *tp;
575
576// Scan through all of the jobs listing each, in turn
577//
578 while((jNum = JobTable.Next(jNext)) >= 0)
579 {myMutex.Lock();
580 if ((jp = JobTable.Item(jNum, &jkey)) && (tp = jp->lstClient()))
581 {tlen = sprintf(buff, "<job id=\"%s\">%s", JobName, jkey);
582 if (tL) tL->next = new XrdOucTList(buff, tlen, tp);
583 else tF = new XrdOucTList(buff, tlen, tp);
584 tL = tp->next = new XrdOucTList("</job>", 6);
585 }
586 myMutex.UnLock();
587 }
588
589// Return the whole schmear
590//
591 return tF;
592}
593
594/******************************************************************************/
595/* S c h e d u l e */
596/******************************************************************************/
597
598int XrdXrootdJob::Schedule(const char *jkey,
599 const char **args,
600 XrdXrootdResponse *resp,
601 int Opts)
602{
603 XrdXrootdJob2Do *jp;
604 const char *msg = "Job resources currently not available.";
605 int jobNum, rc, isSync = Opts & JOB_Sync;
606
607// Make sure we have a target
608//
609 if (!jkey || !(*jkey))
610 return resp->Send(kXR_ArgMissing, "Job target not specified.");
611
612// First find if this is a duplicate or create a new one
613//
614 myMutex.Lock();
615 if (!(Opts & JOB_Unique) && jkey && (jp = JobTable.Find(jkey)))
617 {rc = sendResult(resp, args[0], jp);
618 myMutex.UnLock();
619 return rc;
620 }
621 if (jp->addClient(resp, Opts) < 0) isSync = 1;
622 else msg = "Job scheduled.";
623 } else {
624 if ((jobNum = JobTable.Alloc()) < 0) isSync = 1;
625 else {if ((jp = new XrdXrootdJob2Do(this, jobNum, args, resp, Opts)))
626 {JobTable.Insert(jp, jkey, jobNum);
627 if (numJobs < maxJobs)
628 {Sched->Schedule((XrdJob *)jp);
629 jp->Status = XrdXrootdJob2Do::Job_Active;
630 jp->doRedrive = 1;
631 }
632 numJobs++; msg = "Job Scheduled";
633 }
634 }
635 }
636
637// Tell the client to wait
638//
639 if (isSync) rc = resp->Send(kXR_wait, 30, msg);
640 else rc = resp->Send(kXR_waitresp, 600, "Job scheduled.");
641 myMutex.UnLock();
642 return rc;
643}
644
645/******************************************************************************/
646/* P r i v a t e M e t h o d s */
647/******************************************************************************/
648/******************************************************************************/
649/* C l e a n U p */
650/******************************************************************************/
651
652// The caller must have myMutex locked
653
654void XrdXrootdJob::CleanUp(XrdXrootdJob2Do *jp)
655{
656 int theStatus = jp->Status;
657
658// Now we have to be careful. If the job is waiting or completed schedule
659// it for cancellation. If it's active then kill the associated process. The
660// thread waiting for the result will see the cancellation. Otherwise, it
661// already has been cancelled and is in the scheduled queue.
662//
664 if (theStatus == XrdXrootdJob2Do::Job_Waiting
665 || theStatus == XrdXrootdJob2Do::Job_Done)
666 Sched->Schedule((XrdJob *)jp);
667 else{if (theStatus == XrdXrootdJob2Do::Job_Active) jp->jobStream.Drain();}
668
669 if (theStatus == XrdXrootdJob2Do::Job_Waiting) numJobs--;
670}
671
672/******************************************************************************/
673/* s e n d R e s u l t */
674/******************************************************************************/
675
676// Caller must have myMutex locked.
677
678int XrdXrootdJob::sendResult(XrdXrootdResponse *resp,
679 const char *rpfx,
680 XrdXrootdJob2Do *job)
681{
682 struct iovec jobResp[4];
683 int dlen, i, rc;
684
685// Send an error result if no result is present
686//
687 if (!(job->theResult)) rc = resp->Send(kXR_ServerError,"Program failed");
688 else {if (!rpfx) {dlen = 0; i = 1;}
689 else { jobResp[1].iov_base = (char *)rpfx;
690 dlen = jobResp[1].iov_len = strlen(rpfx);
691 jobResp[2].iov_base = (char *)" ";
692 dlen += jobResp[2].iov_len = 1;
693 i = 3;
694 }
695 jobResp[i].iov_base = job->theResult;
696 dlen += jobResp[i].iov_len = strlen(job->theResult);
697 rc = resp->Send(jobResp, i+1, dlen);
698 }
699
700// Remove the client from the job. Check if clean-up is required
701//
702 job->delClient(resp);
703 if (!job->numClients) CleanUp(job);
704
705// All done
706//
707 return rc;
708}
@ kXR_ArgMissing
Definition XProtocol.hh:989
@ kXR_Cancelled
@ kXR_ServerError
XResponseType
Definition XProtocol.hh:896
@ kXR_waitresp
Definition XProtocol.hh:904
@ kXR_ok
Definition XProtocol.hh:897
@ kXR_wait
Definition XProtocol.hh:903
@ kXR_error
Definition XProtocol.hh:901
int kXR_int32
Definition XPtypes.hh:89
unsigned char kXR_char
Definition XPtypes.hh:65
struct myOpts opts
size_t strlcpy(char *dst, const char *src, size_t sz)
#define TRACE(act, x)
Definition XrdTrace.hh:63
XrdSysTrace XrdXrootdTrace
int XrdXrootdJobWaiting(XrdXrootdJob2Do *item, void *arg)
#define jobInfo
#define JOB_Unique
#define JOB_Sync
static int mapError(int rc)
int RunDone(XrdOucStream &cmd) const
int Run(XrdOucStream *Sp, const char *argV[], int argc=0, const char *envV[]=0) const
char * GetLine()
T * Apply(int(*func)(T *, void *), void *Arg, int Start=0)
T * Remove(int Tnum)
T * Item(int Tnum, char **ikey=0)
int Insert(T *Item, const char *key=0, int Tnum=-1)
T * Find(const char *key, int *Tnum=0)
int Next(int &Tnum)
void Schedule(XrdJob *jp)
void Cancel(XrdJob *jp)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
XrdXrootdJob2Do(XrdXrootdJob *job, int jnum, const char **args, XrdXrootdResponse *Resp, int opts)
XrdSys::RAtomic< JobStatus > Status
XrdXrootdJob(XrdScheduler *schp, XrdOucProg *pgm, const char *jname, int maxjobs=4)
int Schedule(const char *jkey, const char **args, XrdXrootdResponse *resp, int Opts=0)
friend class XrdXrootdJob2Do
int Cancel(const char *jkey=0, XrdXrootdResponse *resp=0)
XrdOucTList * List(void)
void setID(unsigned long long id)
void StreamID(kXR_char *sid)
XrdScheduler * Sched
XrdSysError eLog