xref: /trafficserver/lib/records/RecCore.cc (revision afa69e70)
1 /** @file
2 
3   Record core definitions
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include "tscore/ink_platform.h"
25 #include "tscore/ink_memory.h"
26 #include "tscore/ink_string.h"
27 #include "tscore/Filenames.h"
28 
29 #include "RecordsConfig.h"
30 #include "P_RecFile.h"
31 #include "P_RecCore.h"
32 #include "P_RecUtils.h"
33 #include "tscore/I_Layout.h"
34 
35 // This is needed to manage the size of the librecords record. It can't be static, because it needs to be modified
36 // and used (read) from several binaries / modules.
37 int max_records_entries = REC_INTERNAL_RECORDS + REC_MIN_API_RECORDS;
38 
39 static bool g_initialized = false;
40 
41 RecRecord *g_records = nullptr;
42 std::unordered_map<std::string, RecRecord *> g_records_ht;
43 ink_rwlock g_records_rwlock;
44 int g_num_records = 0;
45 
46 //-------------------------------------------------------------------------
47 // register_record
48 //-------------------------------------------------------------------------
49 static RecRecord *
register_record(RecT rec_type,const char * name,RecDataT data_type,RecData data_default,RecPersistT persist_type,bool * updated_p=nullptr)50 register_record(RecT rec_type, const char *name, RecDataT data_type, RecData data_default, RecPersistT persist_type,
51                 bool *updated_p = nullptr)
52 {
53   RecRecord *r = nullptr;
54 
55   // Metrics are restored from persistence before they are registered. In this case, when the registration arrives, we
56   // might find that they have changed. For example, a metric might change it's type due to a software upgrade. Records
57   // must not flip between config and metrics, but changing within those classes is OK.
58   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
59     r = it->second;
60     if (REC_TYPE_IS_STAT(rec_type)) {
61       ink_release_assert(REC_TYPE_IS_STAT(r->rec_type));
62     }
63 
64     if (REC_TYPE_IS_CONFIG(rec_type)) {
65       ink_release_assert(REC_TYPE_IS_CONFIG(r->rec_type));
66     }
67 
68     if (data_type != r->data_type) {
69       // Clear with the old type before resetting with the new type.
70       RecDataZero(r->data_type, &(r->data));
71       RecDataZero(r->data_type, &(r->data_default));
72 
73       // If the data type changed, reset the current value to the default.
74       RecDataSet(data_type, &(r->data), &(data_default));
75     }
76 
77     // NOTE: Do not set r->data as we want to keep the previous value because we almost certainly restored a persisted
78     // value before the metric was registered.
79     RecDataSet(data_type, &(r->data_default), &(data_default));
80 
81     r->data_type = data_type;
82     r->rec_type  = rec_type;
83 
84     if (updated_p) {
85       *updated_p = true;
86     }
87   } else {
88     if ((r = RecAlloc(rec_type, name, data_type)) == nullptr) {
89       return nullptr;
90     }
91 
92     // Set the r->data to its default value as this is a new record
93     RecDataSet(r->data_type, &(r->data), &(data_default));
94     RecDataSet(r->data_type, &(r->data_default), &(data_default));
95     g_records_ht.emplace(name, r);
96 
97     if (REC_TYPE_IS_STAT(r->rec_type)) {
98       r->stat_meta.persist_type = persist_type;
99     }
100 
101     if (updated_p) {
102       *updated_p = false;
103     }
104   }
105 
106   // we're now registered
107   r->registered = true;
108   r->version    = 0;
109 
110   return r;
111 }
112 
113 //-------------------------------------------------------------------------
114 // link_XXX
115 //-------------------------------------------------------------------------
116 static int
link_int(const char *,RecDataT,RecData data,void * cookie)117 link_int(const char * /* name */, RecDataT /* data_type */, RecData data, void *cookie)
118 {
119   RecInt *rec_int = static_cast<RecInt *>(cookie);
120   ink_atomic_swap(rec_int, data.rec_int);
121   return REC_ERR_OKAY;
122 }
123 
124 static int
link_int32(const char *,RecDataT,RecData data,void * cookie)125 link_int32(const char * /* name */, RecDataT /* data_type */, RecData data, void *cookie)
126 {
127   *(static_cast<int32_t *>(cookie)) = static_cast<int32_t>(data.rec_int);
128   return REC_ERR_OKAY;
129 }
130 
131 static int
link_uint32(const char *,RecDataT,RecData data,void * cookie)132 link_uint32(const char * /* name */, RecDataT /* data_type */, RecData data, void *cookie)
133 {
134   *(static_cast<uint32_t *>(cookie)) = static_cast<uint32_t>(data.rec_int);
135   return REC_ERR_OKAY;
136 }
137 
138 static int
link_float(const char *,RecDataT,RecData data,void * cookie)139 link_float(const char * /* name */, RecDataT /* data_type */, RecData data, void *cookie)
140 {
141   *(static_cast<RecFloat *>(cookie)) = data.rec_float;
142   return REC_ERR_OKAY;
143 }
144 
145 static int
link_counter(const char *,RecDataT,RecData data,void * cookie)146 link_counter(const char * /* name */, RecDataT /* data_type */, RecData data, void *cookie)
147 {
148   RecCounter *rec_counter = static_cast<RecCounter *>(cookie);
149   ink_atomic_swap(rec_counter, data.rec_counter);
150   return REC_ERR_OKAY;
151 }
152 
153 // This is a convenience wrapper, to allow us to treat the RecInt's as a
154 // 1-byte entity internally.
155 static int
link_byte(const char *,RecDataT,RecData data,void * cookie)156 link_byte(const char * /* name */, RecDataT /* data_type */, RecData data, void *cookie)
157 {
158   RecByte *rec_byte = static_cast<RecByte *>(cookie);
159   RecByte byte      = static_cast<RecByte>(data.rec_int);
160 
161   ink_atomic_swap(rec_byte, byte);
162   return REC_ERR_OKAY;
163 }
164 
165 // mimic Config.cc::config_string_alloc_cb
166 // cookie e.g. is the DEFAULT_xxx_str value which this function keeps up to date with
167 // the latest default applied during a config update from records.config
168 static int
link_string_alloc(const char *,RecDataT,RecData data,void * cookie)169 link_string_alloc(const char * /* name */, RecDataT /* data_type */, RecData data, void *cookie)
170 {
171   RecString _ss        = data.rec_string;
172   RecString _new_value = nullptr;
173 
174   if (_ss) {
175     _new_value = ats_strdup(_ss);
176   }
177 
178   // set new string for DEFAULT_xxx_str tp point to
179   RecString _temp2                    = *(static_cast<RecString *>(cookie));
180   *(static_cast<RecString *>(cookie)) = _new_value;
181   // free previous string DEFAULT_xxx_str points to
182   ats_free(_temp2);
183 
184   return REC_ERR_OKAY;
185 }
186 
187 //-------------------------------------------------------------------------
188 // RecCoreInit
189 //-------------------------------------------------------------------------
190 int
RecCoreInit(RecModeT mode_type,Diags * _diags)191 RecCoreInit(RecModeT mode_type, Diags *_diags)
192 {
193   if (g_initialized) {
194     return REC_ERR_OKAY;
195   }
196 
197   // set our diags
198   RecSetDiags(_diags);
199 
200   // Initialize config file parsing data structures.
201   RecConfigFileInit();
202 
203   g_num_records = 0;
204 
205   // initialize record array for our internal stats (this can be reallocated later)
206   g_records = static_cast<RecRecord *>(ats_malloc(max_records_entries * sizeof(RecRecord)));
207 
208   // initialize record rwlock
209   ink_rwlock_init(&g_records_rwlock);
210 
211   // read stats
212   if ((mode_type == RECM_SERVER) || (mode_type == RECM_STAND_ALONE)) {
213     RecReadStatsFile();
214   }
215 
216   // read configs
217   if ((mode_type == RECM_SERVER) || (mode_type == RECM_STAND_ALONE)) {
218     bool file_exists = true;
219 
220     ink_mutex_init(&g_rec_config_lock);
221 
222     g_rec_config_fpath = ats_stringdup(RecConfigReadConfigPath(nullptr, ts::filename::RECORDS));
223     if (RecFileExists(g_rec_config_fpath) == REC_ERR_FAIL) {
224       RecLog(DL_Warning, "Could not find '%s', system will run with defaults\n", ts::filename::RECORDS);
225       file_exists = false;
226     }
227 
228     if (file_exists) {
229       RecReadConfigFile();
230     }
231   }
232 
233   g_initialized = true;
234 
235   return REC_ERR_OKAY;
236 }
237 
238 //-------------------------------------------------------------------------
239 // RecLinkConfigXXX
240 //-------------------------------------------------------------------------
241 RecErrT
RecLinkConfigInt(const char * name,RecInt * rec_int)242 RecLinkConfigInt(const char *name, RecInt *rec_int)
243 {
244   if (RecGetRecordInt(name, rec_int) == REC_ERR_FAIL) {
245     return REC_ERR_FAIL;
246   }
247   return RecRegisterConfigUpdateCb(name, link_int, (void *)rec_int);
248 }
249 
250 RecErrT
RecLinkConfigInt32(const char * name,int32_t * p_int32)251 RecLinkConfigInt32(const char *name, int32_t *p_int32)
252 {
253   return RecRegisterConfigUpdateCb(name, link_int32, (void *)p_int32);
254 }
255 
256 RecErrT
RecLinkConfigUInt32(const char * name,uint32_t * p_uint32)257 RecLinkConfigUInt32(const char *name, uint32_t *p_uint32)
258 {
259   return RecRegisterConfigUpdateCb(name, link_uint32, (void *)p_uint32);
260 }
261 
262 RecErrT
RecLinkConfigFloat(const char * name,RecFloat * rec_float)263 RecLinkConfigFloat(const char *name, RecFloat *rec_float)
264 {
265   if (RecGetRecordFloat(name, rec_float) == REC_ERR_FAIL) {
266     return REC_ERR_FAIL;
267   }
268   return RecRegisterConfigUpdateCb(name, link_float, (void *)rec_float);
269 }
270 
271 RecErrT
RecLinkConfigCounter(const char * name,RecCounter * rec_counter)272 RecLinkConfigCounter(const char *name, RecCounter *rec_counter)
273 {
274   if (RecGetRecordCounter(name, rec_counter) == REC_ERR_FAIL) {
275     return REC_ERR_FAIL;
276   }
277   return RecRegisterConfigUpdateCb(name, link_counter, (void *)rec_counter);
278 }
279 
280 RecErrT
RecLinkConfigString(const char * name,RecString * rec_string)281 RecLinkConfigString(const char *name, RecString *rec_string)
282 {
283   if (RecGetRecordString_Xmalloc(name, rec_string) == REC_ERR_FAIL) {
284     return REC_ERR_FAIL;
285   }
286   return RecRegisterConfigUpdateCb(name, link_string_alloc, (void *)rec_string);
287 }
288 
289 RecErrT
RecLinkConfigByte(const char * name,RecByte * rec_byte)290 RecLinkConfigByte(const char *name, RecByte *rec_byte)
291 {
292   if (RecGetRecordByte(name, rec_byte) == REC_ERR_FAIL) {
293     return REC_ERR_FAIL;
294   }
295   return RecRegisterConfigUpdateCb(name, link_byte, (void *)rec_byte);
296 }
297 
298 RecErrT
RecLinkConfigBool(const char * name,RecBool * rec_bool)299 RecLinkConfigBool(const char *name, RecBool *rec_bool)
300 {
301   if (RecGetRecordBool(name, rec_bool) == REC_ERR_FAIL) {
302     return REC_ERR_FAIL;
303   }
304   return RecRegisterConfigUpdateCb(name, link_byte, (void *)rec_bool);
305 }
306 
307 //-------------------------------------------------------------------------
308 // RecRegisterConfigUpdateCb
309 //-------------------------------------------------------------------------
310 RecErrT
RecRegisterConfigUpdateCb(const char * name,RecConfigUpdateCb update_cb,void * cookie)311 RecRegisterConfigUpdateCb(const char *name, RecConfigUpdateCb update_cb, void *cookie)
312 {
313   RecErrT err = REC_ERR_FAIL;
314 
315   ink_rwlock_rdlock(&g_records_rwlock);
316 
317   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
318     RecRecord *r = it->second;
319 
320     rec_mutex_acquire(&(r->lock));
321     if (REC_TYPE_IS_CONFIG(r->rec_type)) {
322       /* -- upgrade to support a list of callback functions
323          if (!(r->config_meta.update_cb)) {
324          r->config_meta.update_cb = update_cb;
325          r->config_meta.update_cookie = cookie;
326          err = REC_ERR_OKAY;
327          }
328        */
329 
330       RecConfigUpdateCbList *new_callback = static_cast<RecConfigUpdateCbList *>(ats_malloc(sizeof(RecConfigUpdateCbList)));
331       memset(new_callback, 0, sizeof(RecConfigUpdateCbList));
332       new_callback->update_cb     = update_cb;
333       new_callback->update_cookie = cookie;
334 
335       new_callback->next = nullptr;
336 
337       ink_assert(new_callback);
338       if (!r->config_meta.update_cb_list) {
339         r->config_meta.update_cb_list = new_callback;
340       } else {
341         RecConfigUpdateCbList *cur_callback  = nullptr;
342         RecConfigUpdateCbList *prev_callback = nullptr;
343         for (cur_callback = r->config_meta.update_cb_list; cur_callback; cur_callback = cur_callback->next) {
344           prev_callback = cur_callback;
345         }
346         ink_assert(prev_callback);
347         ink_assert(!prev_callback->next);
348         prev_callback->next = new_callback;
349       }
350       err = REC_ERR_OKAY;
351     }
352 
353     rec_mutex_release(&(r->lock));
354   }
355 
356   ink_rwlock_unlock(&g_records_rwlock);
357 
358   return err;
359 }
360 
361 //-------------------------------------------------------------------------
362 // RecGetRecordXXX
363 //-------------------------------------------------------------------------
364 RecErrT
RecGetRecordInt(const char * name,RecInt * rec_int,bool lock)365 RecGetRecordInt(const char *name, RecInt *rec_int, bool lock)
366 {
367   RecErrT err;
368   RecData data;
369 
370   if ((err = RecGetRecord_Xmalloc(name, RECD_INT, &data, lock)) == REC_ERR_OKAY) {
371     *rec_int = data.rec_int;
372   }
373   return err;
374 }
375 
376 RecErrT
RecGetRecordFloat(const char * name,RecFloat * rec_float,bool lock)377 RecGetRecordFloat(const char *name, RecFloat *rec_float, bool lock)
378 {
379   RecErrT err;
380   RecData data;
381 
382   if ((err = RecGetRecord_Xmalloc(name, RECD_FLOAT, &data, lock)) == REC_ERR_OKAY) {
383     *rec_float = data.rec_float;
384   }
385   return err;
386 }
387 
388 RecErrT
RecGetRecordString(const char * name,char * buf,int buf_len,bool lock)389 RecGetRecordString(const char *name, char *buf, int buf_len, bool lock)
390 {
391   RecErrT err = REC_ERR_OKAY;
392 
393   if (lock) {
394     ink_rwlock_rdlock(&g_records_rwlock);
395   }
396   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
397     RecRecord *r = it->second;
398 
399     rec_mutex_acquire(&(r->lock));
400     if (!r->registered || (r->data_type != RECD_STRING)) {
401       err = REC_ERR_FAIL;
402     } else {
403       if (r->data.rec_string == nullptr) {
404         buf[0] = '\0';
405       } else {
406         ink_strlcpy(buf, r->data.rec_string, buf_len);
407       }
408     }
409     rec_mutex_release(&(r->lock));
410   } else {
411     err = REC_ERR_FAIL;
412   }
413   if (lock) {
414     ink_rwlock_unlock(&g_records_rwlock);
415   }
416   return err;
417 }
418 
419 RecErrT
RecGetRecordString_Xmalloc(const char * name,RecString * rec_string,bool lock)420 RecGetRecordString_Xmalloc(const char *name, RecString *rec_string, bool lock)
421 {
422   RecErrT err;
423   RecData data;
424 
425   if ((err = RecGetRecord_Xmalloc(name, RECD_STRING, &data, lock)) == REC_ERR_OKAY) {
426     *rec_string = data.rec_string;
427   }
428   return err;
429 }
430 
431 RecErrT
RecGetRecordCounter(const char * name,RecCounter * rec_counter,bool lock)432 RecGetRecordCounter(const char *name, RecCounter *rec_counter, bool lock)
433 {
434   RecErrT err;
435   RecData data;
436 
437   if ((err = RecGetRecord_Xmalloc(name, RECD_COUNTER, &data, lock)) == REC_ERR_OKAY) {
438     *rec_counter = data.rec_counter;
439   }
440   return err;
441 }
442 
443 RecErrT
RecGetRecordByte(const char * name,RecByte * rec_byte,bool lock)444 RecGetRecordByte(const char *name, RecByte *rec_byte, bool lock)
445 {
446   RecErrT err;
447   RecData data;
448 
449   if ((err = RecGetRecord_Xmalloc(name, RECD_INT, &data, lock)) == REC_ERR_OKAY) {
450     *rec_byte = data.rec_int;
451   }
452   return err;
453 }
454 
455 RecErrT
RecGetRecordBool(const char * name,RecBool * rec_bool,bool lock)456 RecGetRecordBool(const char *name, RecBool *rec_bool, bool lock)
457 {
458   RecErrT err;
459   RecData data;
460 
461   if ((err = RecGetRecord_Xmalloc(name, RECD_INT, &data, lock)) == REC_ERR_OKAY) {
462     *rec_bool = 0 != data.rec_int;
463   }
464   return err;
465 }
466 
467 //-------------------------------------------------------------------------
468 // RecGetRec Attributes
469 //-------------------------------------------------------------------------
470 
471 RecErrT
RecLookupRecord(const char * name,void (* callback)(const RecRecord *,void *),void * data,bool lock)472 RecLookupRecord(const char *name, void (*callback)(const RecRecord *, void *), void *data, bool lock)
473 {
474   RecErrT err = REC_ERR_FAIL;
475 
476   if (lock) {
477     ink_rwlock_rdlock(&g_records_rwlock);
478   }
479 
480   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
481     RecRecord *r = it->second;
482 
483     rec_mutex_acquire(&(r->lock));
484     callback(r, data);
485     err = REC_ERR_OKAY;
486     rec_mutex_release(&(r->lock));
487   }
488 
489   if (lock) {
490     ink_rwlock_unlock(&g_records_rwlock);
491   }
492 
493   return err;
494 }
495 
496 RecErrT
RecLookupMatchingRecords(unsigned rec_type,const char * match,void (* callback)(const RecRecord *,void *),void * data,bool lock)497 RecLookupMatchingRecords(unsigned rec_type, const char *match, void (*callback)(const RecRecord *, void *), void *data, bool lock)
498 {
499   int num_records;
500   DFA regex;
501 
502   if (!regex.compile(match, RE_CASE_INSENSITIVE | RE_UNANCHORED)) {
503     return REC_ERR_FAIL;
504   }
505 
506   num_records = g_num_records;
507   for (int i = 0; i < num_records; i++) {
508     RecRecord *r = &(g_records[i]);
509 
510     if ((r->rec_type & rec_type) == 0) {
511       continue;
512     }
513 
514     if (regex.match(r->name) < 0) {
515       continue;
516     }
517 
518     rec_mutex_acquire(&(r->lock));
519     callback(r, data);
520     rec_mutex_release(&(r->lock));
521   }
522 
523   return REC_ERR_OKAY;
524 }
525 
526 RecErrT
RecGetRecordType(const char * name,RecT * rec_type,bool lock)527 RecGetRecordType(const char *name, RecT *rec_type, bool lock)
528 {
529   RecErrT err = REC_ERR_FAIL;
530 
531   if (lock) {
532     ink_rwlock_rdlock(&g_records_rwlock);
533   }
534 
535   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
536     RecRecord *r = it->second;
537 
538     rec_mutex_acquire(&(r->lock));
539     *rec_type = r->rec_type;
540     err       = REC_ERR_OKAY;
541     rec_mutex_release(&(r->lock));
542   }
543 
544   if (lock) {
545     ink_rwlock_unlock(&g_records_rwlock);
546   }
547 
548   return err;
549 }
550 
551 RecErrT
RecGetRecordDataType(const char * name,RecDataT * data_type,bool lock)552 RecGetRecordDataType(const char *name, RecDataT *data_type, bool lock)
553 {
554   RecErrT err = REC_ERR_FAIL;
555 
556   if (lock) {
557     ink_rwlock_rdlock(&g_records_rwlock);
558   }
559 
560   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
561     RecRecord *r = it->second;
562 
563     rec_mutex_acquire(&(r->lock));
564     if (!r->registered) {
565       err = REC_ERR_FAIL;
566     } else {
567       *data_type = r->data_type;
568       err        = REC_ERR_OKAY;
569     }
570     rec_mutex_release(&(r->lock));
571   }
572 
573   if (lock) {
574     ink_rwlock_unlock(&g_records_rwlock);
575   }
576 
577   return err;
578 }
579 
580 RecErrT
RecGetRecordPersistenceType(const char * name,RecPersistT * persist_type,bool lock)581 RecGetRecordPersistenceType(const char *name, RecPersistT *persist_type, bool lock)
582 {
583   RecErrT err = REC_ERR_FAIL;
584 
585   if (lock) {
586     ink_rwlock_rdlock(&g_records_rwlock);
587   }
588 
589   *persist_type = RECP_NULL;
590 
591   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
592     RecRecord *r = it->second;
593 
594     rec_mutex_acquire(&(r->lock));
595     if (REC_TYPE_IS_STAT(r->rec_type)) {
596       *persist_type = r->stat_meta.persist_type;
597       err           = REC_ERR_OKAY;
598     }
599     rec_mutex_release(&(r->lock));
600   }
601 
602   if (lock) {
603     ink_rwlock_unlock(&g_records_rwlock);
604   }
605 
606   return err;
607 }
608 
609 RecErrT
RecGetRecordOrderAndId(const char * name,int * order,int * id,bool lock)610 RecGetRecordOrderAndId(const char *name, int *order, int *id, bool lock)
611 {
612   RecErrT err = REC_ERR_FAIL;
613 
614   if (lock) {
615     ink_rwlock_rdlock(&g_records_rwlock);
616   }
617 
618   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
619     RecRecord *r = it->second;
620 
621     if (r->registered) {
622       rec_mutex_acquire(&(r->lock));
623       if (order) {
624         *order = r->order;
625       }
626       if (id) {
627         *id = r->rsb_id;
628       }
629       err = REC_ERR_OKAY;
630       rec_mutex_release(&(r->lock));
631     }
632   }
633 
634   if (lock) {
635     ink_rwlock_unlock(&g_records_rwlock);
636   }
637 
638   return err;
639 }
640 
641 RecErrT
RecGetRecordUpdateType(const char * name,RecUpdateT * update_type,bool lock)642 RecGetRecordUpdateType(const char *name, RecUpdateT *update_type, bool lock)
643 {
644   RecErrT err = REC_ERR_FAIL;
645 
646   if (lock) {
647     ink_rwlock_rdlock(&g_records_rwlock);
648   }
649 
650   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
651     RecRecord *r = it->second;
652 
653     rec_mutex_acquire(&(r->lock));
654     if (REC_TYPE_IS_CONFIG(r->rec_type)) {
655       *update_type = r->config_meta.update_type;
656       err          = REC_ERR_OKAY;
657     } else {
658       ink_assert(!"rec_type is not CONFIG");
659     }
660     rec_mutex_release(&(r->lock));
661   }
662 
663   if (lock) {
664     ink_rwlock_unlock(&g_records_rwlock);
665   }
666 
667   return err;
668 }
669 
670 RecErrT
RecGetRecordCheckType(const char * name,RecCheckT * check_type,bool lock)671 RecGetRecordCheckType(const char *name, RecCheckT *check_type, bool lock)
672 {
673   RecErrT err = REC_ERR_FAIL;
674 
675   if (lock) {
676     ink_rwlock_rdlock(&g_records_rwlock);
677   }
678 
679   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
680     RecRecord *r = it->second;
681 
682     rec_mutex_acquire(&(r->lock));
683     if (REC_TYPE_IS_CONFIG(r->rec_type)) {
684       *check_type = r->config_meta.check_type;
685       err         = REC_ERR_OKAY;
686     } else {
687       ink_assert(!"rec_type is not CONFIG");
688     }
689     rec_mutex_release(&(r->lock));
690   }
691 
692   if (lock) {
693     ink_rwlock_unlock(&g_records_rwlock);
694   }
695 
696   return err;
697 }
698 
699 RecErrT
RecGetRecordCheckExpr(const char * name,char ** check_expr,bool lock)700 RecGetRecordCheckExpr(const char *name, char **check_expr, bool lock)
701 {
702   RecErrT err = REC_ERR_FAIL;
703 
704   if (lock) {
705     ink_rwlock_rdlock(&g_records_rwlock);
706   }
707 
708   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
709     RecRecord *r = it->second;
710 
711     rec_mutex_acquire(&(r->lock));
712     if (REC_TYPE_IS_CONFIG(r->rec_type)) {
713       *check_expr = r->config_meta.check_expr;
714       err         = REC_ERR_OKAY;
715     } else {
716       ink_assert(!"rec_type is not CONFIG");
717     }
718     rec_mutex_release(&(r->lock));
719   }
720 
721   if (lock) {
722     ink_rwlock_unlock(&g_records_rwlock);
723   }
724 
725   return err;
726 }
727 
728 RecErrT
RecGetRecordDefaultDataString_Xmalloc(char * name,char ** buf,bool lock)729 RecGetRecordDefaultDataString_Xmalloc(char *name, char **buf, bool lock)
730 {
731   RecErrT err;
732 
733   if (lock) {
734     ink_rwlock_rdlock(&g_records_rwlock);
735   }
736 
737   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
738     RecRecord *r = it->second;
739 
740     *buf = static_cast<char *>(ats_malloc(sizeof(char) * 1024));
741     memset(*buf, 0, 1024);
742     err = REC_ERR_OKAY;
743 
744     switch (r->data_type) {
745     case RECD_INT:
746       snprintf(*buf, 1023, "%" PRId64 "", r->data_default.rec_int);
747       break;
748     case RECD_FLOAT:
749       snprintf(*buf, 1023, "%f", r->data_default.rec_float);
750       break;
751     case RECD_STRING:
752       if (r->data_default.rec_string) {
753         ink_strlcpy(*buf, r->data_default.rec_string, 1024);
754       } else {
755         ats_free(*buf);
756         *buf = nullptr;
757       }
758       break;
759     case RECD_COUNTER:
760       snprintf(*buf, 1023, "%" PRId64 "", r->data_default.rec_counter);
761       break;
762     default:
763       ink_assert(!"Unexpected RecD type");
764       ats_free(*buf);
765       *buf = nullptr;
766       break;
767     }
768   } else {
769     err = REC_ERR_FAIL;
770   }
771 
772   if (lock) {
773     ink_rwlock_unlock(&g_records_rwlock);
774   }
775 
776   return err;
777 }
778 
779 RecErrT
RecGetRecordAccessType(const char * name,RecAccessT * access,bool lock)780 RecGetRecordAccessType(const char *name, RecAccessT *access, bool lock)
781 {
782   RecErrT err = REC_ERR_FAIL;
783 
784   if (lock) {
785     ink_rwlock_rdlock(&g_records_rwlock);
786   }
787 
788   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
789     RecRecord *r = it->second;
790 
791     rec_mutex_acquire(&(r->lock));
792     *access = r->config_meta.access_type;
793     err     = REC_ERR_OKAY;
794     rec_mutex_release(&(r->lock));
795   }
796 
797   if (lock) {
798     ink_rwlock_unlock(&g_records_rwlock);
799   }
800 
801   return err;
802 }
803 
804 RecErrT
RecSetRecordAccessType(const char * name,RecAccessT access,bool lock)805 RecSetRecordAccessType(const char *name, RecAccessT access, bool lock)
806 {
807   RecErrT err = REC_ERR_FAIL;
808 
809   if (lock) {
810     ink_rwlock_rdlock(&g_records_rwlock);
811   }
812 
813   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
814     RecRecord *r = it->second;
815 
816     rec_mutex_acquire(&(r->lock));
817     r->config_meta.access_type = access;
818     err                        = REC_ERR_OKAY;
819     rec_mutex_release(&(r->lock));
820   }
821 
822   if (lock) {
823     ink_rwlock_unlock(&g_records_rwlock);
824   }
825 
826   return err;
827 }
828 
829 RecErrT
RecGetRecordSource(const char * name,RecSourceT * source,bool lock)830 RecGetRecordSource(const char *name, RecSourceT *source, bool lock)
831 {
832   RecErrT err = REC_ERR_FAIL;
833 
834   if (lock) {
835     ink_rwlock_rdlock(&g_records_rwlock);
836   }
837 
838   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
839     RecRecord *r = it->second;
840 
841     rec_mutex_acquire(&(r->lock));
842     *source = r->config_meta.source;
843     err     = REC_ERR_OKAY;
844     rec_mutex_release(&(r->lock));
845   }
846 
847   if (lock) {
848     ink_rwlock_unlock(&g_records_rwlock);
849   }
850 
851   return err;
852 }
853 
854 //-------------------------------------------------------------------------
855 // RecRegisterStat
856 //-------------------------------------------------------------------------
857 RecRecord *
RecRegisterStat(RecT rec_type,const char * name,RecDataT data_type,RecData data_default,RecPersistT persist_type)858 RecRegisterStat(RecT rec_type, const char *name, RecDataT data_type, RecData data_default, RecPersistT persist_type)
859 {
860   RecRecord *r = nullptr;
861 
862   ink_rwlock_wrlock(&g_records_rwlock);
863   if ((r = register_record(rec_type, name, data_type, data_default, persist_type)) != nullptr) {
864     // If the persistence type we found in the records hash is not the same as the persistence
865     // type we are registering, then that means that it changed between the previous software
866     // version and the current version. If the metric changed to non-persistent, reset to the
867     // new default value.
868     if ((r->stat_meta.persist_type == RECP_NULL || r->stat_meta.persist_type == RECP_PERSISTENT) &&
869         persist_type == RECP_NON_PERSISTENT) {
870       RecDebug(DL_Debug, "resetting default value for formerly persisted stat '%s'", r->name);
871       RecDataSet(r->data_type, &(r->data), &(data_default));
872     }
873 
874     r->stat_meta.persist_type = persist_type;
875   } else {
876     ink_assert(!"Can't register record!");
877     RecDebug(DL_Warning, "failed to register '%s' record", name);
878   }
879   ink_rwlock_unlock(&g_records_rwlock);
880 
881   return r;
882 }
883 
884 //-------------------------------------------------------------------------
885 // RecRegisterConfig
886 //-------------------------------------------------------------------------
887 RecRecord *
RecRegisterConfig(RecT rec_type,const char * name,RecDataT data_type,RecData data_default,RecUpdateT update_type,RecCheckT check_type,const char * check_expr,RecSourceT source,RecAccessT access_type)888 RecRegisterConfig(RecT rec_type, const char *name, RecDataT data_type, RecData data_default, RecUpdateT update_type,
889                   RecCheckT check_type, const char *check_expr, RecSourceT source, RecAccessT access_type)
890 {
891   RecRecord *r;
892   bool updated_p;
893 
894   ink_rwlock_wrlock(&g_records_rwlock);
895   if ((r = register_record(rec_type, name, data_type, data_default, RECP_NULL, &updated_p)) != nullptr) {
896     // Note: do not modify 'record->config_meta.update_required'
897     r->config_meta.update_type = update_type;
898     r->config_meta.check_type  = check_type;
899     if (r->config_meta.check_expr) {
900       ats_free(r->config_meta.check_expr);
901     }
902     r->config_meta.check_expr     = ats_strdup(check_expr);
903     r->config_meta.update_cb_list = nullptr;
904     r->config_meta.access_type    = access_type;
905     if (!updated_p) {
906       r->config_meta.source = source;
907     }
908   }
909   ink_rwlock_unlock(&g_records_rwlock);
910 
911   return r;
912 }
913 
914 //-------------------------------------------------------------------------
915 // RecGetRecord_Xmalloc
916 //-------------------------------------------------------------------------
917 RecErrT
RecGetRecord_Xmalloc(const char * name,RecDataT data_type,RecData * data,bool lock)918 RecGetRecord_Xmalloc(const char *name, RecDataT data_type, RecData *data, bool lock)
919 {
920   RecErrT err = REC_ERR_OKAY;
921 
922   if (lock) {
923     ink_rwlock_rdlock(&g_records_rwlock);
924   }
925 
926   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
927     RecRecord *r = it->second;
928 
929     rec_mutex_acquire(&(r->lock));
930     if (!r->registered || (r->data_type != data_type)) {
931       err = REC_ERR_FAIL;
932     } else {
933       // Clear the caller's record just in case it has trash in it.
934       // Passing trashy records to RecDataSet will cause confusion.
935       memset(data, 0, sizeof(RecData));
936       RecDataSet(data_type, data, &(r->data));
937     }
938     rec_mutex_release(&(r->lock));
939   } else {
940     err = REC_ERR_FAIL;
941   }
942 
943   if (lock) {
944     ink_rwlock_unlock(&g_records_rwlock);
945   }
946 
947   return err;
948 }
949 
950 //-------------------------------------------------------------------------
951 // RecForceInsert
952 //-------------------------------------------------------------------------
953 RecRecord *
RecForceInsert(RecRecord * record)954 RecForceInsert(RecRecord *record)
955 {
956   RecRecord *r = nullptr;
957   bool r_is_a_new_record;
958 
959   ink_rwlock_wrlock(&g_records_rwlock);
960 
961   if (auto it = g_records_ht.find(record->name); it != g_records_ht.end()) {
962     r                 = it->second;
963     r_is_a_new_record = false;
964     rec_mutex_acquire(&(r->lock));
965     r->rec_type  = record->rec_type;
966     r->data_type = record->data_type;
967   } else {
968     r_is_a_new_record = true;
969     if ((r = RecAlloc(record->rec_type, record->name, record->data_type)) == nullptr) {
970       ink_rwlock_unlock(&g_records_rwlock);
971       return nullptr;
972     }
973   }
974 
975   // set the record value
976   RecDataSet(r->data_type, &(r->data), &(record->data));
977   RecDataSet(r->data_type, &(r->data_default), &(record->data_default));
978 
979   r->registered = record->registered;
980   r->rsb_id     = record->rsb_id;
981 
982   if (REC_TYPE_IS_STAT(r->rec_type)) {
983     r->stat_meta.persist_type = record->stat_meta.persist_type;
984     r->stat_meta.data_raw     = record->stat_meta.data_raw;
985   } else if (REC_TYPE_IS_CONFIG(r->rec_type)) {
986     r->config_meta.update_required = record->config_meta.update_required;
987     r->config_meta.update_type     = record->config_meta.update_type;
988     r->config_meta.check_type      = record->config_meta.check_type;
989     ats_free(r->config_meta.check_expr);
990     r->config_meta.check_expr  = ats_strdup(record->config_meta.check_expr);
991     r->config_meta.access_type = record->config_meta.access_type;
992     r->config_meta.source      = record->config_meta.source;
993   }
994 
995   if (r_is_a_new_record) {
996     g_records_ht.emplace(r->name, r);
997   } else {
998     rec_mutex_release(&(r->lock));
999   }
1000 
1001   ink_rwlock_unlock(&g_records_rwlock);
1002 
1003   return r;
1004 }
1005 
1006 //-------------------------------------------------------------------------
1007 // RecDumpRecordsHt
1008 //-------------------------------------------------------------------------
1009 
1010 static void
debug_record_callback(RecT,void *,int registered,const char * name,int data_type,RecData * datum)1011 debug_record_callback(RecT /* rec_type */, void * /* edata */, int registered, const char *name, int data_type, RecData *datum)
1012 {
1013   switch (data_type) {
1014   case RECD_INT:
1015     RecDebug(DL_Note, "  ([%d] '%s', '%" PRId64 "')", registered, name, datum->rec_int);
1016     break;
1017   case RECD_FLOAT:
1018     RecDebug(DL_Note, "  ([%d] '%s', '%f')", registered, name, datum->rec_float);
1019     break;
1020   case RECD_STRING:
1021     RecDebug(DL_Note, "  ([%d] '%s', '%s')", registered, name, datum->rec_string ? datum->rec_string : "NULL");
1022     break;
1023   case RECD_COUNTER:
1024     RecDebug(DL_Note, "  ([%d] '%s', '%" PRId64 "')", registered, name, datum->rec_counter);
1025     break;
1026   default:
1027     RecDebug(DL_Note, "  ([%d] '%s', <? ? ?>)", registered, name);
1028     break;
1029   }
1030 }
1031 
1032 void
RecDumpRecords(RecT rec_type,RecDumpEntryCb callback,void * edata)1033 RecDumpRecords(RecT rec_type, RecDumpEntryCb callback, void *edata)
1034 {
1035   int i, num_records;
1036 
1037   num_records = g_num_records;
1038   for (i = 0; i < num_records; i++) {
1039     RecRecord *r = &(g_records[i]);
1040     if ((rec_type == RECT_NULL) || (rec_type & r->rec_type)) {
1041       rec_mutex_acquire(&(r->lock));
1042       callback(r->rec_type, edata, r->registered, r->name, r->data_type, &r->data);
1043       rec_mutex_release(&(r->lock));
1044     }
1045   }
1046 }
1047 
1048 void
RecDumpRecordsHt(RecT rec_type)1049 RecDumpRecordsHt(RecT rec_type)
1050 {
1051   RecDebug(DL_Note, "Dumping Records:");
1052   RecDumpRecords(rec_type, debug_record_callback, nullptr);
1053 }
1054 
1055 //-------------------------------------------------------------------------
1056 // Backwards compatibility ... TODO: Should eliminate these
1057 //-------------------------------------------------------------------------
1058 RecInt
REC_ConfigReadInteger(const char * name)1059 REC_ConfigReadInteger(const char *name)
1060 {
1061   RecInt t = 0;
1062   RecGetRecordInt(name, &t);
1063   return t;
1064 }
1065 
1066 char *
REC_ConfigReadString(const char * name)1067 REC_ConfigReadString(const char *name)
1068 {
1069   char *t = nullptr;
1070   RecGetRecordString_Xmalloc(name, static_cast<RecString *>(&t));
1071   return t;
1072 }
1073 
1074 RecFloat
REC_ConfigReadFloat(const char * name)1075 REC_ConfigReadFloat(const char *name)
1076 {
1077   RecFloat t = 0;
1078   RecGetRecordFloat(name, &t);
1079   return t;
1080 }
1081 
1082 RecCounter
REC_ConfigReadCounter(const char * name)1083 REC_ConfigReadCounter(const char *name)
1084 {
1085   RecCounter t = 0;
1086   RecGetRecordCounter(name, &t);
1087   return t;
1088 }
1089 
1090 //-------------------------------------------------------------------------
1091 // Backwards compatibility. TODO: Should remove these.
1092 //-------------------------------------------------------------------------
1093 RecInt
REC_readInteger(const char * name,bool * found,bool lock)1094 REC_readInteger(const char *name, bool *found, bool lock)
1095 {
1096   ink_assert(name);
1097   RecInt _tmp = 0;
1098   bool _found = (RecGetRecordInt(name, &_tmp, lock) == REC_ERR_OKAY);
1099 
1100   if (found) {
1101     *found = _found;
1102   }
1103   return _tmp;
1104 }
1105 
1106 RecFloat
REC_readFloat(char * name,bool * found,bool lock)1107 REC_readFloat(char *name, bool *found, bool lock)
1108 {
1109   ink_assert(name);
1110   RecFloat _tmp = 0.0;
1111   bool _found   = (RecGetRecordFloat(name, &_tmp, lock) == REC_ERR_OKAY);
1112 
1113   if (found) {
1114     *found = _found;
1115   }
1116   return _tmp;
1117 }
1118 
1119 RecCounter
REC_readCounter(char * name,bool * found,bool lock)1120 REC_readCounter(char *name, bool *found, bool lock)
1121 {
1122   ink_assert(name);
1123   RecCounter _tmp = 0;
1124   bool _found     = (RecGetRecordCounter(name, &_tmp, lock) == REC_ERR_OKAY);
1125 
1126   if (found) {
1127     *found = _found;
1128   }
1129   return _tmp;
1130 }
1131 
1132 RecString
REC_readString(const char * name,bool * found,bool lock)1133 REC_readString(const char *name, bool *found, bool lock)
1134 {
1135   ink_assert(name);
1136   RecString _tmp = nullptr;
1137   bool _found    = (RecGetRecordString_Xmalloc(name, &_tmp, lock) == REC_ERR_OKAY);
1138 
1139   if (found) {
1140     *found = _found;
1141   }
1142   return _tmp;
1143 }
1144 
1145 //-------------------------------------------------------------------------
1146 // RecConfigReadConfigDir
1147 //-------------------------------------------------------------------------
1148 std::string
RecConfigReadConfigDir()1149 RecConfigReadConfigDir()
1150 {
1151   if (const char *env = getenv("PROXY_CONFIG_CONFIG_DIR")) {
1152     return Layout::get()->relative(env);
1153   } else {
1154     return Layout::get()->sysconfdir;
1155   }
1156 }
1157 
1158 //-------------------------------------------------------------------------
1159 // RecConfigReadRuntimeDir
1160 //-------------------------------------------------------------------------
1161 std::string
RecConfigReadRuntimeDir()1162 RecConfigReadRuntimeDir()
1163 {
1164   char buf[PATH_NAME_MAX];
1165 
1166   buf[0] = '\0';
1167   RecGetRecordString("proxy.config.local_state_dir", buf, PATH_NAME_MAX);
1168   if (strlen(buf) > 0) {
1169     return Layout::get()->relative(buf);
1170   } else {
1171     return Layout::get()->runtimedir;
1172   }
1173 }
1174 
1175 //-------------------------------------------------------------------------
1176 // RecConfigReadLogDir
1177 //-------------------------------------------------------------------------
1178 std::string
RecConfigReadLogDir()1179 RecConfigReadLogDir()
1180 {
1181   char buf[PATH_NAME_MAX];
1182 
1183   buf[0] = '\0';
1184   RecGetRecordString("proxy.config.log.logfile_dir", buf, PATH_NAME_MAX);
1185   if (strlen(buf) > 0) {
1186     return Layout::get()->relative(buf);
1187   } else {
1188     return Layout::get()->logdir;
1189   }
1190 }
1191 
1192 //-------------------------------------------------------------------------
1193 // RecConfigReadBinDir
1194 //-------------------------------------------------------------------------
1195 std::string
RecConfigReadBinDir()1196 RecConfigReadBinDir()
1197 {
1198   char buf[PATH_NAME_MAX];
1199 
1200   buf[0] = '\0';
1201   RecGetRecordString("proxy.config.bin_path", buf, PATH_NAME_MAX);
1202   if (strlen(buf) > 0) {
1203     return Layout::get()->relative(buf);
1204   } else {
1205     return Layout::get()->bindir;
1206   }
1207 }
1208 
1209 //-------------------------------------------------------------------------
1210 // RecConfigReadPluginDir
1211 //-------------------------------------------------------------------------
1212 std::string
RecConfigReadPluginDir()1213 RecConfigReadPluginDir()
1214 {
1215   char buf[PATH_NAME_MAX];
1216 
1217   buf[0] = '\0';
1218   RecGetRecordString("proxy.config.plugin.plugin_dir", buf, PATH_NAME_MAX);
1219   if (strlen(buf) > 0) {
1220     return Layout::get()->relative(buf);
1221   } else {
1222     return Layout::get()->libexecdir;
1223   }
1224 }
1225 
1226 //-------------------------------------------------------------------------
1227 // RecConfigReadConfigPath
1228 //-------------------------------------------------------------------------
1229 std::string
RecConfigReadConfigPath(const char * file_variable,const char * default_value)1230 RecConfigReadConfigPath(const char *file_variable, const char *default_value)
1231 {
1232   std::string sysconfdir(RecConfigReadConfigDir());
1233 
1234   // If the file name is in a configuration variable, look it up first ...
1235   if (file_variable) {
1236     char buf[PATH_NAME_MAX];
1237 
1238     buf[0] = '\0';
1239     RecGetRecordString(file_variable, buf, PATH_NAME_MAX);
1240     if (strlen(buf) > 0) {
1241       return Layout::get()->relative_to(sysconfdir, buf);
1242     }
1243   }
1244 
1245   // Otherwise take the default ...
1246   if (default_value) {
1247     return Layout::get()->relative_to(sysconfdir, default_value);
1248   }
1249 
1250   return {};
1251 }
1252 
1253 //-------------------------------------------------------------------------
1254 // RecConfigReadPersistentStatsPath
1255 //-------------------------------------------------------------------------
1256 std::string
RecConfigReadPersistentStatsPath()1257 RecConfigReadPersistentStatsPath()
1258 {
1259   std::string rundir(RecConfigReadRuntimeDir());
1260   return Layout::relative_to(rundir, ts::filename::RECORDS_STATS);
1261 }
1262 
1263 void
RecSignalWarning(int sig,const char * fmt,...)1264 RecSignalWarning(int sig, const char *fmt, ...)
1265 {
1266   char msg[1024];
1267   va_list args;
1268 
1269   va_start(args, fmt);
1270   WarningV(fmt, args);
1271   va_end(args);
1272 
1273   va_start(args, fmt);
1274   vsnprintf(msg, sizeof(msg), fmt, args);
1275   RecSignalManager(sig, msg);
1276   va_end(args);
1277 }
1278 
1279 //-------------------------------------------------------------------------
1280 // RecConfigWarnIfUnregistered
1281 //-------------------------------------------------------------------------
1282 /// Generate a warning if the record is a configuration name/value but is not registered.
1283 void
RecConfigWarnIfUnregistered()1284 RecConfigWarnIfUnregistered()
1285 {
1286   RecDumpRecords(
1287     RECT_CONFIG,
1288     [](RecT, void *, int registered_p, const char *name, int, RecData *) -> void {
1289       if (!registered_p) {
1290         Warning("Unrecognized configuration value '%s'", name);
1291       }
1292     },
1293     nullptr);
1294 }
1295