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