XRootD
Loading...
Searching...
No Matches
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"
61#include "XrdOuc/XrdOucUtils.hh"
63#include "XrdSys/XrdSysE2T.hh"
64#include "XrdSys/XrdSysError.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
76namespace
77{
78struct 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
87typedef std::map<unsigned int, struct idInfo*> idMap_t;
88
89idMap_t gidMap;
90idMap_t uidMap;
91XrdSysMutex idMutex;
92
93void 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
104int 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
128int 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//
135for (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
167char *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
187int 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
232bool 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
246char *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
403bool 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
439int 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
462std::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
506char *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
524int 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
545char *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
602bool 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
618bool 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
636int 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
685int 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
722const 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
737do{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
753const 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
775namespace
776{
777long 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
822char *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
852const 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
869const 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
878int 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
890int 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
938int 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
953int 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
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
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
1037int 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
1068bool 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
1194void 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
1213int 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
1237void 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
1259char *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
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
1300int 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
1340void XrdOucUtils::Undercover(XrdSysError &, int, int *)
1341{
1342}
1343#else
1344void 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
1418int 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
1457int 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
1481const 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
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/******************************************************************************/
1539int 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
1548void 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
1558void 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
1569uint8_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
1587
1588static 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
1609
1610std::string obfuscateAuth(const std::string& input)
1611{
1612 static const regex_t auth_regex = []() {
1613 constexpr char re[] =
1614 "(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
1642static bool is_rfc3986_unreserved(unsigned char c)
1643{
1644 return std::isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~';
1645}
1646
1654std::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
1673static 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
1688std::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
1716
1717void 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
1744void 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
1751void 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
static XrdSysError eDest(0,"crypto_")
#define ENODATA
uint32_t crc32c(uint32_t crc, void const *buf, size_t len)
#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)
#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 chdir(a)
Definition XrdPosix.hh:46
#define unlink(a)
Definition XrdPosix.hh:119
#define stat(a, b)
Definition XrdPosix.hh:105
#define read(a, b, c)
Definition XrdPosix.hh:86
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)
static bool Parse(const char *hSpec, const char **hName, const char **hNend, const char **hPort, const char **hPend)
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)
int length() const
int tokenize(XrdOucString &tok, int from, char del=':')
const char * c_str() const
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)
static const mode_t pathMode
static const char * HSize(size_t bytes, char *buff, int bsz)
static int isFWD(const char *path, int *port=0, char *hBuff=0, int hBLen=0, bool pTrim=false)
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)
static bool getUID(const char *uName, uid_t &uID, gid_t *gID=0)
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)
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)
static int is1of(char *val, const char **clist)
static std::string UrlDecode(const std::string &input)
static const char * InstName(int TranOpt=0)
static std::string UrlEncode(const std::string &input)
static char * eText(int rc, char *eBuff, int eBlen)
static int argList(char *args, char **argV, int argC)
static bool mode2mask(const char *mode, mode_t &mask)
static std::string genHumanSize(size_t size, uint64_t base)
static int GidName(gid_t gID, char *gName, int gNsz, time_t keepT=0)
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)
static int Log10(unsigned long long n)
static int doIf(XrdSysError *eDest, XrdOucStream &Config, const char *what, const char *hname, const char *nname, const char *pname)
static int makePath(char *path, mode_t mode, bool reset=false)
static int GroupName(gid_t gID, char *gName, int gNsz)
static void trim(std::string &str)
static bool findPgm(const char *pgm, XrdOucString &path)
static bool PidFile(XrdSysError &eDest, const char *path)
static const char * i2bstr(char *buff, int blen, int val, bool pad=false)
static void toLower(char *str)
static int fmtBytes(long long val, char *buff, int bsz)
static int Log2(unsigned long long n)
static void makeHome(XrdSysError &eDest, const char *inst)
static bool endsWith(const char *text, const char *ending, int endlen)
static void Undercover(XrdSysError &eDest, int noLog, int *pipeFD=0)
static char * getFile(const char *path, int &rc, int maxsz=10240, bool notempty=true)
static char * subLogfn(XrdSysError &eDest, const char *inst, char *logfn)