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