diff options
Diffstat (limited to 'runtime/timezones.c')
-rw-r--r-- | runtime/timezones.c | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/runtime/timezones.c b/runtime/timezones.c new file mode 100644 index 0000000..e1e0394 --- /dev/null +++ b/runtime/timezones.c @@ -0,0 +1,205 @@ +/* timezones.c + * Support for timezones in RainerScript. + * + * Copyright 2022 Attila Lakatos and Adiscon GmbH. + * + * This file is part of the rsyslog runtime library. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * -or- + * see COPYING.ASL20 in the source distribution + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "config.h" +#include <stdlib.h> +#include <sys/types.h> +#include <unistd.h> +#include <ctype.h> +#include <stdint.h> +#include <errno.h> + +#include "rsyslog.h" +#include "unicode-helper.h" +#include "errmsg.h" +#include "parserif.h" +#include "rainerscript.h" +#include "srUtils.h" +#include "rsconf.h" + + +static struct cnfparamdescr timezonecnfparamdescr[] = { + { "id", eCmdHdlrString, CNFPARAM_REQUIRED}, + { "offset", eCmdHdlrGetWord, CNFPARAM_REQUIRED } +}; +static struct cnfparamblk timezonepblk = { + CNFPARAMBLK_VERSION, + sizeof(timezonecnfparamdescr)/sizeof(struct cnfparamdescr), + timezonecnfparamdescr +}; + +/* Note: this function is NOT thread-safe! + * This is currently not needed as used only during + * initialization. + */ +static rsRetVal +addTimezoneInfo(rsconf_t *cnf, uchar *tzid, char offsMode, int8_t offsHour, int8_t offsMin) +{ + DEFiRet; + tzinfo_t *newti; + CHKmalloc(newti = realloc(cnf->timezones.tzinfos, (cnf->timezones.ntzinfos+1)*sizeof(tzinfo_t))); + if((newti[cnf->timezones.ntzinfos].id = strdup((char*)tzid)) == NULL) { + free(newti); + DBGPRINTF("addTimezoneInfo: strdup failed with OOM\n"); + ABORT_FINALIZE(RS_RET_OUT_OF_MEMORY); + } + newti[cnf->timezones.ntzinfos].offsMode = offsMode; + newti[cnf->timezones.ntzinfos].offsHour = offsHour; + newti[cnf->timezones.ntzinfos].offsMin = offsMin; + ++cnf->timezones.ntzinfos, cnf->timezones.tzinfos = newti; +finalize_it: + RETiRet; +} + +void +glblProcessTimezone(struct cnfobj *o) +{ + struct cnfparamvals *pvals; + uchar *id = NULL; + uchar *offset = NULL; + char offsMode; + int8_t offsHour; + int8_t offsMin; + int i; + + pvals = nvlstGetParams(o->nvlst, &timezonepblk, NULL); + if(pvals == NULL) { + LogError(0, RS_RET_MISSING_CNFPARAMS, "error processing timezone " + "config parameters"); + goto done; + } + if(Debug) { + dbgprintf("timezone param blk after glblProcessTimezone:\n"); + cnfparamsPrint(&timezonepblk, pvals); + } + + for(i = 0 ; i < timezonepblk.nParams ; ++i) { + if(!pvals[i].bUsed) + continue; + if(!strcmp(timezonepblk.descr[i].name, "id")) { + id = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else if(!strcmp(timezonepblk.descr[i].name, "offset")) { + offset = (uchar*) es_str2cstr(pvals[i].val.d.estr, NULL); + } else { + dbgprintf("glblProcessTimezone: program error, non-handled " + "param '%s'\n", timezonepblk.descr[i].name); + } + } + + /* note: the following two checks for NULL are not strictly necessary + * as these are required parameters for the config block. But we keep + * them to make the clang static analyzer happy, which also helps + * guard against logic errors. + */ + if(offset == NULL) { + parser_errmsg("offset parameter missing (logic error?), timezone config ignored"); + goto done; + } + if(id == NULL) { + parser_errmsg("id parameter missing (logic error?), timezone config ignored"); + goto done; + } + + if( strlen((char*)offset) != 6 + || !(offset[0] == '-' || offset[0] == '+') + || !(isdigit(offset[1]) && isdigit(offset[2])) + || offset[3] != ':' + || !(isdigit(offset[4]) && isdigit(offset[5])) + ) { + parser_errmsg("timezone offset has invalid format. Must be +/-hh:mm, e.g. \"-07:00\"."); + goto done; + } + + offsHour = (offset[1] - '0') * 10 + offset[2] - '0'; + offsMin = (offset[4] - '0') * 10 + offset[5] - '0'; + offsMode = offset[0]; + + if(offsHour > 12 || offsMin > 59) { + parser_errmsg("timezone offset outside of supported range (hours 0..12, minutes 0..59)"); + goto done; + } + + addTimezoneInfo(loadConf, id, offsMode, offsHour, offsMin); + +done: + cnfparamvalsDestruct(pvals, &timezonepblk); + free(id); + free(offset); +} + +/* comparison function for qsort() and string array compare + * this is for the string lookup table type + */ +static int +qs_arrcmp_tzinfo(const void *s1, const void *s2) +{ + return strcmp(((tzinfo_t*)s1)->id, ((tzinfo_t*)s2)->id); +} + +void sortTimezones(rsconf_t *cnf) +{ + if (cnf->timezones.ntzinfos > 0) { + qsort(cnf->timezones.tzinfos, cnf->timezones.ntzinfos, + sizeof(tzinfo_t), qs_arrcmp_tzinfo); + } +} + +void +displayTimezones(rsconf_t *cnf) +{ + if(!Debug) + return; + for(int i = 0 ; i < cnf->timezones.ntzinfos ; ++i) + dbgprintf("tzinfo: '%s':%c%2.2d:%2.2d\n", + cnf->timezones.tzinfos[i].id, cnf->timezones.tzinfos[i].offsMode, + cnf->timezones.tzinfos[i].offsHour, cnf->timezones.tzinfos[i].offsMin); +} + +static int +bs_arrcmp_tzinfo(const void *s1, const void *s2) +{ + return strcmp((char*)s1, (char*)((tzinfo_t*)s2)->id); +} + +/* returns matching timezone info or NULL if no entry exists */ +tzinfo_t* +glblFindTimezone(rsconf_t *cnf, char *id) +{ + return (tzinfo_t*) bsearch( + id, cnf->timezones.tzinfos, cnf->timezones.ntzinfos, sizeof(tzinfo_t), bs_arrcmp_tzinfo); +} + +static void +freeTimezone(tzinfo_t *tzinfo) +{ + free(tzinfo->id); +} + +void +freeTimezones(rsconf_t *cnf) +{ + for(int i = 0; i < cnf->timezones.ntzinfos ; ++i) + freeTimezone(&cnf->timezones.tzinfos[i]); + if (cnf->timezones.ntzinfos > 0) + free(cnf->timezones.tzinfos); + cnf->timezones.tzinfos = NULL; +} |