| 9970 | #endif |
| 9971 | |
| 9972 | static int mcp_run_shell_command_cancellable(cbm_mcp_server_t *srv, const char *command, |
| 9973 | char output_path[CBM_SZ_2K], |
| 9974 | cbm_proc_result_t *result_out) { |
| 9975 | if (!srv || !command || !output_path || !result_out || !mcp_command_output_path(output_path)) { |
| 9976 | return -1; |
| 9977 | } |
| 9978 | /* Internal test seam: rejecting after output allocation exercises the same |
| 9979 | * cleanup contract as a contained process-tree failure. */ |
| 9980 | if (srv->command_test_hook && !srv->command_test_hook(srv->command_test_context, command)) { |
| 9981 | return -1; |
| 9982 | } |
| 9983 | #ifdef _WIN32 |
| 9984 | char shell[CBM_SZ_4K]; |
| 9985 | if (!mcp_resolve_windows_cmd(shell)) { |
| 9986 | (void)cbm_unlink(output_path); |
| 9987 | output_path[0] = '\0'; |
| 9988 | return -1; |
| 9989 | } |
| 9990 | #else |
| 9991 | const char *shell = "/bin/sh"; |
| 9992 | const char *argv[] = {"sh", "-c", command, NULL}; |
| 9993 | #endif |
| 9994 | cbm_proc_opts_t options = { |
| 9995 | .bin = shell, |
| 9996 | #ifdef _WIN32 |
| 9997 | .windows_cmd_payload = command, |
| 9998 | #else |
| 9999 | .argv = argv, |
| 10000 | #endif |
| 10001 | .log_file = output_path, |
| 10002 | .quiet_timeout_ms = 0, |
| 10003 | .cancel_grace_ms = CBM_SUBPROCESS_DEFAULT_CANCEL_GRACE_MS, |
| 10004 | .delete_log_on_exit = false, |
| 10005 | }; |
| 10006 | cbm_subprocess_t *process = NULL; |
| 10007 | if (cbm_subprocess_spawn(&options, &process) != 0) { |
| 10008 | (void)cbm_unlink(output_path); |
| 10009 | output_path[0] = '\0'; |
| 10010 | return -1; |
| 10011 | } |
| 10012 | |
| 10013 | cbm_proc_poll_t state; |
| 10014 | for (;;) { |
| 10015 | if (mcp_request_cancelled(srv)) { |
| 10016 | (void)cbm_subprocess_request_cancel(process); |
| 10017 | } |
| 10018 | state = cbm_subprocess_poll(process, result_out); |
| 10019 | if (state != CBM_PROC_POLL_RUNNING) { |
| 10020 | break; |
| 10021 | } |
| 10022 | cbm_usleep(10000); |
| 10023 | } |
| 10024 | bool contained = state == CBM_PROC_POLL_TERMINAL && result_out->tree_quiesced && |
| 10025 | !result_out->supervision_failed; |
| 10026 | cbm_subprocess_destroy(process); |
| 10027 | return contained ? 0 : -1; |
| 10028 | } |
| 10029 |
no test coverage detected