| 10046 | #endif |
| 10047 | |
| 10048 | static int mcp_run_shell_command_cancellable(cbm_mcp_server_t *srv, const char *command, |
| 10049 | char output_path[CBM_SZ_2K], |
| 10050 | cbm_proc_result_t *result_out) { |
| 10051 | if (!srv || !command || !output_path || !result_out || !mcp_command_output_path(output_path)) { |
| 10052 | return -1; |
| 10053 | } |
| 10054 | /* Internal test seam: rejecting after output allocation exercises the same |
| 10055 | * cleanup contract as a contained process-tree failure. */ |
| 10056 | if (srv->command_test_hook && !srv->command_test_hook(srv->command_test_context, command)) { |
| 10057 | return -1; |
| 10058 | } |
| 10059 | #ifdef _WIN32 |
| 10060 | char shell[CBM_SZ_4K]; |
| 10061 | if (!mcp_resolve_windows_cmd(shell)) { |
| 10062 | (void)cbm_unlink(output_path); |
| 10063 | output_path[0] = '\0'; |
| 10064 | return -1; |
| 10065 | } |
| 10066 | #else |
| 10067 | const char *shell = "/bin/sh"; |
| 10068 | const char *argv[] = {"sh", "-c", command, NULL}; |
| 10069 | #endif |
| 10070 | cbm_proc_opts_t options = { |
| 10071 | .bin = shell, |
| 10072 | #ifdef _WIN32 |
| 10073 | .windows_cmd_payload = command, |
| 10074 | #else |
| 10075 | .argv = argv, |
| 10076 | #endif |
| 10077 | .log_file = output_path, |
| 10078 | .quiet_timeout_ms = 0, |
| 10079 | .cancel_grace_ms = CBM_SUBPROCESS_DEFAULT_CANCEL_GRACE_MS, |
| 10080 | .delete_log_on_exit = false, |
| 10081 | }; |
| 10082 | cbm_subprocess_t *process = NULL; |
| 10083 | if (cbm_subprocess_spawn(&options, &process) != 0) { |
| 10084 | (void)cbm_unlink(output_path); |
| 10085 | output_path[0] = '\0'; |
| 10086 | return -1; |
| 10087 | } |
| 10088 | |
| 10089 | cbm_proc_poll_t state; |
| 10090 | for (;;) { |
| 10091 | if (mcp_request_cancelled(srv)) { |
| 10092 | (void)cbm_subprocess_request_cancel(process); |
| 10093 | } |
| 10094 | state = cbm_subprocess_poll(process, result_out); |
| 10095 | if (state != CBM_PROC_POLL_RUNNING) { |
| 10096 | break; |
| 10097 | } |
| 10098 | cbm_usleep(10000); |
| 10099 | } |
| 10100 | bool contained = state == CBM_PROC_POLL_TERMINAL && result_out->tree_quiesced && |
| 10101 | !result_out->supervision_failed; |
| 10102 | cbm_subprocess_destroy(process); |
| 10103 | return contained ? 0 : -1; |
| 10104 | } |
| 10105 |
no test coverage detected