XRootD
XrdMapCluster.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d M a p C l u s t e r . c c */
4 /* */
5 /* (c) 2013 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 Department 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 /* This utility maps the connections in a cluster starting at some node. It
31  can also, optionally, check for file existence at each point. Syntax:
32 
33  xrdmapc <host>:<port> [<path>]
34 
35 */
36 
37 /******************************************************************************/
38 /* i n c l u d e f i l e s */
39 /******************************************************************************/
40 
41 #include <algorithm>
42 #include <cerrno>
43 #include <getopt.h>
44 #include <cstdlib>
45 #include <cstdio>
46 #include <cstring>
47 #include <strings.h>
48 #include <unistd.h>
49 #include <sys/param.h>
50 #include <sys/types.h>
51 #include <vector>
52 
53 #include "XProtocol/XProtocol.hh"
54 #include "XrdCl/XrdClEnv.hh"
55 #include "XrdCl/XrdClFileSystem.hh"
56 #include "XrdCl/XrdClDefaultEnv.hh"
57 #include "XrdNet/XrdNetAddr.hh"
58 #include "XrdOuc/XrdOucHash.hh"
59 #include "XrdSys/XrdSysHeaders.hh"
60 
61 /******************************************************************************/
62 /* L o c a l D e f i n i t i o n s */
63 /******************************************************************************/
64 
65 #define EMSG(x) std::cerr <<"xrdmapc: " <<x <<std::endl
66 
67 // Bypass stupid issue with stupid solaris for missdefining 'struct opt'.
68 //
69 #ifdef __solaris__
70 #define OPT_TYPE (char *)
71 #else
72 #define OPT_TYPE
73 #endif
74 
75 namespace
76 {
77 struct clMap
78 { clMap *nextMan;
79  clMap *nextSrv;
80  clMap *nextLvl;
81  const char *state;
82  char *key;
83  char name[284];
84  char hasfile;
85  char verfile;
86  char valid;
87  char isMan;
88 
89  clMap(const char *addr) : nextMan(0), nextSrv(0), nextLvl(0),
90  state(""), hasfile(' '), verfile(' '),
91  valid(1), isMan(0)
92  {if (addr)
93  {XrdNetAddr epAddr;
94  epAddr.Set(addr); epAddr.Name();
95  epAddr.Format(name, sizeof(name));
96  key = strdup(addr);
97  } else {
98  *name = 0;
99  key = strdup("");
100  }
101  }
102  ~clMap() {}
103 };
104 };
105 
106 /******************************************************************************/
107 /* G l o b a l O b j e c t s */
108 /******************************************************************************/
109 
110 extern int optind, optopt;
111 
112 namespace
113 {
114 bool listMan = true, listSrv = true, doVerify = false, doHush = false, doJson = false;
115 
116 clMap *clLost = 0;
117 
118 char *Path = 0;
119 
120 uint16_t theTO = 30;
121 
122 XrdOucHash<clMap> clHash;
123 };
124 
125 /******************************************************************************/
126 /* M a k e U R L */
127 /******************************************************************************/
128 
129 namespace
130 {
131 const char *MakeURL(const char *name, char *buff, int blen)
132 {
133  snprintf(buff, blen, "xroot://%s//", name);
134  return buff;
135 }
136 };
137 
138 /******************************************************************************/
139 /* M a p C o d e */
140 /******************************************************************************/
141 
142 namespace
143 {
144 void MapCode(XrdCl::XRootDStatus &Status, clMap *node, bool nspl=false)
145 {
146  char buff[128];
147 
148  node->verfile = '?';
149 
150  if (Status.code == XrdCl::errErrorResponse)
151  {switch(Status.errNo)
152  {case kXR_FSError: node->state = " [fs error]"; break;
153  case kXR_IOError: node->state = " [io error]"; break;
154  case kXR_NoMemory: node->state = " [no memory]"; break;
155  case kXR_NotAuthorized: node->state = " [not authorized]";break;
156  case kXR_NotFound: if (nspl)
157  node->state = " [no subscribers]";
158  else {node->state = "";
159  node->verfile = '-';
160  }
161  break;
162  case kXR_NotFile: node->state = " [not a file]"; break;
163  default: sprintf(buff, " [xrootd error %d]", Status.errNo);
164  node->state = strdup(buff);
165  break;
166  }
167  return;
168  }
169 
170  switch(Status.code)
171  {case XrdCl::errInvalidAddr: node->state = " [invalid addr]";
172  break;
173  case XrdCl::errSocketError: node->state = " [socket error]";
174  break;
175  case XrdCl::errSocketTimeout: node->state = " [timeout]";
176  break;
177  case XrdCl::errSocketDisconnected: node->state = " [disconnect]";
178  break;
179  case XrdCl::errStreamDisconnect: node->state = " [disconnect]";
180  break;
181  case XrdCl::errConnectionError: node->state = " [connect error]";
182  break;
183  case XrdCl::errHandShakeFailed: node->state = " [handshake failed]";
184  break;
185  case XrdCl::errLoginFailed: node->state = " [login failed]";
186  break;
187  case XrdCl::errAuthFailed: node->state = " [auth failed]";
188  break;
189  case XrdCl::errOperationExpired: node->state = " [op expired]";
190  break;
191  case XrdCl::errRedirectLimit: node->state = " [redirect loop]";
192  break;
193  default: if (!(*Status.ToStr().c_str()))
194  sprintf(buff, " [client error %d]", Status.code);
195  else snprintf(buff, sizeof(buff), " [%s]",
196  Status.ToStr().c_str());
197  node->state = strdup(buff);
198  break;
199  }
200 }
201 };
202 
203 /******************************************************************************/
204 /* M a p C l u s t e r */
205 /******************************************************************************/
206 
207 namespace
208 {
209 void MapCluster(clMap *node, clMap *origin)
210 {
212  char buff[2048];
213  XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
214  XrdCl::FileSystem xrdFS(theURL);
215  XrdCl::XRootDStatus Status;
216  XrdCl::LocationInfo *info = 0;
219  clMap *clmP, *branch;
220 
221 // Issue a locate
222 //
223  Status = xrdFS.Locate((const std::string)"*", flags, info, theTO);
224 
225 // Make sure all went well
226 //
227  if (!Status.IsOK())
228  {if (Status.errNo != kXR_NotFound && !doHush)
229  EMSG("Unable to get " <<node->name <<" subscribers; "
230  <<Status.ToStr().c_str());
231  MapCode(Status, origin, true);
232  node->valid = 0;
233  return;
234  }
235 
236 // Grab all of the information
237 //
238  for( it = info->Begin(); it != info->End(); ++it )
239  {clmP = new clMap(it->GetAddress().c_str());
240  locType = it->GetType();
241  if (locType == XrdCl::LocationInfo::ServerOnline
243  {clmP->nextSrv = node->nextSrv;
244  node->nextSrv = clmP;
245  } else {
246  clmP->nextMan = node->nextMan;
247  node->nextMan = clmP;
248  clmP->isMan = 1;
249  }
250  clHash.Add(clmP->key, clmP, 0, Hash_keep);
251  }
252 
253 // Now map all managers
254 //
255  clmP = node->nextMan;
256  while(clmP)
257  {branch = new clMap(clmP->name);
258  MapCluster(branch, clmP);
259 // if (branch->nextSrv || branch->nextMan || node->valid)
260  clmP->nextLvl = branch;
261 // else delete branch;
262  clmP = clmP->nextMan;
263  }
264 
265 // All done
266 //
267  delete info;
268 }
269 };
270 
271 /******************************************************************************/
272 /* M a p P a t h */
273 /******************************************************************************/
274 
275 namespace
276 {
277 void MapPath(clMap *node, const char *Path, bool doRefresh=false)
278 {
280  char buff[2048];
281  XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
282  XrdCl::FileSystem xrdFS(theURL);
283  XrdCl::XRootDStatus Status;
284  XrdCl::LocationInfo *info = 0;
286  clMap *clmP;
287 
288 // Insert refresh is so wanted
289 //
290  if (doRefresh) flags = XrdCl::OpenFlags::Refresh;
291 
292 // Issue a locate
293 //
294  Status = xrdFS.Locate((const std::string)Path, flags, info, theTO);
295 
296 // Make sure all went well
297 //
298  if (!Status.IsOK())
299  {if (Status.errNo != kXR_NotFound && !doHush)
300  EMSG("Unable to query " <<node->name <<" about path; "
301  <<Status.ToStr().c_str());
302  MapCode(Status, node);
303  return;
304  }
305 
306 // Recursively mark each node as having the file
307 //
308  for( it = info->Begin(); it != info->End(); ++it )
309  {const char *clAddr = it->GetAddress().c_str();
310  if ((clmP = clHash.Find(clAddr)))
311  {clmP->hasfile = '>';
312  if (clmP->isMan) MapPath(clmP, Path);
313  } else {
314  clmP = new clMap(clAddr);
315  clmP->nextSrv = clLost;
316  clLost = clmP;
317  }
318  }
319 
320 // All done here
321 //
322  delete info;
323 }
324 };
325 
326 /******************************************************************************/
327 /* O p N a m e */
328 /******************************************************************************/
329 
330 namespace
331 {
332 const char *OpName(char *Argv[])
333 {
334  static char oName[4] = {'-', 0, 0, 0};
335 
336  if (!optopt || optopt == '-' || *(Argv[optind-1]+1) == '-')
337  return Argv[optind-1];
338  oName[1] = optopt;
339  return oName;
340 }
341 };
342 
343 /******************************************************************************/
344 /* P a t h C h k */
345 /******************************************************************************/
346 
347 namespace
348 {
349 void PathChk(clMap *node)
350 {
351  char buff[2048];
352  XrdCl::URL theURL((const std::string)MakeURL(node->name,buff,sizeof(buff)));
353  XrdCl::FileSystem xrdFS(theURL);
354  XrdCl::XRootDStatus Status;
355  XrdCl::StatInfo *info = 0;
356 
357 // Issue a stat for the file
358 //
359  Status = xrdFS.Stat((const std::string)Path, info);
360 
361 // Make sure all went well
362 //
363  if (!Status.IsOK()) MapCode(Status, node);
364  else node->verfile = '+';
365 
366 // All done here
367 //
368  delete info;
369 }
370 };
371 
372 /******************************************************************************/
373 /* P r i n t M a p */
374 /******************************************************************************/
375 
376 namespace
377 {
378 void PrintMap(clMap *clmP, int lvl)
379 {
380  clMap *clnow;
381  const char *pfx = "";
382  char *pfxbuff = 0;
383  int n;
384 
385 // Compute index spacing
386 //
387  if ((n = lvl*5))
388  {pfxbuff = (char *)malloc(n+1);
389  memset(pfxbuff, ' ', n); pfxbuff[n] = 0;
390  pfx = pfxbuff;
391  }
392 
393 // Print all of the servers first
394 //
395  if (listSrv)
396  {clnow = clmP->nextSrv;
397  while(clnow)
398  {if (doVerify) PathChk(clnow);
399  if (lvl)
400  {pfxbuff[1] = clnow->hasfile;
401  pfxbuff[2] = clnow->verfile;
402  }
403  std::cout <<' ' <<pfx <<"Srv " <<clnow->name <<clnow->state <<std::endl;
404  clnow = clnow->nextSrv;
405  }
406  }
407 
408 // Now recursively print the managers
409 //
410  if (listMan)
411  {clnow = clmP->nextMan;
412  if (lvl) pfxbuff[2] = ' ';
413  while(clnow)
414  {if (lvl) pfxbuff[1] = clnow->hasfile;
415  std::cout <<lvl <<pfx <<"Man " <<clnow->name <<clnow->state <<std::endl;
416  if (clnow->valid && clnow->nextLvl) PrintMap(clnow->nextLvl,lvl+1);
417  clnow = clnow->nextMan;
418  }
419  }
420 
421 // All done
422 //
423  if (lvl) free(pfxbuff);
424 }
425 };
426 
427 /******************************************************************************/
428 /* P r i n t J s o n */
429 /******************************************************************************/
430 
431 namespace
432 {
433 void PrintJsonSrv(clMap *clnow)
434 {
435  const char *st;
436 
437  printf("{\"name\":\"%s\"", clnow->name);
438  if (*clnow->state)
439  {st = clnow->state; while (*st == ' ') st++;
440  printf(",\"state\":\"%s\"", st);
441  }
442  if (clnow->hasfile != ' ')
443  printf(",\"hasFile\":%s", clnow->hasfile == '>' ? "true" : "false");
444  if (clnow->verfile != ' ' && clnow->verfile != '?')
445  printf(",\"verified\":%s", clnow->verfile == '+' ? "true" : "false");
446  putchar('}');
447 }
448 
449 void PrintJson(clMap *clmP)
450 {
451  clMap *clnow;
452  const char *st;
453  std::vector<clMap*> nodes;
454  size_t i;
455 
456  printf("{\"name\":\"%s\",\"type\":\"manager\"", clmP->name);
457 
458  if (*clmP->state)
459  {st = clmP->state; while (*st == ' ') st++;
460  printf(",\"state\":\"%s\"", st);
461  }
462  if (clmP->hasfile != ' ')
463  printf(",\"hasFile\":%s", clmP->hasfile == '>' ? "true" : "false");
464  if (clmP->verfile != ' ' && clmP->verfile != '?')
465  printf(",\"verified\":%s", clmP->verfile == '+' ? "true" : "false");
466 
467  // Emit children sorted by name so topology JSON is deterministic.
468  // Locate response order is otherwise not stable.
469  //
470  if (clmP->nextSrv && listSrv)
471  {nodes.clear();
472  for (clnow = clmP->nextSrv; clnow; clnow = clnow->nextSrv)
473  nodes.push_back(clnow);
474  std::sort(nodes.begin(), nodes.end(),
475  [](const clMap *a, const clMap *b)
476  { return strcmp(a->name, b->name) < 0; });
477  printf(",\"servers\":[");
478  for (i = 0; i < nodes.size(); ++i)
479  {if (i) putchar(',');
480  PrintJsonSrv(nodes[i]);
481  }
482  putchar(']');
483  }
484 
485  if (clmP->nextMan && listMan)
486  {nodes.clear();
487  for (clnow = clmP->nextMan; clnow; clnow = clnow->nextMan)
488  nodes.push_back(clnow);
489  std::sort(nodes.begin(), nodes.end(),
490  [](const clMap *a, const clMap *b)
491  { return strcmp(a->name, b->name) < 0; });
492  printf(",\"managers\":[");
493  for (i = 0; i < nodes.size(); ++i)
494  {if (i) putchar(',');
495  clnow = nodes[i];
496  if (clnow->nextLvl)
497  {if (*clnow->state) clnow->nextLvl->state = clnow->state;
498  PrintJson(clnow->nextLvl);
499  }
500  else
501  {printf("{\"name\":\"%s\",\"type\":\"manager\"", clnow->name);
502  if (*clnow->state)
503  {st = clnow->state; while (*st == ' ') st++;
504  printf(",\"state\":\"%s\"", st);
505  }
506  putchar('}');
507  }
508  }
509  putchar(']');
510  }
511 
512  putchar('}');
513 }
514 };
515 
516 /******************************************************************************/
517 /* S e t E n v */
518 /******************************************************************************/
519 
520 namespace
521 {
522 int cwValue = 10;
523 int crValue = 0;
524 int trValue = 5;
525 
526 void SetEnv()
527 {
529 
530  env->PutInt("ConnectionWindow", cwValue);
531  env->PutInt("ConnectionRetry", crValue);
532  env->PutInt("TimeoutResolution",trValue);
533 }
534 };
535 
536 /******************************************************************************/
537 /* U s a g e */
538 /******************************************************************************/
539 
540 namespace
541 {
542 void Usage(const char *emsg)
543 {
544  if (emsg) EMSG(emsg);
545  std::cerr <<"Usage: xrdmapc [<opt>] <host>:<port> [<path>]\n"
546  <<"<opt>: [--help] [--format {text|json}] [--list {all|m|s}] [--quiet] [--refresh] [--verify]" <<std::endl;
547  if (!emsg)
548  {std::cerr <<
549 "--format | -f 'text' prints human-readable output (default), 'json' prints\n"
550 " a JSON representation of the cluster topology (managers and\n"
551 " servers arrays are sorted by name).\n"
552 "--list | -l 'all' lists managers and servers (default), 'm' lists only\n"
553 " managers and 's' lists only servers.\n"
554 "--quiet | -q does not print error messages to std::cerr; errors appear inline.\n"
555 "--refresh | -r does not use cached information but will refresh the cache.\n"
556 "--verify | -v verifies <path> existence status at each server.\n"
557 "<path> when specified, uses <host>:<port> to determine the locations\n"
558 " of path and does optional verification."
559  <<std::endl;
560  }
561  exit((emsg ? 1 : 0));
562 }
563 };
564 
565 /******************************************************************************/
566 /* m a i n */
567 /******************************************************************************/
568 
569 int main(int argc, char *argv[])
570 {
571  const char *opLetters = ":f:hl:rv";
572  struct option opVec[] = // For getopt_long()
573  {
574  {OPT_TYPE "format", 1, 0, (int)'f'},
575  {OPT_TYPE "help", 0, 0, (int)'h'},
576  {OPT_TYPE "list", 1, 0, (int)'l'},
577  {OPT_TYPE "quiet", 0, 0, (int)'q'},
578  {OPT_TYPE "refresh", 0, 0, (int)'r'},
579  {OPT_TYPE "verify", 0, 0, (int)'v'},
580  {0, 0, 0, 0}
581  };
582  extern int optind, opterr;
583  extern char *optarg;
584  XrdNetAddr sPoint;
585  clMap *baseNode, *clNow;
586  const char *eMsg;
587  char opC;
588  int i;
589  bool doRefresh = false;
590 
591 // Process options
592 //
593  opterr = 0;
594  optind = 1;
595  while((opC = getopt_long(argc, argv, opLetters, opVec, &i)) != (char)-1)
596  switch(opC)
597  // We implicity silence the errors for the json output since we store
598  // the error state for the site in the state field of the json output
599  {case 'f': if (!strcmp("json",optarg))
600  {doJson = true; doHush = true;}
601  else if (!strcmp("text",optarg))
602  doJson = false;
603  else Usage("Invalid format argument.");
604  break;
605  case 'h': Usage(0);
606  break;
607  case 'l': if (!strcmp("all",optarg))
608  {listMan = true; listSrv = true;}
609  else if (!strcmp("m", optarg))
610  {listMan = true; listSrv = false;}
611  else if (!strcmp("s", optarg))
612  {listMan = false; listSrv = true;}
613  else Usage("Invalid list argument.");
614  break;
615  case 'q': doHush = true;
616  break;
617  case 'r': doRefresh = true;
618  break;
619  case 'v': doVerify = true;
620  break;
621  case ':': EMSG("'" <<OpName(argv) <<"' argument missing.");
622  exit(2); break;
623  case '?': EMSG("Invalid option, '" <<OpName(argv) <<"'.");
624  exit(2); break;
625  default: EMSG("Internal error processing '" <<OpName(argv) <<"'.");
626  exit(2); break;
627  }
628 
629 // Make sure we have a starting point
630 //
631  if (optind >= argc) Usage("Initial node not specified.");
632 
633 // Establish starting point
634 //
635  if ((eMsg = sPoint.Set(argv[optind])))
636  {EMSG("Unable to validate initial node; " <<eMsg);
637  exit(2);
638  }
639 
640 // Make sure it's resolvable
641 //
642  if (!sPoint.Name(0, &eMsg))
643  {EMSG("Unable to resolve " <<argv[optind] <<"; " <<eMsg);
644  exit(2);
645  }
646 
647 // Establish the base node
648 //
649  baseNode = new clMap(argv[optind]);
650 
651 // Check if we will be checking a path
652 //
653  if (optind+1 < argc) Path = argv[optind+1];
654  else doVerify = false;
655 
656 // Set default client values
657 //
658  SetEnv();
659 
660 // Map the cluster
661 //
662  MapCluster(baseNode, baseNode);
663 
664 // Check if we need to do a locate on a file and possibly verify results
665 //
666  if (Path)
667  {MapPath(baseNode, Path, doRefresh);
668  eMsg = (doVerify ? "0*rv* " : "0*r** ");
669  } else eMsg = "0**** ";
670 
671 // Print the output in the selected format
672 //
673  if (doJson)
674  {PrintJson(baseNode);
675  putchar('\n');
676  }
677  else
678  {std::cout <<eMsg <<baseNode->name <<baseNode->state <<std::endl;
679  PrintMap(baseNode, 1);
680  }
681 
682 // Check if we have any phantom nodes
683 //
684  if (Path && clLost)
685  {std::cerr <<"Warning! " <<baseNode->name
686  <<" referred to the following unconnected node:" <<std::endl;
687  clNow = clLost;
688  while(clNow)
689  {std::cerr <<"????? " <<clNow->name <<std::endl;
690  clNow = clNow->nextSrv;
691  }
692  }
693 
694 // All done
695 //
696  exit(0);
697 }
@ kXR_NotAuthorized
Definition: XProtocol.hh:1042
@ kXR_NotFound
Definition: XProtocol.hh:1043
@ kXR_NotFile
Definition: XProtocol.hh:1047
@ kXR_IOError
Definition: XProtocol.hh:1039
@ kXR_FSError
Definition: XProtocol.hh:1037
@ kXR_NoMemory
Definition: XProtocol.hh:1040
void Usage(const char *msg)
Definition: XrdAccTest.cc:105
int main(int argc, char *argv[])
int optopt
#define OPT_TYPE
int optind
#define EMSG(x)
@ Hash_keep
Definition: XrdOucHash.hh:55
XrdOucString Path
#define eMsg(x)
int emsg(int rc, char *msg)
static Env * GetEnv()
Get default client environment.
bool PutInt(const std::string &key, int value)
Definition: XrdClEnv.cc:136
Send file/filesystem queries to an XRootD cluster.
Path location info.
Iterator Begin()
Get the location begin iterator.
LocationType
Describes the node type and file status for a given location.
@ ServerPending
server node where the file is pending to be online
@ ServerOnline
server node where the file is online
LocationList::iterator Iterator
Iterator over locations.
Iterator End()
Get the location end iterator.
Object stat info.
URL representation.
Definition: XrdClURL.hh:31
std::string ToStr() const
Convert to string.
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
const char * Name(const char *eName=0, const char **eText=0)
const char * Set(const char *hSpec, int pNum=PortInSpec)
Definition: XrdNetAddr.cc:216
const uint16_t errInvalidAddr
Definition: XrdClStatus.hh:71
const uint16_t errStreamDisconnect
Definition: XrdClStatus.hh:77
const uint16_t errRedirectLimit
Definition: XrdClStatus.hh:102
const uint16_t errErrorResponse
Definition: XrdClStatus.hh:105
const uint16_t errOperationExpired
Definition: XrdClStatus.hh:90
const uint16_t errLoginFailed
Definition: XrdClStatus.hh:87
const uint16_t errSocketTimeout
Definition: XrdClStatus.hh:73
const uint16_t errHandShakeFailed
Definition: XrdClStatus.hh:86
const uint16_t errConnectionError
Definition: XrdClStatus.hh:78
const uint16_t errSocketError
Definition: XrdClStatus.hh:72
const uint16_t errSocketDisconnected
Definition: XrdClStatus.hh:74
const uint16_t errAuthFailed
Definition: XrdClStatus.hh:88
Flags
Open flags, may be or'd when appropriate.
uint16_t code
Error type, or additional hints on what to do.
Definition: XrdClStatus.hh:147
bool IsOK() const
We're fine.
Definition: XrdClStatus.hh:124
uint32_t errNo
Errno, if any.
Definition: XrdClStatus.hh:148