Run one literal argv vector in a contained process tree. active_git is * published under projects_lock before supervision starts, so stop/unwatch can * request cancellation without racing destruction of the handle. */
| 321 | * published under projects_lock before supervision starts, so stop/unwatch can |
| 322 | * request cancellation without racing destruction of the handle. */ |
| 323 | static watcher_git_status_t watcher_git_run(cbm_watcher_t *w, project_state_t *state, |
| 324 | const char *const *argv, size_t output_limit, |
| 325 | watcher_git_output_t *output) { |
| 326 | if (!w || !state || !argv || !argv[0]) { |
| 327 | return WATCHER_GIT_SUPERVISION_FAILED; |
| 328 | } |
| 329 | if (output) { |
| 330 | memset(output, 0, sizeof(*output)); |
| 331 | } |
| 332 | if (output_limit > 0 && (!output || !watcher_git_output_create(output))) { |
| 333 | return WATCHER_GIT_SUPERVISION_FAILED; |
| 334 | } |
| 335 | |
| 336 | #ifdef _WIN32 |
| 337 | char git_executable[CBM_SZ_4K]; |
| 338 | if (!watcher_resolve_git_executable(git_executable)) { |
| 339 | watcher_git_output_cleanup(output); |
| 340 | return WATCHER_GIT_SUPERVISION_FAILED; |
| 341 | } |
| 342 | #endif |
| 343 | |
| 344 | cbm_proc_opts_t options = { |
| 345 | #ifdef _WIN32 |
| 346 | .bin = git_executable, |
| 347 | #else |
| 348 | .bin = argv[0], |
| 349 | #endif |
| 350 | .argv = argv, |
| 351 | .log_file = output_limit > 0 ? output->path : NULL, |
| 352 | /* A wall deadline below is authoritative. Binary `status -z` output |
| 353 | * deliberately has no line-based quiet-timeout semantics. */ |
| 354 | .quiet_timeout_ms = 0, |
| 355 | .cancel_grace_ms = CBM_SUBPROCESS_DEFAULT_CANCEL_GRACE_MS, |
| 356 | .delete_log_on_exit = false, |
| 357 | }; |
| 358 | cbm_subprocess_t *process = NULL; |
| 359 | if (cbm_subprocess_spawn(&options, &process) != 0) { |
| 360 | watcher_git_output_cleanup(output); |
| 361 | return WATCHER_GIT_SUPERVISION_FAILED; |
| 362 | } |
| 363 | |
| 364 | bool cancelled = false; |
| 365 | bool deadline_expired = false; |
| 366 | bool output_exceeded = false; |
| 367 | bool publication_conflict = false; |
| 368 | cbm_mutex_lock(&w->projects_lock); |
| 369 | if (state->active_git) { |
| 370 | publication_conflict = true; |
| 371 | cancelled = cbm_subprocess_request_cancel(process); |
| 372 | } else { |
| 373 | state->active_git = process; |
| 374 | } |
| 375 | if (!cancelled && (atomic_load_explicit(&w->stopped, memory_order_acquire) || |
| 376 | !atomic_load_explicit(&state->registered, memory_order_acquire))) { |
| 377 | cancelled = cbm_subprocess_request_cancel(process); |
| 378 | } |
| 379 | cbm_mutex_unlock(&w->projects_lock); |
| 380 |
no test coverage detected