#include #include #include #include #include "state.h" #include "config.h" #if ENABLE_LOGGING #include extern FILE *log_fp; #define LOG(fmt, ...) do { if (log_fp) { fprintf(log_fp, "[%s] " fmt "\n", __func__, ##__VA_ARGS__); fflush(log_fp); } } while(0) #else #define LOG(fmt, ...) do {} while(0) #endif static void state_defaults(openuf_state_t *st) { memset(st, 0, sizeof(*st)); st->adopted = false; strncpy(st->authkey, DEFAULT_AUTH_KEY, sizeof(st->authkey) - 1); strncpy(st->cfgversion, "0", sizeof(st->cfgversion) - 1); st->config_applied = false; st->config_schema = 0; st->use_aes_gcm = false; } void state_load(openuf_state_t *st) { state_defaults(st); FILE *f = fopen(OPENUF_STATE_FILE, "r"); if (!f) { LOG("State file not found, using defaults"); return; } /* Read whole file */ fseek(f, 0, SEEK_END); long sz = ftell(f); rewind(f); if (sz <= 0 || sz > 4096) { fclose(f); return; } char *buf = malloc(sz + 1); if (!buf) { fclose(f); return; } fread(buf, 1, sz, f); buf[sz] = '\0'; fclose(f); struct json_object *root = json_tokener_parse(buf); free(buf); if (!root) { LOG("Failed to parse state file"); return; } struct json_object *v; #define LOAD_STR(field, key) \ if (json_object_object_get_ex(root, key, &v) && json_object_is_type(v, json_type_string)) \ strncpy(st->field, json_object_get_string(v), sizeof(st->field) - 1) #define LOAD_BOOL(field, key) \ if (json_object_object_get_ex(root, key, &v)) \ st->field = json_object_get_boolean(v) #define LOAD_INT(field, key) \ if (json_object_object_get_ex(root, key, &v)) \ st->field = json_object_get_int(v) LOAD_BOOL(adopted, "adopted"); LOAD_STR (authkey, "authkey"); LOAD_STR (inform_url, "inform_url"); LOAD_STR (cfgversion, "cfgversion"); LOAD_BOOL(config_applied, "config_applied"); LOAD_INT (config_schema, "config_schema"); LOAD_BOOL(use_aes_gcm, "use_aes_gcm"); LOAD_STR (mac, "mac"); LOAD_STR (ip, "ip"); LOAD_STR (hostname, "hostname"); /* * Older versions stored cfgversion from setparam before applying the * corresponding setstate. Force one provisioning request when migrating. */ if (!st->config_applied || st->config_schema < OPENUF_CONFIG_SCHEMA) { st->config_applied = false; strncpy(st->cfgversion, "0", sizeof(st->cfgversion) - 1); } /* CRITICAL: If not adopted, force DEFAULT_AUTH_KEY */ if (!st->adopted) { LOG("Device not adopted - resetting authkey to DEFAULT"); strncpy(st->authkey, DEFAULT_AUTH_KEY, sizeof(st->authkey) - 1); } LOG("State loaded: adopted=%d, authkey=%.8s..., inform_url=%s, aes_gcm=%d", st->adopted, st->authkey[0] ? st->authkey : "DEFAULT", st->inform_url, st->use_aes_gcm); json_object_put(root); } int state_save(const openuf_state_t *st) { /* Ensure directory exists */ mkdir("/etc/openuf", 0755); LOG("Saving state: adopted=%d, authkey=%.8s..., inform_url=%s, aes_gcm=%d", st->adopted, st->authkey[0] ? st->authkey : "DEFAULT", st->inform_url, st->use_aes_gcm); struct json_object *root = json_object_new_object(); json_object_object_add(root, "adopted", json_object_new_boolean(st->adopted)); json_object_object_add(root, "authkey", json_object_new_string(st->authkey)); json_object_object_add(root, "inform_url", json_object_new_string(st->inform_url)); json_object_object_add(root, "cfgversion", json_object_new_string(st->cfgversion)); json_object_object_add(root, "config_applied", json_object_new_boolean(st->config_applied)); json_object_object_add(root, "config_schema", json_object_new_int(st->config_schema)); json_object_object_add(root, "use_aes_gcm", json_object_new_boolean(st->use_aes_gcm)); json_object_object_add(root, "mac", json_object_new_string(st->mac)); json_object_object_add(root, "ip", json_object_new_string(st->ip)); json_object_object_add(root, "hostname", json_object_new_string(st->hostname)); const char *s = json_object_to_json_string_ext(root, JSON_C_TO_STRING_PRETTY); FILE *f = fopen(OPENUF_STATE_FILE, "w"); if (!f) { LOG("Failed to open state file for writing"); json_object_put(root); return -1; } fputs(s, f); fclose(f); json_object_put(root); LOG("State saved successfully"); return 0; }