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