| 1619 | } cli_scope_fixture_t; |
| 1620 | |
| 1621 | static _Noreturn void cli_scope_host_child(const cli_scope_fixture_t *fixture, int ready_fd, |
| 1622 | int release_fd) { |
| 1623 | cbm_daemon_ipc_endpoint_t *endpoint = |
| 1624 | cbm_daemon_bootstrap_endpoint_new(fixture->runtime_parent); |
| 1625 | cbm_version_cohort_manager_t *manager = |
| 1626 | endpoint ? cbm_version_cohort_manager_new(endpoint) : NULL; |
| 1627 | cbm_version_cohort_lease_t *lease = NULL; |
| 1628 | cbm_daemon_conflict_t conflict; |
| 1629 | cbm_daemon_runtime_service_t *service = NULL; |
| 1630 | bool admitted = endpoint && manager && |
| 1631 | cbm_version_cohort_acquire(manager, &fixture->identity, cbm_now_ms() + 45000U, |
| 1632 | &lease, &conflict) == CBM_VERSION_COHORT_OK; |
| 1633 | if (admitted) { |
| 1634 | cbm_daemon_runtime_service_config_t config = { |
| 1635 | .endpoint = endpoint, |
| 1636 | .identity = fixture->identity, |
| 1637 | .conflict_log_path = fixture->conflict_log, |
| 1638 | .conflict_log_cap_bytes = 64U * 1024U, |
| 1639 | .max_clients = 8, |
| 1640 | .lease_timeout_ms = CLI_SCOPE_TIMEOUT_MS, |
| 1641 | .request_timeout_ms = CLI_SCOPE_TIMEOUT_MS, |
| 1642 | .shutdown_timeout_ms = CLI_SCOPE_TIMEOUT_MS, |
| 1643 | /* Born permanent: only a drain/stop ends it, never the parent's |
| 1644 | * client leaving, so "still alive" is a statement about the |
| 1645 | * drain alone. */ |
| 1646 | .permanent = true, |
| 1647 | }; |
| 1648 | service = cbm_daemon_runtime_service_start(&config); |
| 1649 | } |
| 1650 | bool ready_ok = |
| 1651 | service && cbm_daemon_runtime_service_state(service) == CBM_DAEMON_RUNTIME_SERVICE_RUNNING; |
| 1652 | char ready = ready_ok ? 'R' : 'E'; |
| 1653 | (void)write(ready_fd, &ready, 1); |
| 1654 | close(ready_fd); |
| 1655 | bool drained = false; |
| 1656 | uint64_t deadline = cbm_now_ms() + 120000U; |
| 1657 | while (ready_ok && cbm_now_ms() < deadline) { |
| 1658 | if (cbm_daemon_runtime_service_wait_exited(service, 50U)) { |
| 1659 | drained = true; |
| 1660 | break; |
| 1661 | } |
| 1662 | struct pollfd release_poll = {.fd = release_fd, .events = POLLIN, .revents = 0}; |
| 1663 | if (poll(&release_poll, 1, 0) > 0) { |
| 1664 | break; |
| 1665 | } |
| 1666 | } |
| 1667 | /* A DRAINED child has already had its service torn down by the activation, |
| 1668 | * so every teardown below succeeds on the first attempt; the long deadline |
| 1669 | * exists for the wedged case, where we keep retrying before giving up. On |
| 1670 | * the drained path that budget was pure wall clock -- the parent sits in |
| 1671 | * cli_scope_reap_host waiting for this exit, and it was the single largest |
| 1672 | * idle block in the whole cli suite. Behaviour at the deadline is |
| 1673 | * unchanged (give up and _exit); it is simply reached sooner when there is |
| 1674 | * nothing wedged to wait for. */ |
| 1675 | uint64_t cleanup_deadline = |
| 1676 | cbm_now_ms() + (drained ? CLI_SCOPE_CLEANUP_DRAINED_MS : 2U * CLI_SCOPE_TIMEOUT_MS); |
| 1677 | if (service) { |
| 1678 | if (!drained) { |
no test coverage detected