XRootD
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"
38 #include "XrdSys/XrdSysPthread.hh"
39 #include "XrdSys/XrdSysError.hh"
40 
41 /******************************************************************************/
42 /* G l o b a l O b j e c t s */
43 /******************************************************************************/
44 
45 namespace XrdGlobal
46 {
47 extern XrdSysError Log;
48 }
49 using namespace XrdGlobal;
50 
51 /******************************************************************************/
52 /* L o c a l D e f i n i t i o n s */
53 /******************************************************************************/
54 /*
55 using MRIFam = XrdMonRoll::Item::Family;
56 
57 using MRISch = XrdMonRoll::Item::Schema;
58 
59 using 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 
65 XrdMonitor::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 
75 XrdMonitor::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 
92 XrdMonitor::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 
104 int 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 
127 int 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 
146 int 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 
220 int 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 
292 bool 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 
308 bool 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;
322  case XrdMonRoll::Plugin:
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 
380 int 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 //
420 int 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 
436 void 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 
460 bool 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 
478 void 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 
497 bool 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)
Definition: XrdMonitor.cc:144
#define VALERR(x)
Definition: XrdMonitor.cc:495
#define AddC(x)
Definition: XrdMonitor.cc:142
struct myOpts opts
bool Register(XrdMonRoll::rollType setType, const char *setName, XrdMonRoll::Item itemVec[], int itemCnt)
Definition: XrdMonitor.cc:308
int Format(char *buff, int bsize, int &item, int opts=0)
Definition: XrdMonitor.cc:104
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:116
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:162
XrdSysError Log
Definition: XrdConfig.cc:113