Logo ROOT   6.12/07
Reference Guide
XrdProofGroup.cxx
Go to the documentation of this file.
1 // @(#)root/proofd:$Id$
2 // Author: Gerardo Ganis June 2007
3 
4 /*************************************************************************
5  * Copyright (C) 1995-2005, Rene Brun and Fons Rademakers. *
6  * All rights reserved. *
7  * *
8  * For the licensing terms see $ROOTSYS/LICENSE. *
9  * For the list of contributors see $ROOTSYS/README/CREDITS. *
10  *************************************************************************/
11 
12 //////////////////////////////////////////////////////////////////////////
13 // //
14 // XrdProofGroup //
15 // //
16 // Authors: G. Ganis, CERN, 2007 //
17 // //
18 // Class describing groups //
19 // //
20 //////////////////////////////////////////////////////////////////////////
21 #include "XrdProofdPlatform.h"
22 
23 #include "XrdProofGroup.h"
24 #include "XrdProofdTrace.h"
25 
26 // Functions used in scanning hash tables
27 
28 ////////////////////////////////////////////////////////////////////////////////
29 /// Check if user 'u' is memmebr of group 'grp'
30 
31 static int CheckUser(const char *, XrdProofGroup *g, void *u)
32 {
33  const char *usr = (const char *)u;
34 
35  if (g && usr && g->HasMember(usr))
36  // Found the group
37  return 1;
38 
39  // Check next
40  return 0;
41 }
42 
43 ////////////////////////////////////////////////////////////////////////////////
44 /// Add a string describing group 'g' to a global string
45 
46 static int ExportGroup(const char *, XrdProofGroup *g, void *u)
47 {
48  XrdOucString *msg = (XrdOucString *)u;
49 
50  if (msg->length() > 0)
51  *msg += '\n';
52 
53  *msg = g->Name(); *msg += ": ";
54  *msg += ", size: ";
55  *msg += g->Size();
56  *msg += ", members(s): ";
57  *msg += g->Members();
58 
59  return 0;
60 }
61 
62 ////////////////////////////////////////////////////////////////////////////////
63 /// Print info describing group 'g' to stdout
64 
65 static int PrintGroup(const char *, XrdProofGroup *g, void *)
66 {
67  if (g)
68  g->Print();
69 
70  return 0;
71 }
72 
73 ////////////////////////////////////////////////////////////////////////////////
74 /// Generic function used for auxiliary purpose
75 
76 static int AuxFunc(const char *, XrdProofGroup *g, void *s)
77 {
78  XrdOucString *opt = (XrdOucString *)s;
79 
80  if (!opt || opt->length() <= 0 || (*opt) == "getfirst")
81  // Stop going through the table
82  return 1;
83 
84  if (opt->beginswith("getnextgrp:")) {
85  XrdOucString grp("||");
86  grp.insert(g->Name(),1);
87  if (opt->find(grp) == STR_NPOS) {
88  *opt += grp;
89  return 1;
90  }
91  }
92 
93  // Process next
94  return 0;
95 }
96 
97 ////////////////////////////////////////////////////////////////////////////////
98 /// Constructor
99 
100 XrdProofGroup::XrdProofGroup(const char *n, const char *m)
101  : fName(n), fMembers(m)
102 {
103  fSize = 0;
104  fPriority = -1;
105  fFraction = -1;
106  fFracEff = 0;
107  fMutex = new XrdSysRecMutex;
108 }
109 ////////////////////////////////////////////////////////////////////////////////
110 /// Destructor
111 
113 {
114  if (fMutex)
115  delete fMutex;
116  fMutex = 0;
117 }
118 
119 ////////////////////////////////////////////////////////////////////////////////
120 /// Dump group content
121 
123 {
124  XPDLOC(GMGR, "Group::Print")
125 
127 
128  if (fName != "default") {
129  TRACE(ALL, "+++ Group: "<<fName<<", size "<<fSize<<" member(s) ("<<fMembers<<")");
130  TRACE(ALL, "+++ Priority: "<<fPriority<<", fraction: "<<fFraction);
131  TRACE(ALL, "+++ End of Group: "<<fName);
132  } else {
133  TRACE(ALL, "+++ Group: "<<fName);
134  TRACE(ALL, "+++ Priority: "<<fPriority<<", fraction: "<<fFraction);
135  TRACE(ALL, "+++ End of Group: "<<fName);
136  }
137 }
138 
139 ////////////////////////////////////////////////////////////////////////////////
140 /// Modify the active count
141 
142 void XrdProofGroup::Count(const char *usr, int n)
143 {
144  // A username must be defined and an action required
145  if (!usr || !usr[0] || n == 0)
146  return;
147 
149 
150  XrdProofGroupMember *m = fActives.Find(usr);
151  if (!m) {
152  // Create a new active user
153  m = new XrdProofGroupMember(usr);
154  fActives.Add(usr, m);
155  }
156 
157  // Count
158  if (m) {
159  m->Count(n);
160  // If no active sessions left, remove from active
161  if (m->Active() <= 0) {
162  fActives.Del(usr);
163  delete m;
164  }
165  }
166 }
167 
168 ////////////////////////////////////////////////////////////////////////////////
169 /// Return the number of active groups (usr = 0) or the number of
170 /// active sessions for user 'usr'
171 
172 int XrdProofGroup::Active(const char *usr)
173 {
175 
176  int na = 0;
177  if (!usr || !usr[0]) {
178  na = fActives.Num();
179  } else {
180  XrdProofGroupMember *m = fActives.Find(usr);
181  if (m) na = m->Active();
182  }
183  // Done
184  return na;
185 }
186 
187 ////////////////////////////////////////////////////////////////////////////////
188 /// Check if 'usr' is member of this group
189 
190 bool XrdProofGroup::HasMember(const char *usr)
191 {
193  XrdOucString u(usr); u += ",";
194  int iu = fMembers.find(u);
195  if (iu != STR_NPOS)
196  if (iu == 0 || fMembers[iu-1] == ',')
197  return 1;
198  return 0;
199 }
200 
201 ////////////////////////////////////////////////////////////////////////////////
202 /// Constructor
203 
205 {
206  ResetIter();
207  Config(fn);
208 }
209 
210 ////////////////////////////////////////////////////////////////////////////////
211 /// Apply function 'f' to the hash table of groups; 'arg' is passed to 'f'
212 /// in the last argument. After applying 'f', the action depends on the
213 /// return value with the following rule:
214 /// < 0 - the hash table item is deleted.
215 /// = 0 - the next hash table item is processed.
216 /// > 0 - processing stops and the hash table item is returned.
217 
219  void *), void *arg)
220 {
221  return (fGroups.Num() > 0 ? fGroups.Apply(f,arg) : (XrdProofGroup *)0);
222 }
223 
224 ////////////////////////////////////////////////////////////////////////////////
225 /// Return a string describing the group
226 
227 XrdOucString XrdProofGroupMgr::Export(const char *grp)
228 {
230 
231  XrdOucString msg;
232 
233  if (!grp) {
234  fGroups.Apply(ExportGroup, (void *) &msg);
235  } else {
236  XrdProofGroup *g = fGroups.Find(grp);
237  ExportGroup(grp, g, (void *) &msg);
238  }
239 
240  return msg;
241 }
242 
243 ////////////////////////////////////////////////////////////////////////////////
244 /// Return a string describing the group
245 
246 void XrdProofGroupMgr::Print(const char *grp)
247 {
249 
250  if (!grp) {
251  fGroups.Apply(PrintGroup, 0);
252  } else {
253  XrdProofGroup *g = fGroups.Find(grp);
254  PrintGroup(grp, g, 0);
255  }
256 
257  return;
258 }
259 
260 ////////////////////////////////////////////////////////////////////////////////
261 /// Returns the instance of for group 'grp.
262 /// Return 0 in the case the group does not exist
263 
265 {
266  // If the group is defined and exists, check it
267  if (grp && strlen(grp) > 0) {
269  return fGroups.Find(grp);
270  }
271  return (XrdProofGroup *)0;
272 }
273 
274 ////////////////////////////////////////////////////////////////////////////////
275 /// Returns the instance of the first group to which this user belongs;
276 /// if grp != 0, return the instance corresponding to group 'grp', if
277 /// existing and the // user belongs to it.
278 /// Return 0 in the case the user does not belong to any group or does not
279 /// belong to 'grp'.
280 
281 XrdProofGroup *XrdProofGroupMgr::GetUserGroup(const char *usr, const char *grp)
282 {
283  XrdProofGroup *g = 0;
284 
285  // Check inputs
286  if (!usr || strlen(usr) <= 0)
287  return g;
288 
290 
291  // If the group is defined and exists, check it
292  if (grp && strlen(grp) > 0) {
293  g = fGroups.Find(grp);
294  if (g && (!strncmp(g->Name(),"default",7) || g->HasMember(usr)))
295  return g;
296  else
297  return (XrdProofGroup *)0;
298  }
299 
300  // Scan the table
301  g = fGroups.Apply(CheckUser, (void *)usr);
302 
303  // Assign to "default" group if nothing was found
304  return ((!g) ? fGroups.Find("default") : g);
305 }
306 
307 ////////////////////////////////////////////////////////////////////////////////
308 /// Returns the instance of next group in the pseudo-iterator
309 /// functionality. To scan over all the groups do the following:
310 /// ResetIter();
311 /// while ((g = Next())) {
312 /// // ... Process group
313 /// }
314 /// Return 0 when there are no more groups
315 
317 {
318  return fGroups.Apply(AuxFunc,&fIterator);
319 }
320 
321 ////////////////////////////////////////////////////////////////////////////////
322 /// (Re-)configure the group info using the file 'fn'.
323 /// Return the number of active groups or -1 in case of error.
324 
325 int XrdProofGroupMgr::Config(const char *fn)
326 {
327  XPDLOC(GMGR, "GroupMgr::Config")
328 
329  if ((!fn || strlen(fn) <= 0)) {
330  if (fCfgFile.fName != fn) {
331  // This call is to reset existing info and remain with
332  // the 'default' group only
334  // Reset existing info
335  fGroups.Purge();
336  // Create "default" group
337  fGroups.Add("default", new XrdProofGroup("default"));
338  // Reset fCfgFile
339  fCfgFile.fName = "";
340  fCfgFile.fMtime = 0;
341  }
342  return fGroups.Num();
343  }
344 
345  // Did the file changed ?
346  if (fCfgFile.fName != fn) {
347  fCfgFile.fName = fn;
348  XrdProofdAux::Expand(fCfgFile.fName);
349  fCfgFile.fMtime = 0;
350  }
351 
352  // Get the modification time
353  struct stat st;
354  if (stat(fCfgFile.fName.c_str(), &st) != 0)
355  return -1;
356  TRACE(DBG, "enter: time of last modification: " << st.st_mtime);
357 
358  // Nothing to do if the file did not change
359  if (st.st_mtime <= fCfgFile.fMtime) return fGroups.Num();
360 
361  // Save the modification time
362  fCfgFile.fMtime = st.st_mtime;
363 
364  // This part must be modified in atomic way
366 
367  // Reset existing info
368  fGroups.Purge();
369 
370  // Create "default" group
371  fGroups.Add("default", new XrdProofGroup("default"));
372 
373  // Read now the directives (recursive processing of 'include sub-file'
374  // in here)
375  if (ParseInfoFrom(fCfgFile.fName.c_str()) != 0) {
376  TRACE(XERR, "problems parsing config file "<<fCfgFile.fName);
377  }
378 
379  // Notify the content
380  Print(0);
381 
382  // Return the number of active groups
383  return fGroups.Num();
384 }
385 
386 ////////////////////////////////////////////////////////////////////////////////
387 /// Parse config information from the open file 'fin'. Can be called
388 /// recursively following 'include sub-file' lines.
389 /// Return 0 or -1 in case of error.
390 
392 {
393  XPDLOC(GMGR, "GroupMgr::ParseInfoFrom")
394 
395  // Check input
396  if (!fn || strlen(fn) <= 0) {
397  TRACE(XERR, "file name undefined!");
398  return -1;
399  }
400 
401  // Open the defined path.
402  FILE *fin = 0;
403  if (!(fin = fopen(fn, "r"))) {
404  TRACE(XERR, "cannot open file: "<<fn<<" (errno:"<<errno<<")");
405  return -1;
406  }
407 
408  // Read now the directives
409  char lin[2048];
410  while (fgets(lin,sizeof(lin),fin)) {
411  // Remove trailing '\n'
412  if (lin[strlen(lin)-1] == '\n') lin[strlen(lin)-1] = '\0';
413  // Skip comments or empty lines
414  if (lin[0] == '#' || strlen(lin) <= 0) continue;
415  // Good line: parse it
416  bool gotkey = 0, gotgrp = 0;
417  XrdOucString gl(lin), tok, key, group;
418  gl.replace(" ",",");
419  int from = 0;
420  while ((from = gl.tokenize(tok, from, ',')) != -1) {
421  if (tok.length() > 0) {
422  if (!gotkey) {
423  key = tok;
424  gotkey = 1;
425  } else if (!gotgrp) {
426  group = tok;
427  gotgrp = 1;
428  break;
429  }
430  }
431  }
432  // Check consistency
433  if (!gotkey || !gotgrp) {
434  // Insufficient info
435  TRACE(DBG, "incomplete line: " << lin);
436  continue;
437  }
438 
439  if (key == "include") {
440  // File to be included in the parsing
441  XrdOucString subfn = group;
442  // Expand the path
443  XrdProofdAux::Expand(subfn);
444  // Process it
445  if (ParseInfoFrom(subfn.c_str()) != 0) {
446  TRACE(XERR, "problems parsing included file "<<subfn);
447  }
448  continue;
449  }
450 
451  if (key == "priorityfile") {
452  // File from which (updated) priorities are read
453  fPriorityFile.fName = group;
454  XrdProofdAux::Expand(fPriorityFile.fName);
455  fPriorityFile.fMtime = 0;
456  continue;
457  }
458 
459  // Get linked to the group, if any
460  XrdProofGroup *g = fGroups.Find(group.c_str());
461 
462  // Action depends on key
463  if (key == "group") {
464  if (!g)
465  // Create new group container
466  fGroups.Add(group.c_str(), (g = new XrdProofGroup(group.c_str())));
467  while ((from = gl.tokenize(tok, from, ',')) != -1) {
468  if (tok.length() > 0)
469  // Add group member
470  g->AddMember(tok.c_str());
471  }
472  } else if (key == "property") {
473  // Property definition: format of property is
474  // property <group> <property_name> <nominal_value> [<effective_value>]
475  XrdOucString name;
476  int nom=0;
477  bool gotname = 0, gotnom = 0;
478  while ((from = gl.tokenize(tok, from, ',')) != -1) {
479  if (tok.length() > 0) {
480  if (!gotname) {
481  name = tok;
482  gotname= 1;
483  } else if (!gotnom) {
484  nom = atoi(tok.c_str());
485  gotnom = 1;
486  break;
487  }
488  }
489  }
490  if (!gotname || !gotnom) {
491  // Insufficient info
492  TRACE(DBG, "incomplete property line: " << lin);
493  continue;
494  }
495  if (!g)
496  // Create new group container
497  fGroups.Add(group.c_str(), (g = new XrdProofGroup(group.c_str())));
498  if (name == "priority")
499  g->SetPriority((float)nom);
500  if (name == "fraction")
501  g->SetFraction(nom);
502  }
503  }
504  // Close this file
505  fclose(fin);
506  // Done
507  return 0;
508 }
509 
510 ////////////////////////////////////////////////////////////////////////////////
511 /// Read update priorities from the file defined at configuration time.
512 /// Return 1 if the file did not change, 0 if the file has been read
513 /// correctly, or -1 in case of error.
514 
516 {
517  XPDLOC(GMGR, "GroupMgr::ReadPriorities")
518 
519  // Get the modification time
520  struct stat st;
521  if (stat(fPriorityFile.fName.c_str(), &st) != 0)
522  return -1;
523  TRACE(DBG, "time of last modification: " << st.st_mtime);
524 
525  // File should be loaded only once
526  if (st.st_mtime <= fPriorityFile.fMtime) {
527  TRACE(DBG, "file unchanged since last reading - do nothing ");
528  return 1;
529  }
530 
531  // Save the modification time
532  fPriorityFile.fMtime = st.st_mtime;
533 
534  // Open the defined path.
535  FILE *fin = 0;
536  if (!(fin = fopen(fPriorityFile.fName.c_str(), "r"))) {
537  TRACE(XERR, "cannot open file: "<<fPriorityFile.fName<<" (errno:"<<errno<<")");
538  return -1;
539  }
540 
541  // This part must be modified in atomic way
543 
544  // Read now the directives
545  char lin[2048];
546  while (fgets(lin,sizeof(lin),fin)) {
547  // Remove trailing '\n'
548  if (lin[strlen(lin)-1] == '\n') lin[strlen(lin)-1] = '\0';
549  // Skip comments or empty lines
550  if (lin[0] == '#' || strlen(lin) <= 0) continue;
551  // Good line candidate: parse it
552  XrdOucString gl(lin), group, value;
553  // It must contain a '='
554  int from = 0;
555  if ((from = gl.tokenize(group, 0, '=')) == -1)
556  continue;
557  // Get linked to the group, if any
558  XrdProofGroup *g = fGroups.Find(group.c_str());
559  if (!g) {
560  TRACE(XERR, "found info for unknown group: "<<group<<" - ignoring");
561  continue;
562  }
563  gl.tokenize(value, from, '=');
564  if (value.length() <= 0) {
565  TRACE(XERR, "value missing: read line is: '"<<gl<<"'");
566  continue;
567  }
568  // Transform it in a usable value
569  if (value.find('.') == STR_NPOS)
570  value += '.';
571  // Save it
572  g->SetPriority((float)strtod(value.c_str(),0));
573  }
574 
575  // Close the file
576  fclose(fin);
577 
578  // Done
579  return 0;
580 }
581 
582 ////////////////////////////////////////////////////////////////////////////////
583 /// Fill the global group structure
584 
585 static int GetGroupsInfo(const char *, XrdProofGroup *g, void *s)
586 {
588 
589  if (glo) {
590  if (g->Active() > 0) {
591  // Set the min/max priorities
592  if (glo->prmin == -1 || g->Priority() < glo->prmin)
593  glo->prmin = g->Priority();
594  if (glo->prmax == -1 || g->Priority() > glo->prmax)
595  glo->prmax = g->Priority();
596  // Set the draft fractions
597  if (g->Fraction() > 0) {
598  g->SetFracEff((float)(g->Fraction()));
599  glo->totfrac += (float)(g->Fraction());
600  } else {
601  glo->nofrac += 1;
602  }
603  }
604  } else {
605  // Not enough info: stop
606  return 1;
607  }
608 
609  // Check next
610  return 0;
611 }
612 
613 ////////////////////////////////////////////////////////////////////////////////
614 /// Check if user 'u' is memmebr of group 'grp'
615 
616 static int SetGroupFracEff(const char *, XrdProofGroup *g, void *s)
617 {
618  XpdGroupEff_t *eff = (XpdGroupEff_t *)s;
619 
620  if (eff && eff->glo) {
621  XpdGroupGlobal_t *glo = eff->glo;
622  if (g->Active() > 0) {
623  if (eff->opt == 0) {
624  float ef = g->Priority() / glo->prmin;
625  g->SetFracEff(ef);
626  } else if (eff->opt == 1) {
627  if (g->Fraction() < 0) {
628  float ef = ((100. - glo->totfrac) / glo->nofrac);
629  g->SetFracEff(ef);
630  }
631  } else if (eff->opt == 2) {
632  if (g->FracEff() < 0) {
633  // Share eff->cut (default 5%) between those with undefined fraction
634  float ef = (eff->cut / glo->nofrac);
635  g->SetFracEff(ef);
636  } else {
637  // renormalize
638  float ef = g->FracEff() * eff->norm;
639  g->SetFracEff(ef);
640  }
641  }
642  }
643  } else {
644  // Not enough info: stop
645  return 1;
646  }
647 
648  // Check next
649  return 0;
650 }
651 
652 ////////////////////////////////////////////////////////////////////////////////
653 /// Go through the list of active groups (those having at least a non-idle
654 /// member) and determine the effective resource fraction on the base of
655 /// the scheduling option and of priorities or nominal fractions.
656 /// Return 0 in case of success, -1 in case of error, 1 if every group
657 /// has the same priority so that the system scheduler should do the job.
658 
660 {
661  // Loop over groupd
662  XpdGroupGlobal_t glo = {-1., -1., 0, 0.};
663  Apply(GetGroupsInfo, &glo);
664 
665  XpdGroupEff_t eff = {0, &glo, 0.5, 1.};
666  if (opri) {
667  // Set effective fractions
668  ResetIter();
669  eff.opt = 0;
670  Apply(SetGroupFracEff, &eff);
671 
672  } else {
673  // In the fraction scheme we need to fill up with the remaining resources
674  // if at least one lower bound was found. And of course we need to restore
675  // unitarity, if it was broken
676 
677  if (glo.totfrac < 100. && glo.nofrac > 0) {
678  eff.opt = 1;
679  Apply(SetGroupFracEff, &eff);
680  } else if (glo.totfrac > 100) {
681  // Leave 5% for unnamed or low priority groups
682  eff.opt = 2;
683  eff.norm = (glo.nofrac > 0) ? (100. - eff.cut)/glo.totfrac : 100./glo.totfrac ;
684  Apply(SetGroupFracEff, &eff);
685  }
686  }
687 
688  // Done
689  return 0;
690 }
const char * Name() const
Definition: XrdProofGroup.h:75
static int AuxFunc(const char *, XrdProofGroup *g, void *s)
Generic function used for auxiliary purpose.
auto * m
Definition: textangle.C:8
void AddMember(const char *usr)
Definition: XrdProofGroup.h:64
XrdProofGroupMgr(const char *fn=0)
Constructor.
int ParseInfoFrom(const char *fn)
Parse config information from the open file &#39;fin&#39;.
XrdProofGroup(const char *n, const char *m=0)
Constructor.
#define XrdSysRecMutex
Definition: XrdSysToOuc.h:18
int Active() const
Definition: XrdProofGroup.h:43
#define TRACE(Flag, Args)
Definition: TGHtml.h:120
XpdGroupGlobal_t * glo
int SetEffectiveFractions(bool optprio)
Go through the list of active groups (those having at least a non-idle member) and determine the effe...
void SetPriority(float p)
Definition: XrdProofGroup.h:83
XrdOucHash< XrdProofGroupMember > fActives
Definition: XrdProofGroup.h:55
XrdProofGroup * GetGroup(const char *grp)
Returns the instance of for group &#39;grp.
XrdSysRecMutex * fMutex
Definition: XrdProofGroup.h:62
void Print()
Dump group content.
bool HasMember(const char *usr)
Check if &#39;usr&#39; is member of this group.
int ReadPriorities()
Read update priorities from the file defined at configuration time.
#define XPDLOC(d, x)
int Fraction() const
Definition: XrdProofGroup.h:78
static int ExportGroup(const char *, XrdProofGroup *g, void *u)
Add a string describing group &#39;g&#39; to a global string.
const char * Members() const
Definition: XrdProofGroup.h:74
XrdOucString fName
Definition: XrdProofGroup.h:51
void SetFraction(int f)
Definition: XrdProofGroup.h:82
float Priority() const
Definition: XrdProofGroup.h:80
#define XrdSysMutexHelper
Definition: XrdSysToOuc.h:17
void Count(const char *usr, int n=1)
Modify the active count.
XrdProofGroup * GetUserGroup(const char *usr, const char *grp=0)
Returns the instance of the first group to which this user belongs; if grp != 0, return the instance ...
int Active(const char *usr=0)
Return the number of active groups (usr = 0) or the number of active sessions for user &#39;usr&#39;...
static int PrintGroup(const char *, XrdProofGroup *g, void *)
Print info describing group &#39;g&#39; to stdout.
int Size() const
Definition: XrdProofGroup.h:76
void SetFracEff(float f)
Definition: XrdProofGroup.h:81
XrdProofGroup * Next()
Returns the instance of next group in the pseudo-iterator functionality.
~XrdProofGroup()
Destructor.
static char * Expand(char *p)
Expand path &#39;p&#39; relative to: $HOME if begins with ~/ <user>&#39;s $HOME if begins with ~<user>/ $PWD if d...
static constexpr double s
float FracEff() const
Definition: XrdProofGroup.h:79
XrdOucString Export(const char *grp)
Return a string describing the group.
XrdOucString fMembers
Definition: XrdProofGroup.h:53
static int SetGroupFracEff(const char *, XrdProofGroup *g, void *s)
Check if user &#39;u&#39; is memmebr of group &#39;grp&#39;.
static int GetGroupsInfo(const char *, XrdProofGroup *g, void *s)
Fill the global group structure.
int Config(const char *fn)
(Re-)configure the group info using the file &#39;fn&#39;.
void Print(const char *grp)
Return a string describing the group.
const Int_t n
Definition: legend1.C:16
char name[80]
Definition: TGX11.cxx:109
static int CheckUser(const char *, XrdProofGroup *g, void *u)
Check if user &#39;u&#39; is memmebr of group &#39;grp&#39;.
static constexpr double g
XrdProofGroup * Apply(int(*f)(const char *, XrdProofGroup *, void *), void *arg)
Apply function &#39;f&#39; to the hash table of groups; &#39;arg&#39; is passed to &#39;f&#39; in the last argument...