#include "common.h" #include "sysvars.h" #include #include #include #include bool sysvar_print_updates; #ifndef DEBUG # ifdef ON_FPGA # define DEBUG 0 # else # define DEBUG sysvar_print_updates # endif #endif static const char *sysvar_bool_tostr(sysvar_t from, char *buf) { buf[0] = '0' + from.v_bool; buf[1] = '\0'; return buf; } static bool sysvar_bool_fromstr(sysvar_t *to, const char *from) { char c = from[0] | 0x20; switch (c) { case '1': case 't': case 'y': case 'j': case 's': to->v_bool = true; return true; case ' ': /* Blank or null */ case '0': case 'f': case 'n': to->v_bool = false; return true; default: return false; } } const struct sysvar_ops sysvar_bool_ops = { .tostr = sysvar_bool_tostr, .fromstr = sysvar_bool_fromstr, .buflen = BOOL_BUFLEN }; static const char *sysvar_int_tostr(sysvar_t from, char *buf) { snprintf(buf, INT_BUFLEN, "%ld", from.v_int); return buf; } static bool sysvar_int_fromstr(sysvar_t *to, const char *from) { char *ep; long v = strtol(from, &ep, 0); if (ep == from || *ep) return false; to->v_int = v; return true; } const struct sysvar_ops sysvar_int_ops = { .tostr = sysvar_int_tostr, .fromstr = sysvar_int_fromstr, .buflen = INT_BUFLEN }; static const char *sysvar_uint_tostr(sysvar_t from, char *buf) { snprintf(buf, UINT_BUFLEN, "%lu", from.v_uint); return buf; } static bool sysvar_uint_fromstr(sysvar_t *to, const char *from) { char *ep; unsigned long v = strtoul(from, &ep, 0); if (ep == from || *ep) return false; to->v_uint = v; return true; } const struct sysvar_ops sysvar_uint_ops = { .tostr = sysvar_uint_tostr, .fromstr = sysvar_uint_fromstr, .buflen = UINT_BUFLEN }; static const char *sysvar_str_tostr(sysvar_t from, char *buf) { (void)buf; return from.v_str; } static bool sysvar_str_fromstr(sysvar_t *to, const char *from) { char *ns; if (!from) { ns = NULL; } else { ns = strdup(from); if (!ns) return false; } to->v_str = ns; return true; } static bool sysvar_str_set(sysvar_t *to, sysvar_t from) { return sysvar_str_fromstr(to, from.v_str); } static size_t sysvar_str_datasize(sysvar_t val) { return (val.v_str ? strlen(val.v_str) : 0) + 1; } const struct sysvar_ops sysvar_str_ops = { .set = sysvar_str_set, .tostr = sysvar_str_tostr, .fromstr = sysvar_str_fromstr, .datasize = sysvar_str_datasize }; static void sysvar_tz_update(sysvar_t val, bool isset) { if (isset) setenv("TZ", val.v_tz, 1); else unsetenv("TZ"); tzset(); } const struct sysvar_ops sysvar_tz_ops = { .set = sysvar_str_set, .tostr = sysvar_str_tostr, .fromstr = sysvar_str_fromstr, .update = sysvar_tz_update, .datasize = sysvar_str_datasize }; static const char *sysvar_ip_tostr(sysvar_t from, char *buf) { union ip_bytes { uint8_t b[4]; uint32_t l; } ip; ip.l = from.v_ip; snprintf(buf, IP_BUFLEN, "%u.%u.%u.%u", ip.b[0], ip.b[1], ip.b[2], ip.b[3]); return buf; } static bool sysvar_ip_fromstr(sysvar_t *to, const char *str) { union ip_bytes { uint8_t b[4]; uint32_t l; } ip; ip.l = 0; for (int i = 0; i < 4; i++) { char *ep; unsigned long v = strtoul(str, &ep, 10); if (ep == str || *ep != (i == 3) ? '\0' : '.' || v > 255) return false; str = ep + 1; ip.b[i] = v; } to->v_ip = ip.l; return true; } const struct sysvar_ops sysvar_ip_ops = { .tostr = sysvar_ip_tostr, .fromstr = sysvar_ip_fromstr, .buflen = IP_BUFLEN }; static bool sysvar_mac_set(sysvar_t *to, sysvar_t from) { uint8_t *buf; if (!from.v_mac) { buf = NULL; } else { buf = malloc(6); if (!buf) return false; memcpy(buf, from.v_mac, 6); } to->v_mac = buf; return true; } static const char *sysvar_mac_tostr(sysvar_t from, char *buf) { const uint8_t *m = from.v_mac; if (!m) return NULL; snprintf(buf, MAC_BUFLEN, "%02x:%02x:%02x:%02x:%02x:%02x", m[0], m[1], m[2], m[3], m[4], m[5]); return buf; } static bool sysvar_mac_fromstr(sysvar_t *to, const char *str) { sysvar_t from; uint8_t m[6]; if (!str) { from.v_mac = NULL; } else { for (int i = 0; i < 6; i++) { char *ep; unsigned long v = strtoul(str, &ep, 16); if (ep == str || *ep != (i == 5) ? '\0' : ':' || v > 255) return false; str = ep + 1; m[i] = v; } from.v_mac = m; } return sysvar_mac_set(to, from); } static size_t sysvar_mac_datasize(sysvar_t val) { (void)val; return 6; } const struct sysvar_ops sysvar_mac_ops = { .set = sysvar_mac_set, .tostr = sysvar_mac_tostr, .fromstr = sysvar_mac_fromstr, .buflen = MAC_BUFLEN, .datasize = sysvar_mac_datasize }; /* --- Generic getters/setters --- */ /* Contains the lowest numbered sysvar changed; or sysvar_count if nothing */ enum sysvar_enum sysvar_changed; sysvar_t getvar(size_t var) { if (var >= (size_t)sysvar_count) return sysvar_defval[sysvar_null]; return sysvar_val[var]; } static bool do_setvar(size_t var, sysvar_t val, bool is_set) { const struct sysvar_ops *type = sysvar_types[var]; sysvar_t *to = &sysvar_val[var]; void *free_ptr = NULL; if (type->datasize) free_ptr = to->v_ptr; if (DEBUG) { char tostr_buf[SYSVAR_BUFLEN]; printf("%ssetvar %zu %s <- %s\n", is_set ? "" : "un", var, sysvar_name[var], notempty(type->tostr(val, tostr_buf))); } if (!type->set || (type->datasize && !val.v_ptr)) { sysvar_val[var] = val; } else { if (!type->set(to, val)) return false; } if (free_ptr) free(free_ptr); if (var < (size_t)sysvar_changed) sysvar_changed = (enum sysvar_enum)var; sysvar_isset[var] = is_set; if (type->update) type->update(*to, is_set); return true; } bool setvar(size_t var, sysvar_t val) { if (var >= (size_t)sysvar_count) return false; return do_setvar(var, val, true); } bool unsetvar(size_t var) { if (var >= (size_t)sysvar_count) return false; return do_setvar(var, sysvar_defval[var], false); } /* --- Getters/setters converting to/from strings --- */ const char *getvar_tostr(size_t var) { static char buf[SYSVAR_BUFLEN]; return getvar_tostr_r(var, buf); } const char *getvar_tostr_r(size_t var, char *buf) { if (var >= (size_t)sysvar_count) return NULL; const struct sysvar_ops *type = sysvar_types[var]; /* A tostr method is required */ return type->tostr(sysvar_val[var], buf); } bool setvar_fromstr(size_t var, const char *str) { if (var >= (size_t)sysvar_count) return NULL; if (!str) return unsetvar(var); const struct sysvar_ops *type = sysvar_types[var]; sysvar_t *to = &sysvar_val[var]; void *free_ptr = NULL; if (type->datasize) free_ptr = to->v_ptr; if (DEBUG) { printf("setvar_fromstr %zu %s <- %s\n", var, sysvar_name[var], str); } /* A fromstr method is required */ if (!type->fromstr(to, str)) return false; if (free_ptr) free(free_ptr); if (var < (size_t)sysvar_changed) sysvar_changed = (enum sysvar_enum)var; sysvar_isset[var] = true; if (type->update) type->update(*to, true); return true; } /* --- Find the index of a specific variable --- */ static int string_ptr_compare(const void *a, const void *b) { const char * const *aa = a; const char * const *bb = b; return strcmp(*aa, *bb); } size_t sysvar_find(size_t ns, const char *name) { if (ns >= (size_t)sysvar_nscount) return 0; const sysvar_ns_t *nsi = &sysvar_ns[ns]; const char * const *varname; const size_t count = nsi[1].first - nsi->first; varname = bsearch(&name, sysvar_name + nsi->first, count, sizeof *sysvar_name, string_ptr_compare); if (!varname) return 0; return varname - sysvar_name; } /* --- Initialization/reset to defaults --- */ void sysvar_reset(size_t ns) { if (ns >= (size_t)sysvar_nscount) return; bool print_updates = sysvar_print_updates; enum sysvar_enum i; for (i = sysvar_ns[ns].first; i < sysvar_ns[ns+1].first; i++) unsetvar(i); sysvar_print_updates = print_updates; if (sysvar_changed < i) sysvar_changed = i; } void sysvar_init(void) { sysvar_print_updates = false; for (enum sysvar_enum i = sysvar_null+1; i < sysvar_count; i++) unsetvar(i); sysvar_changed = sysvar_count; }