| 331 | } |
| 332 | |
| 333 | static int run_cli(int argc, char **argv) { |
| 334 | if (argc < MAIN_MIN_ARGC) { |
| 335 | (void)fprintf(stderr, CLI_USAGE); |
| 336 | return SKIP_ONE; |
| 337 | } |
| 338 | |
| 339 | bool progress = cli_strip_flag(&argc, argv, "--progress"); |
| 340 | bool raw_json = cli_strip_flag(&argc, argv, "--json"); |
| 341 | |
| 342 | /* Supervisor worker role: when this process was spawned as a supervised index |
| 343 | * worker, run indexing in-process (never re-supervise) and write the result to |
| 344 | * the given file for the parent to read back. Stripped here so the tool |
| 345 | * dispatch below sees only the tool name + its args. */ |
| 346 | bool index_worker = cli_strip_flag(&argc, argv, "--index-worker"); |
| 347 | const char *response_out = cli_strip_flag_value(&argc, argv, "--response-out"); |
| 348 | cbm_index_set_worker_role(index_worker, response_out); |
| 349 | |
| 350 | #ifndef _WIN32 |
| 351 | /* #845: a supervised worker must not outlive its supervisor. If the parent |
| 352 | * dies without reaping us (agent killed, supervisor crashed), an orphaned |
| 353 | * worker would index on unsupervised — observed contributing to memory |
| 354 | * pressure during the 2026-07-04 host panics. Reuse the parent-death |
| 355 | * watchdog (safe outside server mode: on ppid change it only writes to |
| 356 | * stderr and _exit(0)s — no cleanup dependencies). Detached: the worker |
| 357 | * exits by returning from run_cli; exit() tears the thread down. Failure |
| 358 | * to start is non-fatal, same policy as the MCP-server watchdog. */ |
| 359 | if (index_worker) { |
| 360 | static pid_t worker_initial_ppid; /* static: outlives run_cli for the thread */ |
| 361 | worker_initial_ppid = getppid(); |
| 362 | cbm_thread_t worker_watchdog_tid; |
| 363 | if (cbm_thread_create(&worker_watchdog_tid, PARENT_WATCHDOG_STACK_SIZE, |
| 364 | parent_watchdog_thread, &worker_initial_ppid) == 0) { |
| 365 | (void)cbm_thread_detach(&worker_watchdog_tid); |
| 366 | cbm_log_info("worker.watchdog.start"); |
| 367 | } else { |
| 368 | cbm_log_warn("worker.watchdog.unavailable", "reason", "thread_create_failed"); |
| 369 | } |
| 370 | } |
| 371 | #endif |
| 372 | |
| 373 | if (argc < MAIN_MIN_ARGC) { |
| 374 | (void)fprintf(stderr, CLI_USAGE); |
| 375 | return SKIP_ONE; |
| 376 | } |
| 377 | |
| 378 | const char *tool_name = argv[0]; |
| 379 | int rem_argc = argc - SKIP_ONE; /* args following the tool name */ |
| 380 | char **rem_argv = argv + SKIP_ONE; |
| 381 | |
| 382 | /* --help / -h : print per-tool help (from the tool's input_schema) and exit |
| 383 | * before any server work. */ |
| 384 | for (int i = 0; i < rem_argc; i++) { |
| 385 | if (strcmp(rem_argv[i], "--help") == 0 || strcmp(rem_argv[i], "-h") == 0) { |
| 386 | if (cbm_cli_print_tool_help(tool_name) != 0) { |
| 387 | (void)fprintf(stderr, "error: unknown tool '%s'\n", tool_name); |
| 388 | return SKIP_ONE; |
| 389 | } |
| 390 | return 0; |
no test coverage detected