Make chat settings reads immutable and validation failures atomic
This commit is contained in:
1 parent
27aa76f914
commit
43f9608fbf
2 files changed
+43
-30
No files matched your search
+22
-21
@@ -86,29 +86,27 @@ typedef char chat_setting_capacity_check[CHAT_SETTING_COUNT <= CHAT_SETTING_MAX_
|
||||
/* ─── state helpers ─── */
|
||||
|
||||
static void chat_setting_reset_defaults(struct chat_setting_state* s) {
|
||||
for (size_t i = 0; i < CHAT_SETTING_COUNT; i++)
|
||||
memset(s, 0, sizeof(*s));
|
||||
for (size_t i = 0; i < CHAT_SETTING_COUNT; i++) {
|
||||
s->values[i] = g_setting_defs[i].default_val;
|
||||
snprintf(s->int_text[i], sizeof(s->int_text[i]), "%d", s->values[i]);
|
||||
}
|
||||
s->initialized = 1;
|
||||
}
|
||||
|
||||
static int chat_setting_find(const char* name) {
|
||||
if (!name) return -1;
|
||||
for (size_t i = 0; i < CHAT_SETTING_COUNT; i++)
|
||||
if (strcmp(g_setting_defs[i].name, name) == 0) return (int)i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void chat_setting_apply(struct chat_setting_state* s) {
|
||||
(void)s;
|
||||
/* будет заполняться по мере появления runtime-setter'ов в модулях:
|
||||
if (idx == ...) some_module_set_xxx(value); */
|
||||
}
|
||||
|
||||
/* ─── state-level API ─── */
|
||||
|
||||
int chat_setting_set_state(struct chat_setting_state* s, const char* name, const char* value) {
|
||||
if (!s || !name || !value || !*name || !*value) return -2;
|
||||
|
||||
if (!s->initialized) chat_setting_reset_defaults(s);
|
||||
if (!s || !name || !value || !*name) {
|
||||
DEBUG_ERROR(CS_ID, "chat_setting: invalid state/name/value argument"); return -2;
|
||||
}
|
||||
|
||||
int idx = chat_setting_find(name);
|
||||
if (idx < 0) {
|
||||
@@ -122,18 +120,20 @@ int chat_setting_set_state(struct chat_setting_state* s, const char* name, const
|
||||
if (def->type == CHAT_SETTING_STRING) {
|
||||
if (strcmp(name, "call_video_bitrate_range_kbps") == 0) {
|
||||
uint32_t minimum, maximum;
|
||||
if (call_video_rate_parse_range(value, &minimum, &maximum)) return -2;
|
||||
if (call_video_rate_parse_range(value, &minimum, &maximum)) {
|
||||
DEBUG_ERROR(CS_ID, "chat_setting: '%s' invalid bitrate range '%s'", name, value); return -2;
|
||||
}
|
||||
}
|
||||
if (strlen(value) >= sizeof(s->string_val[0])) {
|
||||
DEBUG_ERROR(CS_ID, "chat_setting: '%s' string too long bytes=%zu limit=%zu", name, strlen(value), sizeof(s->string_val[0]) - 1);
|
||||
return -2;
|
||||
}
|
||||
if (!s->initialized) chat_setting_reset_defaults(s);
|
||||
for (int i = 0; i < s->string_count; i++) {
|
||||
if (s->string_idx[i] == idx) {
|
||||
if (strcmp(s->string_val[i], value) == 0) return 0;
|
||||
snprintf(s->string_val[i], sizeof(s->string_val[i]), "%s", value);
|
||||
DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value);
|
||||
chat_setting_apply(s);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -145,7 +145,6 @@ int chat_setting_set_state(struct chat_setting_state* s, const char* name, const
|
||||
snprintf(s->string_val[s->string_count], sizeof(s->string_val[0]), "%s", value);
|
||||
s->string_count++;
|
||||
DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value);
|
||||
chat_setting_apply(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -156,18 +155,19 @@ int chat_setting_set_state(struct chat_setting_state* s, const char* name, const
|
||||
} else {
|
||||
char* end = NULL;
|
||||
long lv = strtol(value, &end, 10);
|
||||
if (!end || *end != '\0' || lv < def->min_val || lv > def->max_val) {
|
||||
if (end == value || *end != '\0' || lv < def->min_val || lv > def->max_val) {
|
||||
DEBUG_ERROR(CS_ID, "chat_setting: '%s' invalid int '%s' (range %d..%d)", name, value, def->min_val, def->max_val);
|
||||
return -2;
|
||||
}
|
||||
v = (int)lv;
|
||||
}
|
||||
|
||||
if (!s->initialized) chat_setting_reset_defaults(s);
|
||||
if (s->values[idx] == v) return 0;
|
||||
|
||||
s->values[idx] = v;
|
||||
snprintf(s->int_text[idx], sizeof(s->int_text[idx]), "%d", v);
|
||||
DEBUG_INFO(CS_ID, "chat_setting: %s = %s", name, value);
|
||||
chat_setting_apply(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -190,14 +190,13 @@ const char* chat_setting_get_state(const struct chat_setting_state* s, const cha
|
||||
return "";
|
||||
}
|
||||
|
||||
snprintf((char*)s->int_buf, sizeof(s->int_buf), "%d", s->values[idx]);
|
||||
return s->int_buf;
|
||||
return s->int_text[idx];
|
||||
}
|
||||
|
||||
int chat_setting_get_int_state(const struct chat_setting_state* s, const char* name, int def) {
|
||||
if (!s || !s->initialized) return def;
|
||||
int idx = chat_setting_find(name);
|
||||
return idx >= 0 ? s->values[idx] : def;
|
||||
return idx >= 0 && g_setting_defs[idx].type != CHAT_SETTING_STRING ? s->values[idx] : def;
|
||||
}
|
||||
|
||||
/* ─── instance-level API ─── */
|
||||
@@ -208,12 +207,14 @@ static struct chat_setting_state* cs_state(struct UTUN_INSTANCE* inst) {
|
||||
|
||||
int chat_setting_set(struct UTUN_INSTANCE* inst, const char* name, const char* value) {
|
||||
struct chat_setting_state* s = cs_state(inst);
|
||||
if (!s) return -2;
|
||||
if (!s) { DEBUG_ERROR(CS_ID, "chat_setting: instance is NULL"); return -2; }
|
||||
int result = chat_setting_set_state(s, name, value);
|
||||
if (!result && !strcmp(name, "incoming_call_policy"))
|
||||
call_set_phone_state(inst, inst->phone_state, (unsigned)chat_setting_get_int(inst, name, CALL_POLICY_PUBLISH_PHONE));
|
||||
if (!result && (!strcmp(name, "local_invites_enabled") || !strcmp(name, "local_broadcasts_enabled")))
|
||||
return chat_local_apply_settings(inst);
|
||||
if (!result && (!strcmp(name, "local_invites_enabled") || !strcmp(name, "local_broadcasts_enabled")) &&
|
||||
chat_local_apply_settings(inst)) {
|
||||
DEBUG_ERROR(CS_ID, "chat_setting: '%s' stored but LAN apply failed; retry setting to reapply", name); return -3;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
+21
-9
@@ -2,12 +2,13 @@
|
||||
* chat_setting.h — chat-related settings (key-value API)
|
||||
*
|
||||
* Один API для всех потребителей: парсер конфига (headless) и GUI (через bridge).
|
||||
* chat_setting_set(name, value) — валидация + хранение + apply к модулям.
|
||||
* Возвращает 0=OK, -1=неизвестная настройка, -2=невалидное значение.
|
||||
* Нулевое состояние инициализируется значениями реестра при первом успешном set.
|
||||
* Ошибка валидации не меняет состояние. Чтение const, не выделяет память и не пишет в хранилище.
|
||||
*
|
||||
* Состояние — per-instance. Конфиг-парсер заполняет struct chat_setting_state
|
||||
* из [chatserver] ещё до создания instance (state-уровень), затем runtime-модули
|
||||
* читают через instance-уровень (chat_setting_get_int(inst, ...)).
|
||||
* читают через instance-уровень (chat_setting_get_int(inst, ...)). State не синхронизирован:
|
||||
* до старта им владеет парсер, runtime-вызовы выполняются в uasync-потоке instance.
|
||||
*/
|
||||
|
||||
#ifndef CHAT_SETTING_H
|
||||
@@ -27,23 +28,34 @@ extern "C" {
|
||||
#define CHAT_SETTING_STRING_MAX 16
|
||||
|
||||
struct chat_setting_state {
|
||||
int values[CHAT_SETTING_MAX_SETTINGS];
|
||||
int string_idx[CHAT_SETTING_STRING_MAX];
|
||||
char string_val[CHAT_SETTING_STRING_MAX][256];
|
||||
int string_count;
|
||||
int initialized;
|
||||
char int_buf[16]; /* буфер для chat_setting_get_state у int-настроек */
|
||||
int values[CHAT_SETTING_MAX_SETTINGS]; /* Числовые значения по индексу реестра. */
|
||||
int string_idx[CHAT_SETTING_STRING_MAX]; /* Индексы занятых строковых настроек. */
|
||||
char string_val[CHAT_SETTING_STRING_MAX][256]; /* NUL-строки, не более255 байт. */
|
||||
int string_count; /* Число занятых string_val. */
|
||||
int initialized; /* 0 до первого успешного set. */
|
||||
char int_text[CHAT_SETTING_MAX_SETTINGS][16]; /* Текст числа, обновляемый только setter. */
|
||||
};
|
||||
|
||||
/* ─── state-уровень (конфиг-парсер, до создания instance) ─── */
|
||||
|
||||
/* s изначально обнулён. name/value заимствованы до возврата; значение копируется.
|
||||
* 0 — сохранено, -1 — неизвестное имя, -2 — неверные аргументы/значение/нет места.
|
||||
* Числа — decimal в диапазоне реестра; bool — 0/1,no/yes,false/true; пути допускают пустую строку. */
|
||||
int chat_setting_set_state(struct chat_setting_state* s, const char* name, const char* value);
|
||||
/* Заимствованная NUL-строка до изменения этой настройки/уничтожения s; другие getters её не меняют.
|
||||
* NULL — неизвестное имя/NULL s/неинициализированное состояние. Для bitrate range даже до set — "300:4000".
|
||||
* Незаполненный строковый путь после инициализации — "". */
|
||||
const char* chat_setting_get_state(const struct chat_setting_state* s, const char* name);
|
||||
/* Число/bool либо def для неизвестного/строкового имени, NULL s и неинициализированного состояния. */
|
||||
int chat_setting_get_int_state(const struct chat_setting_state* s, const char* name, int def);
|
||||
|
||||
/* ─── instance-уровень (runtime) ─── */
|
||||
|
||||
/* Те же коды; NULL inst — -2. Большинство потребителей читают значение при следующем обращении/старте.
|
||||
* incoming_call_policy сразу обновляет телефонную политику, local_* — LAN permissions.
|
||||
* -3 — значение сохранено, применение LAN не удалось; повтор set того же значения повторяет apply. */
|
||||
int chat_setting_set(struct UTUN_INSTANCE* inst, const char* name, const char* value);
|
||||
/* Instance-варианты чтения с теми же lifetime/default; NULL inst даёт NULL/def. */
|
||||
const char* chat_setting_get(struct UTUN_INSTANCE* inst, const char* name);
|
||||
int chat_setting_get_int(struct UTUN_INSTANCE* inst, const char* name, int def);
|
||||
|
||||
|
||||
Reference in new issue
Block a user