| 924 | } |
| 925 | |
| 926 | int cbm_daemon_host_run(const cbm_daemon_host_config_t *config) { |
| 927 | if (!config || !config->endpoint || !config->executable_path || |
| 928 | !config->identity.semantic_version || !config->identity.build_fingerprint) { |
| 929 | return -1; |
| 930 | } |
| 931 | cbm_ui_log_init(); |
| 932 | char conflict_log[HOST_PATH_CAP]; |
| 933 | if (!host_log_open(conflict_log)) { |
| 934 | (void)fprintf(stderr, "codebase-memory: daemon log path is not private or safe\n"); |
| 935 | return -1; |
| 936 | } |
| 937 | cbm_version_cohort_manager_t *cohort_manager = cbm_version_cohort_manager_new(config->endpoint); |
| 938 | cbm_version_cohort_lease_t *cohort_lease = NULL; |
| 939 | cbm_daemon_conflict_t cohort_conflict; |
| 940 | uint64_t now_ms = cbm_now_ms(); |
| 941 | uint64_t cohort_deadline = now_ms > UINT64_MAX - HOST_INITIAL_CLIENT_TIMEOUT_MS |
| 942 | ? UINT64_MAX |
| 943 | : now_ms + HOST_INITIAL_CLIENT_TIMEOUT_MS; |
| 944 | cbm_version_cohort_status_t cohort_status = |
| 945 | cohort_manager |
| 946 | ? cbm_version_cohort_acquire(cohort_manager, &config->identity, cohort_deadline, |
| 947 | &cohort_lease, &cohort_conflict) |
| 948 | : CBM_VERSION_COHORT_IO; |
| 949 | if (cohort_status != CBM_VERSION_COHORT_OK) { |
| 950 | cbm_log_error("daemon.start_failed", "component", "cohort"); |
| 951 | char message[CBM_DAEMON_CONFLICT_MESSAGE_SIZE]; |
| 952 | bool formatted = cohort_status == CBM_VERSION_COHORT_CONFLICT && |
| 953 | cbm_daemon_conflict_format(&cohort_conflict, message, sizeof(message)); |
| 954 | if (cohort_status == CBM_VERSION_COHORT_CONFLICT) { |
| 955 | (void)cbm_version_cohort_log_conflict(&cohort_conflict); |
| 956 | } |
| 957 | (void)fprintf(stderr, "codebase-memory: %s\n", |
| 958 | formatted ? message : "daemon exact-build admission failed"); |
| 959 | host_cohort_close(&cohort_lease, &cohort_manager); |
| 960 | host_log_close(); |
| 961 | return -1; |
| 962 | } |
| 963 | cbm_daemon_ipc_participant_guard_t *participant_guard = NULL; |
| 964 | if (cbm_daemon_ipc_participant_guard_try_join(config->endpoint, &participant_guard) != 1) { |
| 965 | cbm_log_error("daemon.start_failed", "component", "participant"); |
| 966 | (void)fprintf(stderr, "codebase-memory: daemon participant admission failed\n"); |
| 967 | host_participant_guard_close(&participant_guard); |
| 968 | host_cohort_close(&cohort_lease, &cohort_manager); |
| 969 | host_log_close(); |
| 970 | return -1; |
| 971 | } |
| 972 | cbm_version_cohort_daemon_claim_t *daemon_claim = NULL; |
| 973 | uint64_t claim_deadline = host_deadline_after(HOST_DAEMON_CLAIM_TIMEOUT_MS); |
| 974 | cbm_version_cohort_status_t claim_status = |
| 975 | cbm_version_cohort_daemon_claim_acquire(cohort_manager, &daemon_claim); |
| 976 | while (claim_status == CBM_VERSION_COHORT_BUSY && cbm_now_ms() < claim_deadline) { |
| 977 | /* The previous generation is releasing; retry until the claim is free |
| 978 | * or the handoff window elapses (a persistent BUSY = a real conflict). */ |
| 979 | cbm_usleep(HOST_WAIT_TICK_MS * 1000); |
| 980 | claim_status = cbm_version_cohort_daemon_claim_acquire(cohort_manager, &daemon_claim); |
| 981 | } |
| 982 | if (claim_status != CBM_VERSION_COHORT_OK) { |
| 983 | cbm_log_error("daemon.start_failed", "component", "claim"); |