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_DEFAULT_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,bool check_sync_cb)610 RecGetRecordOrderAndId(const char *name, int *order, int *id, bool lock, bool check_sync_cb)
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       if (!check_sync_cb || r->stat_meta.sync_cb) {
623         rec_mutex_acquire(&(r->lock));
624         if (order) {
625           *order = r->order;
626         }
627         if (id) {
628           *id = r->rsb_id;
629         }
630         err = REC_ERR_OKAY;
631         rec_mutex_release(&(r->lock));
632       }
633     }
634   }
635 
636   if (lock) {
637     ink_rwlock_unlock(&g_records_rwlock);
638   }
639 
640   return err;
641 }
642 
643 RecErrT
RecGetRecordUpdateType(const char * name,RecUpdateT * update_type,bool lock)644 RecGetRecordUpdateType(const char *name, RecUpdateT *update_type, bool lock)
645 {
646   RecErrT err = REC_ERR_FAIL;
647 
648   if (lock) {
649     ink_rwlock_rdlock(&g_records_rwlock);
650   }
651 
652   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
653     RecRecord *r = it->second;
654 
655     rec_mutex_acquire(&(r->lock));
656     if (REC_TYPE_IS_CONFIG(r->rec_type)) {
657       *update_type = r->config_meta.update_type;
658       err          = REC_ERR_OKAY;
659     } else {
660       ink_assert(!"rec_type is not CONFIG");
661     }
662     rec_mutex_release(&(r->lock));
663   }
664 
665   if (lock) {
666     ink_rwlock_unlock(&g_records_rwlock);
667   }
668 
669   return err;
670 }
671 
672 RecErrT
RecGetRecordCheckType(const char * name,RecCheckT * check_type,bool lock)673 RecGetRecordCheckType(const char *name, RecCheckT *check_type, bool lock)
674 {
675   RecErrT err = REC_ERR_FAIL;
676 
677   if (lock) {
678     ink_rwlock_rdlock(&g_records_rwlock);
679   }
680 
681   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
682     RecRecord *r = it->second;
683 
684     rec_mutex_acquire(&(r->lock));
685     if (REC_TYPE_IS_CONFIG(r->rec_type)) {
686       *check_type = r->config_meta.check_type;
687       err         = REC_ERR_OKAY;
688     } else {
689       ink_assert(!"rec_type is not CONFIG");
690     }
691     rec_mutex_release(&(r->lock));
692   }
693 
694   if (lock) {
695     ink_rwlock_unlock(&g_records_rwlock);
696   }
697 
698   return err;
699 }
700 
701 RecErrT
RecGetRecordCheckExpr(const char * name,char ** check_expr,bool lock)702 RecGetRecordCheckExpr(const char *name, char **check_expr, bool lock)
703 {
704   RecErrT err = REC_ERR_FAIL;
705 
706   if (lock) {
707     ink_rwlock_rdlock(&g_records_rwlock);
708   }
709 
710   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
711     RecRecord *r = it->second;
712 
713     rec_mutex_acquire(&(r->lock));
714     if (REC_TYPE_IS_CONFIG(r->rec_type)) {
715       *check_expr = r->config_meta.check_expr;
716       err         = REC_ERR_OKAY;
717     } else {
718       ink_assert(!"rec_type is not CONFIG");
719     }
720     rec_mutex_release(&(r->lock));
721   }
722 
723   if (lock) {
724     ink_rwlock_unlock(&g_records_rwlock);
725   }
726 
727   return err;
728 }
729 
730 RecErrT
RecGetRecordDefaultDataString_Xmalloc(char * name,char ** buf,bool lock)731 RecGetRecordDefaultDataString_Xmalloc(char *name, char **buf, bool lock)
732 {
733   RecErrT err;
734 
735   if (lock) {
736     ink_rwlock_rdlock(&g_records_rwlock);
737   }
738 
739   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
740     RecRecord *r = it->second;
741 
742     *buf = static_cast<char *>(ats_malloc(sizeof(char) * 1024));
743     memset(*buf, 0, 1024);
744     err = REC_ERR_OKAY;
745 
746     switch (r->data_type) {
747     case RECD_INT:
748       snprintf(*buf, 1023, "%" PRId64 "", r->data_default.rec_int);
749       break;
750     case RECD_FLOAT:
751       snprintf(*buf, 1023, "%f", r->data_default.rec_float);
752       break;
753     case RECD_STRING:
754       if (r->data_default.rec_string) {
755         ink_strlcpy(*buf, r->data_default.rec_string, 1024);
756       } else {
757         ats_free(*buf);
758         *buf = nullptr;
759       }
760       break;
761     case RECD_COUNTER:
762       snprintf(*buf, 1023, "%" PRId64 "", r->data_default.rec_counter);
763       break;
764     default:
765       ink_assert(!"Unexpected RecD type");
766       ats_free(*buf);
767       *buf = nullptr;
768       break;
769     }
770   } else {
771     err = REC_ERR_FAIL;
772   }
773 
774   if (lock) {
775     ink_rwlock_unlock(&g_records_rwlock);
776   }
777 
778   return err;
779 }
780 
781 RecErrT
RecGetRecordAccessType(const char * name,RecAccessT * access,bool lock)782 RecGetRecordAccessType(const char *name, RecAccessT *access, bool lock)
783 {
784   RecErrT err = REC_ERR_FAIL;
785 
786   if (lock) {
787     ink_rwlock_rdlock(&g_records_rwlock);
788   }
789 
790   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
791     RecRecord *r = it->second;
792 
793     rec_mutex_acquire(&(r->lock));
794     *access = r->config_meta.access_type;
795     err     = REC_ERR_OKAY;
796     rec_mutex_release(&(r->lock));
797   }
798 
799   if (lock) {
800     ink_rwlock_unlock(&g_records_rwlock);
801   }
802 
803   return err;
804 }
805 
806 RecErrT
RecSetRecordAccessType(const char * name,RecAccessT access,bool lock)807 RecSetRecordAccessType(const char *name, RecAccessT access, bool lock)
808 {
809   RecErrT err = REC_ERR_FAIL;
810 
811   if (lock) {
812     ink_rwlock_rdlock(&g_records_rwlock);
813   }
814 
815   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
816     RecRecord *r = it->second;
817 
818     rec_mutex_acquire(&(r->lock));
819     r->config_meta.access_type = access;
820     err                        = REC_ERR_OKAY;
821     rec_mutex_release(&(r->lock));
822   }
823 
824   if (lock) {
825     ink_rwlock_unlock(&g_records_rwlock);
826   }
827 
828   return err;
829 }
830 
831 RecErrT
RecGetRecordSource(const char * name,RecSourceT * source,bool lock)832 RecGetRecordSource(const char *name, RecSourceT *source, bool lock)
833 {
834   RecErrT err = REC_ERR_FAIL;
835 
836   if (lock) {
837     ink_rwlock_rdlock(&g_records_rwlock);
838   }
839 
840   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
841     RecRecord *r = it->second;
842 
843     rec_mutex_acquire(&(r->lock));
844     *source = r->config_meta.source;
845     err     = REC_ERR_OKAY;
846     rec_mutex_release(&(r->lock));
847   }
848 
849   if (lock) {
850     ink_rwlock_unlock(&g_records_rwlock);
851   }
852 
853   return err;
854 }
855 
856 //-------------------------------------------------------------------------
857 // RecRegisterStat
858 //-------------------------------------------------------------------------
859 RecRecord *
RecRegisterStat(RecT rec_type,const char * name,RecDataT data_type,RecData data_default,RecPersistT persist_type)860 RecRegisterStat(RecT rec_type, const char *name, RecDataT data_type, RecData data_default, RecPersistT persist_type)
861 {
862   RecRecord *r = nullptr;
863 
864   ink_rwlock_wrlock(&g_records_rwlock);
865   if ((r = register_record(rec_type, name, data_type, data_default, persist_type)) != nullptr) {
866     // If the persistence type we found in the records hash is not the same as the persistence
867     // type we are registering, then that means that it changed between the previous software
868     // version and the current version. If the metric changed to non-persistent, reset to the
869     // new default value.
870     if ((r->stat_meta.persist_type == RECP_NULL || r->stat_meta.persist_type == RECP_PERSISTENT) &&
871         persist_type == RECP_NON_PERSISTENT) {
872       RecDebug(DL_Debug, "resetting default value for formerly persisted stat '%s'", r->name);
873       RecDataSet(r->data_type, &(r->data), &(data_default));
874     }
875 
876     r->stat_meta.persist_type = persist_type;
877   } else {
878     ink_assert(!"Can't register record!");
879     RecDebug(DL_Warning, "failed to register '%s' record", name);
880   }
881   ink_rwlock_unlock(&g_records_rwlock);
882 
883   return r;
884 }
885 
886 //-------------------------------------------------------------------------
887 // RecRegisterConfig
888 //-------------------------------------------------------------------------
889 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)890 RecRegisterConfig(RecT rec_type, const char *name, RecDataT data_type, RecData data_default, RecUpdateT update_type,
891                   RecCheckT check_type, const char *check_expr, RecSourceT source, RecAccessT access_type)
892 {
893   RecRecord *r;
894   bool updated_p;
895 
896   ink_rwlock_wrlock(&g_records_rwlock);
897   if ((r = register_record(rec_type, name, data_type, data_default, RECP_NULL, &updated_p)) != nullptr) {
898     // Note: do not modify 'record->config_meta.update_required'
899     r->config_meta.update_type = update_type;
900     r->config_meta.check_type  = check_type;
901     if (r->config_meta.check_expr) {
902       ats_free(r->config_meta.check_expr);
903     }
904     r->config_meta.check_expr     = ats_strdup(check_expr);
905     r->config_meta.update_cb_list = nullptr;
906     r->config_meta.access_type    = access_type;
907     if (!updated_p) {
908       r->config_meta.source = source;
909     }
910   }
911   ink_rwlock_unlock(&g_records_rwlock);
912 
913   return r;
914 }
915 
916 //-------------------------------------------------------------------------
917 // RecGetRecord_Xmalloc
918 //-------------------------------------------------------------------------
919 RecErrT
RecGetRecord_Xmalloc(const char * name,RecDataT data_type,RecData * data,bool lock)920 RecGetRecord_Xmalloc(const char *name, RecDataT data_type, RecData *data, bool lock)
921 {
922   RecErrT err = REC_ERR_OKAY;
923 
924   if (lock) {
925     ink_rwlock_rdlock(&g_records_rwlock);
926   }
927 
928   if (auto it = g_records_ht.find(name); it != g_records_ht.end()) {
929     RecRecord *r = it->second;
930 
931     rec_mutex_acquire(&(r->lock));
932     if (!r->registered || (r->data_type != data_type)) {
933       err = REC_ERR_FAIL;
934     } else {
935       // Clear the caller's record just in case it has trash in it.
936       // Passing trashy records to RecDataSet will cause confusion.
937       memset(data, 0, sizeof(RecData));
938       RecDataSet(data_type, data, &(r->data));
939     }
940     rec_mutex_release(&(r->lock));
941   } else {
942     err = REC_ERR_FAIL;
943   }
944 
945   if (lock) {
946     ink_rwlock_unlock(&g_records_rwlock);
947   }
948 
949   return err;
950 }
951 
952 //-------------------------------------------------------------------------
953 // RecForceInsert
954 //-------------------------------------------------------------------------
955 RecRecord *
RecForceInsert(RecRecord * record)956 RecForceInsert(RecRecord *record)
957 {
958   RecRecord *r = nullptr;
959   bool r_is_a_new_record;
960 
961   ink_rwlock_wrlock(&g_records_rwlock);
962 
963   if (auto it = g_records_ht.find(record->name); it != g_records_ht.end()) {
964     r                 = it->second;
965     r_is_a_new_record = false;
966     rec_mutex_acquire(&(r->lock));
967     r->rec_type  = record->rec_type;
968     r->data_type = record->data_type;
969   } else {
970     r_is_a_new_record = true;
971     if ((r = RecAlloc(record->rec_type, record->name, record->data_type)) == nullptr) {
972       ink_rwlock_unlock(&g_records_rwlock);
973       return nullptr;
974     }
975   }
976 
977   // set the record value
978   RecDataSet(r->data_type, &(r->data), &(record->data));
979   RecDataSet(r->data_type, &(r->data_default), &(record->data_default));
980 
981   r->registered = record->registered;
982   r->rsb_id     = record->rsb_id;
983 
984   if (REC_TYPE_IS_STAT(r->rec_type)) {
985     r->stat_meta.persist_type = record->stat_meta.persist_type;
986     r->stat_meta.data_raw     = record->stat_meta.data_raw;
987   } else if (REC_TYPE_IS_CONFIG(r->rec_type)) {
988     r->config_meta.update_required = record->config_meta.update_required;
989     r->config_meta.update_type     = record->config_meta.update_type;
990     r->config_meta.check_type      = record->config_meta.check_type;
991     ats_free(r->config_meta.check_expr);
992     r->config_meta.check_expr  = ats_strdup(record->config_meta.check_expr);
993     r->config_meta.access_type = record->config_meta.access_type;
994     r->config_meta.source      = record->config_meta.source;
995   }
996 
997   if (r_is_a_new_record) {
998     g_records_ht.emplace(r->name, r);
999   } else {
1000     rec_mutex_release(&(r->lock));
1001   }
1002 
1003   ink_rwlock_unlock(&g_records_rwlock);
1004 
1005   return r;
1006 }
1007 
1008 //-------------------------------------------------------------------------
1009 // RecDumpRecordsHt
1010 //-------------------------------------------------------------------------
1011 
1012 static void
debug_record_callback(RecT,void *,int registered,const char * name,int data_type,RecData * datum)1013 debug_record_callback(RecT /* rec_type */, void * /* edata */, int registered, const char *name, int data_type, RecData *datum)
1014 {
1015   switch (data_type) {
1016   case RECD_INT:
1017     RecDebug(DL_Note, "  ([%d] '%s', '%" PRId64 "')", registered, name, datum->rec_int);
1018     break;
1019   case RECD_FLOAT:
1020     RecDebug(DL_Note, "  ([%d] '%s', '%f')", registered, name, datum->rec_float);
1021     break;
1022   case RECD_STRING:
1023     RecDebug(DL_Note, "  ([%d] '%s', '%s')", registered, name, datum->rec_string ? datum->rec_string : "NULL");
1024     break;
1025   case RECD_COUNTER:
1026     RecDebug(DL_Note, "  ([%d] '%s', '%" PRId64 "')", registered, name, datum->rec_counter);
1027     break;
1028   default:
1029     RecDebug(DL_Note, "  ([%d] '%s', <? ? ?>)", registered, name);
1030     break;
1031   }
1032 }
1033 
1034 void
RecDumpRecords(RecT rec_type,RecDumpEntryCb callback,void * edata)1035 RecDumpRecords(RecT rec_type, RecDumpEntryCb callback, void *edata)
1036 {
1037   int i, num_records;
1038 
1039   num_records = g_num_records;
1040   for (i = 0; i < num_records; i++) {
1041     RecRecord *r = &(g_records[i]);
1042     if ((rec_type == RECT_NULL) || (rec_type & r->rec_type)) {
1043       rec_mutex_acquire(&(r->lock));
1044       callback(r->rec_type, edata, r->registered, r->name, r->data_type, &r->data);
1045       rec_mutex_release(&(r->lock));
1046     }
1047   }
1048 }
1049 
1050 void
RecDumpRecordsHt(RecT rec_type)1051 RecDumpRecordsHt(RecT rec_type)
1052 {
1053   RecDebug(DL_Note, "Dumping Records:");
1054   RecDumpRecords(rec_type, debug_record_callback, nullptr);
1055 }
1056 
1057 //-------------------------------------------------------------------------
1058 // Backwards compatibility ... TODO: Should eliminate these
1059 //-------------------------------------------------------------------------
1060 RecInt
REC_ConfigReadInteger(const char * name)1061 REC_ConfigReadInteger(const char *name)
1062 {
1063   RecInt t = 0;
1064   RecGetRecordInt(name, &t);
1065   return t;
1066 }
1067 
1068 char *
REC_ConfigReadString(const char * name)1069 REC_ConfigReadString(const char *name)
1070 {
1071   char *t = nullptr;
1072   RecGetRecordString_Xmalloc(name, static_cast<RecString *>(&t));
1073   return t;
1074 }
1075 
1076 RecFloat
REC_ConfigReadFloat(const char * name)1077 REC_ConfigReadFloat(const char *name)
1078 {
1079   RecFloat t = 0;
1080   RecGetRecordFloat(name, &t);
1081   return t;
1082 }
1083 
1084 RecCounter
REC_ConfigReadCounter(const char * name)1085 REC_ConfigReadCounter(const char *name)
1086 {
1087   RecCounter t = 0;
1088   RecGetRecordCounter(name, &t);
1089   return t;
1090 }
1091 
1092 //-------------------------------------------------------------------------
1093 // Backwards compatibility. TODO: Should remove these.
1094 //-------------------------------------------------------------------------
1095 RecInt
REC_readInteger(const char * name,bool * found,bool lock)1096 REC_readInteger(const char *name, bool *found, bool lock)
1097 {
1098   ink_assert(name);
1099   RecInt _tmp = 0;
1100   bool _found = (RecGetRecordInt(name, &_tmp, lock) == REC_ERR_OKAY);
1101 
1102   if (found) {
1103     *found = _found;
1104   }
1105   return _tmp;
1106 }
1107 
1108 RecFloat
REC_readFloat(char * name,bool * found,bool lock)1109 REC_readFloat(char *name, bool *found, bool lock)
1110 {
1111   ink_assert(name);
1112   RecFloat _tmp = 0.0;
1113   bool _found   = (RecGetRecordFloat(name, &_tmp, lock) == REC_ERR_OKAY);
1114 
1115   if (found) {
1116     *found = _found;
1117   }
1118   return _tmp;
1119 }
1120 
1121 RecCounter
REC_readCounter(char * name,bool * found,bool lock)1122 REC_readCounter(char *name, bool *found, bool lock)
1123 {
1124   ink_assert(name);
1125   RecCounter _tmp = 0;
1126   bool _found     = (RecGetRecordCounter(name, &_tmp, lock) == REC_ERR_OKAY);
1127 
1128   if (found) {
1129     *found = _found;
1130   }
1131   return _tmp;
1132 }
1133 
1134 RecString
REC_readString(const char * name,bool * found,bool lock)1135 REC_readString(const char *name, bool *found, bool lock)
1136 {
1137   ink_assert(name);
1138   RecString _tmp = nullptr;
1139   bool _found    = (RecGetRecordString_Xmalloc(name, &_tmp, lock) == REC_ERR_OKAY);
1140 
1141   if (found) {
1142     *found = _found;
1143   }
1144   return _tmp;
1145 }
1146 
1147 //-------------------------------------------------------------------------
1148 // RecConfigReadConfigDir
1149 //-------------------------------------------------------------------------
1150 std::string
RecConfigReadConfigDir()1151 RecConfigReadConfigDir()
1152 {
1153   if (const char *env = getenv("PROXY_CONFIG_CONFIG_DIR")) {
1154     return Layout::get()->relative(env);
1155   } else {
1156     return Layout::get()->sysconfdir;
1157   }
1158 }
1159 
1160 //-------------------------------------------------------------------------
1161 // RecConfigReadRuntimeDir
1162 //-------------------------------------------------------------------------
1163 std::string
RecConfigReadRuntimeDir()1164 RecConfigReadRuntimeDir()
1165 {
1166   char buf[PATH_NAME_MAX];
1167 
1168   buf[0] = '\0';
1169   RecGetRecordString("proxy.config.local_state_dir", buf, PATH_NAME_MAX);
1170   if (strlen(buf) > 0) {
1171     return Layout::get()->relative(buf);
1172   } else {
1173     return Layout::get()->runtimedir;
1174   }
1175 }
1176 
1177 //-------------------------------------------------------------------------
1178 // RecConfigReadLogDir
1179 //-------------------------------------------------------------------------
1180 std::string
RecConfigReadLogDir()1181 RecConfigReadLogDir()
1182 {
1183   char buf[PATH_NAME_MAX];
1184 
1185   buf[0] = '\0';
1186   RecGetRecordString("proxy.config.log.logfile_dir", buf, PATH_NAME_MAX);
1187   if (strlen(buf) > 0) {
1188     return Layout::get()->relative(buf);
1189   } else {
1190     return Layout::get()->logdir;
1191   }
1192 }
1193 
1194 //-------------------------------------------------------------------------
1195 // RecConfigReadBinDir
1196 //-------------------------------------------------------------------------
1197 std::string
RecConfigReadBinDir()1198 RecConfigReadBinDir()
1199 {
1200   char buf[PATH_NAME_MAX];
1201 
1202   buf[0] = '\0';
1203   RecGetRecordString("proxy.config.bin_path", buf, PATH_NAME_MAX);
1204   if (strlen(buf) > 0) {
1205     return Layout::get()->relative(buf);
1206   } else {
1207     return Layout::get()->bindir;
1208   }
1209 }
1210 
1211 //-------------------------------------------------------------------------
1212 // RecConfigReadPluginDir
1213 //-------------------------------------------------------------------------
1214 std::string
RecConfigReadPluginDir()1215 RecConfigReadPluginDir()
1216 {
1217   char buf[PATH_NAME_MAX];
1218 
1219   buf[0] = '\0';
1220   RecGetRecordString("proxy.config.plugin.plugin_dir", buf, PATH_NAME_MAX);
1221   if (strlen(buf) > 0) {
1222     return Layout::get()->relative(buf);
1223   } else {
1224     return Layout::get()->libexecdir;
1225   }
1226 }
1227 
1228 //-------------------------------------------------------------------------
1229 // RecConfigReadConfigPath
1230 //-------------------------------------------------------------------------
1231 std::string
RecConfigReadConfigPath(const char * file_variable,const char * default_value)1232 RecConfigReadConfigPath(const char *file_variable, const char *default_value)
1233 {
1234   std::string sysconfdir(RecConfigReadConfigDir());
1235 
1236   // If the file name is in a configuration variable, look it up first ...
1237   if (file_variable) {
1238     char buf[PATH_NAME_MAX];
1239 
1240     buf[0] = '\0';
1241     RecGetRecordString(file_variable, buf, PATH_NAME_MAX);
1242     if (strlen(buf) > 0) {
1243       return Layout::get()->relative_to(sysconfdir, buf);
1244     }
1245   }
1246 
1247   // Otherwise take the default ...
1248   if (default_value) {
1249     return Layout::get()->relative_to(sysconfdir, default_value);
1250   }
1251 
1252   return {};
1253 }
1254 
1255 //-------------------------------------------------------------------------
1256 // RecConfigReadPersistentStatsPath
1257 //-------------------------------------------------------------------------
1258 std::string
RecConfigReadPersistentStatsPath()1259 RecConfigReadPersistentStatsPath()
1260 {
1261   std::string rundir(RecConfigReadRuntimeDir());
1262   return Layout::relative_to(rundir, ts::filename::RECORDS_STATS);
1263 }
1264 
1265 void
RecSignalWarning(int sig,const char * fmt,...)1266 RecSignalWarning(int sig, const char *fmt, ...)
1267 {
1268   char msg[1024];
1269   va_list args;
1270 
1271   va_start(args, fmt);
1272   WarningV(fmt, args);
1273   va_end(args);
1274 
1275   va_start(args, fmt);
1276   vsnprintf(msg, sizeof(msg), fmt, args);
1277   RecSignalManager(sig, msg);
1278   va_end(args);
1279 }
1280 
1281 //-------------------------------------------------------------------------
1282 // RecConfigWarnIfUnregistered
1283 //-------------------------------------------------------------------------
1284 /// Generate a warning if the record is a configuration name/value but is not registered.
1285 void
RecConfigWarnIfUnregistered()1286 RecConfigWarnIfUnregistered()
1287 {
1288   RecDumpRecords(
1289     RECT_CONFIG,
1290     [](RecT, void *, int registered_p, const char *name, int, RecData *) -> void {
1291       if (!registered_p) {
1292         Warning("Unrecognized configuration value '%s'", name);
1293       }
1294     },
1295     nullptr);
1296 }
1297