Index via subprocess — isolates crashes from the main process. */
| 972 | |
| 973 | /* Index via subprocess — isolates crashes from the main process. */ |
| 974 | static void *index_thread_fn(void *arg) { |
| 975 | index_job_t *job = arg; |
| 976 | cbm_log_info("ui.index.start", "path", job->root_path); |
| 977 | |
| 978 | /* Use stored binary path, or try to find it */ |
| 979 | const char *bin = g_binary_path; |
| 980 | char self_path[1024] = {0}; |
| 981 | if (!bin[0]) { |
| 982 | cbm_http_server_resolve_binary_path(NULL, self_path, sizeof(self_path)); |
| 983 | bin = self_path[0] ? self_path : "codebase-memory-mcp"; |
| 984 | } |
| 985 | |
| 986 | char log_file[256]; |
| 987 | |
| 988 | /* JSON-escape root_path and optional project name. */ |
| 989 | char escaped_path[2048]; |
| 990 | cbm_json_escape(escaped_path, (int)sizeof(escaped_path), job->root_path); |
| 991 | char escaped_name[512]; |
| 992 | cbm_json_escape(escaped_name, (int)sizeof(escaped_name), job->project_name); |
| 993 | char json_arg[4096]; |
| 994 | if (job->project_name[0]) { |
| 995 | snprintf(json_arg, sizeof(json_arg), "{\"repo_path\":\"%s\",\"name\":\"%s\"}", escaped_path, |
| 996 | escaped_name); |
| 997 | } else { |
| 998 | snprintf(json_arg, sizeof(json_arg), "{\"repo_path\":\"%s\"}", escaped_path); |
| 999 | } |
| 1000 | |
| 1001 | #ifdef _WIN32 |
| 1002 | snprintf(log_file, sizeof(log_file), "%s\\cbm_index_%d.log", |
| 1003 | getenv("TEMP") ? getenv("TEMP") : ".", (int)_getpid()); |
| 1004 | |
| 1005 | /* Build command line for CreateProcess through the shared MS-CRT quoter so the |
| 1006 | * JSON arg's embedded quotes survive the child's argv re-parse — a naive |
| 1007 | * `"%s"` wrap dropped them, corrupting {"repo_path":"…"} into {repo_path:…}. |
| 1008 | * --index-worker: this http_server spawn is already the crash-isolation layer, |
| 1009 | * so the child runs indexing in-process rather than spawning its own supervisor |
| 1010 | * (avoids redundant process nesting). */ |
| 1011 | char cmdline[2048]; |
| 1012 | const char *const idx_argv[] = {bin, "cli", "--index-worker", "index_repository", |
| 1013 | json_arg, NULL}; |
| 1014 | if (!cbm_build_win_cmdline(cmdline, sizeof(cmdline), idx_argv)) { |
| 1015 | snprintf(job->error_msg, sizeof(job->error_msg), "index command line too long"); |
| 1016 | atomic_store(&job->status, 3); |
| 1017 | return NULL; |
| 1018 | } |
| 1019 | /* Wide command line: CreateProcessA would re-mangle the UTF-8 repo path through the |
| 1020 | * ANSI code page at the spawn boundary, so a non-ASCII repo path never reaches the |
| 1021 | * worker intact (#423/#20). Convert and spawn via CreateProcessW. */ |
| 1022 | wchar_t *wcmd = cbm_utf8_to_wide(cmdline); |
| 1023 | if (!wcmd) { |
| 1024 | snprintf(job->error_msg, sizeof(job->error_msg), "index command line conversion failed"); |
| 1025 | atomic_store(&job->status, 3); |
| 1026 | return NULL; |
| 1027 | } |
| 1028 | |
| 1029 | cbm_log_info("ui.index.spawn", "bin", bin, "log", log_file); |
| 1030 | |
| 1031 | HANDLE hlog = CreateFileA(log_file, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, |
nothing calls this directly
no test coverage detected