| 2678 | } |
| 2679 | |
| 2680 | int main(int argc, char **argv) { |
| 2681 | /* Must remain the first statement: see allocator binding contract above. */ |
| 2682 | cbm_alloc_init(); |
| 2683 | #ifndef _WIN32 |
| 2684 | pid_t process_initial_ppid = getppid(); |
| 2685 | #else |
| 2686 | /* Captured at the same instant as POSIX: the later OpenProcess in |
| 2687 | * client_start_parent_watchdog must target the process that spawned us, |
| 2688 | * not whatever may have recycled the PID meanwhile. */ |
| 2689 | DWORD process_initial_ppid = win_parent_pid_from_snapshot(); |
| 2690 | #endif |
| 2691 | #ifdef _WIN32 |
| 2692 | { |
| 2693 | int win_argc = 0; |
| 2694 | char **win_argv = cbm_win_utf8_argv(&win_argc); |
| 2695 | if (win_argv) { |
| 2696 | argc = win_argc; |
| 2697 | argv = win_argv; |
| 2698 | } |
| 2699 | } |
| 2700 | #endif |
| 2701 | cbm_daemon_process_role_t role = cbm_daemon_process_role(argc, argv); |
| 2702 | if (role == CBM_DAEMON_PROCESS_WORKER) { |
| 2703 | /* Before this process writes ANYTHING. A worker's stderr is a file the |
| 2704 | * supervisor keeps for post-mortem, and setvbuf only binds before a |
| 2705 | * stream's first operation — claim it here so even the "could not |
| 2706 | * start" messages below reach disk. The header follows once the argv |
| 2707 | * grammar has been validated. */ |
| 2708 | cbm_log_set_crash_durable(true); |
| 2709 | } |
| 2710 | if (role == CBM_DAEMON_PROCESS_INVALID) { |
| 2711 | (void)fprintf(stderr, "codebase-memory-mcp: invalid internal process arguments\n"); |
| 2712 | return EXIT_FAILURE; |
| 2713 | } |
| 2714 | #ifndef _WIN32 |
| 2715 | if (role == CBM_DAEMON_PROCESS_DAEMON) { |
| 2716 | (void)umask(077); |
| 2717 | } |
| 2718 | #endif |
| 2719 | |
| 2720 | cbm_cli_set_version(CBM_VERSION); |
| 2721 | cbm_profile_init(); |
| 2722 | /* The library default stays INFO (embedders and the test runner observe |
| 2723 | * INFO records through the sink); the process policy is applied here, as |
| 2724 | * early as argv can be classified: thin frontends go quiet, detached |
| 2725 | * daemons retain INFO lifecycle records, and physical workers require |
| 2726 | * INFO because those records are their supervisor's quiet-timeout |
| 2727 | * heartbeat. */ |
| 2728 | bool quiet_log_default = role != CBM_DAEMON_PROCESS_DAEMON && role != CBM_DAEMON_PROCESS_WORKER; |
| 2729 | cbm_log_init_for_process(quiet_log_default, role == CBM_DAEMON_PROCESS_WORKER); |
| 2730 | if (role == CBM_DAEMON_PROCESS_LOCAL_CLI && main_cli_flag_present(argc, argv, "--quiet")) { |
| 2731 | /* Apply quiet before local coordination begins, not only once tool |
| 2732 | * dispatch is reached, so pre-dispatch WARN chatter is suppressed. */ |
| 2733 | cbm_cli_diagnostics_configure(true, false); |
| 2734 | } |
| 2735 | |
| 2736 | cbm_mcp_tool_profile_t tool_profile = CBM_MCP_TOOL_PROFILE_ALL; |
| 2737 | if (role == CBM_DAEMON_PROCESS_MCP_CLIENT && |
nothing calls this directly
no test coverage detected