M2.54: 字体归MeowOS/fonts+SDK跟随+多字体+全屏?(等)+boot板载+状态栏28+亮度/音量修复+mDNS固化(meow_svcd双源+meow_mdns)+hostname=meowos+文档

This commit is contained in:
MeowOS Dev committed 2026-08-30 09:25:16 +08:00
1 parent 064635b3c4
commit 9b8a1e5c8b
13 files changed
+807 -35

No files matched your search

+84
View File
@@ -0,0 +1,84 @@
#!/bin/sh
#
# S89meow_svcd - MeowOS 服务管理器开机自启 + 主机名应用 + 板载服务预置
#
# 位置:S84ssh 之后、S90meow_bootmenu 之前(S80 < S84 < S89 < S90)。
# * rcS 顺序执行 /etc/init.d/S??*,而 S90meow_bootmenu 里 `exec /usr/bin/meow_bootmenu
# --fork` 会阻塞 rcS(选单后 exec meow_launcherd 或 fork 播放器监督),故任何 S90
# 之后的 init.d(如 S91/S92)**都不会执行**——svcd 必须排在 S90 之前。
# * 本脚本只装「稳定框架/KB级设置」:meow_svcd(固件 /usr/bin/)+ 板载服务配置
# (/usr/data/meow/services.d/,Data 区,首启预置 mdnsd.conf)+ 应用主机名。
#
# 红线:不 killall、不碰播放器信号、不刷机。svcd 以 -f 前台被本脚本重挂循环拉起
#(失败降级不死——若 svcd 死,其所托管服务已是独立子进程、被 init 收养继续运行);
# 服务本身(mdnsd)由 svcd 按 restart=on-fail 托管。
#
case "$1" in
start)
echo -n "Starting meow_svcd (S89): "
# 1) 应用主机名:读板载设置 system.hostname(settings.conf),缺省 meowos。
# 须早于 meow_mdns 启动(mdnsd 默认用 gethostname() 公布 <host>.local)。
host="meowos"
if [ -f /usr/data/meow/settings.conf ]; then
v=$(awk -F= '$1 ~ /^[[:space:]]*system\.hostname[[:space:]]*$/ {gsub(/[[:space:]]/,"",$2); print $2; exit}' /usr/data/meow/settings.conf)
[ -n "$v" ] && host="$v"
fi
# 仅允许 RFC1035 首标签字符(A-Za-z0-9 和 '-'/'.'/'_'),否则回退 meowos
case "$host" in
''|*[!A-Za-z0-9_.-]*) host="meowos" ;;
esac
sethostname "$host" 2>/dev/null || hostname "$host" 2>/dev/null
echo -n "host=$host "
# 2) 首启预置板载 mdnsd.conf(Data 区可写才建;已存在不覆盖)
if [ -d /usr/data/meow ] && [ -w /usr/data/meow ]; then
mkdir -p /usr/data/meow/services.d
if [ -f /usr/data/meow/services.d/mdnsd.conf ]; then
echo -n "mdnsd.conf exists "
else
cat > /usr/data/meow/services.d/mdnsd.conf <<'EOF'
exec=/usr/bin/meow_mdns
args=
enabled=1
restart=on-fail
backoff_ms=2000
max_restart=5
trigger=boot
cap=
EOF
chmod 644 /usr/data/meow/services.d/mdnsd.conf
echo -n "provisioned mdnsd.conf "
fi
else
echo -n "skip mdnsd.conf (/usr/data/meow not writable) "
fi
# 3) 拉起 meow_svcd(-f 前台,重挂循环防死;/tmp/meow_svcd.stop 标记防停循环)
rm -f /tmp/meow_svcd.stop
if [ -x /usr/bin/meow_svcd ]; then
if [ -f /tmp/meow_svcd.pid ] && kill -0 "$(cat /tmp/meow_svcd.pid 2>/dev/null)" 2>/dev/null; then
echo "already running"
else
( while [ ! -f /tmp/meow_svcd.stop ]; do /usr/bin/meow_svcd -f; sleep 1; done ) &
echo "started (supervisor pid $!)"
fi
else
echo "MISSING /usr/bin/meow_svcd"
fi
;;
stop)
echo -n "Stopping meow_svcd: "
touch /tmp/meow_svcd.stop
for p in $(ps | grep "[m]eow_svcd" | awk '{print $1}'); do kill "$p" 2>/dev/null; done
echo "OK"
;;
restart|reload)
;;
*)
echo "Usage: $0 {start|stop}"
exit 1
;;
esac
exit 0
Binary file not shown.
+83 -19
View File
@@ -8,9 +8,13 @@
* - svcd 本身极简:无 dlopen、无外部依赖,仅依赖 POSIX + Linux 基础接口,
* 交叉编译为 mipsel 静态(zig-mipsel-cc),打进固件(不入卡)。
*
* 配置来源(TF 卡,目录式):
* `$CARD/MeowOS/services.d/<name>.conf`(默认 `CARD=/usr/data/mnt/sd_0`)。
* 卡拔出 → svcd 优雅停止所托管服务并保留运行状态;插卡 → 自动重建服务。
* 配置来源(双源,目录式;板载优先):
* - 板载 `/usr/data/meow/services.d/<name>.conf`(Data 区,恒定存在,权威源);
* - TF 卡 `$CARD/MeowOS/services.d/<name>.conf`(默认 `CARD=/usr/data/mnt/sd_0`)。
* 扫描**两处都读**;同名服务**板载优先**(扫 TF 时若 name 已在板载 → 跳过 + WARN
* 「<name> 板载与 TF 都存在,采用板载」)。无双源冲突时行为不变。
* 卡拔出 → svcd 优雅停止所托管**卡上**服务并保留运行状态(板载服务视 trigger 保留);
* 插卡 → 自动重建。
*
* .conf 字段(极简 key = value,'#'/';' 注释,见 meow_svcd.md 文档):
* name / exec / args / env / enabled / restart / backoff_ms / max_restart /
@@ -60,7 +64,9 @@
/* ---- 默认路径 / 常量 ---- */
#define SVCD_CARD_DEFAULT "/usr/data/mnt/sd_0" /* TF 卡挂载点(SPEC §1.2) */
#define SVCD_SERVICES_REL "MeowOS/services.d" /* 配置目录(用户定案目录式) */
#define SVCD_SERVICES_REL "MeowOS/services.d" /* TF 配置目录(用户定案目录式) */
#define SVCD_SERVICES_BOARD "/usr/data/meow/services.d" /* 板载配置目录(首启预置/Data 区) */
#define SVCD_PATH_MAX 256 /* 服务源目录路径缓冲上限 */
#define SVCD_SOCK_DEFAULT "/tmp/meow_svcd.sock"
#define SVCD_STATUS_DEFAULT "/tmp/meow_svcd.status"
#define SVCD_PIDFILE_DEFAULT "/tmp/meow_svcd.pid"
@@ -111,6 +117,7 @@ typedef struct {
int max_restart; /* <0=无限 */
char cap[SVCD_CAP_MAX]; /* 解析保留;实装为后续任务 */
char trigger[SVCD_TRIG_STR_MAX]; /* boot|card-mount|wifi-up */
int src; /* 0=TF 卡, 1=板载(同名优先源) */
/* 运行状态 */
pid_t pid;
@@ -131,12 +138,14 @@ static int g_nsvc = 0;
/* 全局环境(可被 --card/--sock/--status 覆盖,亦读 env) */
static char g_card[256] = SVCD_CARD_DEFAULT;
static char g_board_dir[256] = SVCD_SERVICES_BOARD; /* 板载服务目录(--board/MEOW_SVCD_BOARD 可覆写) */
static char g_sock[SVCD_SOCK_PATH_MAX] = SVCD_SOCK_DEFAULT;
static char g_status[256] = SVCD_STATUS_DEFAULT;
static char g_pidfile[256] = SVCD_PIDFILE_DEFAULT;
static char g_log[256] = ""; /* 可选日志文件 */
static int g_card_present = 0;
static int g_board_online = 0; /* 板载服务目录本帧可读 */
static int g_wifi_up = 0;
static int g_shutdown = 0;
static int g_fg = 0; /* -f:前台(supervisor 托管) */
@@ -513,9 +522,9 @@ static int trigger_satisfied(const svc_t *s)
return g_card_present;
case 'w': /* wifi-up */
return g_card_present && g_wifi_up;
case 'b': /* boot:配置在卡 → 随卡存在 */
case 'b': /* boot:板载服务恒定存在,触发不依赖卡;TF 服务仍需卡 */
default:
return g_card_present;
return (s->src == 1) ? 1 : g_card_present;
}
}
@@ -570,11 +579,23 @@ static int card_present(void)
return 0; /* 同文件系统空目录 = 未挂载/未用 */
}
static int scan_directory(char out[][SVCD_NAME_MAX], int max)
static int board_services_exist(void)
{
struct stat st;
if (stat(g_board_dir, &st) != 0 || !S_ISDIR(st.st_mode)) return 0;
return 1;
}
static int name_in_list(const char *name, char list[][SVCD_NAME_MAX], int n)
{
for (int i = 0; i < n; i++)
if (strcmp(list[i], name) == 0) return i;
return -1;
}
/* 枚举某目录下的 *.conf,去除 .conf 得服务名。返回个数。 */
static int scan_conf_names(const char *dir, char out[][SVCD_NAME_MAX], int max)
{
char dir[512];
if (snprintf(dir, sizeof dir, "%s/%s", g_card, SVCD_SERVICES_REL)
>= (int)sizeof dir) return 0;
DIR *d = opendir(dir);
if (!d) return 0;
int n = 0;
@@ -593,18 +614,48 @@ static int scan_directory(char out[][SVCD_NAME_MAX], int max)
return n;
}
/* 卡存在时(初始/插入/reload)重新读配置并调和服务表。 */
/* 双源目录调和:板载(恒定存在、权威优先)先扫,卡上有则叠加扫 TF,
* 同名服务 TF 被板载屏蔽(WARN 记录)。无双源冲突时行为不变。 */
static void scan_and_reconcile(void)
{
char names[SVCD_MAX_SERVICES][SVCD_NAME_MAX];
int n = scan_directory(names, SVCD_MAX_SERVICES);
char srcdir[SVCD_MAX_SERVICES][SVCD_PATH_MAX]; /* 每服务实际 .conf 所在目录 */
int n = 0;
if (board_services_exist()) {
int nb = scan_conf_names(g_board_dir, names, SVCD_MAX_SERVICES);
for (int i = 0; i < nb; i++)
safe_strcpy(srcdir[i], sizeof srcdir[0], g_board_dir);
n = nb;
}
if (g_card_present) {
char tfdir[SVCD_PATH_MAX];
if (snprintf(tfdir, sizeof tfdir, "%s/%s", g_card, SVCD_SERVICES_REL)
< (int)sizeof tfdir) {
char tfnames[SVCD_MAX_SERVICES][SVCD_NAME_MAX];
int nt = scan_conf_names(tfdir, tfnames, SVCD_MAX_SERVICES);
for (int t = 0; t < nt; t++) {
if (name_in_list(tfnames[t], names, n) >= 0) {
svcd_log("%s 板载与 TF 都存在,采用板载", tfnames[t]);
continue;
}
if (n >= SVCD_MAX_SERVICES) {
svcd_log("service table full, drop %s", tfnames[t]); continue;
}
safe_strcpy(names[n], sizeof names[0], tfnames[t]);
safe_strcpy(srcdir[n], sizeof srcdir[0], tfdir);
n++;
}
}
}
for (int i = 0; i < g_nsvc; i++) g_svcs[i].in_config = 0;
for (int i = 0; i < n; i++) {
char path[512];
if (snprintf(path, sizeof path, "%s/%s/%s.conf", g_card, SVCD_SERVICES_REL,
names[i]) >= (int)sizeof path) continue;
if (snprintf(path, sizeof path, "%s/%s.conf", srcdir[i], names[i])
>= (int)sizeof path) continue;
int idx = find_svc(names[i]);
if (idx < 0) {
if (g_nsvc >= SVCD_MAX_SERVICES) { svcd_log("service table full, drop %s", names[i]); continue; }
@@ -612,6 +663,7 @@ static void scan_and_reconcile(void)
}
svc_t tmp;
if (load_conf(path, names[i], &tmp) != 0) { continue; }
tmp.src = (strcmp(srcdir[i], g_board_dir) == 0) ? 1 : 0;
svc_t *s = &g_svcs[idx];
/* 判定是否需重启(结构字段变化) */
@@ -692,6 +744,8 @@ static void on_card_out(void)
for (int i = 0; i < g_nsvc; i++) {
svc_t *s = &g_svcs[i];
s->restart_at_ms = 0;
/* 拔卡后 trigger 仍满足的服务(板载-boot)不随卡停止(板载服务保留,MDNS 等常驻) */
if (s->pid > 0 && trigger_satisfied(s)) continue;
if (s->pid > 0) { stop_service(s, 0, 0); }
else if (s->state == SV_RUNNING || s->state == SV_STOPPING) s->state = SV_STOPPED;
}
@@ -822,7 +876,7 @@ static void handle_connection(int fd)
} else if (strcmp(line, "status") == 0) {
cmd_status(fd);
} else if (strcmp(line, "reload") == 0) {
if (g_card_present) scan_and_reconcile();
scan_and_reconcile();
send_resp(fd, "OK\n");
} else if (sp && strcmp(line, "start") == 0) {
send_resp(fd, cmd_start(sp) == 0 ? "OK\n" : "ERR no-such-service\n");
@@ -942,12 +996,13 @@ static int start_daemon(void)
if (g_status_fd < 0) svcd_log("status file open fail: %s", strerror(errno));
else set_cloexec(g_status_fd);
/* 初始卡探测 */
/* 初始卡探测 + 板载服务目录状态 */
g_card_present = card_present();
if (g_card_present) scan_and_reconcile();
g_board_online = board_services_exist();
scan_and_reconcile(); /* 板载服务恒在,无卡也扫;卡上有则叠加扫 TF */
svcd_log("meow_svcd up: card=%s present=%d sock=%s pid=%d",
g_card, g_card_present, g_sock, (int)getpid());
svcd_log("meow_svcd up: card=%s present=%d board=%s sock=%s pid=%d",
g_card, g_card_present, g_board_dir, g_sock, (int)getpid());
struct pollfd pfds[2];
nfds_t nfds = 2;
@@ -997,6 +1052,12 @@ static int start_daemon(void)
if (present) { g_card_present = 1; svcd_log("card mounted, rebuild"); scan_and_reconcile(); }
else { g_card_present = 0; on_card_out(); }
}
int bonline = board_services_exist();
if (bonline != g_board_online) {
g_board_online = bonline;
svcd_log("board services dir %s %s, rebuild", g_board_dir, bonline ? "online" : "offline");
scan_and_reconcile();
}
}
tick_evaluate();
build_status_file();
@@ -1048,6 +1109,7 @@ static void usage(const char *argv0)
" options:\n"
" -f, --foreground stay in foreground (supervisor fork/exec 托管)\n"
" --card <path> TF card mount point (default /usr/data/mnt/sd_0)\n"
" --board <path> board services dir (default /usr/data/meow/services.d)\n"
" --sock <path> control socket (default /tmp/meow_svcd.sock)\n"
" --status <path> status file (default /tmp/meow_svcd.status)\n"
" --pidfile <path> pid file (default /tmp/meow_svcd.pid)\n"
@@ -1065,6 +1127,7 @@ int main(int argc, char **argv)
const char *a = argv[i];
if (strcmp(a, "-f") == 0 || strcmp(a, "--foreground") == 0) g_fg = 1;
else if (strcmp(a, "--card") == 0 && i + 1 < argc) safe_strcpy(g_card, sizeof g_card, argv[++i]);
else if (strcmp(a, "--board") == 0 && i + 1 < argc) safe_strcpy(g_board_dir, sizeof g_board_dir, argv[++i]);
else if (strcmp(a, "--sock") == 0 && i + 1 < argc) safe_strcpy(g_sock, sizeof g_sock, argv[++i]);
else if (strcmp(a, "--status") == 0 && i + 1 < argc) safe_strcpy(g_status, sizeof g_status, argv[++i]);
else if (strcmp(a, "--pidfile") == 0 && i + 1 < argc) safe_strcpy(g_pidfile, sizeof g_pidfile, argv[++i]);
@@ -1076,6 +1139,7 @@ int main(int argc, char **argv)
/* env 覆盖 */
const char *e;
if ((e = getenv("MEOW_SVCD_CARD")) && e[0]) safe_strcpy(g_card, sizeof g_card, e);
if ((e = getenv("MEOW_SVCD_BOARD")) && e[0]) safe_strcpy(g_board_dir, sizeof g_board_dir, e);
if ((e = getenv("MEOW_SVCD_SOCK")) && e[0]) safe_strcpy(g_sock, sizeof g_sock, e);
if ((e = getenv("MEOW_SVCD_STATUS")) && e[0]) safe_strcpy(g_status, sizeof g_status, e);
if ((e = getenv("MEOW_SVCD_LOG")) && e[0]) safe_strcpy(g_log, sizeof g_log, e);
+38 -9
View File
@@ -8,9 +8,11 @@
二进制入口:`patch_v2/meow_svcd/meow_svcd`(mipsel 静态,~98KB,<200KB 固件阈值)。
宿主编译:`patch_v2/meow_svcd/meow_svcd_host`。
> 本阶段范围:守护 + 宿主测试。**接入 supervisor 启动链**
> (bootmenu/launcher fork+exec 拉起、waitpid)为后续任务——启动改固件有风险,
> 需另行确认。接入时用 `meow_svcd -f`(前台)保证 supervisor 可 `waitpid`。
> 本阶段(M2.54)范围:守护 + 双源 services.d + 宿主测试。**接入系统启动链**已定:
> 固件预置 `etc/init.d/S89meow_svcd`(开机自启,`myow_svcd -f` 前台 + shell 重挂在 rcS 早期,
> 先于 S90meow_bootmenu——因 S90 exec 引导选单后阻塞 rcS,故须排在 S90 之前)。
> 直接由 bootmenu/launcher `fork+exec` + `waitpid` 托管的方案仍成立(改固件有风险),
> 作为备选——用 `-f` 前台即可被 waitpid。
---
@@ -19,7 +21,8 @@
| 用途 | 默认值 | 覆盖方式 |
|---|---|---|
| TF 卡挂载点 | `/usr/data/mnt/sd_0`(SPEC §1.2) | `--card` / `MEOW_SVCD_CARD` |
| 服务配置目录 | `<card>/MeowOS/services.d/` | 随 `--card` |
| 板载服务目录 | `/usr/data/meow/services.d`(首启预置/Data 区) | `--board` / `MEOW_SVCD_BOARD` |
| TF 服务目录 | `<card>/MeowOS/services.d/` | 随 `--card` |
| 控制 socket | `/tmp/meow_svcd.sock` | `--sock` / `MEOW_SVCD_SOCK` |
| 状态文件 | `/tmp/meow_svcd.status` | `--status` / `MEOW_SVCD_STATUS` |
| pid 文件 | `/tmp/meow_svcd.pid` | `--pidfile` |
@@ -28,10 +31,18 @@
> 状态文件写板载 `/tmp`(tmpfs,存活期)或可配置;svcd 日志走 stderr 便于
> supervisor 捕获(`-f` 前台时子进程 stdout/stderr 也落到同一 sink)。
## 2. services.d 目录式配置
## 2. services.d 目录式配置(**双源**:板载 + TF,板载优先)
卡片上 `MeowOS/services.d/<name>.conf`,一个服务一个 `.conf`。极简 `key = value`,
`#`/`;` 注释,空行忽略;**未知键忽略**(前向兼容)。行内注释可用 `\#` 转义。
服务配置**两处都读**,一个服务一个 `<name>.conf`:
- **板载** `/usr/data/meow/services.d/<name>.conf`:Data 区,恒定存在、权威优先;
首启由固件 `S89meow_svcd` 预置(如 `mdnsd.conf`)。拔卡不丢。
- **TF** `<card>/MeowOS/services.d/<name>.conf`:随卡存在/消失。
**同名遮蔽规则**:先扫板载(权威),再扫 TF;若 TF 出现与板载同名服务 → **跳过 +
WARN**「<name> 板载与 TF 都存在,采用板载」。无双源冲突时行为不变。
极简 `key = value`,`#`/`;` 注释,空行忽略;**未知键忽略**(前向兼容)。行内注释可用 `\#` 转义。
| 字段 | 必填 | 类型 | 说明 | 默认 |
|---|---|---|---|---|
@@ -71,6 +82,23 @@ trigger = card-mount
# cap = cap_net_bind_service,cap_net_raw
```
**板载 mDNS 应答器示例**(固件预置 `/usr/data/meow/services.d/mdnsd.conf`,首启建):
```ini
# 自研 IPv4-only mDNS 应答器(固件 /usr/bin/meow_mdns;板载服务,拔卡不丢)
exec = /usr/bin/meow_mdns
args =
enabled = 1
restart = on-fail
backoff_ms = 2000
max_restart = 5
trigger = boot # 板载服务:boot 触发不依赖卡(恒定存在);cap 空=root 绑 5353/组播
cap =
```
> `trigger` 为单值枚举(`boot|card-mount|wifi-up`)。板载服务用 `boot`(恒存在即启动);
> `wifi-up` 属扩展点(`/sys/class/net/<iface>/operstate`),当前平台无无线接口恒 0。
## 3. 状态机
```
@@ -112,7 +140,7 @@ FAILED 崩溃且不再重启(超 max_restart / restart=never)
优雅停所有服务**并保留 `restart_count`/`last_exit` 状态**;插卡 → `scan_and_reconcile()`
重建并按触发拉起。
- **`wifi-up`**:读 `/sys/class/net/<iface>/operstate`,无无线接口恒 `0`(扩展点)。
- `boot`:配置在卡 → 随卡存在即启动(拔卡即停)。
- `boot`:TF 配置在卡 → 随卡存在即启动(拔卡即停);**板载配置**(`src=board`)→ 恒定存在即启动(不依赖卡,拔卡保留,供 mdnsd 等常驻服务)。
## 6. CLI / 控制协议
@@ -139,7 +167,8 @@ meow_svcd pid
## 后续(不在本阶段)
- 接入 supervisor 启动链(bootmenu/launcher fork+exec、waitpid,`-f` 前台)。
- ~~接入 supervisor 启动链~~(已改由 `etc/init.d/S89meow_svcd` 开机自启;bootmenu/launcher
`fork+exec` + `waitpid` 托管仍为备选)。
- `cap` 真机实装(平台是否支持 capset/pthread caps 待确认;需先探测)。
- `wifi-up` 事件细节(是否用 `/proc/net/wireless` / 重试文件,接口判定收紧)。
- svcd/服务日志落位到卡 `MeowOS/logs/` 的行为(当前 svcd 日志 stderr + 可选 `--log`)。