oscam-2.26.01-11942-802-wit.../oscam-conf.c

535 lines
13 KiB
C
Raw Normal View History

#define MODULE_LOG_PREFIX "config"
#include "globals.h"
#include "oscam-conf.h"
#include "oscam-config.h"
#include "oscam-files.h"
#include "oscam-garbage.h"
#include "oscam-string.h"
#define CONFVARWIDTH 30
/* Returns the default value if string length is zero, otherwise atoi is called*/
int32_t strToIntVal(char *value, int32_t defaultvalue)
{
if(cs_strlen(value) == 0) { return defaultvalue; }
errno = 0; // errno should be set to 0 before calling strtol
int32_t i = strtol(value, NULL, 10);
return (errno == 0) ? i : defaultvalue;
}
/* Returns the default value if string length is zero, otherwise strtoul is called*/
uint32_t strToUIntVal(char *value, uint32_t defaultvalue)
{
if(cs_strlen(value) == 0) { return defaultvalue; }
errno = 0; // errno should be set to 0 before calling strtoul
uint32_t i = strtoul(value, NULL, 10);
return (errno == 0) ? i : defaultvalue;
}
/* Replacement of fprintf which adds necessary whitespace to fill up the varname to a fixed width.
If varname is longer than CONFVARWIDTH, no whitespace is added*/
void fprintf_conf(FILE *f, const char *varname, const char *fmtstring, ...)
{
int32_t varlen = cs_strlen(varname);
int32_t max = (varlen > CONFVARWIDTH) ? varlen : CONFVARWIDTH;
char varnamebuf[max + 3];
char *ptr = varnamebuf + varlen;
va_list argptr;
cs_strncpy(varnamebuf, varname, sizeof(varnamebuf));
while(varlen < CONFVARWIDTH)
{
ptr[0] = ' ';
++ptr;
++varlen;
}
cs_strncpy(ptr, "= ", sizeof(varnamebuf) - (ptr - varnamebuf));
if(fwrite(varnamebuf, sizeof(char), cs_strlen(varnamebuf), f))
{
if(cs_strlen(fmtstring) > 0)
{
va_start(argptr, fmtstring);
vfprintf(f, fmtstring, argptr);
va_end(argptr);
}
}
}
int config_list_parse(const struct config_list *clist, const char *token, char *value, void *config_data)
{
const struct config_list *c;
for(c = clist; c->opt_type != OPT_UNKNOWN; c++)
{
if(c->opt_type == OPT_SAVE_FUNC || c->opt_type == OPT_FIXUP_FUNC)
{ continue; }
if(strcasecmp(token, c->config_name) != 0)
{ continue; }
void *var = config_data + c->var_offset;
switch(c->opt_type)
{
case OPT_INT8:
{
2026-02-23 16:40:08 +00:00
*(int8_t *)var = (int8_t)strToIntVal(value, c->def.d_int8);
return 1;
}
case OPT_UINT8:
{
uint32_t tmp = strToUIntVal(value, c->def.d_uint8);
*(uint8_t *)var = (uint8_t)(tmp <= 0xff ? tmp : 0xff);
return 1;
}
case OPT_INT32:
{
int32_t tmp = strToIntVal(value, c->def.d_int32);
memcpy(var, &tmp, sizeof(int32_t));
return 1;
}
case OPT_UINT32:
{
uint32_t tmp = strToUIntVal(value, c->def.d_uint32);
memcpy(var, &tmp, sizeof(uint32_t));
return 1;
}
case OPT_STRING:
{
char **scfg = var;
if(c->def.d_char && cs_strlen(value) == 0) // Set default
{ value = c->def.d_char; }
NULLFREE(*scfg);
if(cs_strlen(value))
{ *scfg = cs_strdup(value); }
return 1;
}
case OPT_SSTRING:
{
char *scfg = var;
if(c->def.d_char && cs_strlen(value) == 0) // Set default
{ value = c->def.d_char; }
scfg[0] = '\0';
unsigned int len = cs_strlen(value);
if(len)
{
cs_strncpy(scfg, value, c->str_size);
if(len > c->str_size)
{
fprintf(stderr, "WARNING: Config value for '%s' (%s, len=%u) exceeds max length: %d (%s)\n",
token, value, len, c->str_size - 1, scfg);
}
}
return 1;
}
case OPT_HEX_ARRAY:
{
uint8_t *hex_array = var;
if(!cs_strlen(value))
{ memset(hex_array, 0, c->def.array_size); }
else if(key_atob_l(value, hex_array, c->def.array_size * 2))
{
memset(hex_array, 0, c->def.array_size);
fprintf(stderr, "WARNING: Config value for '%s' (%s, len=%zu) requires %d chars.\n",
token, value, cs_strlen(value), c->def.array_size * 2);
}
return 1;
}
case OPT_FUNC:
{
c->ops.process_fn(token, value, var, NULL);
return 1;
}
case OPT_FUNC_EXTRA:
{
c->ops.process_fn_extra(token, value, var, c->def.d_extra, NULL);
return 1;
}
case OPT_FIXUP_FUNC:
case OPT_SAVE_FUNC:
return 1;
case OPT_UNKNOWN:
{
fprintf(stderr, "Unknown config type (%s = %s).", token, value);
break;
}
}
}
return 0;
}
void config_list_save_ex(FILE *f, const struct config_list *clist, void *config_data, int save_all,
bool (*check_func)(const struct config_list *clist, void *config_data, const char *setting))
{
const struct config_list *c;
for(c = clist; c->opt_type != OPT_UNKNOWN; c++)
{
void *var = config_data + c->var_offset;
if(check_func && c->opt_type != OPT_UNKNOWN)
{
if(!check_func(clist, config_data, c->config_name))
{ continue; }
}
switch(c->opt_type)
{
case OPT_INT8:
{
int8_t val = *(int8_t *)var;
2026-02-23 16:40:08 +00:00
// always save pmt_mode, because external tools parse it
if(save_all || val != c->def.d_int8 || !strcmp(c->config_name, "pmt_mode"))
{ fprintf_conf(f, c->config_name, "%d\n", val); }
continue;
}
case OPT_UINT8:
{
uint8_t val = *(uint8_t *)var;
if(save_all || val != c->def.d_uint8)
2026-02-23 16:40:08 +00:00
{ fprintf_conf(f, c->config_name, "%u\n", val); }
continue;
}
case OPT_INT32:
{
int32_t val;
memcpy(&val, var, sizeof(int32_t));
2026-02-23 16:40:08 +00:00
if(save_all || val != c->def.d_int32)
{ fprintf_conf(f, c->config_name, "%d\n", val); }
continue;
}
case OPT_UINT32:
{
uint32_t val;
memcpy(&val, var, sizeof(uint32_t));
if(save_all || val != c->def.d_uint32)
2026-02-23 16:40:08 +00:00
{ fprintf_conf(f, c->config_name, "%u\n", val); }
continue;
}
case OPT_STRING:
{
char **val = var;
if(save_all || !streq(*val, c->def.d_char))
{
fprintf_conf(f, c->config_name, "%s\n", *val ? *val : "");
}
continue;
}
case OPT_SSTRING:
{
char *val = var;
if(save_all || !streq(val, c->def.d_char))
{
fprintf_conf(f, c->config_name, "%s\n", val[0] ? val : "");
}
continue;
}
case OPT_HEX_ARRAY:
{
uint8_t *hex_array = var;
uint32_t ok = array_has_nonzero_byte(hex_array, c->def.array_size);
if(save_all || ok)
{
fprintf_conf(f, c->config_name, "%s", ""); // it should not have \n at the end
if(ok)
{
for(ok = 0; ok < c->def.array_size; ok++)
{
fprintf(f, "%02X", hex_array[ok]);
}
}
fprintf(f, "\n");
}
continue;
}
case OPT_FUNC:
{
2026-02-23 16:40:08 +00:00
c->ops.process_fn((const char *)c->config_name, NULL, var, f);
continue;
}
case OPT_FUNC_EXTRA:
{
c->ops.process_fn_extra((const char *)c->config_name, NULL, var, c->def.d_extra, f);
continue;
}
case OPT_FIXUP_FUNC:
case OPT_SAVE_FUNC:
continue;
case OPT_UNKNOWN:
break;
}
}
}
bool config_list_should_be_saved(const struct config_list *clist, void *var)
{
const struct config_list *c;
for(c = clist; c->opt_type != OPT_UNKNOWN; c++)
{
if(c->opt_type == OPT_SAVE_FUNC)
{
return c->ops.should_save_fn(var);
}
}
return true;
}
void config_list_apply_fixups(const struct config_list *clist, void *var)
{
const struct config_list *c;
for(c = clist; c->opt_type != OPT_UNKNOWN; c++)
{
if(c->opt_type == OPT_FIXUP_FUNC)
{
c->ops.fixup_fn(var);
break;
}
}
}
void config_list_set_defaults(const struct config_list *clist, void *config_data)
{
const struct config_list *c;
for(c = clist; c->opt_type != OPT_UNKNOWN; c++)
{
void *var = config_data + c->var_offset;
switch(c->opt_type)
{
case OPT_INT8:
{
*(int8_t *)var = c->def.d_int8;
break;
}
case OPT_UINT8:
{
*(uint8_t *)var = c->def.d_uint8;
break;
}
case OPT_INT32:
{
memcpy(var, &c->def.d_int32, sizeof(int32_t));
break;
}
case OPT_UINT32:
{
memcpy(var, &c->def.d_uint32, sizeof(uint32_t));
break;
}
case OPT_STRING:
{
char **scfg = var;
NULLFREE(*scfg);
if(c->def.d_char)
{ *scfg = cs_strdup(c->def.d_char); }
break;
}
case OPT_SSTRING:
{
char *scfg = var;
scfg[0] = '\0';
if(c->def.d_char && cs_strlen(c->def.d_char))
{ cs_strncpy(scfg, c->def.d_char, c->str_size); }
break;
}
case OPT_HEX_ARRAY:
{
uint8_t *hex_array = var;
memset(hex_array, 0, c->def.array_size);
break;
}
case OPT_FUNC:
{
c->ops.process_fn((const char *)c->config_name, "", var, NULL);
break;
}
case OPT_FUNC_EXTRA:
{
c->ops.process_fn_extra((const char *)c->config_name, "", var, c->def.d_extra, NULL);
break;
}
case OPT_SAVE_FUNC:
case OPT_FIXUP_FUNC:
case OPT_UNKNOWN:
continue;
}
}
return;
}
void config_list_free_values(const struct config_list *clist, void *config_data)
{
const struct config_list *c;
for(c = clist; c->opt_type != OPT_UNKNOWN; c++)
{
void *var = config_data + c->var_offset;
if(c->opt_type == OPT_STRING)
{
char **scfg = var;
NULLFREE(*scfg);
}
if(c->free_value && (c->opt_type == OPT_FUNC || c->opt_type == OPT_FUNC_EXTRA))
{
c->free_value(var);
}
}
return;
}
void config_list_gc_values(const struct config_list *clist, void *config_data)
{
const struct config_list *c;
for(c = clist; c->opt_type != OPT_UNKNOWN; c++)
{
void *var = config_data + c->var_offset;
if(c->opt_type == OPT_STRING)
{
char **scfg = var;
add_garbage(*scfg);
}
}
return;
}
int config_section_is_active(const struct config_sections *sec)
{
if(!sec)
{ return 0; }
if(sec->config[0].opt_type == OPT_UNKNOWN)
{ return 0; }
return 1;
}
const struct config_sections *config_find_section(const struct config_sections *conf, char *section_name)
{
const struct config_sections *sec;
for(sec = conf; sec && sec->section; sec++)
{
if(streq(section_name, sec->section))
{
return sec;
}
}
return NULL;
}
void config_sections_save(const struct config_sections *conf, FILE *f, void *var)
{
const struct config_sections *sec;
for(sec = conf; sec && sec->section; sec++)
{
if(config_section_is_active(sec) && config_list_should_be_saved(sec->config, var))
{
fprintf(f, "[%s]\n", sec->section);
config_list_apply_fixups(sec->config, var);
config_list_save(f, sec->config, var, cfg.http_full_cfg);
fprintf(f, "\n");
}
}
}
void config_sections_set_defaults(const struct config_sections *conf, void *var)
{
const struct config_sections *sec;
for(sec = conf; sec && sec->section; sec++)
{
if(config_section_is_active(sec))
{ config_list_set_defaults(sec->config, var); }
}
}
void config_sections_free(const struct config_sections *conf, void *var)
{
const struct config_sections *sec;
for(sec = conf; sec && sec->section; sec++)
{
if(config_section_is_active(sec))
{
config_list_free_values(sec->config, var);
}
}
}
void config_set_value(const struct config_sections *conf, char *section, const char *token, char *value, void *var)
{
const struct config_sections *sec = config_find_section(conf, section);
if(!sec)
{
fprintf(stderr, "WARNING: Unknown section '%s'.\n", section);
return;
}
if(config_section_is_active(sec))
{
if(!config_list_parse(sec->config, token, value, var))
{
fprintf(stderr, "WARNING: In section [%s] unknown setting '%s=%s' tried.\n",
section, token, value);
}
}
else
{
fprintf(stderr, "WARNING: Section is not active '%s'.\n", section);
}
}
static FILE *__open_config_file(const char *conf_filename, bool die_on_err)
{
char filename[256];
FILE *f = fopen(get_config_filename(filename, sizeof(filename), conf_filename), "r");
if(!f)
{
if(die_on_err)
{
fprintf(stderr, "ERROR: Cannot open file \"%s\" (errno=%d %s)", filename, errno, strerror(errno));
fprintf(stderr, "\n");
exit(1);
}
else
{
cs_log_dbg(D_TRACE, "INFO: Cannot open file \"%s\" (errno=%d %s)", filename, errno, strerror(errno));
}
return NULL;
}
return f;
}
FILE *open_config_file(const char *conf_filename)
{
return __open_config_file(conf_filename, false);
}
FILE *open_config_file_or_die(const char *conf_filename)
{
return __open_config_file(conf_filename, true);
}
FILE *create_config_file(const char *conf_filename)
{
char temp_file[256];
get_config_filename(temp_file, sizeof(temp_file), conf_filename);
if (!cs_strncat(temp_file, ".tmp", sizeof(temp_file))) {
return NULL;
}
FILE *f = fopen(temp_file, "w");
if(!f)
{
cs_log("ERROR: Cannot create file \"%s\" (errno=%d %s)", temp_file, errno, strerror(errno));
return NULL;
}
fprintf(f, "# %s generated automatically by Streamboard OSCam %s\n",
conf_filename, CS_VERSION);
fprintf(f, "# Read more: %s/blob/master/Distribution/doc/txt/%s.txt\n\n",
SCM_URL, conf_filename);
return f;
}
bool flush_config_file(FILE *f, const char *conf_filename)
{
2026-02-23 16:40:08 +00:00
char dst_file[256], tmp_file[256], bak_file[256];
get_config_filename(dst_file, sizeof(dst_file), conf_filename);
memcpy(tmp_file, dst_file, sizeof(tmp_file));
memcpy(bak_file, dst_file, sizeof(bak_file));
strncat(tmp_file, ".tmp", sizeof(tmp_file) - cs_strlen(tmp_file) - 1);
strncat(bak_file, ".bak", sizeof(bak_file) - cs_strlen(bak_file) - 1);
if(f)
{
fclose(f);
}
2026-02-23 16:40:08 +00:00
return safe_overwrite_with_bak(dst_file, tmp_file, bak_file, cfg.http_overwrite_bak_file);
}