XRootD
Loading...
Searching...
No Matches
XrdMonitor.cc
Go to the documentation of this file.
1/******************************************************************************/
2/* */
3/* X r d M o n i t o r . c c */
4/* */
5/* (c) 2024 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 <set>
32#include <stack>
33#include <stdexcept>
34#include <stdio.h>
35#include <string.h>
36
37#include "Xrd/XrdMonitor.hh"
39#include "XrdSys/XrdSysError.hh"
40
41/******************************************************************************/
42/* G l o b a l O b j e c t s */
43/******************************************************************************/
44
45namespace XrdGlobal
46{
47extern XrdSysError Log;
48}
49using namespace XrdGlobal;
50
51/******************************************************************************/
52/* L o c a l D e f i n i t i o n s */
53/******************************************************************************/
54/*
55using MRIFam = XrdMonRoll::Item::Family;
56
57using MRISch = XrdMonRoll::Item::Schema;
58
59using MRITrt = XrdMonRoll::Item::Trait;
60*/
61/******************************************************************************/
62/* X r d M o n i t o r : : R e g I n f o */
63/******************************************************************************/
64
65XrdMonitor::RegInfo::RegInfo(const char* sName, const char* tName,
66 XrdMonitor::sType sTVal)
67 : typName(strdup(tName)),
68 setName(strdup(sName)),
69 setType(sTVal)
70{ char buff[512];
71 snprintf(buff,sizeof(buff),"%s %s.Item[%%d]%%s",tName,sName);
72 eTmplt = strdup(buff);
73}
74
75XrdMonitor::RegInfo::~RegInfo()
76{
77 if (typName) free(typName);
78 if (setName) free(setName);
79 if (Json_hdr) free(Json_hdr);
80 if (Xml_hdr) free(Xml_hdr);
81 if (eTmplt) free(eTmplt);
82}
83
84/******************************************************************************/
85/* X r d M o n i t o r */
86/******************************************************************************/
87
88/******************************************************************************/
89/* Private: F i n d S e t */
90/******************************************************************************/
91
92XrdMonitor::RegInfo* XrdMonitor::FindSet(const char* setName, int sType)
93{
94 for (auto it = regVec.begin(); it != regVec.end(); it++)
95 if ((*it)->setType & sType && !std::strcmp((*it)->setName, setName))
96 return *it;
97 return 0;
98}
99
100/******************************************************************************/
101/* F o r m a t */
102/******************************************************************************/
103
104int XrdMonitor::Format(char* buff, int bsize, int& item, int opts)
105{
106// Make sure we are in the range of things to format
107//
108 if (item < 0 || item >= (int)regVec.size()) return 0;
109
110// Skip over types that are not wanted
111//
112 while((regVec[item]->setType & opts) == 0)
113 {item++;
114 if (item >= (int)regVec.size()) return 0;
115 }
116
117// Format the item
118//
119 if (opts & F_JSON) bsize = FormJSON(*regVec[item], buff, bsize);
120 else bsize = FormXML (*regVec[item], buff, bsize);
121 item++;
122 return bsize;
123}
124
125/******************************************************************************/
126
127int XrdMonitor::Format(char* buff, int bsize, const char* setName, int opts)
128{
129// Find the set and format it
130//
131 RegInfo* regInfo = FindSet(setName, opts);
132 if (!regInfo) return 0;
133 if (opts & F_JSON) bsize = FormJSON(*regInfo, buff, bsize);
134 else bsize = FormXML (*regInfo, buff, bsize);
135 return bsize;
136}
137
138/******************************************************************************/
139/* Private: F o r m J S O N */
140/******************************************************************************/
141
142#define AddC(x) {if (bsize-- < 1) return 0; *buff++ = x; bLen++;}
143
144#define Updt(x) {if (x >= bsize) return 0; bsize -= x; buff += x; bLen += x;}
145
146int XrdMonitor::FormJSON(XrdMonitor::RegInfo& regInfo, char* buff, int bsize)
147{
148 char* aStart;
149 int n, bLen = 0;
150
151// Format the header
152//
153 n = snprintf(buff, bsize, "%s", regInfo.Json_hdr);
154 Updt(n);
155
156// Prine the first object
157//
158 aStart = buff;
159
160// Format all the elements as needed
161//
162 for (int i = 0; i < regInfo.iCount; i++)
163 {XrdMonRoll::Item& item = regInfo.iVec[i];
164
165 // First handle non-data/key elements because they have no separator
166 //
167 if (item.Kind == MRIFam::isSchema)
168 {if (item.Plan == MRISch::endArray) {AddC(']'); continue;}
169 if (item.Plan == MRISch::endObject) {AddC('}'); continue;}
170 } else if (item.Kind == MRIFam::isMutex)
171 {if (item.doLK) item.mtxP->Lock();
172 else item.mtxP->UnLock();
173 continue;
174 }
175
176 // Add a comma if we need to
177 //
178 if (buff != aStart) {AddC(',');}
179
180 // Add the key if it needs to apear
181 //
182 if (!item.Array)
183 {n = snprintf(buff, bsize, "\"%s\":", item.keyP);
184 Updt(n);
185 }
186
187 // Insert proper value of schema element
188 //
189 switch(item.Kind)
190 {case MRIFam::isBinary:
191 if (!(n = V2S(regInfo, item, buff, bsize))) return 0;
192 Updt(n);
193 break;
194 case MRIFam::isSchema:
195 if (item.Plan == MRISch::begArray)
196 {AddC('['); aStart = buff;}
197 else if (item.Plan == MRISch::begObject)
198 {AddC('{'); aStart = buff;}
199 break;
200 case MRIFam::isText:
201 if (!(n = V2T(regInfo, item, buff, bsize, true))) return 0;
202 Updt(n);
203 break;
204 default: ValErr(regInfo, item.iNum,
205 "Unknown item family encountered!!!");
206 return 0;
207 }
208 }
209
210// Insert end and return
211//
212 AddC('}');
213 return bLen;
214}
215
216/******************************************************************************/
217/* Private: F o r m X M L */
218/******************************************************************************/
219
220int XrdMonitor::FormXML(XrdMonitor::RegInfo& regInfo, char* buff, int bsize)
221{
222 int n, bLen = 0;
223// unsigned int kVal;
224
225// Format the header
226//
227 n = snprintf(buff, bsize, "%s", regInfo.Xml_hdr);
228 Updt(n);
229
230// Format all the variables
231//
232 for (int i = 0; i < regInfo.iCount; i++)
233 {XrdMonRoll::Item& item = regInfo.iVec[i];
234
235 // First handle non-data/key elements
236 //
237 if (item.Kind == MRIFam::isSchema)
238 {if (item.Plan == MRISch::endArray
239 || item.Plan == MRISch::endObject)
240 {n = snprintf(buff, bsize, "</%s>", item.keyP);
241 Updt(n);
242 continue;
243 }
244 } else if (item.Kind == MRIFam::isMutex)
245 {if (item.doLK) item.mtxP->Lock();
246 else item.mtxP->UnLock();
247 continue;
248 }
249
250 // All XML values have start and end tags. Insert the front tag.
251 //
252 n = snprintf(buff, bsize, "<%s>", item.keyP);
253 Updt(n);
254
255 // Insert proper value of schema element
256 //
257 switch(item.Kind)
258 {case MRIFam::isBinary:
259 if (!(n = V2S(regInfo, item, buff, bsize))) return 0;
260 Updt(n);
261 break;
262 case MRIFam::isSchema:
263 if (item.Plan == MRISch::begArray
264 || item.Plan == MRISch::begObject) continue;
265 break;
266 case MRIFam::isText:
267 if (!(n = V2T(regInfo, item, buff, bsize, false))) return 0;
268 Updt(n);
269 break;
270 default: ValErr(regInfo, item.iNum,
271 "Unknown item family encountered!!!");
272 return 0;
273 }
274
275 // All XML values have start and end tags. Insert the back tag.
276 //
277 n = snprintf(buff, bsize, "</%s>", item.keyP);
278 Updt(n);
279 }
280
281// Insert end and return
282//
283 n = snprintf(buff, bsize, "%s", "</stats>");
284 Updt(n);
285 return bLen;
286}
287
288/******************************************************************************/
289/* Private: R e g F a i l */
290/******************************************************************************/
291
292bool XrdMonitor::RegFail(const char* TName, const char* SName, const char* why)
293{
294 char buff[512];
295
296// Format message and spit it out
297//
298 snprintf(buff, sizeof(buff), "Attempt to register monitor summary for "
299 "%s set %s failed; %s", TName, SName, why);
300 Log.Say("Config warning:", buff);
301 return false;
302}
303
304/******************************************************************************/
305/* R e g i s t e r */
306/******************************************************************************/
307
308bool XrdMonitor::Register(XrdMonRoll::rollType setType, const char* setName,
309 XrdMonRoll::Item itemVec[], int itemCnt)
310{
311 const char* tName = "Plugin";
312 char buff[512];
313
314// Determine type of set
315//
316 sType stype;
317 switch(setType)
318 {case XrdMonRoll::AddOn:
319 case XrdMonRoll::Misc: stype = isAdon; // Deprecated
320 tName = "AddOn";
321 break;
323 case XrdMonRoll::Protocol: stype = isPlug; // Deprecated
324 break;
325 default: stype = isPlug;
326 break;
327 }
328
329// Reject invalid sets
330//
331 if (itemCnt < 1 || itemVec == 0)
332 return RegFail(tName, setName, "invaled parameters");
333
334// Make sure this map has not been previously defined
335//
336 if (FindSet(setName,-1))
337 return RegFail(tName, setName, "set name already registered");
338
339
340// Allocate a new registry
341//
342 RegInfo* regInfo = new RegInfo(setName, tName, stype);
343 regInfo->iCount = itemCnt;
344 regInfo->iVec = itemVec;
345
346// Before registering this specification we need to validate it
347//
348 if (!Validate(*regInfo))
349 {delete regInfo;
350 return RegFail(tName, setName, "invalid description");
351 }
352
353// Complete the registry
354//
355 regInfo->eTmplt = strdup(buff);
356 snprintf(buff, sizeof(buff), "\"stats_%s\":{", setName);
357 regInfo->Json_hdr = strdup(buff);
358 snprintf(buff, sizeof(buff), "<stats id=\"%s\">", setName);
359 regInfo->Xml_hdr = strdup(buff);
360
361// Add registry to our list of registries
362//
363 regVec.push_back(regInfo);
364
365// Display information
366//
367 snprintf(buff, sizeof(buff), "%s set %s registered with %d items(s)",
368 tName, setName,itemCnt);
369 Log.Say("Config monitor: ", buff);
370 return true;
371}
372
373/******************************************************************************/
374/* Private: V 2 S */
375/******************************************************************************/
376
377// Warning: This function may only be called for Clan types isAtomic, isFloat,
378// or isDouble.
379
380int XrdMonitor::V2S(XrdMonitor::RegInfo& rI, XrdMonRoll::Item& item,
381 char* buff, int blen)
382{
383 std::string s;
384
385// Test first for the most common case - isAtomic
386//
387 if (item.Clan == MRITrt::isAtomic || item.Clan == MRITrt::isBtomic) try
388 {if (item.Clan == MRITrt::isAtomic)
389 s = std::visit([](const auto arg) -> std::string
390 {auto val = arg->load();
391 return std::to_string(val);
392 }, item.ratV);
393 else
394 s = std::visit([](const auto arg) -> std::string
395 {auto val = arg->load();
396 return std::to_string(val);
397 }, item.rbtV);
398
399 } catch (const std::runtime_error& e)
400 {char eBuff[512];
401 snprintf(eBuff, sizeof(eBuff), rI.eTmplt, int(item.iNum),
402 " atomic number formatting failed;");
403 Log.Emsg("XrdMonitor", eBuff, e.what());
404 return 0;
405 }
406 else if (item.Clan == MRITrt::isFloat) s = std::to_string(*item.fltP);
407 else s = std::to_string(*item.dblP);
408
409// Copy result to the supplied buffer
410//
411 return snprintf(buff, blen, "%s", s.c_str());
412}
413
414/******************************************************************************/
415/* Private: V 2 T */
416/******************************************************************************/
417
418// This method may only be called for Clan types isChar and isString
419//
420int XrdMonitor::V2T(XrdMonitor::RegInfo& rI, XrdMonRoll::Item& item,
421 char* buff, int blen, bool isJSON)
422{
423 const char* text = (item.Clan == MRITrt::isChar ? item.chrP
424 : item.strP->c_str());
425
426// Copy result to the supplied buffer
427//
428 if (isJSON) return snprintf(buff, blen, "\"%s\"", text);
429 return snprintf(buff, blen, "%s", text);
430}
431
432/******************************************************************************/
433/* Private: V a l E n d */
434/******************************************************************************/
435
436void XrdMonitor::ValEnd(bool& isBad, XrdMonitor::RegInfo& rI, const char* aoT,
437 const char* begKey, XrdMonRoll::Item* endP)
438{
439// If there is no starting key then the starting item generated an error
440// and there is no reason to perform any tests. Otherwise, if there is no
441// ending key, then we simply use the starting key. If there is an ending key
442// it must match the starting key.
443//
444 if (begKey)
445 {if (!(endP->keyP)) endP->keyP = begKey;
446 else {if (strcmp(begKey, endP->keyP))
447 {char etxt[80];
448 snprintf(etxt, sizeof(etxt),
449 "end%s key differs from beg%s key", aoT, aoT);
450 isBad = ValErr(rI, endP->iNum, etxt);
451 }
452 }
453 }
454}
455
456/******************************************************************************/
457/* Private: V a l E r r */
458/******************************************************************************/
459
460bool XrdMonitor::ValErr(XrdMonitor::RegInfo& rInfo, int iNum, const char* eTxt)
461{
462 char eBuff[1024];
463
464// Format the header
465//
466 snprintf(eBuff, sizeof(eBuff), rInfo.eTmplt, iNum, " incorrect;");
467
468// Send of message
469//
470 Log.Say("Config warning: MonRoll ", eBuff, eTxt);
471 return false;
472}
473
474/******************************************************************************/
475/* Private: V a l K e y */
476/******************************************************************************/
477
478void XrdMonitor::ValKey(bool& isBad, XrdMonitor::RegInfo& rI,
479 XrdMonRoll::Item* itemP)
480{
481// Keys are required in object context and are option in array context.
482// When JSON is emitted keys in array context are ignored but used for XML.
483// Otherwise, a key must be specified.
484//
485 if (itemP->Array) {if (!(itemP->keyP)) itemP->keyP = "item";}
486 else {if (!(itemP->keyP))
487 isBad = ValErr(rI, itemP->iNum, "key not specified");
488 }
489}
490
491/******************************************************************************/
492/* Private: V a l i d a t e */
493/******************************************************************************/
494
495#define VALERR(x) isBad |= ValErr(regInfo, i, x)
496
497bool XrdMonitor::Validate(XrdMonitor::RegInfo& regInfo)
498{
499 std::set<XrdSysMutex*> lokActv;
500 std::stack<XrdMonRoll::Item*> synStax;
501 bool isBad = false;
502
503// Add a defined element to out stacks so that top() is always defined
504//
505 XrdMonRoll::Item myItem("", MRISch::unk);
506 synStax.push(&myItem);
507
508// Run through the the items making sure they provide a consistent syntax
509//
510 for (int i = 0; i < regInfo.iCount; i++)
511 {XrdMonRoll::Item* itemP = &regInfo.iVec[i];
512 itemP->iNum = static_cast<short>(i);
513 if (itemP->keyP && *(itemP->keyP) == 0) itemP->keyP = 0;
514 itemP->Array = synStax.top()->Plan == MRISch::begArray;
515
516 switch(itemP->Kind)
517 {case MRIFam::isBinary:
518 ValKey(isBad, regInfo, itemP);
519 break;
520 case MRIFam::isMutex:
521 if (itemP->doLK)
522 {if (lokActv.find(itemP->mtxP) == lokActv.end())
523 lokActv.insert(itemP->mtxP);
524 else VALERR("locks a previously locked mutex");
525 } else {
526 auto it = lokActv.find(itemP->mtxP);
527 if (it != lokActv.end()) lokActv.erase(it);
528 else VALERR("unLocks a mutex not previously locked");
529 }
530 break;
531 case MRIFam::isSchema:
532 if (itemP->Plan == MRISch::begArray
533 || itemP->Plan == MRISch::begObject)
534 {ValKey(isBad, regInfo, itemP);
535 synStax.push(itemP);
536 break;
537 }
538 {XrdMonRoll::Item* topP = synStax.top();
539 if (itemP->Plan == MRISch::endArray)
540 {if (topP->Plan == MRISch::begArray)
541 {synStax.pop();
542 ValEnd(isBad, regInfo, "Array", topP->keyP, itemP);
543 } else {
544 VALERR("endArray is not paired with begArray");
545 }
546 break;
547 }
548 if (itemP->Plan == MRISch::endObject)
549 {if (topP->Plan == MRISch::begObject)
550 {synStax.pop();
551 ValEnd(isBad, regInfo, "Object", topP->keyP, itemP);
552 } else {
553 VALERR("endObject is not paired with begObject");
554 }
555 break;
556 }
557 }
558 VALERR("Invalid schema specification");
559 break;
560 case MRIFam::isText:
561 if (itemP->Clan == MRITrt::isChar && !(itemP->chrP))
562 {VALERR("text value pointer is nil");}
563 ValKey(isBad, regInfo, itemP);
564 break;
565 default: VALERR("Unknown item family encountered!!!");
566 break;
567 }
568 }
569
570// Make sure everything has ended
571//
572 if (!isBad)
573 {char eBuff[1024];
574 if (synStax.size() > 1)
575 {snprintf(eBuff, sizeof(eBuff), regInfo.eTmplt,
576 int(synStax.top()->iNum), " definition not ended");
577 Log.Say("Config warning: MonRoll ", eBuff);
578 }
579 if (!lokActv.empty())
580 {snprintf(eBuff, sizeof(eBuff), regInfo.eTmplt, 0,
581 " one or more locks not unlocked!");
582 Log.Say("Config warning: MonRoll ", eBuff);
583 }
584 }
585
586// Return result
587//
588 if (isBad) return false;
589 return true;
590}
#define Updt(x)
#define VALERR(x)
#define AddC(x)
struct myOpts opts
bool Register(XrdMonRoll::rollType setType, const char *setName, XrdMonRoll::Item itemVec[], int itemCnt)
int Format(char *buff, int bsize, int &item, int opts=0)
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)
XrdSysError Log
Definition XrdConfig.cc:113