XRootD
XrdOucUtils.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d O u c U t i l s . c c */
4 /* */
5 /* (c) 2005 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 <cctype>
32 #include <grp.h>
33 #include <cstdio>
34 #include <list>
35 #include <vector>
36 #include <unordered_set>
37 #include <algorithm>
38 #include <charconv>
39 #include <random>
40 
41 #include <regex.h>
42 
43 #ifdef WIN32
44 #include <direct.h>
45 #include "XrdSys/XrdWin32.hh"
46 #else
47 #include <fcntl.h>
48 #include <math.h>
49 #include <pwd.h>
50 #include <sys/stat.h>
51 #include <sys/types.h>
52 #endif
53 #include <map>
54 #include <iomanip>
55 #include "XrdNet/XrdNetUtils.hh"
56 #include "XrdOuc/XrdOucCRC.hh"
57 #include "XrdOuc/XrdOucEnv.hh"
58 #include "XrdOuc/XrdOucSHA3.hh"
59 #include "XrdOuc/XrdOucStream.hh"
60 #include "XrdOuc/XrdOucString.hh"
61 #include "XrdOuc/XrdOucUtils.hh"
63 #include "XrdSys/XrdSysE2T.hh"
64 #include "XrdSys/XrdSysError.hh"
65 #include "XrdSys/XrdSysPlatform.hh"
66 #include "XrdSys/XrdSysPthread.hh"
67 
68 #ifndef ENODATA
69 #define ENODATA ENOATTR
70 #endif
71 
72 /******************************************************************************/
73 /* L o c a l M e t h o d s */
74 /******************************************************************************/
75 
76 namespace
77 {
78 struct idInfo
79 { time_t Expr;
80  char *Name;
81 
82  idInfo(const char *name, time_t keep)
83  : Expr(time(0)+keep), Name(strdup(name)) {}
84  ~idInfo() {free(Name);}
85 };
86 
87 typedef std::map<unsigned int, struct idInfo*> idMap_t;
88 
89 idMap_t gidMap;
90 idMap_t uidMap;
91 XrdSysMutex idMutex;
92 
93 void AddID(idMap_t &idMap, unsigned int id, const char *name, time_t keepT)
94 {
95  std::pair<idMap_t::iterator,bool> ret;
96  idInfo *infoP = new idInfo(name, keepT);
97 
98  idMutex.Lock();
99  ret = idMap.insert(std::pair<unsigned int, struct idInfo*>(id, infoP));
100  if (ret.second == false) delete infoP;
101  idMutex.UnLock();
102 }
103 
104 int LookUp(idMap_t &idMap, unsigned int id, char *buff, int blen)
105 {
106  idMap_t::iterator it;
107  int luRet = 0;
108 
109  idMutex.Lock();
110  it = idMap.find(id);
111  if (it != idMap.end())
112  {if (it->second->Expr <= time(0))
113  {delete it->second;
114  idMap.erase(it);
115  } else {
116  if (blen > 0) luRet = snprintf(buff, blen, "%s", it->second->Name);
117  }
118  }
119  idMutex.UnLock();
120  return luRet;
121 }
122 }
123 
124 /******************************************************************************/
125 /* a r g L i s t */
126 /******************************************************************************/
127 
128 int XrdOucUtils::argList(char *args, char **argV, int argC)
129 {
130  char *aP = args;
131  int j;
132 
133 // Construct the argv array based on passed command line.
134 //
135 for (j = 0; j < argC; j++)
136  {while(*aP == ' ') aP++;
137  if (!(*aP)) break;
138 
139  if (*aP == '"' || *aP == '\'')
140  {argV[j] = aP+1;
141  aP = index(aP+1, *aP);
142  if (!aP || (*(aP+1) != ' ' && *(aP+1)))
143  {if (!j) argV[0] = 0; return -EINVAL;}
144  *aP++ = '\0';
145  } else {
146  argV[j] = aP;
147  if ((aP = index(aP+1, ' '))) *aP++ = '\0';
148  else {j++; break;}
149  }
150 
151  }
152 
153 // Make sure we did not overflow the vector
154 //
155  if (j > argC-1) return -E2BIG;
156 
157 // End list with a null pointer and return the actual number of arguments
158 //
159  argV[j] = 0;
160  return j;
161 }
162 
163 /******************************************************************************/
164 /* b i n 2 h e x */
165 /******************************************************************************/
166 
167 char *XrdOucUtils::bin2hex(char *inbuff, int dlen, char *buff, int blen,
168  bool sep)
169 {
170  static const char hv[] = "0123456789abcdef";
171  char *outbuff = buff;
172  for (int i = 0; i < dlen && blen > 2; i++) {
173  *outbuff++ = hv[(inbuff[i] >> 4) & 0x0f];
174  *outbuff++ = hv[ inbuff[i] & 0x0f];
175  blen -= 2;
176  if (sep && blen > 1 && ((i & 0x03) == 0x03 || i+1 == dlen))
177  {*outbuff++ = ' '; blen--;}
178  }
179  *outbuff = '\0';
180  return buff;
181 }
182 
183 /******************************************************************************/
184 /* h e x 2 b i n */
185 /******************************************************************************/
186 
187 int XrdOucUtils::hex2bin(const char *hex, char *bin, int size)
188 {
189  int len = 0, lo = -1, hi = -1;
190 
191  while (int c = *hex++) {
192  if (c == ' ' || c == '\n')
193  break;
194 
195  switch (c) {
196  case '0' ... '9':
197  c -= '0';
198  break;
199  case 'a' ... 'f':
200  c -= 'a' - 10;
201  break;
202  case 'A' ... 'F':
203  c -= 'A' - 10;
204  break;
205  default:
206  return -EINVAL;
207  }
208 
209  if (hi == -1)
210  hi = c;
211  else
212  lo = c;
213 
214  if (lo == -1)
215  continue;
216 
217  if (len >= size)
218  return -EOVERFLOW;
219 
220  bin[len++] = (hi << 4) | lo;
221  hi = lo = -1;
222  }
223  if (hi >= 0 || lo >= 0 || len == 0)
224  return -EINVAL;
225  return len;
226 }
227 
228 /******************************************************************************/
229 /* e n d s W i t h */
230 /******************************************************************************/
231 
232 bool XrdOucUtils::endsWith(const char *text, const char *ending, int endlen)
233 {
234  int tlen = strlen(text);
235 
236  return (tlen >= endlen && !strcmp(text+(tlen-endlen), ending));
237 }
238 
239 /******************************************************************************/
240 /* e T e x t */
241 /******************************************************************************/
242 
243 // eText() returns the text associated with the error.
244 // The text buffer pointer is returned.
245 
246 char *XrdOucUtils::eText(int rc, char *eBuff, int eBlen)
247 {
248  const char *etP;
249 
250 // Get error text
251 //
252  etP = XrdSysE2T(rc);
253 
254 // Copy the text and lower case the first letter
255 //
256  strlcpy(eBuff, etP, eBlen);
257 
258 // All done
259 //
260  return eBuff;
261 }
262 
263 /******************************************************************************/
264 /* d o I f */
265 /******************************************************************************/
266 
267 // doIf() parses "if [<hostlist>] [<conds>]"
268 // conds: <cond1> | <cond2> | <cond3>
269 // cond1: defined <varlist> [&& <conds>]
270 // cond2: exec <pgmlist> [&& <cond3>]
271 // cond3: named <namelist>
272 
273 // Returning 1 if true (i.e., this machine is one of the named hosts in hostlist
274 // and is running one of the programs pgmlist and named by one of the names in
275 // namelist).
276 // Return -1 (negative truth) if an error occurred.
277 // Otherwise, returns false (0). Some combination of hostlist, pgm, and
278 // namelist, must be specified.
279 
281  const char *what, const char *hname,
282  const char *nname, const char *pname)
283 {
284  static const char *brk[] = {"defined", "exec", "named", 0};
285  XrdOucEnv *theEnv = 0;
286  char *val;
287  int hostok, isDef;
288 
289 // Make sure that at least one thing appears after the if
290 //
291  if (!(val = Config.GetWord()))
292  {if (eDest) eDest->Emsg("Config","Host name missing after 'if' in", what);
293  return -1;
294  }
295 
296 // Check if we are one of the listed hosts
297 //
298  if (!is1of(val, brk))
299  {do {hostok = XrdNetUtils::Match(hname, val);
300  val = Config.GetWord();
301  } while(!hostok && val && !is1of(val, brk));
302  if (hostok)
303  { while(val && !is1of(val, brk)) val = Config.GetWord();
304  // No more directives
305  if (!val) return 1;
306  } else return 0;
307  }
308 
309 // Check if this is a defined test
310 //
311  while(!strcmp(val, "defined"))
312  {if (!(val = Config.GetWord()) || *val != '?')
313  {if (eDest)
314  {eDest->Emsg("Config","'?var' missing after 'defined' in",what);}
315  return -1;
316  }
317  // Get environment if we have none
318  //
319  if (!theEnv && (theEnv = Config.SetEnv(0))) Config.SetEnv(theEnv);
320  if (!theEnv && *(val+1) != '~') return 0;
321 
322  // Check if any listed variable is defined.
323  //
324  isDef = 0;
325  while(val && *val == '?')
326  {if (*(val+1) == '~' ? getenv(val+2) : theEnv->Get(val+1)) isDef=1;
327  val = Config.GetWord();
328  }
329  if (!val || !isDef) return isDef;
330  if (strcmp(val, "&&"))
331  {if (eDest)
332  {eDest->Emsg("Config",val,"is invalid for defined test in",what);}
333  return -1;
334  } else {
335  if (!(val = Config.GetWord()))
336  {if (eDest)
337  {eDest->Emsg("Config","missing keyword after '&&' in",what);}
338  return -1;
339  }
340  }
341  if (!is1of(val, brk))
342  {if (eDest)
343  {eDest->Emsg("Config",val,"is invalid after '&&' in",what);}
344  return -1;
345  }
346  }
347 
348 // Check if we need to compare program names (we are here only if we either
349 // passed the hostlist test or there was no hostlist present)
350 //
351  if (!strcmp(val, "exec"))
352  {if (!(val = Config.GetWord()) || !strcmp(val, "&&"))
353  {if (eDest)
354  {eDest->Emsg("Config","Program name missing after 'if exec' in",what);}
355  return -1;
356  }
357 
358  // Check if we are one of the programs.
359  //
360  if (!pname) return 0;
361  while(val && strcmp(val, pname))
362  if (!strcmp(val, "&&")) return 0;
363  else val = Config.GetWord();
364  if (!val) return 0;
365  while(val && strcmp(val, "&&")) val = Config.GetWord();
366  if (!val) return 1;
367 
368  if (!(val = Config.GetWord()))
369  {if (eDest)
370  {eDest->Emsg("Config","Keyword missing after '&&' in",what);}
371  return -1;
372  }
373  if (strcmp(val, "named"))
374  {if (eDest)
375  {eDest->Emsg("Config",val,"is invalid after '&&' in",what);}
376  return -1;
377  }
378  }
379 
380 // Check if we need to compare net names (we are here only if we either
381 // passed the hostlist test or there was no hostlist present)
382 //
383  if (!(val = Config.GetWord()))
384  {if (eDest)
385  {eDest->Emsg("Config","Instance name missing after 'if named' in", what);}
386  return -1;
387  }
388 
389 // Check if we are one of the names
390 //
391  if (!nname) return 0;
392  while(val && strcmp(val, nname)) val = Config.GetWord();
393 
394 // All done
395 //
396  return (val != 0);
397 }
398 
399 /******************************************************************************/
400 /* f i n d P g m */
401 /******************************************************************************/
402 
403 bool XrdOucUtils::findPgm(const char *pgm, XrdOucString& path)
404 {
405  struct stat Stat;
406 
407 // Check if only executable bit needs to be checked
408 //
409  if (*pgm == '/')
410  {if (stat(pgm, &Stat) || !(Stat.st_mode & S_IXOTH)) return false;
411  path = pgm;
412  return true;
413  }
414 
415 // Make sure we have the paths to check
416 //
417  const char *pEnv = getenv("PATH");
418  if (!pEnv) return false;
419 
420 // Setup to find th executable
421 //
422  XrdOucString prog, pList(pEnv);
423  int from = 0;;
424  prog += '/'; prog += pgm;
425 
426 // Find it!
427 //
428  while((from = pList.tokenize(path, from, ':')) != -1)
429  {path += prog;
430  if (!stat(path.c_str(), &Stat) && Stat.st_mode & S_IXOTH) return true;
431  }
432  return false;
433 }
434 
435 /******************************************************************************/
436 /* f m t B y t e s */
437 /******************************************************************************/
438 
439 int XrdOucUtils::fmtBytes(long long val, char *buff, int bsz)
440 {
441  static const long long Kval = 1024LL;
442  static const long long Mval = 1024LL*1024LL;
443  static const long long Gval = 1024LL*1024LL*1024LL;
444  static const long long Tval = 1024LL*1024LL*1024LL*1024LL;
445  char sName = ' ';
446  int resid;
447 
448 // Get correct scaling
449 //
450  if (val < 1024) return snprintf(buff, bsz, "%lld", val);
451  if (val < Mval) {val = val*10/Kval; sName = 'K';}
452  else if (val < Gval) {val = val*10/Mval; sName = 'M';}
453  else if (val < Tval) {val = val*10/Gval; sName = 'G';}
454  else {val = val*10/Tval; sName = 'T';}
455  resid = val%10LL; val = val/10LL;
456 
457 // Format it
458 //
459  return snprintf(buff, bsz, "%lld.%d%c", val, resid, sName);
460 }
461 
462 std::string XrdOucUtils::genHumanSize(size_t size, uint64_t base) {
463  static const char* units[] = {"", "K", "M", "G", "T", "P", "E"};
464  const int maxUnit = 6;
465 
466  double value = static_cast<double>(size);
467  int unitIndex = 0;
468 
469  while (value >= static_cast<double>(base) && unitIndex < maxUnit) {
470  value /= static_cast<double>(base);
471  ++unitIndex;
472  }
473 
474  auto ceil_to = [](double x, int decimals) {
475  if (x == 0.0) return 0.0; // avoid creating a -0 in the output
476  const double p = std::pow(10.0, decimals);
477  // small epsilon to avoid 1.0 becoming 1.1 due to floating noise
478  return std::ceil(x * p - 1e-12) / p;
479  };
480 
481  auto decimals_for = [](double x, int u) {
482  return (u > 0 && x < 10.0) ? 1 : 0; // e.g: 1.0G for exact powers
483  };
484 
485  int prec = decimals_for(value, unitIndex);
486  double shown = ceil_to(value, prec);
487 
488  // If rounding pushed us to the next unit (e.g. 1024.0K), promote
489  if (shown >= static_cast<double>(base) && unitIndex < maxUnit) {
490  shown /= static_cast<double>(base);
491  ++unitIndex;
492 
493  prec = decimals_for(shown, unitIndex);
494  shown = ceil_to(shown, prec);
495  }
496 
497  std::ostringstream out;
498  out << std::fixed << std::setprecision(prec) << shown << units[unitIndex];
499  return out.str();
500 }
501 
502 /******************************************************************************/
503 /* g e n P a t h */
504 /******************************************************************************/
505 
506 char *XrdOucUtils::genPath(const char *p_path, const char *inst,
507  const char *s_path)
508 {
509  char buff[2048];
510  int i = strlcpy(buff, p_path, sizeof(buff));
511 
512  if (buff[i-1] != '/') {buff[i++] = '/'; buff[i] = '\0';}
513  if (inst) {strcpy(buff+i, inst); strcat(buff, "/");}
514  if (s_path) strcat(buff, s_path);
515 
516  i = strlen(buff);
517  if (buff[i-1] != '/') {buff[i++] = '/'; buff[i] = '\0';}
518 
519  return strdup(buff);
520 }
521 
522 /******************************************************************************/
523 
524 int XrdOucUtils::genPath(char *buff, int blen, const char *path, const char *psfx)
525 {
526  int i, j;
527 
528  i = strlen(path);
529  j = (psfx ? strlen(psfx) : 0);
530  if (i+j+3 > blen) return -ENAMETOOLONG;
531 
532  strcpy(buff, path);
533  if (psfx)
534  {if (buff[i-1] != '/') buff[i++] = '/';
535  strcpy(&buff[i], psfx);
536  if (psfx[j-1] != '/') strcat(buff, "/");
537  }
538  return 0;
539 }
540 
541 /******************************************************************************/
542 /* g e t F i l e */
543 /******************************************************************************/
544 
545 char *XrdOucUtils::getFile(const char *path, int &rc, int maxsz, bool notempty)
546 {
547  struct stat Stat;
548  struct fdHelper
549  {int fd = -1;
550  fdHelper() {}
551  ~fdHelper() {if (fd >= 0) close(fd);}
552  } file;
553  char *buff;
554  int flen;
555 
556 // Preset RC
557 //
558  rc = 0;
559 
560 // Open the file in read mode
561 //
562  if ((file.fd = open(path, O_RDONLY)) < 0) {rc = errno; return 0;}
563 
564 // Get the size of the file
565 //
566  if (fstat(file.fd, &Stat)) {rc = errno; return 0;}
567 
568 // Check if the size exceeds the maximum allowed
569 //
570  if (Stat.st_size > maxsz) {rc = EFBIG; return 0;}
571 
572 // Make sure the file is not empty if empty files are disallowed
573 //
574  if (Stat.st_size == 0 && notempty) {rc = ENODATA; return 0;}
575 
576 // Allocate a buffer
577 //
578  if ((buff = (char *)malloc(Stat.st_size+1)) == 0)
579  {rc = errno; return 0;}
580 
581 // Read the contents of the file into the buffer
582 //
583  if (Stat.st_size)
584  {if ((flen = read(file.fd, buff, Stat.st_size)) < 0)
585  {rc = errno; free(buff); return 0;}
586  } else flen = 0;
587 
588 // Add null byte. recall the buffer is bigger by one byte
589 //
590  buff[flen] = 0;
591 
592 // Return the size aand the buffer
593 //
594  rc = flen;
595  return buff;
596 }
597 
598 /******************************************************************************/
599 /* g e t G I D */
600 /******************************************************************************/
601 
602 bool XrdOucUtils::getGID(const char *gName, gid_t &gID)
603 {
604  struct group Grp, *result;
605  char buff[65536];
606 
607  getgrnam_r(gName, &Grp, buff, sizeof(buff), &result);
608  if (!result) return false;
609 
610  gID = Grp.gr_gid;
611  return true;
612 }
613 
614 /******************************************************************************/
615 /* g e t U I D */
616 /******************************************************************************/
617 
618 bool XrdOucUtils::getUID(const char *uName, uid_t &uID, gid_t *gID)
619 {
620  struct passwd pwd, *result;
621  char buff[16384];
622 
623  getpwnam_r(uName, &pwd, buff, sizeof(buff), &result);
624  if (!result) return false;
625 
626  uID = pwd.pw_uid;
627  if (gID) *gID = pwd.pw_gid;
628 
629  return true;
630 }
631 
632 /******************************************************************************/
633 /* G i d N a m e */
634 /******************************************************************************/
635 
636 int XrdOucUtils::GidName(gid_t gID, char *gName, int gNsz, time_t keepT)
637 {
638  static const int maxgBsz = 256*1024;
639  static const int addGsz = 4096;
640  struct group *gEnt, gStruct;
641  char gBuff[1024], *gBp = gBuff;
642  int glen = 0, gBsz = sizeof(gBuff), aOK = 1;
643  int n, retVal = 0;
644 
645 // Get ID from cache, if allowed
646 //
647  if (keepT)
648  {int n = LookUp(gidMap, static_cast<unsigned int>(gID),gName,gNsz);
649  if (n > 0) return (n < gNsz ? n : 0);
650  }
651 
652 // Get the the group struct. If we don't have a large enough buffer, get a
653 // larger one and try again up to the maximum buffer we will tolerate.
654 //
655  while(( retVal = getgrgid_r(gID, &gStruct, gBp, gBsz, &gEnt) ) == ERANGE)
656  {if (gBsz >= maxgBsz) {aOK = 0; break;}
657  if (gBsz > addGsz) free(gBp);
658  gBsz += addGsz;
659  if (!(gBp = (char *)malloc(gBsz))) {aOK = 0; break;}
660  }
661 
662 // Return a group name if all went well
663 //
664  if (aOK && retVal == 0 && gEnt != NULL)
665  {if (keepT)
666  AddID(gidMap, static_cast<unsigned int>(gID), gEnt->gr_name, keepT);
667  glen = strlen(gEnt->gr_name);
668  if (glen >= gNsz) glen = 0;
669  else strcpy(gName, gEnt->gr_name);
670  } else {
671  n = snprintf(gName, gNsz, "%ud", static_cast<unsigned int>(gID));
672  if (n >= gNsz) glen = 0;
673  }
674 
675 // Free any allocated buffer and return result
676 //
677  if (gBsz > addGsz && gBp) free(gBp);
678  return glen;
679 }
680 
681 /******************************************************************************/
682 /* G r o u p N a m e */
683 /******************************************************************************/
684 
685 int XrdOucUtils::GroupName(gid_t gID, char *gName, int gNsz)
686 {
687  static const int maxgBsz = 256*1024;
688  static const int addGsz = 4096;
689  struct group *gEnt, gStruct;
690  char gBuff[1024], *gBp = gBuff;
691  int glen, gBsz = sizeof(gBuff), aOK = 1;
692  int retVal = 0;
693 
694 // Get the the group struct. If we don't have a large enough buffer, get a
695 // larger one and try again up to the maximum buffer we will tolerate.
696 //
697  while(( retVal = getgrgid_r(gID, &gStruct, gBp, gBsz, &gEnt) ) == ERANGE)
698  {if (gBsz >= maxgBsz) {aOK = 0; break;}
699  if (gBsz > addGsz) free(gBp);
700  gBsz += addGsz;
701  if (!(gBp = (char *)malloc(gBsz))) {aOK = 0; break;}
702  }
703 
704 // Return a group name if all went well
705 //
706  if (aOK && retVal == 0 && gEnt != NULL)
707  {glen = strlen(gEnt->gr_name);
708  if (glen >= gNsz) glen = 0;
709  else strcpy(gName, gEnt->gr_name);
710  } else glen = 0;
711 
712 // Free any allocated buffer and return result
713 //
714  if (gBsz > addGsz && gBp) free(gBp);
715  return glen;
716 }
717 
718 /******************************************************************************/
719 /* H S i z e */
720 /******************************************************************************/
721 
722 const char *XrdOucUtils::HSize(size_t bytes, char* buff, int bsz)
723 {
724 
725 // Do fast conversion of the quantity is less than 1K
726 //
727  if (bytes < 1024)
728  {snprintf(buff, bsz, "%zu", bytes);
729  return buff;
730  }
731 
732 // Scale this down
733 //
734  const char *suffix = " KMGTPEYZ";
735  double dBytes = static_cast<double>(bytes);
736 
737 do{dBytes /= 1024.0; suffix++;
738  } while(dBytes >= 1024.0 && *(suffix+1));
739 
740 
741 // Format and return result. Include fractions only if they meaningfully exist.
742 //
743  double whole, frac = modf(dBytes, &whole);
744  if (frac >= .005) snprintf(buff, bsz, "%.02lf%c", dBytes, *suffix);
745  else snprintf(buff, bsz, "%g%c", whole, *suffix);
746  return buff;
747 }
748 
749 /******************************************************************************/
750 /* i 2 b s t r */
751 /******************************************************************************/
752 
753 const char *XrdOucUtils::i2bstr(char *buff, int blen, int val, bool pad)
754 {
755  char zo[2] = {'0', '1'};
756 
757  if (blen < 2) return "";
758 
759  buff[--blen] = 0;
760  if (!val) buff[blen--] = '0';
761  else while(val && blen >= 0)
762  {buff[blen--] = zo[val & 0x01];
763  val >>= 1;
764  }
765 
766  if (blen >= 0 && pad) while(blen >= 0) buff[blen--] = '0';
767 
768  return &buff[blen+1];
769 }
770 
771 /******************************************************************************/
772 /* I d e n t */
773 /******************************************************************************/
774 
775 namespace
776 {
777 long long genSID(char *&urSID, const char *iHost, int iPort,
778  const char *iName, const char *iProg)
779 {
780  static const XrdOucSHA3::MDLen mdLen = XrdOucSHA3::SHA3_512;
781  static const uint32_t fpOffs = 2, fpSize = 6; // 48 bit finger print
782 
783  const char *iSite = getenv("XRDSITE");
784  unsigned char mDigest[mdLen];
785  XrdOucString myID;
786  union {uint64_t mdLL; unsigned char mdUC[8];}; // Works for fpSize only!
787 
788 // Construct our unique identification
789 //
790  if (iSite) myID = iSite;
791  myID += iHost;
792  myID += iPort;
793  if (iName) myID += iName;
794  myID += iProg;
795 
796 // Generate a SHA3 digest of this string.
797 //
798  memset(mDigest, 0, sizeof(mDigest));
799  XrdOucSHA3::Calc(myID.c_str(), myID.length(), mDigest, mdLen);
800 
801 // Generate a CRC32C of the same string
802 //
803  uint32_t crc32c = XrdOucCRC::Calc32C(myID.c_str(), myID.length());
804 
805 // We need a 48-bit fingerprint that has a very low probability of collision.
806 // We accomplish this by convoluting the CRC32C checksum with the SHA3 checksum.
807 //
808  uint64_t fpPos = crc32c % (((uint32_t)mdLen) - fpSize);
809  mdLL = 0;
810  memcpy(mdUC+fpOffs, mDigest+fpPos, fpSize);
811  long long fpVal = static_cast<long long>(ntohll(mdLL));
812 
813 // Generate the character version of our fingerprint and return the binary one.
814 //
815  char fpBuff[64];
816  snprintf(fpBuff, sizeof(fpBuff), "%lld", fpVal);
817  urSID = strdup(fpBuff);
818  return fpVal;
819 }
820 }
821 
822 char *XrdOucUtils::Ident(long long &mySID, char *iBuff, int iBlen,
823  const char *iHost, const char *iProg,
824  const char *iName, int iPort)
825 {
826  static char *theSIN;
827  static long long theSID = genSID(theSIN, iHost, iPort, iName, iProg);
828  const char *sP = getenv("XRDSITE");
829  char uName[256];
830  int myPid = static_cast<int>(getpid());
831 
832 // Get our username
833 //
834  if (UserName(getuid(), uName, sizeof(uName)))
835  sprintf(uName, "%d", static_cast<int>(getuid()));
836 
837 // Create identification record
838 //
839  snprintf(iBuff,iBlen,"%s.%d:%s@%s\n&site=%s&port=%d&inst=%s&pgm=%s",
840  uName, myPid, theSIN, iHost, (sP ? sP : ""), iPort, iName, iProg);
841 
842 // Return a copy of the sid key
843 //
844  h2nll(theSID, mySID);
845  return strdup(theSIN);
846 }
847 
848 /******************************************************************************/
849 /* I n s t N a m e */
850 /******************************************************************************/
851 
852 const char *XrdOucUtils::InstName(int TranOpt)
853 {
854  const char *iName = getenv("XRDNAME");
855 
856 // If tran is zero, return what we have
857 //
858  if (!TranOpt) return iName;
859 
860 // If trans is positive then make sure iName has a value. Otherwise, make sure
861 // iName has no value if it's actually "anon".
862 //
863  if (TranOpt > 0) {if (!iName || !*iName) iName = "anon";}
864  else if (iName && !strcmp(iName, "anon")) iName = 0;
865  return iName;
866 }
867 /******************************************************************************/
868 
869 const char *XrdOucUtils::InstName(const char *name, int Fillit)
870 { return (Fillit ? name && *name ? name : "anon"
871  : name && strcmp(name,"anon") && *name ? name : 0);
872 }
873 
874 /******************************************************************************/
875 /* i s 1 o f */
876 /******************************************************************************/
877 
878 int XrdOucUtils::is1of(char *val, const char **clist)
879 {
880  int i = 0;
881  while(clist[i]) if (!strcmp(val, clist[i])) return 1;
882  else i++;
883  return 0;
884 }
885 
886 /******************************************************************************/
887 /* i s F W D */
888 /******************************************************************************/
889 
890 int XrdOucUtils::isFWD(const char *path, int *port, char *hBuff, int hBLen,
891  bool pTrim)
892 {
893  const char *hName, *hNend, *hPort, *hPend, *hP = path;
894  char *eP;
895  int n;
896 
897  if (*path == '/') hP++; // Note: It's assumed an objectid if no slash
898  if (*hP == 'x') hP++;
899  if (strncmp("root:/", hP, 6)) return 0;
900  if (hBuff == 0 || hBLen <= 0) return (hP - path) + 6;
901  hP += 6;
902 
903  if (!XrdNetUtils::Parse(hP, &hName, &hNend, &hPort, &hPend)) return 0;
904  if (*hNend == ']') hNend++;
905  else {if (!(*hNend) && !(hNend = index(hName, '/'))) return 0;
906  if (!(*hPend)) hPend = hNend;
907  }
908 
909  if (pTrim || !(*hPort)) n = hNend - hP;
910  else n = hPend - hP;
911  if (n >= hBLen) return 0;
912  strncpy(hBuff, hP, n);
913  hBuff[n] = 0;
914 
915  if (port)
916  {if (*hNend != ':') *port = 0;
917  else {*port = strtol(hPort, &eP, 10);
918  if (*port < 0 || *port > 65535 || eP != hPend) return 0;
919  }
920  }
921 
922  return hPend-path;
923 }
924 
925 /******************************************************************************/
926 /* L o g 2 */
927 /******************************************************************************/
928 
929 // Based on an algorithm produced by Todd Lehman. However, this one returns 0
930 // when passed 0 (which is invalid). The caller must check the validity of
931 // the input prior to calling Log2(). Essentially, the algorithm subtracts
932 // away progressively smaller squares in the sequence
933 // { 0 <= k <= 5: 2^(2^k) } = { 2**32, 2**16, 2**8 2**4 2**2, 2**1 } =
934 // = { 4294967296, 65536, 256, 16, 4, 2 }
935 // and sums the exponents k of the subtracted values. It is generally the
936 // fastest way to compute log2 for a wide range of possible input values.
937 
938 int XrdOucUtils::Log2(unsigned long long n)
939 {
940  int i = 0;
941 
942  #define SHFT(k) if (n >= (1ULL << k)) { i += k; n >>= k; }
943 
944  SHFT(32); SHFT(16); SHFT(8); SHFT(4); SHFT(2); SHFT(1); return i;
945 
946  #undef SHFT
947 }
948 
949 /******************************************************************************/
950 /* L o g 1 0 */
951 /******************************************************************************/
952 
953 int XrdOucUtils::Log10(unsigned long long n)
954 {
955  int i = 0;
956 
957  #define SHFT(k, m) if (n >= m) { i += k; n /= m; }
958 
959  SHFT(16,10000000000000000ULL); SHFT(8,100000000ULL);
960  SHFT(4,10000ULL); SHFT(2,100ULL); SHFT(1,10ULL);
961  return i;
962 
963  #undef SHFT
964 }
965 
966 /******************************************************************************/
967 /* m a k e H o m e */
968 /******************************************************************************/
969 
970 void XrdOucUtils::makeHome(XrdSysError &eDest, const char *inst)
971 {
972  char buff[2048];
973 
974  if (!inst || !getcwd(buff, sizeof(buff))) return;
975 
976  strcat(buff, "/"); strcat(buff, inst);
977  if (MAKEDIR(buff, pathMode) && errno != EEXIST)
978  {eDest.Emsg("Config", errno, "create home directory", buff);
979  return;
980  }
981 
982  if (chdir(buff) < 0)
983  eDest.Emsg("Config", errno, "chdir to home directory", buff);
984 }
985 
986 /******************************************************************************/
987 
988 bool XrdOucUtils::makeHome(XrdSysError &eDest, const char *inst,
989  const char *path, mode_t mode)
990 {
991  char cwDir[2048];
992  const char *slash = "", *slash2 = "";
993  int n, rc;
994 
995 // Provide backward compatibility for instance name qualification
996 //
997 
998  if (!path || !(n = strlen(path)))
999  {if (inst) makeHome(eDest, inst);
1000  return true;
1001  }
1002 
1003 // Augment the path with instance name, if need be
1004 //
1005  if (path[n-1] != '/') slash = "/";
1006  if (!inst || !(n = strlen(inst))) inst = "";
1007  else slash2 = "/";
1008  n = snprintf(cwDir, sizeof(cwDir), "%s%s%s%s", path, slash, inst, slash2);
1009  if (n >= (int)sizeof(cwDir))
1010  {eDest.Emsg("Config", ENAMETOOLONG, "create home directory", cwDir);
1011  return false;
1012  }
1013 
1014 // Create the path if it doesn't exist
1015 //
1016  if ((rc = makePath(cwDir, mode, true)))
1017  {eDest.Emsg("Config", rc, "create home directory", cwDir);
1018  return false;
1019  }
1020 
1021 // Switch to this directory
1022 //
1023  if (chdir(cwDir) < 0)
1024  {eDest.Emsg("Config", errno, "chdir to home directory", cwDir);
1025  return false;
1026  }
1027 
1028 // All done
1029 //
1030  return true;
1031 }
1032 
1033 /******************************************************************************/
1034 /* m a k e P a t h */
1035 /******************************************************************************/
1036 
1037 int XrdOucUtils::makePath(char *path, mode_t mode, bool reset)
1038 {
1039  char *next_path = path+1;
1040  struct stat buf;
1041  bool dochmod = false; // The 1st component stays as is
1042 
1043 // Typically, the path exists. So, do a quick check before launching into it
1044 //
1045  if (!reset && !stat(path, &buf)) return 0;
1046 
1047 // Start creating directories starting with the root
1048 //
1049  while((next_path = index(next_path, int('/'))))
1050  {*next_path = '\0';
1051  if (MAKEDIR(path, mode))
1052  if (errno != EEXIST) return -errno;
1053  if (dochmod) CHMOD(path, mode);
1054  dochmod = reset;
1055  *next_path = '/';
1056  next_path = next_path+1;
1057  }
1058 
1059 // All done
1060 //
1061  return 0;
1062 }
1063 
1064 /******************************************************************************/
1065 /* m o d e 2 m a s k */
1066 /******************************************************************************/
1067 
1068 bool XrdOucUtils::mode2mask(const char *mode, mode_t &mask)
1069 {
1070  mode_t mval[3] = {0}, mbit[3] = {0x04, 0x02, 0x01};
1071  const char *mok = "rwx";
1072  char mlet;
1073 
1074 // Accept octal mode
1075 //
1076  if (isdigit(*mode))
1077  {char *eP;
1078  mask = strtol(mode, &eP, 8);
1079  return *eP == 0;
1080  }
1081 
1082 // Make sure we have the correct number of characters
1083 //
1084  int n = strlen(mode);
1085  if (!n || n > 9 || n/3*3 != n) return false;
1086 
1087 // Convert groups of three
1088 //
1089  int k = 0;
1090  do {for (int i = 0; i < 3; i++)
1091  {mlet = *mode++;
1092  if (mlet != '-')
1093  {if (mlet != mok[i]) return false;
1094  mval[k] |= mbit[i];
1095  }
1096  }
1097  } while(++k < 3 && *mode);
1098 
1099 // Combine the modes and return success
1100 //
1101  mask = mval[0]<<6 | mval[1]<<3 | mval[2];
1102  return true;
1103 }
1104 
1105 /******************************************************************************/
1106 /* p a r s e L i b */
1107 /******************************************************************************/
1108 
1110  const char *libName, char *&libPath, char **libParm)
1111 {
1112  char *val, parms[2048];
1113 
1114 // Get the next token
1115 //
1116  val = Config.GetWord();
1117 
1118 // We do not support stacking as the caller does not support stacking
1119 //
1120  if (val && !strcmp("++", val))
1121  {eDest.Say("Config warning: stacked plugins are not supported in "
1122  "this context; directive ignored!");
1123  return true;
1124  }
1125 
1126 // Now skip over any options
1127 //
1128  while(val && *val && *val == '+') val = Config.GetWord();
1129 
1130 // Check if we actually have a path
1131 //
1132  if (!val || !val[0])
1133  {eDest.Emsg("Config", libName, "not specified"); return false;}
1134 
1135 // Record the path
1136 //
1137  if (libPath) free(libPath);
1138  libPath = strdup(val);
1139 
1140 // Handle optional parameter
1141 //
1142  if (!libParm) return true;
1143  if (*libParm) free(*libParm);
1144  *libParm = 0;
1145 
1146 // Record any parms
1147 //
1148  *parms = 0;
1149  if (!Config.GetRest(parms, sizeof(parms)))
1150  {eDest.Emsg("Config", libName, "parameters too long"); return false;}
1151  if (*parms) *libParm = strdup(parms);
1152  return true;
1153 }
1154 
1155 /******************************************************************************/
1156 /* p a r s e H o m e */
1157 /******************************************************************************/
1158 
1160 {
1161  char *pval, *val, *HomePath = 0;
1162 
1163 // Get the path
1164 //
1165  pval = Config.GetWord();
1166  if (!pval || !pval[0])
1167  {eDest.Emsg("Config", "home path not specified"); return 0;}
1168 
1169 // Make sure it's an absolute path
1170 //
1171  if (*pval != '/')
1172  {eDest.Emsg("Config", "home path not absolute"); return 0;}
1173 
1174 // Record the path
1175 //
1176  HomePath = strdup(pval);
1177 
1178 // Get the optional access rights
1179 //
1180  mode = S_IRWXU;
1181  if ((val = Config.GetWord()) && val[0])
1182  {if (!strcmp("group", val)) mode |= (S_IRGRP | S_IXGRP);
1183  else {eDest.Emsg("Config", "invalid home path modifier -", val);
1184  free(HomePath);
1185  return 0;
1186  }
1187  }
1188  return HomePath;
1189 }
1190 /******************************************************************************/
1191 /* R a n d o m */
1192 /******************************************************************************/
1193 
1194 void XrdOucUtils::Random(unsigned char* buff, unsigned int inblen)
1195 {
1196  static XrdSysMutex randMutex;
1197  XrdSysMutexHelper randMHelp(randMutex);
1198  static std::random_device rd;
1199  static std::mt19937 gen(rd());
1200  static std::uniform_int_distribution<unsigned short> dist(0, 255); // Bytes
1201 
1202  // The following is the most portable way of getting random byte values
1203  //
1204  std::generate(buff, buff + inblen, [&]() {
1205  return static_cast<unsigned char>(dist(gen));
1206  });
1207 }
1208 
1209 /******************************************************************************/
1210 /* R e L i n k */
1211 /******************************************************************************/
1212 
1213 int XrdOucUtils::ReLink(const char *path, const char *target, mode_t mode)
1214 {
1215  const mode_t AMode = S_IRWXU; // Only us as a default
1216  char pbuff[MAXPATHLEN+64];
1217  int n;
1218 
1219 // Copy the path
1220 //
1221  n = strlen(path);
1222  if (n >= (int)sizeof(pbuff)) return ENAMETOOLONG;
1223  strcpy(pbuff, path);
1224 
1225 // Unlink the target, make the path, and create the symlink
1226 //
1227  unlink(path);
1228  makePath(pbuff, (mode ? mode : AMode));
1229  if (symlink(target, path)) return errno;
1230  return 0;
1231 }
1232 
1233 /******************************************************************************/
1234 /* S a n i t i z e */
1235 /******************************************************************************/
1236 
1237 void XrdOucUtils::Sanitize(char *str, char subc)
1238 {
1239 
1240 // Sanitize string according to POSIX.1-2008 stanadard using only the
1241 // Portable Filename Character Set: a-z A-Z 0-9 ._- with 1st char not being -
1242 //
1243  if (*str)
1244  {if (*str == '-') *str = subc;
1245  else if (*str == ' ') *str = subc;
1246  char *blank = rindex(str, ' ');
1247  if (blank) while(*blank == ' ') *blank-- = 0;
1248  while(*str)
1249  {if (!isalnum(*str) && index("_-.", *str) == 0) *str = subc;
1250  str++;
1251  }
1252  }
1253 }
1254 
1255 /******************************************************************************/
1256 /* s u b L o g f n */
1257 /******************************************************************************/
1258 
1259 char *XrdOucUtils::subLogfn(XrdSysError &eDest, const char *inst, char *logfn)
1260 {
1261  const mode_t lfm = S_IRWXU|S_IRWXG|S_IROTH|S_IXOTH;
1262  char buff[2048], *sp;
1263  int rc;
1264 
1265  if (!inst || !*inst) return logfn;
1266  if (!(sp = rindex(logfn, '/'))) strcpy(buff, "./");
1267  else {*sp = '\0'; strcpy(buff, logfn); strcat(buff, "/");}
1268 
1269  strcat(buff, inst); strcat(buff, "/");
1270 
1271  if ((rc = XrdOucUtils::makePath(buff, lfm)))
1272  {eDest.Emsg("Config", rc, "create log file path", buff);
1273  return 0;
1274  }
1275 
1276  if (sp) {*sp = '/'; strcat(buff, sp+1);}
1277  else strcat(buff, logfn);
1278 
1279  free(logfn);
1280  return strdup(buff);
1281 }
1282 
1283 /******************************************************************************/
1284 /* t o L o w e r */
1285 /******************************************************************************/
1286 
1287 void XrdOucUtils::toLower(char *str)
1288 {
1289  unsigned char* ustr = (unsigned char*)str; // Avoid undefined behaviour
1290 
1291 // Change each character to lower case
1292 //
1293  while(*ustr) {*ustr = tolower(*ustr); ustr++;}
1294 }
1295 
1296 /******************************************************************************/
1297 /* T o k e n */
1298 /******************************************************************************/
1299 
1300 int XrdOucUtils::Token(const char **str, char delim, char *buff, int bsz)
1301 {
1302  const char *eP, *bP = *str;
1303  int aLen, mLen;
1304 
1305 // Trim off the delimeters. Return zero if nothing left.
1306 //
1307  while(*bP && *bP == delim) bP++;
1308  if (*bP == 0) {*buff = 0; return 0;}
1309 
1310 // Find the next delimiter
1311 //
1312  eP = bP;
1313  while(*eP && *eP != delim) eP++;
1314 
1315 // If we ended at a null, make sure next call will return zero
1316 //
1317  if (*eP == 0) *str = eP;
1318  else *str = eP+1;
1319 
1320 // Calculate length and make sure we don't overrun the buffer
1321 //
1322  aLen = eP-bP;
1323  if (aLen >= bsz) mLen = bsz-1;
1324  else mLen = aLen;
1325 
1326 // Copy token into buffer and end with null byte
1327 //
1328  strncpy(buff, bP, mLen);
1329  buff[mLen] = 0;
1330 
1331 // Return actual length
1332 //
1333  return aLen;
1334 }
1335 
1336 /******************************************************************************/
1337 /* U n d e r c o v e r */
1338 /******************************************************************************/
1339 #ifdef WIN32
1340 void XrdOucUtils::Undercover(XrdSysError &, int, int *)
1341 {
1342 }
1343 #else
1344 void XrdOucUtils::Undercover(XrdSysError &eDest, int noLog, int *pipeFD)
1345 {
1346  static const int maxFiles = 256;
1347  pid_t mypid;
1348  int myfd, logFD = eDest.baseFD();
1349 
1350 // Issue warning if there is no logfile attached
1351 //
1352  if (noLog) eDest.Emsg("Config", "Warning! No log file specified; "
1353  "backgrounding disables all logging!");
1354 
1355 // Fork so that we are not tied to a shell
1356 //
1357  if ((mypid = fork()) < 0)
1358  {eDest.Emsg("Config", errno, "fork process 1 for backgrounding");
1359  return;
1360  }
1361  else if (mypid)
1362  {
1363  // we have been given a pair of pipe descriptors to be able to read the
1364  // status of the child process
1365  if( pipeFD )
1366  {
1367  int status = 1;
1368  close( pipeFD[1] );
1369  // read will wait untill the status is communicated by the
1370  // child process, if the child process dies before being able
1371  // to comunicate the status then read will see EOF
1372  if( read( pipeFD[0], &status, sizeof(status) ) != sizeof(status) )
1373  _exit(1);
1374  _exit(status);
1375  }
1376  // no pipes given, return success
1377  else _exit(0);
1378  }
1379 
1380  if( pipeFD )
1381  close( pipeFD[0] );
1382 
1383 // Become the process group leader
1384 //
1385  if (setsid() < 0)
1386  {eDest.Emsg("Config", errno, "doing setsid() for backgrounding");
1387  return;
1388  }
1389 
1390 // Fork to that we are cannot get a controlling terminal
1391 //
1392  if ((mypid = fork()) < 0)
1393  {eDest.Emsg("Config", errno, "fork process 2 for backgrounding");
1394  return;
1395  }
1396  else if (mypid) _exit(0);
1397 
1398 // Switch stdin, stdout, and stderr to /dev/null (we can't use /dev/console
1399 // unless we are root which is unlikely).
1400 //
1401  if ((myfd = open("/dev/null", O_RDWR)) < 0)
1402  {eDest.Emsg("Config", errno, "open /dev/null for backgrounding");
1403  return;
1404  }
1405  dup2(myfd, 0); dup2(myfd, 1); dup2(myfd, 2); dup2(myfd, logFD);
1406 
1407 // Close any open file descriptors left open by the parent process
1408 // but the communication pipe and the logger's shadow file descriptor.
1409 //
1410  for (myfd = 3; myfd < maxFiles; myfd++)
1411  if( (!pipeFD || myfd != pipeFD[1]) && myfd != logFD ) close(myfd);
1412 }
1413 
1414 /******************************************************************************/
1415 /* U i d N a m e */
1416 /******************************************************************************/
1417 
1418 int XrdOucUtils::UidName(uid_t uID, char *uName, int uNsz, time_t keepT)
1419 {
1420  struct passwd *pEnt, pStruct;
1421  char pBuff[1024];
1422  int n, rc;
1423 
1424 // Get ID from cache, if allowed
1425 //
1426  if (keepT)
1427  {int n = LookUp(uidMap, static_cast<unsigned int>(uID),uName,uNsz);
1428  if (n > 0) return (n < uNsz ? n : 0);
1429  }
1430 
1431 // Try to obtain the username. We use this form to make sure we are using
1432 // the standards conforming version (compilation error otherwise).
1433 //
1434  rc = getpwuid_r(uID, &pStruct, pBuff, sizeof(pBuff), &pEnt);
1435  if (rc || !pEnt)
1436  {n = snprintf(uName, uNsz, "%ud", static_cast<unsigned int>(uID));
1437  return (n >= uNsz ? 0 : n);
1438  }
1439 
1440 // Add entry to the cache if need be
1441 //
1442  if (keepT)
1443  AddID(uidMap, static_cast<unsigned int>(uID), pEnt->pw_name, keepT);
1444 
1445 // Return length of username or zero if it is too big
1446 //
1447  n = strlen(pEnt->pw_name);
1448  if (uNsz <= (int)strlen(pEnt->pw_name)) return 0;
1449  strcpy(uName, pEnt->pw_name);
1450  return n;
1451 }
1452 
1453 /******************************************************************************/
1454 /* U s e r N a m e */
1455 /******************************************************************************/
1456 
1457 int XrdOucUtils::UserName(uid_t uID, char *uName, int uNsz)
1458 {
1459  struct passwd *pEnt, pStruct;
1460  char pBuff[1024];
1461  int rc;
1462 
1463 // Try to obtain the username. We use this form to make sure we are using
1464 // the standards conforming version (compilation error otherwise).
1465 //
1466  rc = getpwuid_r(uID, &pStruct, pBuff, sizeof(pBuff), &pEnt);
1467  if (rc) return rc;
1468  if (!pEnt) return ESRCH;
1469 
1470 // Return length of username or zero if it is too big
1471 //
1472  if (uNsz <= (int)strlen(pEnt->pw_name)) return ENAMETOOLONG;
1473  strcpy(uName, pEnt->pw_name);
1474  return 0;
1475 }
1476 
1477 /******************************************************************************/
1478 /* V a l P a t h */
1479 /******************************************************************************/
1480 
1481 const char *XrdOucUtils::ValPath(const char *path, mode_t allow, bool isdir)
1482 {
1483  static const mode_t mMask = S_IRWXU | S_IRWXG | S_IRWXO;
1484  struct stat buf;
1485 
1486 // Check if this really exists
1487 //
1488  if (stat(path, &buf))
1489  {if (errno == ENOENT) return "does not exist.";
1490  return XrdSysE2T(errno);
1491  }
1492 
1493 // Verify that this is the correct type of file
1494 //
1495  if (isdir)
1496  {if (!S_ISDIR(buf.st_mode)) return "is not a directory.";
1497  } else {
1498  if (!S_ISREG(buf.st_mode)) return "is not a file.";
1499  }
1500 
1501 // Verify that the does not have excessive privileges
1502 //
1503  if ((buf.st_mode & mMask) & ~allow) return "has excessive access rights.";
1504 
1505 // All went well
1506 //
1507  return 0;
1508 }
1509 
1510 /******************************************************************************/
1511 /* P i d F i l e */
1512 /******************************************************************************/
1513 
1514 bool XrdOucUtils::PidFile(XrdSysError &eDest, const char *path)
1515 {
1516  char buff[32];
1517  int fd;
1518 
1519  if( (fd = open( path, O_WRONLY|O_CREAT|O_TRUNC, 0644 )) < 0 )
1520  {
1521  eDest.Emsg( "Config", errno, "create pidfile" );
1522  return false;
1523  }
1524 
1525  if( write( fd, buff, snprintf( buff, sizeof(buff), "%d",
1526  static_cast<int>(getpid()) ) ) < 0 )
1527  {
1528  eDest.Emsg( "Config", errno, "write to pidfile" );
1529  close(fd);
1530  return false;
1531  }
1532 
1533  close(fd);
1534  return true;
1535 }
1536 /******************************************************************************/
1537 /* getModificationTime */
1538 /******************************************************************************/
1539 int XrdOucUtils::getModificationTime(const char *path, time_t &modificationTime) {
1540  struct stat buf;
1541  int statRet = ::stat(path,&buf);
1542  if(!statRet) {
1543  modificationTime = buf.st_mtime;
1544  }
1545  return statRet;
1546 }
1547 
1548 void XrdOucUtils::trim(std::string &str) {
1549  // Trim leading non-letters
1550  while( str.size() && !isgraph(str[0]) ) str.erase(str.begin());
1551 
1552  // Trim trailing non-letters
1553 
1554  while( str.size() && !isgraph(str[str.size()-1]) )
1555  str.resize (str.size () - 1);
1556 }
1557 
1558 void XrdOucUtils::trim(std::string_view & sv) {
1559  const auto toTrim = [](char c) { return !isgraph(c); };
1560  size_t start = 0;
1561  size_t end = sv.size();
1562 
1563  while (start < end && toTrim(sv[start])) ++start;
1564  while (end > start && toTrim(sv[end - 1])) --end;
1565 
1566  sv = sv.substr(start, end - start);
1567 }
1568 
1569 uint8_t XrdOucUtils::touint8_t(const std::string_view sv) {
1570  unsigned int temp; // wider type for parsing
1571  auto [ptr, ec] = std::from_chars(sv.data(), sv.data() + sv.size(), temp);
1572 
1573  if (ec == std::errc::invalid_argument) {
1574  throw std::invalid_argument("Invalid number format");
1575  }
1576  if (ec == std::errc::result_out_of_range || temp > std::numeric_limits<uint8_t>::max()) {
1577  throw std::out_of_range("Value out of range for unsigned short");
1578  }
1579 
1580  return static_cast<unsigned short>(temp);
1581 }
1582 
1588 static bool is_token_character(int c)
1589 {
1590  if (isalnum(c))
1591  return true;
1592 
1593  static constexpr char token_chars[] = "-._~+/=:%";
1594 
1595  for (char ch : token_chars)
1596  if (c == ch)
1597  return true;
1598 
1599  return false;
1600 }
1601 
1610 std::string obfuscateAuth(const std::string& input)
1611 {
1612  static const regex_t auth_regex = []() {
1613  constexpr char re[] =
1614  "(access_token=|authz=|(transferheader)?(www-|proxy-)?auth(orization|enticate)[[:space:]]*:[[:space:]]*)"
1615  "(Bearer([[:space:]]|%20)?(token([[:space:]]|%20)?)?)?";
1616 
1617  regex_t regex;
1618 
1619  if (regcomp(&regex, re, REG_EXTENDED | REG_ICASE) != 0)
1620  throw std::runtime_error("Failed to compile regular expression");
1621 
1622  return regex;
1623  }();
1624 
1625  regmatch_t match;
1626  size_t offset = 0;
1627  std::string redacted;
1628  const char *const text = input.c_str();
1629 
1630  while (regexec(&auth_regex, text + offset, 1, &match, 0) == 0) {
1631  redacted.append(text + offset, match.rm_eo).append("REDACTED");
1632 
1633  offset += match.rm_eo;
1634 
1635  while (offset < input.size() && is_token_character(input[offset]))
1636  ++offset;
1637  }
1638 
1639  return redacted.append(text + offset);
1640 }
1641 
1642 static bool is_rfc3986_unreserved(unsigned char c)
1643 {
1644  return std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~';
1645 }
1646 
1654 std::string XrdOucUtils::UrlEncode(const std::string &input)
1655 {
1656  static const char hex[] = "0123456789ABCDEF";
1657 
1658  std::string out;
1659  out.reserve(input.size() * 3);
1660 
1661  for (unsigned char c: input) {
1662  if (is_rfc3986_unreserved(c)) {
1663  out.push_back(c);
1664  } else {
1665  out.push_back('%');
1666  out.push_back(hex[c >> 4]);
1667  out.push_back(hex[c & 0x0f]);
1668  }
1669  }
1670  return out;
1671 }
1672 
1673 static int from_hex(char c)
1674 {
1675  if (c >= '0' && c <= '9') return c - '0';
1676  if (c >= 'A' && c <= 'F') return c - 'A' + 10;
1677  if (c >= 'a' && c <= 'f') return c - 'a' + 10;
1678  return -1;
1679 }
1680 
1688 std::string XrdOucUtils::UrlDecode(const std::string &input)
1689 {
1690  std::string out;
1691  out.reserve(input.size());
1692 
1693  for (size_t i = 0; i < input.size(); ++i) {
1694  if (input[i] == '%' && i + 2 < input.size() &&
1695  std::isxdigit(input[i + 1]) &&
1696  std::isxdigit(input[i + 2])) {
1697  const int hi = from_hex(input[i + 1]);
1698  const int lo = from_hex(input[i + 2]);
1699  out.push_back(static_cast<char>((hi << 4) | lo));
1700  i += 2;
1701  } else if (input[i] == '+') {
1702  out.push_back(' ');
1703  } else {
1704  out.push_back(input[i]);
1705  }
1706  }
1707  return out;
1708 }
1709 
1717 void stripCgi(std::string& url, const std::unordered_set<std::string> &cgiKeys)
1718 {
1719  for (const auto &key : cgiKeys) {
1720  if (key.empty())
1721  continue;
1722 
1723  const std::string needle = key + "=";
1724  size_t spos = 0, epos = 0;
1725 
1726  while ((spos = url.find(needle, spos)) != std::string::npos) {
1727  epos = spos;
1728  while (epos < url.size() && is_token_character(url[epos]))
1729  ++epos;
1730  url.erase(spos, epos - spos);
1731  }
1732  }
1733 
1734  // If a stripped CGI was the first element, remove the extra &
1735  size_t spos = 0;
1736  if ((spos = url.find("?&")) != std::string::npos)
1737  url.erase(spos + 1, 1);
1738 
1739  // If stripping removed the only query parameter, remove the dangling ?
1740  if (!url.empty() && url.back() == '?')
1741  url.pop_back();
1742 }
1743 
1744 void stripCgi(XrdOucString& url, const std::unordered_set<std::string> &cgiKeys)
1745 {
1746  std::string tmp = url.c_str();
1747  stripCgi(tmp, cgiKeys);
1748  url = tmp.c_str();
1749 }
1750 
1751 void splitHostCgi(std::string_view target, std::string &host,
1752  std::string &cgi)
1753 {
1754  const size_t q = target.find('?');
1755  if (q == std::string::npos) {host.assign(target); cgi.clear();}
1756  else {host.assign(target.data(), q);
1757  cgi.assign(target.data() + q, target.size() - q);
1758  }
1759 }
1760 
1761 #endif
struct stat Stat
Definition: XrdCks.cc:49
XrdSysError eDest(0, "HttpMon")
uint32_t crc32c(uint32_t crc, void const *buf, size_t len)
#define ENODATA
Definition: XrdOucUtils.cc:69
#define SHFT(k)
void stripCgi(std::string &url, const std::unordered_set< std::string > &cgiKeys)
static bool is_token_character(int c)
static int from_hex(char c)
void splitHostCgi(std::string_view target, std::string &host, std::string &cgi)
static bool is_rfc3986_unreserved(unsigned char c)
std::string obfuscateAuth(const std::string &input)
int chdir(const char *path)
ssize_t read(int fildes, void *buf, size_t nbyte)
#define close(a)
Definition: XrdPosix.hh:48
#define fstat(a, b)
Definition: XrdPosix.hh:62
#define write(a, b, c)
Definition: XrdPosix.hh:121
#define open
Definition: XrdPosix.hh:78
#define unlink(a)
Definition: XrdPosix.hh:119
#define stat(a, b)
Definition: XrdPosix.hh:105
const char * XrdSysE2T(int errcode)
Definition: XrdSysE2T.cc:104
size_t strlcpy(char *dst, const char *src, size_t sz)
#define CHMOD(path, mode)
#define MAKEDIR(path, mode)
static bool Match(const char *hName, const char *pattern)
Definition: XrdNetUtils.cc:658
static bool Parse(const char *hSpec, const char **hName, const char **hNend, const char **hPort, const char **hPend)
Definition: XrdNetUtils.cc:780
static uint32_t Calc32C(const void *data, size_t count, uint32_t prevcs=0)
Definition: XrdOucCRC.cc:190
char * Get(const char *varname)
Definition: XrdOucEnv.hh:69
MDLen
SHA3 digest lengths (bits to bytes).
Definition: XrdOucSHA3.hh:56
static void * Calc(const void *in, size_t inlen, void *md, MDLen mdlen)
Definition: XrdOucSHA3.cc:150
const char * c_str() const
int length() const
int tokenize(XrdOucString &tok, int from, char del=':')
static char * parseHome(XrdSysError &eDest, XrdOucStream &Config, int &mode)
static void Sanitize(char *instr, char subc='_')
static bool getGID(const char *gName, gid_t &gID)
Definition: XrdOucUtils.cc:602
static const mode_t pathMode
Definition: XrdOucUtils.hh:48
static const char * HSize(size_t bytes, char *buff, int bsz)
Definition: XrdOucUtils.cc:722
static int isFWD(const char *path, int *port=0, char *hBuff=0, int hBLen=0, bool pTrim=false)
Definition: XrdOucUtils.cc:890
static int UserName(uid_t uID, char *uName, int uNsz)
static char * Ident(long long &mySID, char *iBuff, int iBlen, const char *iHost, const char *iProg, const char *iName, int Port)
Definition: XrdOucUtils.cc:822
static bool getUID(const char *uName, uid_t &uID, gid_t *gID=0)
Definition: XrdOucUtils.cc:618
static int getModificationTime(const char *path, time_t &modificationTime)
static const char * ValPath(const char *path, mode_t allow, bool isdir)
static char * genPath(const char *path, const char *inst, const char *psfx=0)
Definition: XrdOucUtils.cc:506
static uint8_t touint8_t(const std::string_view sv)
static int Token(const char **str, char delim, char *buff, int bsz)
static void Random(unsigned char *buff, unsigned int inblen)
static int ReLink(const char *path, const char *target, mode_t mode=0)
static bool parseLib(XrdSysError &eDest, XrdOucStream &Config, const char *libName, char *&path, char **libparm)
static int hex2bin(const char *hex, char *bin, int size)
Definition: XrdOucUtils.cc:187
static int is1of(char *val, const char **clist)
Definition: XrdOucUtils.cc:878
static std::string UrlDecode(const std::string &input)
static const char * InstName(int TranOpt=0)
Definition: XrdOucUtils.cc:852
static std::string UrlEncode(const std::string &input)
static char * eText(int rc, char *eBuff, int eBlen)
Definition: XrdOucUtils.cc:246
static int argList(char *args, char **argV, int argC)
Definition: XrdOucUtils.cc:128
static bool mode2mask(const char *mode, mode_t &mask)
static std::string genHumanSize(size_t size, uint64_t base)
Definition: XrdOucUtils.cc:462
static int GidName(gid_t gID, char *gName, int gNsz, time_t keepT=0)
Definition: XrdOucUtils.cc:636
static int UidName(uid_t uID, char *uName, int uNsz, time_t keepT=0)
static char * bin2hex(char *inbuff, int dlen, char *buff, int blen, bool sep=true)
Definition: XrdOucUtils.cc:167
static int Log10(unsigned long long n)
Definition: XrdOucUtils.cc:953
static int doIf(XrdSysError *eDest, XrdOucStream &Config, const char *what, const char *hname, const char *nname, const char *pname)
Definition: XrdOucUtils.cc:280
static int makePath(char *path, mode_t mode, bool reset=false)
static int GroupName(gid_t gID, char *gName, int gNsz)
Definition: XrdOucUtils.cc:685
static void trim(std::string &str)
static bool findPgm(const char *pgm, XrdOucString &path)
Definition: XrdOucUtils.cc:403
static bool PidFile(XrdSysError &eDest, const char *path)
static const char * i2bstr(char *buff, int blen, int val, bool pad=false)
Definition: XrdOucUtils.cc:753
static void toLower(char *str)
static int fmtBytes(long long val, char *buff, int bsz)
Definition: XrdOucUtils.cc:439
static int Log2(unsigned long long n)
Definition: XrdOucUtils.cc:938
static void makeHome(XrdSysError &eDest, const char *inst)
Definition: XrdOucUtils.cc:970
static bool endsWith(const char *text, const char *ending, int endlen)
Definition: XrdOucUtils.cc:232
static void Undercover(XrdSysError &eDest, int noLog, int *pipeFD=0)
static char * getFile(const char *path, int &rc, int maxsz=10240, bool notempty=true)
Definition: XrdOucUtils.cc:545
static char * subLogfn(XrdSysError &eDest, const char *inst, char *logfn)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:116
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)
Definition: XrdSysError.cc:162
int baseFD()
Definition: XrdSysError.cc:74
XrdCmsConfig Config
XrdOucEnv theEnv
@ hex
Definition: XrdSysTrace.hh:42