FUCKKKK
This commit is contained in:
@@ -16,14 +16,20 @@
|
||||
|
||||
#include <utsm/linux_compat.h>
|
||||
#include <utsm/linux_resume.h>
|
||||
#include <utsm/linux_loader.h> /* VSCode Phase 5: linux_find_vscode_module */
|
||||
#include <utsm/ipc_shm.h>
|
||||
#include <utsm/vmm.h>
|
||||
#include <utsm/virtio_mmio.h> /* VSCode Phase 2: virtio_input_push_* */
|
||||
#include <utsm/log.h>
|
||||
#include <utsm/types.h>
|
||||
#include <utsm/block.h>
|
||||
#include <ipc_proto.h>
|
||||
#include "../../tools/fat32_io.h"
|
||||
|
||||
/* 兼容层全局配置(由 UTSM kernel/main.c 定义) */
|
||||
extern char g_compat_lib_path[];
|
||||
extern char g_compat_linux_guest_path[];
|
||||
|
||||
/* ===== 内部辅助 ===== */
|
||||
|
||||
static u64 my_strlen(const char *s) {
|
||||
@@ -64,9 +70,17 @@ static int lxc_is_available(void) {
|
||||
return (g_compat_ready && linux_is_parked() && ipc_shm_is_ready()) ? 1 : 0;
|
||||
}
|
||||
|
||||
/* 前向声明:payload_pool 拷出助手(定义在文件传输段,exec 新协议复用) */
|
||||
static u64 lxc_pool_copyout(char *buf, u64 cap, u32 data_len);
|
||||
|
||||
/* 等待 Linux daemon 的响应消息。
|
||||
* linux_resume() 返回后,daemon 已 park,所有响应消息应已在 ring 中。
|
||||
* 我们循环 pop 直到拿到 EXEC_EXIT 或 ring 空(超时保护)。 */
|
||||
* 我们循环 pop 直到拿到 EXEC_EXIT 或 ring 空(超时保护)。
|
||||
*
|
||||
* 新协议(P3):daemon 把 stdout 直写 payload_pool,EXEC_EXIT 为 16B
|
||||
* (exit_code + flags + stdout_len + stdout_total),flags&F_POOL 时
|
||||
* 从 pool 拷出;F_TRUNC/F_TIMEOUT 记串口日志。旧协议(8B exit_code)
|
||||
* 输出仍走 EXEC_STDOUT 内联,下方分支兼容。 */
|
||||
static int lxc_drain_responses(char *out_buf, u64 out_cap, u64 *out_len,
|
||||
u64 *exit_code) {
|
||||
u64 written = 0;
|
||||
@@ -84,7 +98,7 @@ static int lxc_drain_responses(char *out_buf, u64 out_cap, u64 *out_len,
|
||||
switch (msg.type) {
|
||||
case UTSM_MSG_EXEC_STDOUT:
|
||||
case UTSM_MSG_EXEC_STDERR: {
|
||||
/* 拼接到 stdout 缓冲区(stderr 也并入,简化调用方处理) */
|
||||
/* 旧式内联输出(新协议下 stdout 走 pool,正常不会再收到) */
|
||||
u32 n = msg.data_len;
|
||||
if (out_buf && out_cap > written) {
|
||||
u32 space = (u32)(out_cap - written);
|
||||
@@ -99,6 +113,22 @@ static int lxc_drain_responses(char *out_buf, u64 out_cap, u64 *out_len,
|
||||
struct ipc_exec_exit ex;
|
||||
my_memcpy(&ex, msg.data, sizeof(ex));
|
||||
exit_val = ex.exit_code;
|
||||
if (ex.flags & UTSM_EXEC_EXIT_F_POOL) {
|
||||
/* stdout 已 staged 在 payload_pool[0..stdout_len) */
|
||||
written = lxc_pool_copyout(out_buf, out_cap,
|
||||
ex.stdout_len);
|
||||
if (ex.flags & UTSM_EXEC_EXIT_F_TRUNC) {
|
||||
log_warn("[LNXC] exec: output truncated at pool cap");
|
||||
log_hex64("[LNXC] exec: total bytes=", ex.stdout_total);
|
||||
}
|
||||
if (ex.flags & UTSM_EXEC_EXIT_F_TIMEOUT)
|
||||
log_warn("[LNXC] exec: child timed out, SIGKILLed");
|
||||
}
|
||||
} else if (msg.data_len >= 8) {
|
||||
/* 旧式 8B:exit_code + reserved */
|
||||
u32 ec = 0;
|
||||
my_memcpy(&ec, msg.data, 4);
|
||||
exit_val = ec;
|
||||
}
|
||||
got_exit = 1;
|
||||
break;
|
||||
@@ -117,9 +147,13 @@ static int lxc_drain_responses(char *out_buf, u64 out_cap, u64 *out_len,
|
||||
return got_exit ? 0 : -4; /* -4 = 超时无 exit 响应 */
|
||||
}
|
||||
|
||||
static int lxc_exec(const char *path, int argc, const char *const *argv,
|
||||
char *stdout_buf, u64 stdout_cap, u64 *stdout_len,
|
||||
u64 *exit_code) {
|
||||
/* 构建并发送 EXEC_REQUEST,唤醒 guest,然后 drain 响应。
|
||||
* flags 取 0(同步,捕获 stdout)或 UTSM_EXEC_FLAG_ASYNC(detach 立即返回)。
|
||||
* 返回 0 = 收到 EXEC_EXIT(exit_code 已写出),负值失败。 */
|
||||
static int lxc_exec_internal(const char *path, int argc,
|
||||
const char *const *argv, u32 flags,
|
||||
char *stdout_buf, u64 stdout_cap, u64 *stdout_len,
|
||||
u64 *exit_code) {
|
||||
if (!lxc_is_available()) {
|
||||
return -1;
|
||||
}
|
||||
@@ -131,7 +165,7 @@ static int lxc_exec(const char *path, int argc, const char *const *argv,
|
||||
struct ipc_exec_request req;
|
||||
my_memzero(&req, sizeof(req));
|
||||
req.argc = (u32)argc;
|
||||
req.flags = 0;
|
||||
req.flags = flags;
|
||||
|
||||
u64 plen = my_strlen(path);
|
||||
if (plen >= UTSM_EXEC_PATH_MAX) plen = UTSM_EXEC_PATH_MAX - 1;
|
||||
@@ -167,6 +201,29 @@ static int lxc_exec(const char *path, int argc, const char *const *argv,
|
||||
return lxc_drain_responses(stdout_buf, stdout_cap, stdout_len, exit_code);
|
||||
}
|
||||
|
||||
static int lxc_exec(const char *path, int argc, const char *const *argv,
|
||||
char *stdout_buf, u64 stdout_cap, u64 *stdout_len,
|
||||
u64 *exit_code) {
|
||||
return lxc_exec_internal(path, argc, argv, 0,
|
||||
stdout_buf, stdout_cap, stdout_len, exit_code);
|
||||
}
|
||||
|
||||
/* VSCode Phase 5: 异步执行——daemon fork+setsid+detach 后立即回
|
||||
* EXEC_EXIT(0),不捕获输出、不等待退出。用于 X server / VSCode 等长
|
||||
* 生命周期 GUI 进程。返回 0 = spawn 成功。 */
|
||||
static int lxc_exec_async(const char *path, int argc,
|
||||
const char *const *argv) {
|
||||
u64 exit_code = 1;
|
||||
int rc = lxc_exec_internal(path, argc, argv, UTSM_EXEC_FLAG_ASYNC,
|
||||
0, 0, 0, &exit_code);
|
||||
if (rc != 0) return rc;
|
||||
if (exit_code != 0) {
|
||||
log_hex64("[LNXC] exec_async: daemon spawn failed, code=", exit_code);
|
||||
return -5;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lxc_status(char *buf, u64 cap) {
|
||||
if (!buf || cap == 0) return 0;
|
||||
const char *s;
|
||||
@@ -445,6 +502,73 @@ static int lxc_file_push(const char *fat32_name, const char *linux_path,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ===== VSCode Phase 2: 输入转发(PS/2 → virtio-input) ===== */
|
||||
|
||||
/* 上次鼠标按钮状态,用于边沿检测(press/release)。 */
|
||||
static u8 g_prev_mouse_buttons = 0;
|
||||
|
||||
static void lxc_input_forward_keyboard(u16 code, u32 value) {
|
||||
virtio_input_push_keyboard(code, value);
|
||||
}
|
||||
|
||||
static void lxc_input_forward_mouse(i32 dx, i32 dy, u8 buttons) {
|
||||
/* 相对位移 */
|
||||
if (dx != 0) virtio_input_push_mouse_rel(VIO_INPUT_REL_X, dx);
|
||||
if (dy != 0) virtio_input_push_mouse_rel(VIO_INPUT_REL_Y, dy);
|
||||
|
||||
/* 按钮变化检测:XOR 找出变化的位 */
|
||||
u8 changed = buttons ^ g_prev_mouse_buttons;
|
||||
if (changed & 0x01)
|
||||
virtio_input_push_mouse_button(VIO_INPUT_BTN_LEFT, (buttons & 0x01) ? 1 : 0);
|
||||
if (changed & 0x02)
|
||||
virtio_input_push_mouse_button(VIO_INPUT_BTN_RIGHT, (buttons & 0x02) ? 1 : 0);
|
||||
if (changed & 0x04)
|
||||
virtio_input_push_mouse_button(VIO_INPUT_BTN_MIDDLE, (buttons & 0x04) ? 1 : 0);
|
||||
g_prev_mouse_buttons = buttons;
|
||||
|
||||
/* SYN_REPORT:libinput 依赖此帧结束标记 */
|
||||
virtio_input_push_mouse_syn();
|
||||
}
|
||||
|
||||
/* ===== VSCode Phase 3: virtio-gpu scanout 查询转发 =====
|
||||
* desktop 在 IDE attached 时周期调用,把 guest 合成的帧 blit 到 framebuffer。
|
||||
* 我们只做字段拷贝(linux_compat_scanout_info 与 virtio_gpu_scanout_info 布局一致)。 */
|
||||
static int lxc_gpu_get_scanout_info(struct linux_compat_scanout_info *out) {
|
||||
if (!out) return -1;
|
||||
struct virtio_gpu_scanout_info vi;
|
||||
int rc = virtio_gpu_get_scanout_info(&vi);
|
||||
if (rc != 0) {
|
||||
out->host_vaddr = 0;
|
||||
out->width = 0; out->height = 0; out->stride = 0;
|
||||
out->dirty = 0; out->enabled = 0;
|
||||
out->dirty_x = 0; out->dirty_y = 0;
|
||||
out->dirty_w = 0; out->dirty_h = 0;
|
||||
out->cursor_visible = 0; out->cursor_x = 0; out->cursor_y = 0;
|
||||
out->cursor_hot_x = 0; out->cursor_hot_y = 0;
|
||||
out->cursor_w = 0; out->cursor_h = 0; out->cursor_bitmap = 0;
|
||||
return rc;
|
||||
}
|
||||
out->host_vaddr = vi.host_vaddr;
|
||||
out->width = vi.width;
|
||||
out->height = vi.height;
|
||||
out->stride = vi.stride;
|
||||
out->dirty = vi.dirty;
|
||||
out->enabled = vi.enabled;
|
||||
out->dirty_x = vi.dirty_x;
|
||||
out->dirty_y = vi.dirty_y;
|
||||
out->dirty_w = vi.dirty_w;
|
||||
out->dirty_h = vi.dirty_h;
|
||||
out->cursor_visible = vi.cursor_visible;
|
||||
out->cursor_x = vi.cursor_x;
|
||||
out->cursor_y = vi.cursor_y;
|
||||
out->cursor_hot_x = vi.cursor_hot_x;
|
||||
out->cursor_hot_y = vi.cursor_hot_y;
|
||||
out->cursor_w = vi.cursor_w;
|
||||
out->cursor_h = vi.cursor_h;
|
||||
out->cursor_bitmap = vi.cursor_bitmap;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ===== 服务表 ===== */
|
||||
|
||||
static const linux_compat_service g_lxc_service = {
|
||||
@@ -457,8 +581,376 @@ static const linux_compat_service g_lxc_service = {
|
||||
lxc_file_write,
|
||||
lxc_file_pull,
|
||||
lxc_file_push,
|
||||
lxc_input_forward_keyboard,
|
||||
lxc_input_forward_mouse,
|
||||
lxc_gpu_get_scanout_info,
|
||||
lxc_exec_async,
|
||||
};
|
||||
|
||||
const linux_compat_service *linux_compat_get_service(void) {
|
||||
return &g_lxc_service;
|
||||
}
|
||||
|
||||
/* ===== Phase 2: SYSTEM/lib .so 全量推送到 Linux guest =====
|
||||
*
|
||||
* 启动时把 FAT32 根目录下 lib/ 子目录(g_compat_lib_path)中所有 .so
|
||||
* 文件流式推送到 Linux guest 的 g_compat_linux_guest_path 目录。
|
||||
* 流式推送:按簇(4KB)读取 FAT32 文件 → 立即 lxc_file_write 写入 guest,
|
||||
* 不依赖 fat32_io.h 的整文件 256KB 缓冲,可处理 MB 级 .so。
|
||||
*
|
||||
* 路径假设:build.ps1 把 SYSTEM/ 打包为 FAT32 根,因此 Windows
|
||||
* D:\Code\Deshab\SYSTEM\lib\ 在 FAT32 上对应根目录下的 "lib/" 子目录。
|
||||
*/
|
||||
|
||||
/* 把内存中已有数据按 pool 容量分块推送到 Linux 文件。
|
||||
* 用于整文件内存数据的批量上传。 */
|
||||
__attribute__((unused))
|
||||
static int lxc_file_push_mem(const char *linux_path, const u8 *data, u32 size) {
|
||||
if (!lxc_is_available()) return -1;
|
||||
if (!linux_path || (!data && size > 0)) return -1;
|
||||
|
||||
u32 pool_cap = ipc_shm_payload_capacity();
|
||||
u64 off = 0;
|
||||
for (;;) {
|
||||
u64 remain = (u64)size - off;
|
||||
u64 chunk = remain > pool_cap ? pool_cap : remain;
|
||||
u64 written = 0;
|
||||
int rc = lxc_file_write(linux_path, off, (const char *)data + off,
|
||||
chunk, &written);
|
||||
if (rc != 0) return rc;
|
||||
if (written != chunk) return -5; /* 短写 */
|
||||
off += written;
|
||||
if (off >= size) break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 流式推送:按簇读取 FAT32 文件,立即写入 Linux guest。
|
||||
* first_clus : FAT32 文件首簇号
|
||||
* file_size : 文件字节大小
|
||||
* linux_path : guest 端目标绝对路径
|
||||
* 返回 0 成功,负值失败。
|
||||
* 复用 lxc_f32_ensure() 已初始化的 FAT32 库(f32_blk_read)。 */
|
||||
static int lxc_push_fat32_file_streaming(u32 first_clus, u32 file_size,
|
||||
const char *linux_path) {
|
||||
if (!lxc_is_available()) return -1;
|
||||
if (!linux_path) return -1;
|
||||
if (f32_disk_load() != 0) return -2;
|
||||
|
||||
const f32_bpb *bpb = (const f32_bpb *)f32_disk;
|
||||
u32 data_lba = bpb->rsvd + (u32)bpb->fc * bpb->spf;
|
||||
u32 fat_byte_off = bpb->rsvd * 512;
|
||||
u32 spc = bpb->spc;
|
||||
u32 pool_cap = ipc_shm_payload_capacity();
|
||||
u32 cluster_bytes = spc * 512;
|
||||
if (cluster_bytes > sizeof(f32_cluster)) cluster_bytes = sizeof(f32_cluster);
|
||||
|
||||
/* 用 f32_data 作为单簇中转缓冲(每次最多 pool_cap 字节) */
|
||||
u32 buf_cap = pool_cap;
|
||||
if (buf_cap > sizeof(f32_data)) buf_cap = sizeof(f32_data);
|
||||
|
||||
u64 off = 0;
|
||||
u32 fc = first_clus;
|
||||
u32 buf_used = 0;
|
||||
while (fc >= 2 && fc < 0x0FFFFFF8 && off < file_size) {
|
||||
u32 fc_lba = data_lba + (fc - 2) * spc;
|
||||
u32 fc_bytes = spc * 512;
|
||||
if (fc_bytes > sizeof(f32_cluster)) fc_bytes = sizeof(f32_cluster);
|
||||
if (f32_read_sectors(fc_lba, spc, f32_cluster) != 0) return -3;
|
||||
|
||||
/* 把当前簇数据按 buf_cap 分批写入 */
|
||||
u32 ci = 0;
|
||||
while (ci < fc_bytes && off + ci < file_size) {
|
||||
u32 space = buf_cap - buf_used;
|
||||
u32 copy = fc_bytes - ci;
|
||||
if (copy > space) copy = space;
|
||||
if (copy > file_size - (u32)(off + ci)) copy = (u32)(file_size - (off + ci));
|
||||
for (u32 b = 0; b < copy; b++) {
|
||||
f32_data[buf_used + b] = f32_cluster[ci + b];
|
||||
}
|
||||
buf_used += copy;
|
||||
ci += copy;
|
||||
/* 缓冲满 → 写入 guest */
|
||||
if (buf_used >= buf_cap || off + ci >= file_size) {
|
||||
u64 written = 0;
|
||||
int rc = lxc_file_write(linux_path, off, (const char *)f32_data,
|
||||
buf_used, &written);
|
||||
if (rc != 0) return rc;
|
||||
if (written != buf_used) return -5;
|
||||
off += written;
|
||||
buf_used = 0;
|
||||
}
|
||||
}
|
||||
/* 跟随 FAT 链 */
|
||||
u32 fo = fat_byte_off + fc * 4;
|
||||
if (fo + 4 > sizeof(f32_disk)) break;
|
||||
fc = f32_r32(f32_disk + fo) & 0x0FFFFFFF;
|
||||
}
|
||||
/* 处理剩余缓冲 */
|
||||
if (buf_used > 0 && off < file_size) {
|
||||
u64 written = 0;
|
||||
int rc = lxc_file_write(linux_path, off, (const char *)f32_data,
|
||||
buf_used, &written);
|
||||
if (rc != 0) return rc;
|
||||
off += written;
|
||||
}
|
||||
return (off >= file_size) ? 0 : -6;
|
||||
}
|
||||
|
||||
/* .so 文件收集回调上下文 */
|
||||
typedef struct {
|
||||
u32 count; /* 已收集的 .so 数量 */
|
||||
u32 max_count; /* 上限 */
|
||||
int error; /* 0=正常, 非 0=出错 */
|
||||
} lxc_so_collect_ctx;
|
||||
|
||||
/* f32_list_dir 回调:过滤 .so 后缀的文件,记录发现 */
|
||||
static int lxc_so_collect_cb(const f32_entry *e, void *user_data) {
|
||||
lxc_so_collect_ctx *ctx = (lxc_so_collect_ctx *)user_data;
|
||||
if (!e || !ctx) return 0;
|
||||
if (e->is_dir) return 0; /* 跳过子目录 */
|
||||
|
||||
/* 判断 .so 后缀(大小写不敏感) */
|
||||
const char *name = e->has_lfn ? e->long_name : e->name;
|
||||
if (!fat32_lfn_ends_with_ci(name, ".so")) return 0;
|
||||
|
||||
if (ctx->count < ctx->max_count) {
|
||||
ctx->count++;
|
||||
} else {
|
||||
ctx->error = 1; /* 超过上限 */
|
||||
return 1; /* 停止遍历 */
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 单文件推送回调上下文(用于流式推送所有 .so 到 guest) */
|
||||
typedef struct {
|
||||
const char *guest_path; /* guest 端目录绝对路径 */
|
||||
u32 pushed; /* 已推送文件数 */
|
||||
u32 failed; /* 推送失败文件数 */
|
||||
} lxc_push_ctx;
|
||||
|
||||
/* f32_list_dir 回调:流式推送每个 .so 到 guest */
|
||||
static int lxc_push_so_cb(const f32_entry *e, void *user_data) {
|
||||
lxc_push_ctx *ctx = (lxc_push_ctx *)user_data;
|
||||
if (!e || !ctx) return 0;
|
||||
if (e->is_dir) return 0;
|
||||
|
||||
const char *name = e->has_lfn ? e->long_name : e->name;
|
||||
if (!fat32_lfn_ends_with_ci(name, ".so")) return 0;
|
||||
|
||||
/* 构造 guest 端路径: <guest_path>/<filename> */
|
||||
char linux_path[256];
|
||||
u32 pl = 0;
|
||||
while (ctx->guest_path[pl] && pl + 1 < sizeof(linux_path)) {
|
||||
linux_path[pl] = ctx->guest_path[pl];
|
||||
pl++;
|
||||
}
|
||||
if (pl + 1 >= sizeof(linux_path)) {
|
||||
ctx->failed++;
|
||||
return 0; /* 路径过长,跳过 */
|
||||
}
|
||||
if (linux_path[pl - 1] != '/') {
|
||||
linux_path[pl++] = '/';
|
||||
}
|
||||
/* 追加文件名 */
|
||||
u32 nl = 0;
|
||||
while (name[nl] && pl + 1 < sizeof(linux_path)) {
|
||||
linux_path[pl++] = name[nl++];
|
||||
}
|
||||
linux_path[pl] = 0;
|
||||
|
||||
log_info("[LNXC] pushing .so:");
|
||||
log_info(name);
|
||||
log_info(" -> ");
|
||||
log_info(linux_path);
|
||||
|
||||
int rc = lxc_push_fat32_file_streaming(e->clus, e->size, linux_path);
|
||||
if (rc == 0) {
|
||||
ctx->pushed++;
|
||||
log_hex64("[LNXC] push ok size=", e->size);
|
||||
} else {
|
||||
ctx->failed++;
|
||||
log_warn("[LNXC] push failed");
|
||||
log_hex64("[LNXC] push rc=", (u64)(i64)rc);
|
||||
}
|
||||
return 0; /* 继续遍历下一个 */
|
||||
}
|
||||
|
||||
/* 同步 FAT32 lib 目录下所有 .so 到 Linux guest。
|
||||
* 调用时机:linux_compat_init 成功后。失败不阻断启动。
|
||||
* guest_lib_path : guest 端库目录路径(如 "/usr/lib/deshab")
|
||||
* 返回 0 成功,负值失败。 */
|
||||
int lxc_sync_lib_dir(const char *guest_lib_path) {
|
||||
if (!guest_lib_path) return -1;
|
||||
if (!lxc_is_available()) {
|
||||
log_warn("[LNXC] sync_lib_dir: service not available");
|
||||
return -1;
|
||||
}
|
||||
int rc = lxc_f32_ensure();
|
||||
if (rc != 0) {
|
||||
log_warn("[LNXC] sync_lib_dir: FAT32 not available");
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* 定位 lib 目录簇(lib 在 FAT32 根目录下,不是 SYSTEM/lib!) */
|
||||
u32 lib_clus = 0;
|
||||
if (f32_find_path_dir_lfn(g_compat_lib_path, &lib_clus) != 0) {
|
||||
log_warn("[LNXC] sync_lib_dir: lib dir not found:");
|
||||
log_warn(g_compat_lib_path);
|
||||
return -2; /* 目录不存在(容忍:可能是首次启动未放库) */
|
||||
}
|
||||
log_info("[LNXC] sync_lib_dir: lib dir found");
|
||||
log_hex64("[LNXC] lib_clus=", lib_clus);
|
||||
|
||||
/* 先枚举确认有 .so 文件(用于日志) */
|
||||
lxc_so_collect_ctx collect = {0, 256, 0};
|
||||
f32_list_dir(lib_clus, lxc_so_collect_cb, &collect);
|
||||
if (collect.error) {
|
||||
log_warn("[LNXC] sync_lib_dir: too many .so files, capped at 256");
|
||||
}
|
||||
log_hex64("[LNXC] sync_lib_dir: .so count=", collect.count);
|
||||
if (collect.count == 0) {
|
||||
log_info("[LNXC] sync_lib_dir: no .so files, nothing to push");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 流式推送每个 .so */
|
||||
lxc_push_ctx pctx;
|
||||
pctx.guest_path = guest_lib_path;
|
||||
pctx.pushed = 0;
|
||||
pctx.failed = 0;
|
||||
f32_list_dir(lib_clus, lxc_push_so_cb, &pctx);
|
||||
|
||||
log_hex64("[LNXC] sync_lib_dir: pushed=", pctx.pushed);
|
||||
log_hex64("[LNXC] sync_lib_dir: failed=", pctx.failed);
|
||||
return (pctx.failed == 0) ? 0 : -3;
|
||||
}
|
||||
|
||||
/* ===== VSCode Phase 5: VSCode tarball → guest /opt/vscode 安装 =====
|
||||
*
|
||||
* tarball 作为 Limine boot module(路径含 "vscode")随镜像预加载到主机
|
||||
* 内存;本函数把它流式推入 guest /tmp/vscode.tar.gz,再在 guest 内解包到
|
||||
* /opt/vscode(/opt 位于 extra-rootfs overlay,持久可写,二次启动经
|
||||
* 标记探测直接跳过)。 */
|
||||
|
||||
/* guest 内文件可执行性探测:同步 exec /bin/test -e <path>。
|
||||
* 返回 1 = 存在,0 = 不存在或探测失败。 */
|
||||
static int lxc_guest_path_exists(const char *path) {
|
||||
const char *argv[3];
|
||||
argv[0] = "test";
|
||||
argv[1] = "-e";
|
||||
argv[2] = path;
|
||||
u64 exit_code = 1;
|
||||
int rc = lxc_exec("/bin/test", 3, argv, 0, 0, 0, &exit_code);
|
||||
return (rc == 0 && exit_code == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
int lxc_vscode_install(void) {
|
||||
if (!lxc_is_available()) {
|
||||
log_warn("[LNXC] vscode install: service not available");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* 1. 已安装标记:overlay 持久化后二次启动直接跳过 */
|
||||
if (lxc_guest_path_exists("/opt/vscode/bin/code")) {
|
||||
log_info("[LNXC] vscode: already installed, skipping");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 2. 查找 tarball boot module(缺失属正常——未投放则跳过安装) */
|
||||
u64 tar_size = 0;
|
||||
const u8 *tar = (const u8 *)linux_find_vscode_module(&tar_size);
|
||||
if (!tar || tar_size == 0) {
|
||||
return -11;
|
||||
}
|
||||
|
||||
log_info("[LNXC] vscode: streaming tarball to guest /tmp ...");
|
||||
log_hex64("[LNXC] vscode: tarball size=", tar_size);
|
||||
|
||||
/* 3. 流式推送:module 内存 → /tmp/vscode.tar.gz(payload pool 分块) */
|
||||
u32 pool_cap = ipc_shm_payload_capacity();
|
||||
u64 off = 0;
|
||||
while (off < tar_size) {
|
||||
u64 chunk = tar_size - off;
|
||||
if (chunk > pool_cap) chunk = pool_cap;
|
||||
u64 written = 0;
|
||||
int rc = lxc_file_write("/tmp/vscode.tar.gz", off,
|
||||
(const char *)tar + off, chunk, &written);
|
||||
if (rc != 0 || written != chunk) {
|
||||
log_warn("[LNXC] vscode: tarball push failed");
|
||||
log_hex64("[LNXC] vscode: push off=", off);
|
||||
return -12;
|
||||
}
|
||||
off += written;
|
||||
}
|
||||
log_info("[LNXC] vscode: tarball pushed ok");
|
||||
|
||||
/* 4. mkdir -p /opt/vscode */
|
||||
{
|
||||
const char *argv[3];
|
||||
argv[0] = "mkdir";
|
||||
argv[1] = "-p";
|
||||
argv[2] = "/opt/vscode";
|
||||
u64 ec = 1;
|
||||
int rc = lxc_exec("/bin/mkdir", 3, argv, 0, 0, 0, &ec);
|
||||
if (rc != 0 || ec != 0) {
|
||||
log_warn("[LNXC] vscode: mkdir /opt/vscode failed");
|
||||
return -13;
|
||||
}
|
||||
}
|
||||
|
||||
/* 5. 解包:VSCode 官方 tarball 顶层为 VSCode-linux-x64/,
|
||||
* --strip-components=1 把内容直接落入 /opt/vscode;
|
||||
* 若 tarball 是平铺结构(无顶层目录)则回退不解层重试。 */
|
||||
int unpacked = 0;
|
||||
{
|
||||
const char *argv[7];
|
||||
argv[0] = "tar";
|
||||
argv[1] = "-xzf";
|
||||
argv[2] = "/tmp/vscode.tar.gz";
|
||||
argv[3] = "-C";
|
||||
argv[4] = "/opt/vscode";
|
||||
argv[5] = "--strip-components=1";
|
||||
argv[6] = 0;
|
||||
u64 ec = 1;
|
||||
int rc = lxc_exec("/bin/tar", 6, argv, 0, 0, 0, &ec);
|
||||
if (rc == 0 && ec == 0) unpacked = 1;
|
||||
}
|
||||
if (!unpacked) {
|
||||
log_warn("[LNXC] vscode: strip-components extract failed, trying flat");
|
||||
const char *argv[5];
|
||||
argv[0] = "tar";
|
||||
argv[1] = "-xzf";
|
||||
argv[2] = "/tmp/vscode.tar.gz";
|
||||
argv[3] = "-C";
|
||||
argv[4] = "/opt/vscode";
|
||||
u64 ec = 1;
|
||||
int rc = lxc_exec("/bin/tar", 5, argv, 0, 0, 0, &ec);
|
||||
if (rc == 0 && ec == 0) unpacked = 1;
|
||||
}
|
||||
|
||||
/* 6. 清理 guest 端 tarball(成败都清,/tmp 是 tmpfs 占 guest 内存) */
|
||||
{
|
||||
const char *argv[3];
|
||||
argv[0] = "rm";
|
||||
argv[1] = "-f";
|
||||
argv[2] = "/tmp/vscode.tar.gz";
|
||||
u64 ec = 0;
|
||||
lxc_exec("/bin/rm", 3, argv, 0, 0, 0, &ec);
|
||||
}
|
||||
|
||||
if (!unpacked) {
|
||||
log_warn("[LNXC] vscode: extract failed");
|
||||
return -13;
|
||||
}
|
||||
|
||||
/* 7. 验证:/opt/vscode/bin/code 存在即可启动 */
|
||||
if (!lxc_guest_path_exists("/opt/vscode/bin/code")) {
|
||||
log_warn("[LNXC] vscode: verify failed (bin/code missing)");
|
||||
return -14;
|
||||
}
|
||||
|
||||
log_info("[LNXC] vscode: install ok (/opt/vscode/bin/code)");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user