XRootD
Loading...
Searching...
No Matches
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"
44#include "XrdNet/XrdNetUtils.hh"
45#include "XrdOuc/XrdOuca2x.hh"
46#include "XrdOuc/XrdOucJson.hh"
48#include "XrdOuc/XrdOucProg.hh"
51#include "XrdOuc/XrdOucUtils.hh"
53#include "XrdSys/XrdSysError.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{
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) {}
87};
88
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) {}
102 };
103
104// Permanent maps to determine the experiment
105//
106std::map<std::string, ExpInfo> expMap;
108std::map<std::string, ExpInfo*> v2eMap;
109
110// Other configuration values
111//
113XrdNetMsg *netMsg = 0; // UDP object for collector
114XrdNetMsg *netOrg = 0; // UDP object for origin
117const char *myHostName = "-";
118const char *myDomain = "";
119
120
122
123char *ffDest = 0;
124int ffEcho = 0;
125static
126const int ffPORT = 10514; // The default port
127int ffPortD = 0; // The dest port to use
128int ffPortO = 0; // The reply port to use
129
130static const int domAny = 0;
131static const int domLcl = 1;
132static const int domRmt = 2;
133
135bool tryPath = false;
136bool tryVO = false;
137bool useDefs = false;
138
139bool useFLbl = false;
140signed char useFFly = -1;
141bool addFLFF = false;
142bool useSTag = true;
143
144bool noFail = true;
145bool doDebug = false;
146bool doTrace = false;
147
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
161 };
162
164}
165using 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
381namespace
382{
383bool Recover()
384{
385 if (!noFail) return false;
386 useDefs = false;
387 return true;
388}
389}
390
391bool 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
474namespace
475{
476void 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
492bool 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 {
517 XrdOucMapP2X<ExpInfo*> *px = (new XrdOucMapP2X<ExpInfo*>
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
536bool 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
575namespace
576{
577const char *Code2S(int code)
578{
579 static char buff[16];
580 snprintf(buff, sizeof(buff), " [%d]", code);
581 return buff;
582}
583
584void 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
596void 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
676const 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
696bool 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
750bool 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
825using json = nlohmann::json;
826
827namespace
828{
829const 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
837bool 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
867bool 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//
975do{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
#define DEBUG(x)
void Display()
Definition XrdCks.cc:56
static XrdSysError eDest(0,"crypto_")
#define EPName(ep)
nlohmann::json json
#define DBGID(tid, txt)
#define unlink(a)
Definition XrdPosix.hh:108
#define TRACE(act, x)
Definition XrdTrace.hh:63
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)
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)
static const int maxExpID
static const int minActID
static const int maxActID
static const int minExpID
static char * MyHostName(const char *eName="*unknown*", const char **eText=0)
const char * thePath()
void RepName(const char *newname)
XrdOucMapP2X< T > * Match(const char *pd, const int pl=0)
XrdOucMapP2X< T > * theNext()
void RepValu(T arg)
bool beginswith(char c)
const char * c_str() const
static bool findPgm(const char *pgm, XrdOucString &path)
static char * getFile(const char *path, int &rc, int maxsz=10240, bool notempty=true)
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)
XrdNetAddrInfo * addrInfo
Entity's connection details.
const char * tident
Trace identifier always preset.
char * name
Entity's name.
char * role
Entity's role(s)
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
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)
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
std::set< std::string > x2eSet
const char * pgmOpts[pgmOptN]
std::set< std::string > x2aSet