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