Merge branch 'feat/m69-reboot-must-not-rewrite-persistent-b'
This commit is contained in:
commit
08b3e64c8a
4 files changed
+581
-41
No files matched your search
@@ -120,6 +120,8 @@
|
|||||||
#define FB_DEV "/dev/fb0"
|
#define FB_DEV "/dev/fb0"
|
||||||
#define MEOWAPPS "/usr/data/mnt/sd_0/MeowAPPs"
|
#define MEOWAPPS "/usr/data/mnt/sd_0/MeowAPPs"
|
||||||
#define CONF_FILE MEOWAPPS "/meow.conf" /* TF 卡配置:boot_timeout_ms/boot_menu/bg_color;boot_default 权威在板载 /data/meow_boot_default */
|
#define CONF_FILE MEOWAPPS "/meow.conf" /* TF 卡配置:boot_timeout_ms/boot_menu/bg_color;boot_default 权威在板载 /data/meow_boot_default */
|
||||||
|
#define BOOT_DEFAULT_FILE "/data/meow_boot_default" /* 持久默认=设置里的「默认系统」:0=MeowOS,1=HibyOS */
|
||||||
|
#define BOOT_ONCE_FILE "/data/meow_boot_once" /* 一次性指示:1=这次开机进 HibyOS(本程序读一次即删) */
|
||||||
#define MODE_FILE "/tmp/meow_boot_mode" /* 开机模式标记(tmpfs,开机清空) */
|
#define MODE_FILE "/tmp/meow_boot_mode" /* 开机模式标记(tmpfs,开机清空) */
|
||||||
#define PLAYER_BIN "/usr/bin/hiby_player"
|
#define PLAYER_BIN "/usr/bin/hiby_player"
|
||||||
#define PLAYER_LOG "/usr/data/meow_player.log"
|
#define PLAYER_LOG "/usr/data/meow_player.log"
|
||||||
@@ -173,8 +175,11 @@
|
|||||||
#endif
|
#endif
|
||||||
#define KEYBUF_WORDS 32 /* 1024 bits, 覆盖 KEY_MAX 767 */
|
#define KEYBUF_WORDS 32 /* 1024 bits, 覆盖 KEY_MAX 767 */
|
||||||
|
|
||||||
/* ---- 编译默认值(可被 /usr/data/mnt/sd_0/MeowAPPs/meow.conf 覆写)---- */
|
/* ---- 编译默认值(可被 /usr/data/mnt/sd_0/MeowAPPs/meow.conf 覆写)----
|
||||||
#define DEF_BOOT_DEFAULT 1 /* 1=HibyOS 0=MeowOS */
|
* M69(2026-11-12 定案):默认改 0(MeowOS)。理由:设置页「默认系统」的默认值就是 meow
|
||||||
|
* (meow_settings.c `boot.default` 默认 "meow"),且这是 MeowOS 自己的固件——用户从未设过默认
|
||||||
|
* 时不该被丢进 Hiby(旧值 1 与设置页默认矛盾,也是「每次升级后要手动切」的主因)。 */
|
||||||
|
#define DEF_BOOT_DEFAULT 0 /* 1=HibyOS 0=MeowOS */
|
||||||
#define DEF_BOOT_TIMEOUT_MS 5000
|
#define DEF_BOOT_TIMEOUT_MS 5000
|
||||||
#define DEF_BOOT_MENU 1
|
#define DEF_BOOT_MENU 1
|
||||||
#define DEF_BG 0x000000
|
#define DEF_BG 0x000000
|
||||||
@@ -514,18 +519,30 @@ static void draw_clock(uint32_t bg)
|
|||||||
|
|
||||||
/* ================================================================== */
|
/* ================================================================== */
|
||||||
/* 配置:boot_default / boot_timeout_ms / boot_menu / bg_color */
|
/* 配置:boot_default / boot_timeout_ms / boot_menu / bg_color */
|
||||||
/* boot_default:权威 = 板载 /data/meow_boot_default(O_TRUNC 单值、不写 TF 卡);
|
/* boot_default 优先级(高 → 低;M69 定案 2026-11-12):
|
||||||
* meow.conf 的 boot_default= 仅作无板载标记时的兜底(不新增写入,见 set_boot_default_meow)。 */
|
* ① /data/meow_boot_once 一次性指示(「这一次去 HibyOS」,读一次即删)
|
||||||
|
* ② /data/meow_boot_default 持久默认 = 设置里的「默认系统」(权威)
|
||||||
|
* ③ TF 卡 meow.conf 的 boot_default=(无板载标记时的兜底)
|
||||||
|
* ④ 编译默认 DEF_BOOT_DEFAULT(=MeowOS)
|
||||||
|
* 一次性指示必须压过持久默认,否则用户把默认设成 MeowOS 后「切换到 HibyOS」永远失效。 */
|
||||||
/* ================================================================== */
|
/* ================================================================== */
|
||||||
static int cfg_boot_default = DEF_BOOT_DEFAULT;
|
static int cfg_boot_default = DEF_BOOT_DEFAULT;
|
||||||
static long cfg_boot_timeout_ms = DEF_BOOT_TIMEOUT_MS;
|
static long cfg_boot_timeout_ms = DEF_BOOT_TIMEOUT_MS;
|
||||||
static int cfg_boot_menu = DEF_BOOT_MENU;
|
static int cfg_boot_menu = DEF_BOOT_MENU;
|
||||||
static uint32_t cfg_bg = DEF_BG;
|
static uint32_t cfg_bg = DEF_BG;
|
||||||
|
|
||||||
/* 读板载 /data/meow_boot_default(开机默认权威标记)。返回 0/1;-1=不存在/非法。 */
|
/* 配置/标记文件的生产路径;宿主自测可直接重定向这三个指针(只换路径,不换语义)。 */
|
||||||
|
static const char *g_conf_path = CONF_FILE;
|
||||||
|
static const char *g_boot_default_path = BOOT_DEFAULT_FILE;
|
||||||
|
static const char *g_boot_once_path = BOOT_ONCE_FILE;
|
||||||
|
|
||||||
|
/* bm_log 定义在后方(卡日志);load_config 记录一次性指示消费结果要用。 */
|
||||||
|
static void bm_log(const char *fmt, ...);
|
||||||
|
|
||||||
|
/* 读板载 /data/meow_boot_default(=设置里的「默认系统」)。返回 0/1;-1=不存在/非法。 */
|
||||||
static int read_boot_default_onboard(void)
|
static int read_boot_default_onboard(void)
|
||||||
{
|
{
|
||||||
int fd = open("/data/meow_boot_default", O_RDONLY);
|
int fd = open(g_boot_default_path, O_RDONLY);
|
||||||
if (fd < 0) return -1;
|
if (fd < 0) return -1;
|
||||||
char buf[8];
|
char buf[8];
|
||||||
int n = read(fd, buf, sizeof buf - 1);
|
int n = read(fd, buf, sizeof buf - 1);
|
||||||
@@ -536,6 +553,50 @@ static int read_boot_default_onboard(void)
|
|||||||
return (v == 0 || v == 1) ? v : -1;
|
return (v == 0 || v == 1) ? v : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 消费一次性指示 /data/meow_boot_once(meow_svc_power 写,本程序开机读一次即删)。
|
||||||
|
* 返回 0/1;-1=不存在/内容非法。
|
||||||
|
* 删除失败**不许影响开机**:本次照用该值;再尽量把内容清空,保证下一次开机读不到有效值
|
||||||
|
* (两条都失败只记日志继续走,宁可日志脏也不让机器开不了机)。 */
|
||||||
|
static int consume_boot_once(void)
|
||||||
|
{
|
||||||
|
int fd = open(g_boot_once_path, O_RDONLY);
|
||||||
|
if (fd < 0) return -1;
|
||||||
|
char buf[8];
|
||||||
|
int n = read(fd, buf, sizeof buf - 1);
|
||||||
|
close(fd);
|
||||||
|
int v = -1;
|
||||||
|
if (n > 0) {
|
||||||
|
/* 严格取值:只接受 "0" / "1"(允许前后空白)。不能直接用 atoi —— "garbage" 会被
|
||||||
|
* atoi 读成 0,等于把垃圾内容当成「进 MeowOS」的指示。 */
|
||||||
|
buf[n] = 0;
|
||||||
|
char *p = buf;
|
||||||
|
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
|
||||||
|
if (*p == '0' || *p == '1') {
|
||||||
|
char c = *p++;
|
||||||
|
while (*p == ' ' || *p == '\t' || *p == '\r' || *p == '\n') p++;
|
||||||
|
if (*p == 0) v = c - '0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if (unlink(g_boot_once_path) == 0) {
|
||||||
|
bm_log("once: %s -> %s (used, removed)", g_boot_once_path, v < 0 ? "invalid" : (v ? "hiby" : "meow"));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
int uerr = errno;
|
||||||
|
/* 删不掉:退而求其次清空内容(下次 open 读到 0 字节 → 非法 → 回落持久默认)。 */
|
||||||
|
int wf = open(g_boot_once_path, O_WRONLY | O_TRUNC);
|
||||||
|
if (wf >= 0) {
|
||||||
|
close(wf);
|
||||||
|
bm_log("once: %s -> %s (used; unlink failed: %s; truncated so next boot ignores it)",
|
||||||
|
g_boot_once_path, v < 0 ? "invalid" : (v ? "hiby" : "meow"), strerror(uerr));
|
||||||
|
} else {
|
||||||
|
bm_log("once: %s -> %s (used; unlink+truncate failed: %s/%s; file may apply again next boot)",
|
||||||
|
g_boot_once_path, v < 0 ? "invalid" : (v ? "hiby" : "meow"), strerror(uerr), strerror(errno));
|
||||||
|
}
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
static void load_config(void)
|
static void load_config(void)
|
||||||
{
|
{
|
||||||
cfg_boot_default = DEF_BOOT_DEFAULT;
|
cfg_boot_default = DEF_BOOT_DEFAULT;
|
||||||
@@ -543,13 +604,13 @@ static void load_config(void)
|
|||||||
cfg_boot_menu = DEF_BOOT_MENU;
|
cfg_boot_menu = DEF_BOOT_MENU;
|
||||||
cfg_bg = DEF_BG;
|
cfg_bg = DEF_BG;
|
||||||
int has_conf = 1;
|
int has_conf = 1;
|
||||||
FILE *f = fopen(CONF_FILE, "r");
|
FILE *f = fopen(g_conf_path, "r");
|
||||||
if (!f) { fprintf(stderr, "bootmenu: no %s, using defaults\n", CONF_FILE); has_conf = 0; }
|
if (!f) { fprintf(stderr, "bootmenu: no %s, using defaults\n", g_conf_path); has_conf = 0; }
|
||||||
if (has_conf) {
|
if (has_conf) {
|
||||||
char buf[128];
|
char buf[128];
|
||||||
while (fgets(buf, sizeof buf, f)) {
|
while (fgets(buf, sizeof buf, f)) {
|
||||||
if (!strncmp(buf, "boot_default=", 13)) {
|
if (!strncmp(buf, "boot_default=", 13)) {
|
||||||
/* 板载 /data/meow_boot_default 为权威;此处仅作无板载标记时的兜底(不再新增写入)。 */
|
/* 板载标记为权威;此处仅作无板载标记时的兜底(不再新增写入)。 */
|
||||||
cfg_boot_default = atoi(buf + 13);
|
cfg_boot_default = atoi(buf + 13);
|
||||||
if (cfg_boot_default != 0 && cfg_boot_default != 1) cfg_boot_default = DEF_BOOT_DEFAULT;
|
if (cfg_boot_default != 0 && cfg_boot_default != 1) cfg_boot_default = DEF_BOOT_DEFAULT;
|
||||||
} else if (!strncmp(buf, "boot_timeout_ms=", 16)) {
|
} else if (!strncmp(buf, "boot_timeout_ms=", 16)) {
|
||||||
@@ -565,12 +626,20 @@ static void load_config(void)
|
|||||||
}
|
}
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
/* 板载标记优先(权威):覆盖 meow.conf 兜底/编译默认。 */
|
/* 一次性指示(这次开机专用)压过一切持久来源;读到即删。 */
|
||||||
int ob = read_boot_default_onboard();
|
int oc = consume_boot_once();
|
||||||
if (ob == 0 || ob == 1) {
|
if (oc == 0 || oc == 1) {
|
||||||
if (ob != cfg_boot_default)
|
if (oc != cfg_boot_default)
|
||||||
fprintf(stderr, "bootmenu: boot_default onboard=%d (override %d)\n", ob, cfg_boot_default);
|
fprintf(stderr, "bootmenu: boot_default once=%d (override %d)\n", oc, cfg_boot_default);
|
||||||
cfg_boot_default = ob;
|
cfg_boot_default = oc;
|
||||||
|
} else {
|
||||||
|
/* 持久默认(设置里的「默认系统」)优先:覆盖 meow.conf 兜底/编译默认。 */
|
||||||
|
int ob = read_boot_default_onboard();
|
||||||
|
if (ob == 0 || ob == 1) {
|
||||||
|
if (ob != cfg_boot_default)
|
||||||
|
fprintf(stderr, "bootmenu: boot_default onboard=%d (override %d)\n", ob, cfg_boot_default);
|
||||||
|
cfg_boot_default = ob;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fprintf(stderr, "bootmenu: cfg boot_default=%d timeout_ms=%ld boot_menu=%d bg=%06x\n",
|
fprintf(stderr, "bootmenu: cfg boot_default=%d timeout_ms=%ld boot_menu=%d bg=%06x\n",
|
||||||
cfg_boot_default, cfg_boot_timeout_ms, cfg_boot_menu, cfg_bg);
|
cfg_boot_default, cfg_boot_timeout_ms, cfg_boot_menu, cfg_bg);
|
||||||
@@ -1644,14 +1713,18 @@ static void write_mode(int choice)
|
|||||||
choice == 1 ? "hiby" : "meow");
|
choice == 1 ? "hiby" : "meow");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Bug 1:选 MeowOS → 把它持久为开机默认(写 spec 标记 + meow.conf boot_default=0),与 launcher
|
/* Bug 1:选 MeowOS → 把它持久为开机默认(写 spec 标记),与设置页「默认系统」/ `set-default-*` 对称。
|
||||||
* «回到HibyOS»(set_boot_default_hiby 写 boot_default=1)对称。效果:本次选中即进 MeowOS 主屏,且
|
* 效果:本次选中即进 MeowOS 主屏,且下一次开机选单默认 MeowOS(不再「选 MeowOS 仍落回 HibyOS」)。
|
||||||
* 下一次开机选单默认 MeowOS(不再「选 MeowOS 仍落回 HibyOS」)。
|
* 权威标记 = 板载 /data/meow_boot_default(O_TRUNC 单值);meow.conf 不再写/承载 boot_default。
|
||||||
* 权威标记 = 板载 /data/meow_boot_default(O_TRUNC 单值);meow.conf 不再写/承载 boot_default。 */
|
* 注意:这是**用户在选单里显式选了 MeowOS**(等同设默认),与电源菜单「重启」路径无关。 */
|
||||||
static void set_boot_default_meow(void)
|
static void set_boot_default_meow(void)
|
||||||
{
|
{
|
||||||
int fd = open("/data/meow_boot_default", O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
int fd = open(g_boot_default_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||||
if (fd >= 0) { write(fd, "0\n", 2); close(fd); fprintf(stderr, "bootmenu: /data/meow_boot_default=0\n"); }
|
if (fd >= 0) {
|
||||||
|
write(fd, "0\n", 2);
|
||||||
|
close(fd);
|
||||||
|
fprintf(stderr, "bootmenu: %s=0\n", g_boot_default_path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================================================================== */
|
/* ================================================================== */
|
||||||
|
|||||||
@@ -0,0 +1,197 @@
|
|||||||
|
/* test_bm_bootdefault.c — 宿主自测:启动菜单「默认项从哪来」+ 一次性指示(M69)。
|
||||||
|
*
|
||||||
|
* 覆盖的验收推演:
|
||||||
|
* ① 文件全不存在 → 编译默认 MeowOS(DEF_BOOT_DEFAULT=0);
|
||||||
|
* ② /data/meow_boot_default(设置里的「默认系统」)压过 meow.conf 兜底;
|
||||||
|
* ③ 一次性指示 /data/meow_boot_once 压过持久默认,且**读一次即删**;
|
||||||
|
* ④ 删掉之后的下一次开机回落持久默认;
|
||||||
|
* ⑤ 内容非法 → 忽略并删除;
|
||||||
|
* ⑥ 删不掉时「能用就用、不许开机失败」,并尽量清空内容让下一次开机不生效。
|
||||||
|
*
|
||||||
|
* 编译/运行(Termux 上 /tmp 不可写,带 TMPDIR 最稳):
|
||||||
|
* mkdir -p "$HOME/tmp/m69test"
|
||||||
|
* cc -Wall -Wextra -Os -I../meow_launcherd -Wno-unused-function -Wno-unused-parameter \
|
||||||
|
* -Wno-unused-variable test_bm_bootdefault.c ../meow_launcherd/meow_touch.c \
|
||||||
|
* -o "$HOME/tmp/m69test/bm_bt" &&
|
||||||
|
* TMPDIR=$HOME/tmp/m69test "$HOME/tmp/m69test/bm_bt" # 全 PASS -> exit 0
|
||||||
|
*/
|
||||||
|
#define BM_NO_MAIN
|
||||||
|
#include "meow_bootmenu.c"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
static int g_pass = 0, g_fail = 0;
|
||||||
|
|
||||||
|
static void check(const char *name, int got, int want)
|
||||||
|
{
|
||||||
|
if (got == want) { g_pass++; printf("PASS %s\n", name); }
|
||||||
|
else { g_fail++; printf("FAIL %s: got %d want %d\n", name, got, want); }
|
||||||
|
}
|
||||||
|
|
||||||
|
static char g_dir[512], g_conf[600], g_def[600], g_once[600];
|
||||||
|
|
||||||
|
/* 临时目录:$TMPDIR → /tmp → $HOME/tmp/m69test(Termux 上 /tmp 属别的用户,不可写)。 */
|
||||||
|
static int mk_tmp_dir(void)
|
||||||
|
{
|
||||||
|
const char *cand[3];
|
||||||
|
char homebuf[512] = "";
|
||||||
|
int n = 0;
|
||||||
|
const char *t = getenv("TMPDIR");
|
||||||
|
if (t && t[0]) cand[n++] = t;
|
||||||
|
cand[n++] = "/tmp";
|
||||||
|
const char *home = getenv("HOME");
|
||||||
|
if (home && home[0]) {
|
||||||
|
snprintf(homebuf, sizeof homebuf, "%s/tmp/m69test", home);
|
||||||
|
mkdir(homebuf, 0700);
|
||||||
|
cand[n++] = homebuf;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
snprintf(g_dir, sizeof g_dir, "%s/m69_bm_XXXXXX", cand[i]);
|
||||||
|
if (!mkdtemp(g_dir)) continue;
|
||||||
|
snprintf(g_conf, sizeof g_conf, "%s/meow.conf", g_dir);
|
||||||
|
snprintf(g_def, sizeof g_def, "%s/boot_default", g_dir);
|
||||||
|
snprintf(g_once, sizeof g_once, "%s/boot_once", g_dir);
|
||||||
|
g_conf_path = g_conf;
|
||||||
|
g_boot_default_path = g_def;
|
||||||
|
g_boot_once_path = g_once;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put(const char *path, const char *body)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "w");
|
||||||
|
if (!f) { g_fail++; printf("FAIL cannot write %s\n", path); return; }
|
||||||
|
fputs(body, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void reset(void)
|
||||||
|
{
|
||||||
|
unlink(g_conf);
|
||||||
|
unlink(g_def);
|
||||||
|
unlink(g_once);
|
||||||
|
}
|
||||||
|
|
||||||
|
static long file_size(const char *path)
|
||||||
|
{
|
||||||
|
struct stat st;
|
||||||
|
if (stat(path, &st) != 0) return -1;
|
||||||
|
return (long)st.st_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- ① 什么都没有 → 编译默认 MeowOS ---- */
|
||||||
|
static void case_compile_default(void)
|
||||||
|
{
|
||||||
|
printf("== no files -> compile default ==\n");
|
||||||
|
reset();
|
||||||
|
load_config();
|
||||||
|
check("compile default is MeowOS (0)", cfg_boot_default, 0);
|
||||||
|
check("DEF_BOOT_DEFAULT == 0", DEF_BOOT_DEFAULT, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- ② 持久默认 = 设置里的「默认系统」,压过 conf 兜底 ---- */
|
||||||
|
static void case_persistent_is_authoritative(void)
|
||||||
|
{
|
||||||
|
printf("== persistent boot default ==\n");
|
||||||
|
reset();
|
||||||
|
put(g_def, "1\n");
|
||||||
|
load_config();
|
||||||
|
check("persistent=1 -> Hiby", cfg_boot_default, 1);
|
||||||
|
|
||||||
|
put(g_conf, "boot_default=0\n");
|
||||||
|
load_config();
|
||||||
|
check("persistent=1 overrides conf boot_default=0", cfg_boot_default, 1);
|
||||||
|
|
||||||
|
put(g_def, "0\n");
|
||||||
|
load_config();
|
||||||
|
check("persistent=0 -> MeowOS", cfg_boot_default, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- ③④ 一次性指示:优先、读一次即删、下次回落持久默认 ---- */
|
||||||
|
static void case_once_overrides_and_is_consumed(void)
|
||||||
|
{
|
||||||
|
printf("== one-shot boot_once ==\n");
|
||||||
|
reset();
|
||||||
|
put(g_def, "0\n"); /* 用户设的默认 = MeowOS */
|
||||||
|
put(g_once, "1\n"); /* 「切换到 HibyOS」= 这一次去 Hiby */
|
||||||
|
|
||||||
|
load_config();
|
||||||
|
check("once=1 overrides persistent=0", cfg_boot_default, 1);
|
||||||
|
check("once file removed after use", access(g_once, F_OK), -1);
|
||||||
|
|
||||||
|
load_config(); /* 模拟下一次开机 */
|
||||||
|
check("next boot falls back to persistent 0", cfg_boot_default, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- ⑤ 非法内容:忽略 + 删除(不留垃圾反复干扰) ---- */
|
||||||
|
static void case_invalid_once(void)
|
||||||
|
{
|
||||||
|
printf("== invalid boot_once ==\n");
|
||||||
|
reset();
|
||||||
|
put(g_def, "1\n");
|
||||||
|
put(g_once, "garbage\n");
|
||||||
|
load_config();
|
||||||
|
check("invalid once ignored (persistent=1 wins)", cfg_boot_default, 1);
|
||||||
|
check("invalid once removed", access(g_once, F_OK), -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- ⑥ 删不掉:能用就用(不许开机失败),并尽量清空让下次不生效 ----
|
||||||
|
* 构造「目录不可写 + 文件可写」:unlink 需要目录写权限 → 失败;O_TRUNC 只需文件写权限 → 成功。
|
||||||
|
* (以 root 跑时 unlink 会成功,本用例自动 SKIP。) */
|
||||||
|
static void case_unlink_failure(void)
|
||||||
|
{
|
||||||
|
printf("== unlink failure is survivable ==\n");
|
||||||
|
reset();
|
||||||
|
put(g_def, "0\n");
|
||||||
|
put(g_once, "1\n");
|
||||||
|
|
||||||
|
chmod(g_dir, 0555);
|
||||||
|
if (unlink(g_once) == 0) { /* 探测:权限没生效(多半是 root) */
|
||||||
|
chmod(g_dir, 0755);
|
||||||
|
put(g_once, "1\n");
|
||||||
|
printf("SKIP unlink-failure case (unlink succeeded, uid=%d)\n", (int)getuid());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
load_config();
|
||||||
|
check("unremovable once still used this boot", cfg_boot_default, 1);
|
||||||
|
check("once content truncated to empty", (int)file_size(g_once), 0);
|
||||||
|
|
||||||
|
chmod(g_dir, 0755);
|
||||||
|
load_config(); /* 模拟下一次开机 */
|
||||||
|
check("next boot ignores emptied once (persistent=0)", cfg_boot_default, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 组件契约:两个组件必须用同一对生产路径 ---- */
|
||||||
|
static void case_prod_paths(void)
|
||||||
|
{
|
||||||
|
printf("== production paths ==\n");
|
||||||
|
check("BOOT_DEFAULT_FILE", strcmp(BOOT_DEFAULT_FILE, "/data/meow_boot_default"), 0);
|
||||||
|
check("BOOT_ONCE_FILE", strcmp(BOOT_ONCE_FILE, "/data/meow_boot_once"), 0);
|
||||||
|
check("CONF_FILE on TF card", strcmp(CONF_FILE, "/usr/data/mnt/sd_0/MeowAPPs/meow.conf"), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
if (mk_tmp_dir() != 0) { printf("FATAL: no writable tmp dir\n"); return 1; }
|
||||||
|
|
||||||
|
case_compile_default();
|
||||||
|
case_persistent_is_authoritative();
|
||||||
|
case_once_overrides_and_is_consumed();
|
||||||
|
case_invalid_once();
|
||||||
|
case_unlink_failure();
|
||||||
|
case_prod_paths();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
rmdir(g_dir);
|
||||||
|
|
||||||
|
if (g_fail) { printf("\nBOOTMENU BOOTDEFAULT TEST: %d FAIL (%d pass)\n", g_fail, g_pass); return 1; }
|
||||||
|
printf("\nBOOTMENU BOOTDEFAULT TEST: ALL PASS (%d checks)\n", g_pass);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -10,6 +10,12 @@
|
|||||||
* 动作逻辑与 launcher `power_menu.c` 的 `sys_do_reboot`/`set_boot_default_hiby`/`bootmode.sh Recovery`
|
* 动作逻辑与 launcher `power_menu.c` 的 `sys_do_reboot`/`set_boot_default_hiby`/`bootmode.sh Recovery`
|
||||||
* **同源 0-diff 搬移**,仅入口(CLI vs IPC)与鉴权层不同。
|
* **同源 0-diff 搬移**,仅入口(CLI vs IPC)与鉴权层不同。
|
||||||
*
|
*
|
||||||
|
* 开机默认语义(M69,用户定案 2026-11-12):`/data/meow_boot_default` 是「设置里的默认系统」,
|
||||||
|
* **只有显式设默认(POWER_SET_DEFAULT_* / CLI set-default-*)才能写**;普通重启(POWER_REBOOT /
|
||||||
|
* CLI reboot)与 OTA 升级后的重启(POWER_UPGRADE)都不碰它——重启后选单按配置默认(含倒计时)。
|
||||||
|
* 「这一次去 Hiby」(POWER_BACK_HIBY / CLI `reboot --hiby`)只写一次性指示 `/data/meow_boot_once`,
|
||||||
|
* 由 `meow_bootmenu` 开机读一次即删,不改持久默认。
|
||||||
|
*
|
||||||
* 鉴权(II 期,MPK_SIGNATURE_DESIGN §6):IPC 服务端按【连接级身份】分级——`SO_PEERCRED` 取 pid →
|
* 鉴权(II 期,MPK_SIGNATURE_DESIGN §6):IPC 服务端按【连接级身份】分级——`SO_PEERCRED` 取 pid →
|
||||||
* `readlink /proc/pid/exe` → 命中 `MeowAPPs/<pkg>/` 判定包名,或命中系统二进制 allowlist
|
* `readlink /proc/pid/exe` → 命中 `MeowAPPs/<pkg>/` 判定包名,或命中系统二进制 allowlist
|
||||||
* (/usr/bin/meow_*、/usr/data/meow/meow*、MeowPanel 后端)判定为系统身份;其余为 OTHER。
|
* (/usr/bin/meow_*、/usr/data/meow/meow*、MeowPanel 后端)判定为系统身份;其余为 OTHER。
|
||||||
@@ -51,7 +57,8 @@
|
|||||||
#define FBIOBLANK 0x4611
|
#define FBIOBLANK 0x4611
|
||||||
#define BACKLIGHT "/sys/class/backlight/backlight_pwm0/brightness"
|
#define BACKLIGHT "/sys/class/backlight/backlight_pwm0/brightness"
|
||||||
#define BACKLIGHT_POWER "/sys/class/backlight/backlight_pwm0/bl_power"
|
#define BACKLIGHT_POWER "/sys/class/backlight/backlight_pwm0/bl_power"
|
||||||
#define BOOT_DEFAULT "/data/meow_boot_default" /* 0=MeowOS(默认),1=HibyOS */
|
#define BOOT_DEFAULT "/data/meow_boot_default" /* 持久默认=设置里的「默认系统」:0=MeowOS,1=HibyOS */
|
||||||
|
#define BOOT_ONCE "/data/meow_boot_once" /* 一次性指示:1=这次开机进 HibyOS(bootmenu 读一次即删) */
|
||||||
#define COLD_BOOT "/data/meow_cold_boot" /* 冷启标记:1=上次关机,0=上次重启 */
|
#define COLD_BOOT "/data/meow_cold_boot" /* 冷启标记:1=上次关机,0=上次重启 */
|
||||||
#define MTD_OTA "/dev/mtd5" /* ota 标记分区(8M) */
|
#define MTD_OTA "/dev/mtd5" /* ota 标记分区(8M) */
|
||||||
#define SETTINGS_FILE "/usr/data/meow/settings.conf"
|
#define SETTINGS_FILE "/usr/data/meow/settings.conf"
|
||||||
@@ -72,6 +79,10 @@
|
|||||||
#define CHG_AC_ONLINE MEOW_PS_DIR "/ac/online"
|
#define CHG_AC_ONLINE MEOW_PS_DIR "/ac/online"
|
||||||
#define UPGRADE_FW "/usr/data/mnt/sd_0/r3proii.upt" /* 卡根升级固件(写 mtd5 前完整性校验) */
|
#define UPGRADE_FW "/usr/data/mnt/sd_0/r3proii.upt" /* 卡根升级固件(写 mtd5 前完整性校验) */
|
||||||
|
|
||||||
|
/* 启动默认两个文件的生产路径(语义见文件头);宿主自测可直接重定向这两个指针,不改语义。 */
|
||||||
|
static const char *g_boot_default_path = BOOT_DEFAULT;
|
||||||
|
static const char *g_boot_once_path = BOOT_ONCE;
|
||||||
|
|
||||||
/* 音量语义(与 launcher MEOW_VOL_DEFAULT/MEOW_VOL_MAX_DEFAULT、SDK MEOW_UI_VOL_* 一致):
|
/* 音量语义(与 launcher MEOW_VOL_DEFAULT/MEOW_VOL_MAX_DEFAULT、SDK MEOW_UI_VOL_* 一致):
|
||||||
* audio.volume 默认 60(写 DAC 映射值 0-100);audio.volume_max 默认 100(软上限 clamp)。 */
|
* audio.volume 默认 60(写 DAC 映射值 0-100);audio.volume_max 默认 100(软上限 clamp)。 */
|
||||||
#ifndef MEOW_VOL_DEFAULT
|
#ifndef MEOW_VOL_DEFAULT
|
||||||
@@ -279,14 +290,57 @@ static void sys_reboot_do(int cmd)
|
|||||||
for (;;) usleep(1000);
|
for (;;) usleep(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------- boot 默认 ---------------- */
|
/* ---------------- boot 默认 ----------------
|
||||||
/* 写 /data/meow_boot_default(0=MeowOS 默认 / 1=HibyOS 默认)。 */
|
* 两个文件,两种语义(M69,用户定案 2026-11-12):
|
||||||
static void write_boot_default(int v)
|
* · /data/meow_boot_default = 设置里的「默认系统」(持久)。**只有** SET_DEFAULT_* / set-default-*
|
||||||
|
* 写它;普通重启/升级/关机都不写,重启后由选单读它决定默认项。
|
||||||
|
* · /data/meow_boot_once = 「这一次去 Hiby」一次性指示。bootmenu 开机读一次即删、优先级更高。
|
||||||
|
* 写入策略集中在 boot_default_intent()(纯函数,宿主可测),CLI 与 IPC 共用同一张表。 */
|
||||||
|
static int write_boot_default(int v)
|
||||||
{
|
{
|
||||||
char b[8];
|
char b[8];
|
||||||
snprintf(b, sizeof b, "%d\n", v);
|
snprintf(b, sizeof b, "%d\n", v);
|
||||||
if (write_file_str(BOOT_DEFAULT, b, 0644) < 0) svc_log("write %s fail: %s", BOOT_DEFAULT, strerror(errno));
|
int rc = write_file_str(g_boot_default_path, b, 0644);
|
||||||
|
if (rc < 0) svc_log("write %s fail: %s", g_boot_default_path, strerror(errno));
|
||||||
else svc_log("boot_default=%d", v);
|
else svc_log("boot_default=%d", v);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_boot_once(int v)
|
||||||
|
{
|
||||||
|
char b[8];
|
||||||
|
snprintf(b, sizeof b, "%d\n", v);
|
||||||
|
int rc = write_file_str(g_boot_once_path, b, 0644);
|
||||||
|
if (rc < 0) svc_log("write %s fail: %s", g_boot_once_path, strerror(errno));
|
||||||
|
else svc_log("boot_once=%d (one-shot; persistent default untouched)", v);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 动作 → 开机默认写入意图(纯函数):
|
||||||
|
* *persist: -1=不写持久默认;0/1=把 /data/meow_boot_default 写成该值
|
||||||
|
* *once: -1=不写一次性指示;0/1=把 /data/meow_boot_once 写成该值
|
||||||
|
* 表外动作(POWER_REBOOT/POWER_UPGRADE/POWER_SHUTDOWN/读级…)两个都不写。 */
|
||||||
|
static void boot_default_intent(uint32_t cmd, int *persist, int *once)
|
||||||
|
{
|
||||||
|
*persist = -1;
|
||||||
|
*once = -1;
|
||||||
|
switch (cmd) {
|
||||||
|
case POWER_SET_DEFAULT_MEOW: *persist = 0; break; /* 用户显式设默认 */
|
||||||
|
case POWER_SET_DEFAULT_HIBY: *persist = 1; break; /* 用户显式设默认 */
|
||||||
|
case POWER_BACK_HIBY: *once = 1; break; /* 这一次去 Hiby(临时) */
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按意图落盘(CLI 与 IPC 共用,防两条入口语义漂移)。
|
||||||
|
* 返回 0;-1=某次写入失败(动作仍照常继续:重启请求不该因标记写失败就不重启)。 */
|
||||||
|
static int apply_boot_default_intent(uint32_t cmd)
|
||||||
|
{
|
||||||
|
int persist = -1, once = -1, rc = 0;
|
||||||
|
boot_default_intent(cmd, &persist, &once);
|
||||||
|
if (persist >= 0 && write_boot_default(persist) < 0) rc = -1;
|
||||||
|
if (once >= 0 && write_boot_once(once) < 0) rc = -1;
|
||||||
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---------------- 关机 logo ---------------- */
|
/* ---------------- 关机 logo ---------------- */
|
||||||
@@ -358,7 +412,7 @@ static void battery_fill(char *out, int cap)
|
|||||||
|
|
||||||
static const char *boot_default_str(void)
|
static const char *boot_default_str(void)
|
||||||
{
|
{
|
||||||
int v = read_file_int_def(BOOT_DEFAULT, -1);
|
int v = read_file_int_def(g_boot_default_path, -1);
|
||||||
if (v < 0) return "unknown";
|
if (v < 0) return "unknown";
|
||||||
return v ? "hiby" : "meow";
|
return v ? "hiby" : "meow";
|
||||||
}
|
}
|
||||||
@@ -455,10 +509,11 @@ static int execute_action(const m_power_action_t *a, m_power_status_t *st)
|
|||||||
snprintf(st->msg, sizeof st->msg, "shutdown accepted");
|
snprintf(st->msg, sizeof st->msg, "shutdown accepted");
|
||||||
return 1; /* 应答后:logo + poweroff(不返回) */
|
return 1; /* 应答后:logo + poweroff(不返回) */
|
||||||
case POWER_REBOOT:
|
case POWER_REBOOT:
|
||||||
write_boot_default(0); /* 普通重启恒回 MeowOS(无视之前) */
|
/* 普通重启:**不写**持久默认、也不写一次性指示。重启后选单按配置默认(5s 倒计时)。
|
||||||
|
* 用户定案(2026-11-12):POWER_REBOOT 不该碰 /data/meow_boot_default。 */
|
||||||
sync();
|
sync();
|
||||||
st->code = MEOW_OK;
|
st->code = MEOW_OK;
|
||||||
snprintf(st->msg, sizeof st->msg, "reboot accepted (boot=meow)");
|
snprintf(st->msg, sizeof st->msg, "reboot accepted (boot menu uses configured default)");
|
||||||
return 1;
|
return 1;
|
||||||
case POWER_UPGRADE:
|
case POWER_UPGRADE:
|
||||||
{ /* 写 mtd5 前先校验卡上升级固件(升级写保护);校验失败→非 OK、绝不写 mtd5/不 reboot。 */
|
{ /* 写 mtd5 前先校验卡上升级固件(升级写保护);校验失败→非 OK、绝不写 mtd5/不 reboot。 */
|
||||||
@@ -474,18 +529,25 @@ static int execute_action(const m_power_action_t *a, m_power_status_t *st)
|
|||||||
snprintf(st->msg, sizeof st->msg, "upgrade accepted");
|
snprintf(st->msg, sizeof st->msg, "upgrade accepted");
|
||||||
return 1; }
|
return 1; }
|
||||||
case POWER_BACK_HIBY:
|
case POWER_BACK_HIBY:
|
||||||
write_boot_default(1);
|
{ /* 「切换到 HibyOS」= 这一次去 Hiby:只写一次性指示,持久默认不动。
|
||||||
sync();
|
* 与 CLI `reboot --hiby` 走同一张意图表(boot_default_intent),不写持久默认。 */
|
||||||
st->code = MEOW_OK;
|
int rc = apply_boot_default_intent(POWER_BACK_HIBY);
|
||||||
snprintf(st->msg, sizeof st->msg, "reboot -> hiby accepted");
|
sync();
|
||||||
return 1;
|
st->code = MEOW_OK;
|
||||||
|
if (rc < 0)
|
||||||
|
snprintf(st->msg, sizeof st->msg,
|
||||||
|
"reboot -> hiby accepted; once flag write FAILED (will boot configured default)");
|
||||||
|
else
|
||||||
|
snprintf(st->msg, sizeof st->msg,
|
||||||
|
"reboot -> hiby accepted (once; persistent default unchanged)");
|
||||||
|
return 1; }
|
||||||
case POWER_SET_DEFAULT_MEOW:
|
case POWER_SET_DEFAULT_MEOW:
|
||||||
write_boot_default(0);
|
apply_boot_default_intent(POWER_SET_DEFAULT_MEOW);
|
||||||
st->code = MEOW_OK;
|
st->code = MEOW_OK;
|
||||||
snprintf(st->msg, sizeof st->msg, "boot_default=meow (next boot)");
|
snprintf(st->msg, sizeof st->msg, "boot_default=meow (next boot)");
|
||||||
return 0;
|
return 0;
|
||||||
case POWER_SET_DEFAULT_HIBY:
|
case POWER_SET_DEFAULT_HIBY:
|
||||||
write_boot_default(1);
|
apply_boot_default_intent(POWER_SET_DEFAULT_HIBY);
|
||||||
st->code = MEOW_OK;
|
st->code = MEOW_OK;
|
||||||
snprintf(st->msg, sizeof st->msg, "boot_default=hiby (next boot)");
|
snprintf(st->msg, sizeof st->msg, "boot_default=hiby (next boot)");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -846,8 +908,15 @@ static int cli_run(int n, char **v)
|
|||||||
return 0; /* 不返回 */
|
return 0; /* 不返回 */
|
||||||
}
|
}
|
||||||
if (!strcmp(cmd, "reboot")) {
|
if (!strcmp(cmd, "reboot")) {
|
||||||
if (n > 1 && !strcmp(v[1], "--hiby")) { write_boot_default(1); }
|
if (n > 1 && !strcmp(v[1], "--hiby")) {
|
||||||
else write_boot_default(0); /* 普通重启恒回 MeowOS */
|
/* 「这一次去 Hiby」:与 IPC POWER_BACK_HIBY 同一张意图表 —— 只写一次性指示,
|
||||||
|
* 持久默认(设置里的「默认系统」)不动。 */
|
||||||
|
if (apply_boot_default_intent(POWER_BACK_HIBY) < 0)
|
||||||
|
fprintf(stderr, "warning: %s write failed; next boot uses configured default\n", BOOT_ONCE);
|
||||||
|
else
|
||||||
|
printf("reboot -> hiby (once; persistent default unchanged)\n");
|
||||||
|
}
|
||||||
|
/* 普通重启:不写持久默认、不写一次性指示 —— 重启后选单读配置默认。 */
|
||||||
sys_reboot_do(LINUX_REBOOT_CMD_RESTART);
|
sys_reboot_do(LINUX_REBOOT_CMD_RESTART);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -863,12 +932,12 @@ static int cli_run(int n, char **v)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!strcmp(cmd, "set-default-hiby")) {
|
if (!strcmp(cmd, "set-default-hiby")) {
|
||||||
write_boot_default(1);
|
apply_boot_default_intent(POWER_SET_DEFAULT_HIBY);
|
||||||
printf("boot_default=hiby (next boot)\n");
|
printf("boot_default=hiby (next boot)\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (!strcmp(cmd, "set-default-meow")) {
|
if (!strcmp(cmd, "set-default-meow")) {
|
||||||
write_boot_default(0);
|
apply_boot_default_intent(POWER_SET_DEFAULT_MEOW);
|
||||||
printf("boot_default=meow (next boot)\n");
|
printf("boot_default=meow (next boot)\n");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -921,7 +990,7 @@ static int cli_run(int n, char **v)
|
|||||||
|
|
||||||
static const char *cli_usage(void)
|
static const char *cli_usage(void)
|
||||||
{
|
{
|
||||||
return "shutdown|reboot[--hiby]|upgrade|set-default-hiby|set-default-meow|"
|
return "shutdown|reboot[--hiby=一次性去Hiby,不改默认]|upgrade|set-default-hiby|set-default-meow|"
|
||||||
"status|battery|reboot-reason|upgrade-status|screen on|off|timeout<秒>|dac on|off|status";
|
"status|battery|reboot-reason|upgrade-status|screen on|off|timeout<秒>|dac on|off|status";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,201 @@
|
|||||||
|
/* svc_power_bootdefault_test.c — 宿主自测:电源动作对「开机默认」的写入语义(M69)。
|
||||||
|
*
|
||||||
|
* 被测对象(诊断见文件头 M69 段):
|
||||||
|
* /data/meow_boot_default 持久默认 = 设置里的「默认系统」,只有显式设默认才写;
|
||||||
|
* /data/meow_boot_once 一次性「这一次去 Hiby」,POWER_BACK_HIBY / CLI `reboot --hiby` 写。
|
||||||
|
*
|
||||||
|
* 做法:#include 生产源(#define main 改名),把 g_boot_default_path / g_boot_once_path 重定向到
|
||||||
|
* 临时目录,直接断言**落盘副作用**。不调用 execute_action / cli_run —— 那两个会真的 reboot 宿主。
|
||||||
|
* 覆盖是靠「写入只有一条路径」保证的:全文件 write_boot_default/write_boot_once 的唯一调用点是
|
||||||
|
* apply_boot_default_intent(),CLI 与 IPC 共用它 + boot_default_intent() 这张表。
|
||||||
|
*
|
||||||
|
* 编译/运行(Termux 上 /tmp 不可写,带 TMPDIR 最稳):
|
||||||
|
* mkdir -p "$HOME/tmp/m69test"
|
||||||
|
* cc -Wall -Wextra -I../meow_launcherd svc_power_bootdefault_test.c -o "$HOME/tmp/m69test/svc_bt" &&
|
||||||
|
* TMPDIR=$HOME/tmp/m69test "$HOME/tmp/m69test/svc_bt" # 全 PASS -> exit 0
|
||||||
|
*/
|
||||||
|
#define main meow_svc_power_prod_main_unused
|
||||||
|
#include "meow_svc_power.c"
|
||||||
|
#undef main
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
static int g_fail = 0;
|
||||||
|
#define CHECK(cond) do { if (!(cond)) { printf(" FAIL: %s (line %d)\n", #cond, __LINE__); g_fail++; } \
|
||||||
|
else { printf(" ok : %s\n", #cond); } } while (0)
|
||||||
|
|
||||||
|
static char g_dir[512], g_persist[600], g_once[600];
|
||||||
|
|
||||||
|
/* 临时目录:$TMPDIR → /tmp → $HOME/tmp/m69test(Termux 上 /tmp 属别的用户,不可写)。 */
|
||||||
|
static int mk_tmp_dir(void)
|
||||||
|
{
|
||||||
|
const char *cand[3];
|
||||||
|
char homebuf[512] = "";
|
||||||
|
int n = 0;
|
||||||
|
const char *t = getenv("TMPDIR");
|
||||||
|
if (t && t[0]) cand[n++] = t;
|
||||||
|
cand[n++] = "/tmp";
|
||||||
|
const char *home = getenv("HOME");
|
||||||
|
if (home && home[0]) {
|
||||||
|
snprintf(homebuf, sizeof homebuf, "%s/tmp/m69test", home);
|
||||||
|
mkdir(homebuf, 0700);
|
||||||
|
cand[n++] = homebuf;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
snprintf(g_dir, sizeof g_dir, "%s/m69_svcp_XXXXXX", cand[i]);
|
||||||
|
if (!mkdtemp(g_dir)) continue;
|
||||||
|
snprintf(g_persist, sizeof g_persist, "%s/boot_default", g_dir);
|
||||||
|
snprintf(g_once, sizeof g_once, "%s/boot_once", g_dir);
|
||||||
|
g_boot_default_path = g_persist;
|
||||||
|
g_boot_once_path = g_once;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void put(const char *path, const char *body)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "w");
|
||||||
|
if (!f) { g_fail++; printf("FAIL cannot write %s\n", path); return; }
|
||||||
|
fputs(body, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 读文件首个整数;文件不存在/空 → -1。 */
|
||||||
|
static int get_int(const char *path)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(path, "r");
|
||||||
|
int v = -1;
|
||||||
|
if (!f) return -1;
|
||||||
|
if (fscanf(f, "%d", &v) != 1) v = -1;
|
||||||
|
fclose(f);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int exists(const char *path) { return access(path, F_OK) == 0 ? 1 : 0; }
|
||||||
|
|
||||||
|
static void reset(void)
|
||||||
|
{
|
||||||
|
unlink(g_persist);
|
||||||
|
unlink(g_once);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- A) 意图表:谁是「设持久默认」,谁是「一次性」,谁两个都不写 ---- */
|
||||||
|
static void test_intent_table(void)
|
||||||
|
{
|
||||||
|
int p, o;
|
||||||
|
|
||||||
|
printf("== boot_default_intent ==\n");
|
||||||
|
|
||||||
|
/* 普通重启 / OTA 升级重启 / 关机:两个文件都不写(本次修的病根) */
|
||||||
|
boot_default_intent(POWER_REBOOT, &p, &o); CHECK(p == -1 && o == -1);
|
||||||
|
boot_default_intent(POWER_UPGRADE, &p, &o); CHECK(p == -1 && o == -1);
|
||||||
|
boot_default_intent(POWER_SHUTDOWN, &p, &o); CHECK(p == -1 && o == -1);
|
||||||
|
|
||||||
|
/* 「这一次去 Hiby」:只写一次性指示,绝不写持久默认 */
|
||||||
|
boot_default_intent(POWER_BACK_HIBY, &p, &o); CHECK(p == -1 && o == 1);
|
||||||
|
|
||||||
|
/* 用户显式设默认:写持久默认,不写一次性指示 */
|
||||||
|
boot_default_intent(POWER_SET_DEFAULT_MEOW, &p, &o); CHECK(p == 0 && o == -1);
|
||||||
|
boot_default_intent(POWER_SET_DEFAULT_HIBY, &p, &o); CHECK(p == 1 && o == -1);
|
||||||
|
|
||||||
|
/* 只读/其它动作:都不写 */
|
||||||
|
boot_default_intent(POWER_STATUS, &p, &o); CHECK(p == -1 && o == -1);
|
||||||
|
boot_default_intent(POWER_SCREEN, &p, &o); CHECK(p == -1 && o == -1);
|
||||||
|
boot_default_intent(POWER_DAC, &p, &o); CHECK(p == -1 && o == -1);
|
||||||
|
boot_default_intent(9999, &p, &o); CHECK(p == -1 && o == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- B) 普通重启 / OTA 重启:落盘副作用 = 没有 ---- */
|
||||||
|
static void test_reboot_and_upgrade_write_nothing(void)
|
||||||
|
{
|
||||||
|
printf("== POWER_REBOOT / POWER_UPGRADE leave boot default alone ==\n");
|
||||||
|
|
||||||
|
/* 用户把默认设成 HiByOS(设置里的「默认系统」) */
|
||||||
|
reset();
|
||||||
|
put(g_persist, "1\n");
|
||||||
|
|
||||||
|
/* 改后:普通重启不写任何启动默认 */
|
||||||
|
CHECK(apply_boot_default_intent(POWER_REBOOT) == 0);
|
||||||
|
CHECK(get_int(g_persist) == 1);
|
||||||
|
CHECK(exists(g_once) == 0);
|
||||||
|
|
||||||
|
/* 反向控制(= 改前 POWER_REBOOT 干的活):直接写持久默认会真的改文件,
|
||||||
|
* 说明上面的断言不是空转、文件路径重定向也确实生效。 */
|
||||||
|
write_boot_default(0);
|
||||||
|
CHECK(get_int(g_persist) == 0);
|
||||||
|
put(g_persist, "1\n"); /* 复原用户设置 */
|
||||||
|
|
||||||
|
/* OTA 升级后的那次重启:同样两个文件都不碰 */
|
||||||
|
CHECK(apply_boot_default_intent(POWER_UPGRADE) == 0);
|
||||||
|
CHECK(get_int(g_persist) == 1);
|
||||||
|
CHECK(exists(g_once) == 0);
|
||||||
|
|
||||||
|
/* 关机同理 */
|
||||||
|
CHECK(apply_boot_default_intent(POWER_SHUTDOWN) == 0);
|
||||||
|
CHECK(get_int(g_persist) == 1);
|
||||||
|
CHECK(exists(g_once) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- C) 「切换到 HibyOS」= 一次性:持久默认不变、只留一次性指示 ---- */
|
||||||
|
static void test_back_hiby_is_one_shot(void)
|
||||||
|
{
|
||||||
|
printf("== POWER_BACK_HIBY is one-shot ==\n");
|
||||||
|
|
||||||
|
reset();
|
||||||
|
put(g_persist, "0\n"); /* 用户默认 MeowOS */
|
||||||
|
|
||||||
|
CHECK(apply_boot_default_intent(POWER_BACK_HIBY) == 0);
|
||||||
|
CHECK(get_int(g_persist) == 0); /* 持久默认没被改成 1 —— 这就是本次病根 */
|
||||||
|
CHECK(get_int(g_once) == 1); /* 一次性指示已写 */
|
||||||
|
|
||||||
|
/* 反向控制(= 改前 POWER_BACK_HIBY 干的活):写持久默认 1。 */
|
||||||
|
write_boot_default(1);
|
||||||
|
CHECK(get_int(g_persist) == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- D) 显式设默认仍然持久写 ---- */
|
||||||
|
static void test_set_default_still_persists(void)
|
||||||
|
{
|
||||||
|
printf("== POWER_SET_DEFAULT_* still persist ==\n");
|
||||||
|
|
||||||
|
reset();
|
||||||
|
CHECK(apply_boot_default_intent(POWER_SET_DEFAULT_HIBY) == 0);
|
||||||
|
CHECK(get_int(g_persist) == 1);
|
||||||
|
CHECK(exists(g_once) == 0); /* 设默认不该产生一次性指示 */
|
||||||
|
|
||||||
|
CHECK(apply_boot_default_intent(POWER_SET_DEFAULT_MEOW) == 0);
|
||||||
|
CHECK(get_int(g_persist) == 0);
|
||||||
|
CHECK(exists(g_once) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- E) 生产路径常量:两组件必须写/读同一对文件 ---- */
|
||||||
|
static void test_prod_paths(void)
|
||||||
|
{
|
||||||
|
printf("== production paths ==\n");
|
||||||
|
CHECK(strcmp(BOOT_DEFAULT, "/data/meow_boot_default") == 0);
|
||||||
|
CHECK(strcmp(BOOT_ONCE, "/data/meow_boot_once") == 0);
|
||||||
|
CHECK(strcmp(g_boot_default_path, g_persist) == 0); /* 自测确实重定向了 */
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
if (mk_tmp_dir() != 0) { printf("FATAL: no writable tmp dir\n"); return 1; }
|
||||||
|
|
||||||
|
test_intent_table();
|
||||||
|
test_reboot_and_upgrade_write_nothing();
|
||||||
|
test_back_hiby_is_one_shot();
|
||||||
|
test_set_default_still_persists();
|
||||||
|
test_prod_paths();
|
||||||
|
|
||||||
|
reset();
|
||||||
|
rmdir(g_dir);
|
||||||
|
|
||||||
|
if (g_fail) { printf("\nBOOTDEFAULT TEST: %d FAIL\n", g_fail); return 1; }
|
||||||
|
printf("\nBOOTDEFAULT TEST: ALL PASS\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user