XRootD
XrdNetPMarkCfg.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* X r d N e t P M a r k C f g . h h */
4 /* */
5 /* (c) 2021 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 <map>
32 #include <set>
33 #include <string>
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <sys/types.h>
40 
41 #include "XrdNet/XrdNetMsg.hh"
42 #include "XrdNet/XrdNetPMarkCfg.hh"
43 #include "XrdNet/XrdNetPMarkFF.hh"
44 #include "XrdNet/XrdNetUtils.hh"
45 #include "XrdOuc/XrdOuca2x.hh"
46 #include "XrdOuc/XrdOucJson.hh"
47 #include "XrdOuc/XrdOucMapP2X.hh"
48 #include "XrdOuc/XrdOucProg.hh"
49 #include "XrdOuc/XrdOucStream.hh"
50 #include "XrdOuc/XrdOucString.hh"
51 #include "XrdOuc/XrdOucUtils.hh"
52 #include "XrdSec/XrdSecEntity.hh"
53 #include "XrdSys/XrdSysError.hh"
54 #include "XrdSys/XrdSysPthread.hh"
55 #include "XrdSys/XrdSysTimer.hh"
56 #include "XrdSys/XrdSysTrace.hh"
57 
58 /******************************************************************************/
59 /* L o c a l M a c r o s */
60 /******************************************************************************/
61 
62 #define TRACE(txt) \
63  if (doTrace) SYSTRACE(Trace->, client.tident, epName, 0, txt)
64 
65 #define DEBUG(txt) \
66  if (doDebug) SYSTRACE(Trace->, 0, epName, 0, txt)
67 
68 #define DBGID(tid, txt) \
69  if (doDebug) SYSTRACE(Trace->, tid, epName, 0, txt)
70 
71 #define EPName(ep) const char *epName = ep
72 
73 /******************************************************************************/
74 /* s t a t i c O b j e c t s */
75 /******************************************************************************/
76 
78 {
79 class MapInfo
80  {public:
81  std::string Name; // Activity name
82  int Code; // Act code for role/user
83 
84  MapInfo() : Code(0) {}
85  MapInfo(const char *name, int code) : Name(name), Code(code) {}
86  ~MapInfo() {}
87 };
88 
89 class ExpInfo
90  {public:
91  std::map<std::string, int> actMap;
92  std::map<std::string, MapInfo> r2aMap;
93  std::map<std::string, MapInfo> u2aMap;
94  short Code;
95  short dAct = -1;
96  bool Roles = false;
97  bool Users = false;
98  bool inUse = false;
99 
100  ExpInfo(int code=0) : Code(code) {}
101  ~ExpInfo() {}
102  };
103 
104 // Permanent maps to determine the experiment
105 //
106 std::map<std::string, ExpInfo> expMap;
108 std::map<std::string, ExpInfo*> v2eMap;
109 
110 // Other configuration values
111 //
113 XrdNetMsg *netMsg = 0; // UDP object for collector
114 XrdNetMsg *netOrg = 0; // UDP object for origin
117 const char *myHostName = "-";
118 const char *myDomain = "";
119 
120 
122 
123 char *ffDest = 0;
124 int ffEcho = 0;
125 static
126 const int ffPORT = 10514; // The default port
127 int ffPortD = 0; // The dest port to use
128 int ffPortO = 0; // The reply port to use
129 
130 static const int domAny = 0;
131 static const int domLcl = 1;
132 static const int domRmt = 2;
133 
134 char chkDom = domRmt;
135 bool tryPath = false;
136 bool tryVO = false;
137 bool useDefs = false;
138 
139 bool useFLbl = false;
140 signed char useFFly = -1;
141 bool addFLFF = false;
142 bool useSTag = true;
143 
144 bool noFail = true;
145 bool doDebug = false;
146 bool doTrace = false;
147 
148 struct CfgInfo
151 
152  static const int pgmOptN = 6;
153  const char *pgmOpts[pgmOptN] = {0};
154 
155  int defsTO =30;
156  std::set<std::string> x2aSet;
157  std::set<std::string> x2eSet;
158 
159  CfgInfo() {}
160  ~CfgInfo() {}
161  };
162 
164 }
165 using namespace XrdNetPMarkConfig;
166 
167 /******************************************************************************/
168 /* S t a t i c M e m b e r s */
169 /******************************************************************************/
170 
171 /******************************************************************************/
172 /* B e g i n */
173 /******************************************************************************/
174 
176  const char *path,
177  const char *cgi,
178  const char *app)
179 {
180  EPName("PMBegin");
181  int eCode, aCode;
182 
183 // If we need to screen out domains, do that
184 //
185  if (chkDom)
186  {XrdNetAddrInfo &addrInfo = *client.addrInfo;
187  char domType = (addrInfo.isPrivate() ? domLcl : domRmt);
188  if (domType == domRmt && *myDomain)
189  {const char *urName = addrInfo.Name();
190  if (urName && XrdNetAddrInfo::isHostName(urName))
191  {const char *dot = index(urName, '.');
192  if (dot && !strcmp(dot+1, myDomain)) domType = domLcl;
193  }
194  }
195  if (domType != chkDom)
196  {DBGID(client.tident, "Skipping sending flow info; unwanted domain");
197  return 0;
198  }
199  }
200 
201 // Now get the experiment and activity code. If we can't get at least the
202 // experiment code, then proceed without marking the flow.
203 //
204  if (!getCodes(client, path, cgi, eCode, aCode))
205  {TRACE("Unable to determine experiment; flow not marked.");
206  return 0;
207  }
208 
209 // Continue with successor function to complete the logic
210 //
211  XrdNetPMark::Handle handle(app, eCode, aCode);
212  return Begin(*client.addrInfo, handle, client.tident);
213 }
214 
215 /******************************************************************************/
216 
218  XrdNetPMark::Handle &handle,
219  const char *tident)
220 {
221 
222 // If we are allowed to use the flow label set on the incoming connection
223 // then try to do so. This is only valid for IPv6 connections. Currently,
224 // this is not implemented.
225 //
226 // if (useFLbl && addrInfo.isIPType(XrdNetAddrInfo::IPv6)
227 // && !addrInfo.isMapped())
228 // {
229 // TODO???
230 // }
231 
232 // If we are allowed to use firefly, return a firefly handle
233 //
234  if (handle.Valid() && useFFly)
235  {XrdNetPMarkFF *pmFF = new XrdNetPMarkFF(handle, tident);
236  if (pmFF->Start(addrInfo)) return pmFF;
237  delete pmFF;
238  }
239 
240 // All done, nothing will be pmarked
241 //
242  return 0;
243 }
244 
245 /******************************************************************************/
246 /* C o n f i g */
247 /******************************************************************************/
248 
250  XrdSysTrace *trc, bool &fatal)
251 {
252  class DelCfgInfo
253  {public: DelCfgInfo(CfgInfo *&cfg) : cfgInfo(cfg) {}
254  ~DelCfgInfo() {if (cfgInfo) {delete cfgInfo; cfgInfo = 0;}}
255  private:
256  CfgInfo *&cfgInfo;
257  } cleanup(Cfg);
258 
259 // If we have not been configured then simply retrn nil
260 //
261  if (!Cfg)
262  {useFFly = false;
263  return 0;
264  }
265 
266 // Save the message handler
267 //
268  eDest = eLog;
269  Sched = sched;
270  Trace = trc;
271  fatal = false;
272 
273 // If firefly is enabled, make sure we have an ffdest
274 //
275  if (useFFly < 0)
276  {if (ffPortD || ffPortO)
277  {useFFly = true;
278  if (!ffPortO) ffPortO = ffPORT;
279  } else {
280  useFFly = false;
281  eLog->Say("Config warning: firefly disabled; "
282  "configuration incomplete!");
283  return 0;
284  }
285  } else if (useFFly && !ffPortO) ffPortO = ffPORT;
286 
287 // Resolve trace and debug settings
288 //
289  if (doDebug) doTrace = true;
290 
291 // Check if we need a defsfile, if so, construct the map.
292 //
293  if (Cfg->x2aSet.size() == 0 && Cfg->x2eSet.size() == 0)
294  {if (Cfg->defsFile.length())
295  eLog->Say("Config warning: ignoring defsfile; "
296  "no mappings have been specified!");
297  useDefs = false;
298  } else {
299  if (!Cfg->defsFile.length())
300  {eLog->Say("Config invalid: pmark mappings cannot be resolved "
301  "without specifying defsfile!");
302  fatal = true;
303  return 0;
304  }
305  useDefs = true;
306  if (!ConfigDefs())
307  {if (useDefs)
308  {fatal = true;
309  return 0;
310  }
311  eLog->Say("Config warning: pmark ignoring defsfile; "
312  "unable to process and nofail is in effect!");
313  }
314  }
315 
316 // At this point either we still enabled or not. We can be disabled for a
317 // number of reasons and appropriate messages will have been issued.
318 //
319  if (!useFFly) return 0;
320 
321 // Create a netmsg object for firefly reporting if a dest was specified
322 //
323  bool aOK = false;
324  if (ffDest)
325  {XrdNetAddr spec;
326  char buff[1024];
327  const char *eTxt = spec.Set(ffDest, -ffPortD);
328  if (eTxt)
329  {snprintf(buff, sizeof(buff), "%s:%d; %s", ffDest, ffPortD, eTxt);
330  eLog->Emsg("Config", "pmark unable to create UDP tunnel to", buff);
331  useFFly = false;
332  fatal = true;
333  return 0;
334  }
335  if (spec.Format(buff, sizeof(buff)))
336  netMsg = new XrdNetMsg(eDest, buff, &aOK);
337  if (!aOK)
338  {eLog->Emsg("Config", "pmark unable to create UDP tunnel to", ffDest);
339  fatal = true;
340  delete netMsg;
341  netMsg = 0;
342  useFFly= false;
343  return 0;
344  }
345  }
346 
347 // Handle the firefly messages to origin
348 //
349  if (ffPortO)
350  {netOrg = new XrdNetMsg(eDest, 0, &aOK);
351  if (!aOK)
352  {eLog->Emsg("Config","pmark unable to create origin UDP tunnel");
353  fatal = true;
354  useFFly= false;
355  return 0;
356  }
357  }
358 
359 // Get our host name.
360 //
361  myHostName = XrdNetUtils::MyHostName("-"); // Never deleted!
362 
363 // Setup for domain checking
364 //
365  if (chkDom)
366  {const char *dot = index(myHostName, '.');
367  if (dot) myDomain = dot+1;
368  else eDest->Say("Config warning: Unable to determine local domain; "
369  " domain check restricted to IP address type!");
370  }
371 
372 // Finally, we are done. Return the packet markling stub.
373 //
374  return new XrdNetPMarkCfg;
375 }
376 
377 /******************************************************************************/
378 /* Private: C o n f i g D e f s */
379 /******************************************************************************/
380 
381 namespace
382 {
383 bool Recover()
384 {
385  if (!noFail) return false;
386  useDefs = false;
387  return true;
388 }
389 }
390 
391 bool XrdNetPMarkCfg::ConfigDefs()
392 {
393  class Const2Char
394  {public:
395  char *data;
396  Const2Char(const char *str) : data(strdup(str)) {}
397  ~Const2Char() {free(data);}
398  };
399  EPName("ConfigDefs");
400  std::set<std::string>::iterator it;
401  std::map<std::string, ExpInfo>::iterator itE;
402  bool isDload, aOK = true;
403 
404 // If we need tp fetch the file, do so.
405 //
406  if ((isDload = !(Cfg->defsFile.beginswith('/'))) && !FetchFile())
407  return Recover();
408 
409 // Now parse the defsfile (it is a json file)
410 //
411  aOK = LoadFile();
412 
413 // Get rid of the defsfile if we dowloaded it
414 //
415  if (isDload) unlink(Cfg->defsFile.c_str());
416 
417 // Only continue if all is well
418 //
419  if (!aOK) return Recover();
420 
421 // Configure the experiment mapping
422 //
423  for (it = Cfg->x2eSet.begin(); it != Cfg->x2eSet.end(); it++)
424  {Const2Char pv(it->c_str());
425  if (!ConfigPV2E(pv.data)) aOK = false;
426  }
427  Cfg->x2eSet.clear();
428 
429 // Configure the activity mapping
430 //
431  for (it = Cfg->x2aSet.begin(); it != Cfg->x2aSet.end(); it++)
432  {Const2Char ru(it->c_str());
433  if (!ConfigRU2A(ru.data)) aOK = false;
434  }
435  Cfg->x2aSet.clear();
436 
437 // Eliminate any experiment that we will not be using. This is will restrict
438 // flow marking to url passed information as there will be no internal deduction
439 //
440  itE = expMap.begin();
441  while(itE != expMap.end())
442  {if (itE->second.inUse)
443  {itE->second.Roles = itE->second.r2aMap.size() != 0;
444  itE->second.Users = itE->second.u2aMap.size() != 0;
445  itE++;
446  } else {
447  DEBUG("Deleting unused experiment '"<<itE->first.c_str()<<"'");
448  itE = expMap.erase(itE);
449  }
450  }
451  if (aOK && expMap.size() == 0)
452  {useDefs = false; useFFly = false;
453  if (useSTag)
454  {eDest->Say("Config warning: No experiments referenced; "
455  "packet marking restricted to scitagged url's!");
456  } else {
457  eDest->Say("Config warning: No experiments referenced and scitags "
458  "not enabled; packet marking has been disabled!");
459  useFFly = false;
460  }
461  } else if (!aOK)
462  {useFFly = false; useDefs = false;
463  } else {tryPath = !p2eMap.isEmpty();
464  tryVO = v2eMap.size() != 0;
465  if (doTrace) Display();
466  }
467  return aOK;
468 }
469 
470 /******************************************************************************/
471 /* Private: C o n f i g P V 2 E */
472 /******************************************************************************/
473 
474 namespace
475 {
476 void Complain(const char *rWho, const char *rName,
477  const char *uWho, const char *uName, const char *eName=0)
478 {
479  char *et0P = 0, eText0[256], eText1[256], eText2[256];
480  if (eName)
481  {snprintf(eText0, sizeof(eText0), "experiment %s", eName);
482  et0P = eText0;
483  }
484  snprintf(eText1, sizeof(eText1), "%s '%s'", rWho, rName);
485  snprintf(eText2, sizeof(eText2), "%s '%s'", uWho, uName);
486  eDest->Say("Config failure: ",et0P, eText1," references undefined ",eText2);
487 }
488 }
489 
490 // Note: info contains {path <path> | vo <voname> | default default} >exname>
491 
492 bool XrdNetPMarkCfg::ConfigPV2E(char *info)
493 {
494  std::map<std::string, ExpInfo >::iterator itE;
495  std::map<std::string, ExpInfo*>::iterator itV;
496  char *eName, *xName, *xType = info;
497  xName = index(info, ' '); *xName = 0; xName++; // path | vo name
498  eName = index(xName, ' '); *eName = 0; eName++; // experiment name
499 
500  if ((itE = expMap.find(std::string(eName))) == expMap.end())
501  {Complain(xType, xName, "experiment", eName);
502  return false;
503  }
504  itE->second.inUse = true;
505 
506  if (*xType == 'd')
507  {expDflt = &itE->second;
508  return true;
509  }
510 
511  if (*xType == 'p')
512  {XrdOucMapP2X<ExpInfo*> *p2nP = p2eMap.Find(xName);
513  if (p2nP)
514  {p2nP->RepName(eName);
515  p2nP->RepValu(&(itE->second));
516  } else {
518  (xName, eName, &(itE->second)));
519  p2eMap.Insert(px);
520  }
521  } else {
522  itV = v2eMap.find(std::string(xName));
523  if (itV != v2eMap.end()) itV->second = &(itE->second);
524  else v2eMap[xName] = &(itE->second);
525  }
526 
527  return true;
528 }
529 
530 /******************************************************************************/
531 /* Private: C o n f i g R U 2 A */
532 /******************************************************************************/
533 
534 // Note: info contains <ename> {dflt dflt | {{role | user} <xname>}} <aname>
535 
536 bool XrdNetPMarkCfg::ConfigRU2A(char *info)
537 {
538  std::map<std::string, int>::iterator itA;
539  std::map<std::string, ExpInfo>::iterator itE;
540  std::map<std::string, MapInfo>::iterator itX;
541  char *aName, *eName, *xName, *xType;
542  eName = info; // experiment name
543  xType = index(info, ' '); *xType = 0; xType++; // dflt | role | user
544  xName = index(xType, ' '); *xName = 0; xName++; // role name | user name
545  aName = index(xName, ' '); *aName = 0; aName++; // activity name
546 
547  if ((itE = expMap.find(std::string(eName))) == expMap.end())
548  {Complain(xType, xName, "experiment", eName);
549  return false;
550  }
551 
552  itA = itE->second.actMap.find(std::string(aName));
553  if (itA == itE->second.actMap.end())
554  {Complain(xType, xName, "activity", aName, eName);
555  return false;
556  }
557 
558  if (*xType == 'd') itE->second.dAct = itA->second;
559  else {std::map<std::string, MapInfo> &xMap =
560  (*xType == 'r' ? itE->second.r2aMap : itE->second.u2aMap);
561 
562  itX = xMap.find(std::string(xName));
563  if (itX != xMap.end())
564  {itX->second.Name = aName; itX->second.Code = itA->second;}
565  else xMap[std::string(xName)] = MapInfo(aName, itA->second);
566  }
567 
568  return true;
569 }
570 
571 /******************************************************************************/
572 /* Private: D i s p l a y */
573 /******************************************************************************/
574 
575 namespace
576 {
577 const char *Code2S(int code)
578 {
579  static char buff[16];
580  snprintf(buff, sizeof(buff), " [%d]", code);
581  return buff;
582 }
583 
584 void ShowActs(std::map<std::string, MapInfo>& map, const char *hdr,
585  const char *mName)
586 {
587  std::map<std::string, MapInfo>::iterator it;
588 
589  for (it = map.begin(); it != map.end(); it++)
590  {eDest->Say(hdr, mName, it->first.c_str(), " activity ",
591  it->second.Name.c_str(), Code2S(it->second.Code));
592  }
593 }
594 }
595 
596 void XrdNetPMarkCfg::Display()
597 {
598  std::map<std::string, ExpInfo>::iterator itE;
599  std::map<int, std::vector<const char*>> pvRefs;
600  const char *hdr = " ", *hdrplu = " ++ ";
601  char buff[80];
602 
603 // Build map from path to experiment
604 //
605  std::map<int, std::vector<const char*>>::iterator it2E;
606  XrdOucMapP2X<ExpInfo*> *p2e = p2eMap.theNext();
607 
608  while(p2e)
609  {ExpInfo *expinfo = p2e->theValu();
610  if ((it2E = pvRefs.find(expinfo->Code)) != pvRefs.end())
611  it2E->second.push_back(p2e->thePath());
612  else {std::vector<const char*> vec;
613  vec.push_back(p2e->thePath());
614  pvRefs[expinfo->Code] = vec;
615  }
616  p2e = p2e->theNext();
617  }
618 
619 // Add in the vo references
620 //
621  std::map<std::string, ExpInfo*>::iterator itV;
622  for (itV = v2eMap.begin(); itV != v2eMap.end(); itV++)
623  {int eCode = itV->second->Code;
624  if ((it2E = pvRefs.find(eCode)) != pvRefs.end())
625  it2E->second.push_back(itV->first.c_str());
626  else {std::vector<const char*> vec;
627  vec.push_back(itV->first.c_str());
628  pvRefs[eCode] = vec;
629  }
630  }
631 
632 
633 // Indicate the number of experiments
634 //
635  snprintf(buff, sizeof(buff), "%d", static_cast<int>(expMap.size()));
636  const char *txt = (expMap.size() == 1 ? " expirement " : " experiments ");
637  eDest->Say("Config pmark results: ", buff, txt, "directly referenced:");
638 
639 // Display information
640 //
641  for (itE = expMap.begin(); itE != expMap.end(); itE++)
642  {int expCode = itE->second.Code;
643  eDest->Say(hdr, itE->first.c_str(), Code2S(expCode),
644  (&itE->second == expDflt ? " (default)" : 0));
645  if ((it2E = pvRefs.find(expCode)) != pvRefs.end())
646  {std::vector<const char*> &vec = it2E->second;
647  for (int i = 0; i < (int)vec.size(); i++)
648  {const char *rType = (*vec[i] == '/' ? "path " : "vorg ");
649  eDest->Say(hdrplu, rType, vec[i]);
650  }
651  }
652  if (itE->second.u2aMap.size() != 0)
653  ShowActs(itE->second.u2aMap, hdrplu, "user ");
654  if (itE->second.r2aMap.size() != 0)
655  ShowActs(itE->second.r2aMap, hdrplu, "role ");
656  if (itE->second.dAct >= 0)
657  {std::map<std::string, int>::iterator itA;
658  int aCode = itE->second.dAct;
659  for (itA = itE->second.actMap.begin();
660  itA != itE->second.actMap.end(); itA++)
661  {if (aCode == itA->second)
662  {eDest->Say(hdrplu, "Default activity ",
663  itA->first.c_str(), Code2S(aCode));
664  break;
665  }
666  }
667  if (itA == itE->second.actMap.end()) itE->second.dAct = -1;
668  }
669  }
670 }
671 
672 /******************************************************************************/
673 /* Private: E x t r a c t */
674 /******************************************************************************/
675 
676 const char *XrdNetPMarkCfg::Extract(const char *sVec, char *buff, int blen)
677 {
678  const char *space;
679 
680 // If there is only one token in sVec then return it.
681 //
682  if (!(space = index(sVec, ' '))) return sVec;
683 
684 // Extract out the token using the supplied buffer
685 //
686  int n = space - sVec;
687  if (!n || n >= blen) return 0;
688  snprintf(buff, blen, "%.*s", n, sVec);
689  return buff;
690 }
691 
692 /******************************************************************************/
693 /* Private: F e t c h F i l e */
694 /******************************************************************************/
695 
696 bool XrdNetPMarkCfg::FetchFile()
697 {
698  EPName("FetchFile");
699  XrdOucProg fetchJob(eDest);
700  char tmo[16], outfile[512];
701  int rc;
702 
703 // Setup the job
704 //
705  if ((rc = fetchJob.Setup(Cfg->pgmPath.c_str(), eDest)))
706  {eDest->Emsg("Config", rc, "setup job to fetch defsfile");
707  return false;
708  }
709 
710 // Create the output file name (it willl be written to /tmp)
711 //
712  snprintf(outfile, sizeof(outfile), "/tmp/XrdPMark-%ld.json",
713  static_cast<long>(getpid()));
714  unlink(outfile);
715 
716 // Insert the timeout value argument list and complete it.
717 //
718  snprintf(tmo, sizeof(tmo), "%d", Cfg->defsTO);
719  Cfg->pgmOpts[1] = tmo; // 0:-x 1:tmo 2:-y 3:-z 4:outfile 5:defsfile
720  Cfg->pgmOpts[4] = outfile;
721  Cfg->pgmOpts[5] = Cfg->defsFile.c_str();
722 
723 // Do some debugging
724 //
725  if (doDebug)
726  {for (int i = 0; i < CfgInfo::pgmOptN; i++)
727  {Cfg->pgmPath += ' '; Cfg->pgmPath += Cfg->pgmOpts[i];}
728  DEBUG("Running: " <<Cfg->pgmPath.c_str());
729  }
730 
731 // Run the appropriate fetch command
732 //
733  rc = fetchJob.Run(Cfg->pgmOpts, CfgInfo::pgmOptN);
734  if (rc)
735  {snprintf(outfile, sizeof(outfile), "failed with rc=%d", rc);
736  eDest->Emsg("Config", "Fetch via", Cfg->pgmPath.c_str(), outfile);
737  return false;
738  }
739 
740 // Set the actual output file
741 //
742  Cfg->defsFile = outfile;
743  return true;
744 }
745 
746 /******************************************************************************/
747 /* Private: g e t C o d e s */
748 /******************************************************************************/
749 
750 bool XrdNetPMarkCfg::getCodes(XrdSecEntity &client, const char *path,
751  const char *cgi, int &ecode, int &acode)
752 {
753  ExpInfo* expP = 0;
754 
755 // If we are allowed to use scitags, then try that first
756 //
757  if (useSTag && cgi && XrdNetPMark::getEA(cgi, ecode, acode)) return true;
758 
759 // If we can use the definitions (i.e. in error) return w/o packet marking
760 //
761  if (!useDefs) return false;
762 
763 // Try to use the path argument.
764 //
765  if (tryPath && path)
766  {XrdOucMapP2X<ExpInfo*> *p2nP = p2eMap.Match(path);
767  if (p2nP) expP = p2nP->theValu();
768  }
769 
770 // If the path did not succeed, then try the vo
771 //
772  if (!expP && tryVO && client.vorg)
773  {std::map<std::string, ExpInfo*>::iterator itV;
774  char voBuff[256];
775  const char *VO = Extract(client.vorg, voBuff, sizeof(voBuff));
776  if (VO && (itV = v2eMap.find(std::string(client.vorg))) != v2eMap.end())
777  expP = itV->second;
778  }
779 
780 // If there is no experiment yet, use the default if one exists
781 //
782  if (!expP && expDflt) expP = expDflt;
783 
784 // If we still have no experiment then fail. We cannot packet mark.
785 //
786  if (!expP) return false;
787  ecode = expP->Code;
788 
789 // If there are user to activity mappings, see if we can use that
790 //
791  if (expP->Users && client.name)
792  {std::map<std::string, MapInfo>::iterator itU;
793  itU = expP->u2aMap.find(std::string(client.name));
794  if (itU != expP->u2aMap.end())
795  {acode = itU->second.Code;
796  return true;
797  }
798  }
799 
800 // If there are role to activity mappings, see if we can use that
801 //
802  if (expP->Roles && client.role)
803  {std::map<std::string, MapInfo>::iterator itR;
804  char roBuff[256];
805  const char *RO = Extract(client.role, roBuff, sizeof(roBuff));
806  if (RO)
807  {itR = expP->r2aMap.find(std::string(client.role));
808  if (itR != expP->r2aMap.end())
809  {acode = itR->second.Code;
810  return true;
811  }
812  }
813  }
814 
815 // If a default activity exists, return that. Otherwise, it's unspecified.
816 //
817  acode = (expP->dAct >= 0 ? expP->dAct : 0);
818  return true;
819 }
820 
821 /******************************************************************************/
822 /* Private: L o a d F i l e */
823 /******************************************************************************/
824 
826 
827 namespace
828 {
829 const char *MsgTrim(const char *msg)
830 {
831  const char *sP;
832  if ((sP = index(msg, ' ')) && *(sP+1)) return sP+1;
833  return msg;
834 }
835 }
836 
837 bool XrdNetPMarkCfg::LoadFile()
838 {
839  struct fBuff {char *buff; fBuff() : buff(0) {}
840  ~fBuff() {if (buff) free(buff);}
841  } defs;
842  int rc;
843 
844 // The json file is relatively small so read the whole thing in
845 //
846  if (!(defs.buff = XrdOucUtils::getFile(Cfg->defsFile.c_str(), rc)))
847  {eDest->Emsg("Config", rc, "read defsfile", Cfg->defsFile.c_str());
848  return false;
849  }
850 
851 // Parse the file and return result. The parser may throw an exception
852 // so we will catch it here.
853 //
854  try {bool result = LoadJson(defs.buff);
855  return result;
856  } catch (json::exception& e)
857  {eDest->Emsg("Config", "Unable to process defsfile;",
858  MsgTrim(e.what()));
859  }
860  return false;
861 }
862 
863 /******************************************************************************/
864 /* Private: L o a d J s o n */
865 /******************************************************************************/
866 
867 bool XrdNetPMarkCfg::LoadJson(char *buff)
868 {
869  json j;
870  std::map<std::string, ExpInfo>::iterator itE;
871 
872 // Parse the file; caller will catch any exceptions
873 //
874  j = json::parse(buff);
875 
876 // Extract out modification data
877 //
878  std::string modDate;
879  json j_mod = j["modified"];
880  if (j_mod != 0) modDate = j_mod.get<std::string>();
881  else modDate = "*unspecified*";
882 
883  eDest->Say("Config using pmark defsfile '", Cfg->defsFile.c_str(),
884  "' last modified on ", modDate.c_str());
885 
886 // Extract out the experiments
887 //
888  json j_exp = j["experiments"];
889  if (j_exp == 0)
890  {eDest->Emsg("Config", "The defsfile does not define any experiments!");
891  return false;
892  }
893 
894 // Now iterate through all of the experiments and the activities within
895 // and define our local maps for each.
896 //
897  for (auto it : j_exp)
898  {std::string expName = it["expName"].get<std::string>();
899  if (expName.empty()) continue;
900  if (!it["expId"].is_number() || it["expId"] < minExpID || it["expId"] > maxExpID)
901  {eDest->Say("Config warning: ignoring experiment '", expName.c_str(),
902  "'; associated ID is invalid.");
903  continue;
904  }
905  expMap[expName] = ExpInfo(it["expId"].get<int>());
906 
907  if ((itE = expMap.find(expName)) == expMap.end())
908  {eDest->Say("Config warning: ignoring experiment '", expName.c_str(),
909  "'; map insertion failed!");
910  continue;
911  }
912 
913  json j_acts = it["activities"];
914  if (j_acts == 0)
915  {eDest->Say("Config warning: ignoring experiment '", expName.c_str(),
916  "'; has no activities!");
917  continue;
918  }
919 
920  for (unsigned int i = 0; i < j_acts.size(); i++)
921  {std::string actName = j_acts[i]["activityName"].get<std::string>();
922  if (actName.empty()) continue;
923  if (!j_acts[i]["activityId"].is_number()
924  || j_acts[i]["activityId"] < minActID
925  || j_acts[i]["activityId"] > maxActID)
926  {eDest->Say("Config warning:", "ignoring ", expName.c_str(),
927  " actitivity '", actName.c_str(),
928  "'; associated ID is invalid.");
929  continue;
930  }
931  itE->second.actMap[actName] = j_acts[i]["activityId"].get<int>();
932  }
933  }
934 
935 // Make sure we have at least one experiment defined
936 //
937  if (!expMap.size())
938  {eDest->Say("Config warning: unable to define any experiments via defsfile!");
939  return false;
940  }
941  return true;
942 }
943 
944 /******************************************************************************/
945 /* P a r s e */
946 /******************************************************************************/
947 
949 {
950 // Parse pmark directive parameters:
951 //
952 // [[no]debug] [defsfile [[no]fail] {<path> | {curl | wget} [tmo] <url>}]
953 // [domain {any | local | remote}] [[no]fail] [ffdest <udpdest>]
954 // [ffecho <intvl>]
955 // [map2act <ename> {default | {role | user} <name>} <aname>]
956 // [map2exp {default | {path <path> | vo <vo>} <ename>}] [[no]trace]
957 // [use {[no]flowlabel | flowlabel+ff | [no]firefly | [no]scitag}
958 //
959 // <udpdest>: {origin[:<port>] | <host>[:port]} [,<udpdest>]
960 //
961  std::string name;
962  char *val;
963 
964 // If this is the first time here, allocate config info object
965 //
966  if (!Cfg) Cfg = new CfgInfo;
967 
968 // Make sure we have something to parse
969 //
970  if (!(val = Config.GetWord()))
971  {eLog->Say("Config invalid: pmark argument not specified"); return 1;}
972 
973 // Parse the directive options
974 //
975 do{if (!strcmp("debug", val) || !strcmp("nodebug", val))
976  {doDebug = (*val != 'n');
977  continue;
978  }
979 
980  if (!strcmp("defsfile", val))
981  {if (!(val = Config.GetWord()))
982  {eLog->Say("Config invalid: pmark defsfile value not specified");
983  return 1;
984  }
985 
986  if (*val == '/')
987  {Cfg->defsFile = val;
988  continue;
989  }
990 
991  if (strcmp("curl", val) && strcmp("wget", val))
992  {eLog->Say("Config invalid: unknown defsfile transfer agent '",val,"'");
993  return 1;
994  }
995  if (!XrdOucUtils::findPgm(val, Cfg->pgmPath))
996  {eLog->Say("Config invalid: defsfile transfer agent '",val,"' not found.");
997  return 1;
998  }
999 
1000  if (*val == 'c')
1001  {Cfg->pgmOpts[0]="-m"; Cfg->pgmOpts[2]="-s"; Cfg->pgmOpts[3]="-o";
1002  } else {
1003  Cfg->pgmOpts[0]="-T"; Cfg->pgmOpts[2]="-q"; Cfg->pgmOpts[3]="-O";
1004  }
1005 
1006  val = Config.GetWord();
1007  if (val && isdigit(*val))
1008  {if (XrdOuca2x::a2tm(*eLog,"defsfile timeout",val,&Cfg->defsTO,10))
1009  return 1;
1010  val = Config.GetWord();
1011  }
1012 
1013  if (!val) {eLog->Say("Config invalid: pmark defsfile url not specified");
1014  return 1;
1015  }
1016  Cfg->defsFile = val;
1017  continue;
1018  }
1019 
1020  if (!strcmp("domain", val))
1021  {if (!(val = Config.GetWord()))
1022  {eLog->Say("Config invalid: pmark domain value not specified");
1023  return 1;
1024  }
1025  if (!strcmp(val, "any" )
1026  || !strcmp(val, "all" )) chkDom = domAny;
1027  else if (!strcmp(val, "local" )) chkDom = domLcl;
1028  else if (!strcmp(val, "remote")) chkDom = domRmt;
1029  else {eLog->Say("Config invalid: pmark invalid domain determinant '",
1030  val, "'");
1031  return 1;
1032  }
1033  continue;
1034  }
1035 
1036  if (!strcmp("fail", val) || !strcmp("nofail", val))
1037  {noFail = (*val == 'n');
1038  continue;
1039  }
1040 
1041  // We accept 'origin' as a dest for backward compatibility. That is the
1042  // enforced default should 'use firefly' be specified.
1043  //
1044  if (!strcmp("ffdest", val))
1045  {const char *addtxt = "";
1046  char *colon, *comma;
1047  int xPort;
1048  val = Config.GetWord();
1049  do {if (!val || *val == 0 || *val == ',' || *val == ':')
1050  {eLog->Say("Config invalid: pmark ffdest value not specified",
1051  addtxt); return 1;
1052  }
1053  if ((comma = index(val, ','))) *comma++ = 0;
1054  if ((colon = index(val, ':')))
1055  {*colon++ = 0;
1056  if ((xPort = XrdOuca2x::a2p(*eLog, "udp", colon, false)) <= 0)
1057  return 1;
1058  } else xPort = ffPORT;
1059  if (!strcmp(val, "origin")) ffPortO = xPort;
1060  else {if (ffDest) free(ffDest);
1061  ffDest = strdup(val);
1062  ffPortD = xPort;
1063  }
1064  addtxt = " after comma";
1065  } while((val = comma));
1066  if (useFFly < 0) useFFly = 1;
1067  continue;
1068  }
1069 
1070  if (!strcmp("ffecho", val))
1071  {if (!(val = Config.GetWord()))
1072  {eLog->Say("Config invalid: pmark ffecho value not specified");
1073  return 1;
1074  }
1075  if (XrdOuca2x::a2tm(*eLog,"ffecho interval", val, &ffEcho, 0)) return 1;
1076  if (ffEcho < 30) ffEcho = 0;
1077  continue;
1078  }
1079 
1080  if (!strcmp("map2act", val))
1081  {if (!(val = Config.GetWord()))
1082  {eLog->Say("Config invalid: pmark activity experiment not specified");
1083  return 1;
1084  }
1085  name = val;
1086 
1087  if (!(val = Config.GetWord()))
1088  {eLog->Say("Config invalid: pmark activity determinant not specified");
1089  return 1;
1090  }
1091 
1092  const char *adet;
1093  if (!strcmp(val, "default")) adet = "dflt";
1094  else if (!strcmp(val, "role")) adet = "role";
1095  else if (!strcmp(val, "user")) adet = "user";
1096  else {eLog->Say("Config invalid: pmark invalid activity determinant '",
1097  val, "'");
1098  return 1;
1099  }
1100  name += ' '; name += val;
1101 
1102  if (*adet != 'd' && !(val = Config.GetWord()))
1103  {eLog->Say("Config invalid: pmark activity", adet, "not specified");
1104  return 1;
1105  }
1106  name += ' '; name += val;
1107 
1108  if (!(val = Config.GetWord()))
1109  {eLog->Say("Config invalid: pmark", adet, "activity not specified");
1110  return 1;
1111  }
1112  name += ' '; name += val;
1113 
1114  Cfg->x2aSet.insert(name);
1115  continue;
1116  }
1117 
1118  if (!strcmp("map2exp", val))
1119  {if (!(val = Config.GetWord()))
1120  {eLog->Say("Config invalid: pmark map2exp type not specified");
1121  return 1;
1122  }
1123  if (strcmp("default", val) && strcmp("path", val)
1124  && strcmp("vo", val) && strcmp("vorg", val))
1125  {eLog->Say("Config invalid: invalid pmark map2exp type, '",val,"'.");
1126  return 1;
1127  }
1128  name = val;
1129 
1130  if (*val != 'd' && !(val = Config.GetWord()))
1131  {eLog->Say("Config invalid: pmark map2exp ", name.c_str(),
1132  "not specified");
1133  return 1;
1134  }
1135  name += ' '; name += val;
1136 
1137  if (!(val = Config.GetWord()))
1138  {eLog->Say("Config invalid: pmark map2exp expirement not specified");
1139  return 1;
1140  }
1141  name += ' '; name += val;
1142 
1143  Cfg->x2eSet.insert(name);
1144  continue;
1145  }
1146 
1147  if (!strcmp("trace", val) || !strcmp("notrace", val))
1148  {doTrace = (*val != 'n');
1149  continue;
1150  }
1151 
1152  if (!strcmp("use", val))
1153  {if (!(val = Config.GetWord()))
1154  {eLog->Say("Config invalid: pmark use argument not specified");
1155  return 1;
1156  }
1157  bool argOK = false;
1158  char *arg;
1159  do {bool theval = strncmp(val, "no", 2) != 0;
1160  arg = (!theval ? val += 2 : val);
1161  if (!strcmp("flowlabel", arg))
1162  {useFLbl = theval; addFLFF = false; argOK = true;}
1163  else if (!strcmp("flowlabel+ff", arg))
1164  {addFLFF = useFLbl = theval; argOK = true;}
1165  else if (!strcmp("firefly", arg))
1166  {useFFly = (theval ? 1 : 0); argOK = true;}
1167  else if (!strcmp("scitag", arg)) {useSTag = theval; argOK = true;}
1168  else if (argOK) {Config.RetToken(); break;}
1169  else {eLog->Say("Config invalid: 'use ",val,"' is invalid");
1170  return 1;
1171  }
1172  } while((val = Config.GetWord()));
1173  if (!val) break;
1174  continue;
1175  }
1176 
1177  eLog->Say("Config warning: ignoring unknown pmark argument'",val,"'.");
1178 
1179  } while ((val = Config.GetWord()));
1180 
1181  return 0;
1182 }
#define tident
void Display()
Definition: XrdCks.cc:56
#define DEBUG(txt)
#define TRACE(txt)
#define EPName(ep)
nlohmann::json json
#define DBGID(tid, txt)
int unlink(const char *path)
static bool isHostName(const char *name)
int Format(char *bAddr, int bLen, fmtUse fmtType=fmtAuto, int fmtOpts=0)
const char * Name(const char *eName=0, const char **eText=0)
const char * Set(const char *hSpec, int pNum=PortInSpec)
Definition: XrdNetAddr.cc:216
static int Parse(XrdSysError *eLog, XrdOucStream &Config)
static XrdNetPMark * Config(XrdSysError *eLog, XrdScheduler *sched, XrdSysTrace *trc, bool &fatal)
XrdNetPMark::Handle * Begin(XrdSecEntity &Client, const char *path=0, const char *cgi=0, const char *app=0) override
std::map< std::string, int > actMap
std::map< std::string, MapInfo > u2aMap
std::map< std::string, MapInfo > r2aMap
MapInfo(const char *name, int code)
bool Start(XrdNetAddrInfo &addr)
static bool getEA(const char *cgi, int &ecode, int &acode)
Definition: XrdNetPMark.cc:40
static char * MyHostName(const char *eName="*unknown*", const char **eText=0)
Definition: XrdNetUtils.cc:667
void RepName(const char *newname)
Definition: XrdOucMapP2X.hh:71
const char * thePath()
Definition: XrdOucMapP2X.hh:67
XrdOucMapP2X< T > * theNext()
Definition: XrdOucMapP2X.hh:65
void RepValu(T arg)
Definition: XrdOucMapP2X.hh:74
const char * c_str() const
bool beginswith(char c)
int length() const
static bool findPgm(const char *pgm, XrdOucString &path)
Definition: XrdOucUtils.cc:354
static char * getFile(const char *path, int &rc, int maxsz=10240, bool notempty=true)
Definition: XrdOucUtils.cc:456
static int a2tm(XrdSysError &, const char *emsg, const char *item, int *val, int minv=-1, int maxv=-1)
Definition: XrdOuca2x.cc:288
static int a2p(XrdSysError &, const char *ptype, const char *val, bool anyOK=true)
Definition: XrdOuca2x.cc:140
char * vorg
Entity's virtual organization(s)
Definition: XrdSecEntity.hh:71
XrdNetAddrInfo * addrInfo
Entity's connection details.
Definition: XrdSecEntity.hh:80
const char * tident
Trace identifier always preset.
Definition: XrdSecEntity.hh:81
char * name
Entity's name.
Definition: XrdSecEntity.hh:69
char * role
Entity's role(s)
Definition: XrdSecEntity.hh:72
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
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:141
XrdCmsConfig Config
XrdSysTrace * Trace
std::map< std::string, ExpInfo > expMap
static const int domLcl
std::map< std::string, ExpInfo * > v2eMap
static const int ffPORT
XrdScheduler * Sched
XrdSysError * eDest
const char * myDomain
const char * myHostName
XrdOucMapP2X< ExpInfo * > p2eMap
static const int domAny
static const int domRmt
XrdSysError * eLog
std::set< std::string > x2eSet
const char * pgmOpts[pgmOptN]
std::set< std::string > x2aSet