commit 93187c6a91fe197eb66fad1367446cc936b99389 Author: gmega Date: Tue Jun 17 16:45:18 2025 -0300 feat: add initial procmon implementation and test diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b7efcb4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "test/bats"] + path = test/bats + url = https://github.com/bats-core/bats-core.git +[submodule "test/test_helper/bats-support"] + path = test/test_helper/bats-support + url = https://github.com/bats-core/bats-support.git +[submodule "test/test_helper/bats-assert"] + path = test/test_helper/bats-assert + url = https://github.com/bats-core/bats-assert.git diff --git a/src/config.bash b/src/config.bash new file mode 100644 index 0000000..f7ae262 --- /dev/null +++ b/src/config.bash @@ -0,0 +1,2 @@ +LIB_SRC=${LIB_SRC:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)} +OUTPUTS=${OUTPUTS:-${LIB_SRC}/../outputs} \ No newline at end of file diff --git a/src/process_monitor.bash b/src/process_monitor.bash new file mode 100644 index 0000000..71fe43c --- /dev/null +++ b/src/process_monitor.bash @@ -0,0 +1,89 @@ +#!/usr/bin/env bash + +LIB_SRC=${LIB_SRC:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)} + +# shellcheck source=./config.bash +source "${LIB_SRC}/config.bash" +# shellcheck source=./utils.bash +source "${LIB_SRC}/utils.bash" + +_procmon_output=$(clh_output_folder "procmon") +_procmon_pid="" + +_procmon_init_output() { + rm -rf "${_procmon_output}" || true + mkdir -p "${_procmon_output}" +} + +clh_start_process_monitor() { + _procmon_init_output + + if [ -n "$_procmon_pid" ]; then + echoerr "[procmon] process monitor already started" + return 1 + fi + + local pid=$$ + _pgid=$(ps -o pgid= -p ${pid} | sed 's/ //g') + export _pgid + export _procmon_output + + echoerr "[procmon] start" + + ( + shutdown=false + while ! $shutdown; do + for pid_file in "${_procmon_output}"/*.pid; do + [[ -f "${pid_file}" ]] || continue # null glob + base_name=$(basename "${pid_file}") + pid=${base_name%.pid} + if ! kill -0 "${pid}"; then + echoerr "[procmon] ${pid} is dead" + rm "${pid_file}" + fi + sleep 1 + done + done + ) & + _procmon_pid=$! + echoerr "[procmon] started with PID $_procmon_pid" + return 0 +} + +clh_track_last_background_job() { + local pid=$! + if [ ! -f "${_procmon_output}/${pid}.pid" ]; then + touch "${_procmon_output}/${pid}.pid" + fi +} + +clh_get_tracked_pids() { + result=() + for pid_file in "${_procmon_output}"/*.pid; do + base_name=$(basename "${pid_file}") + pid=${base_name%.pid} + result+=("${pid}") + done +} + +clh_stop_process_monitor() { + if [ -z "$_procmon_pid" ]; then + echoerr "[procmon] process monitor not started" + return 1 + fi + + if ! kill -0 "$_procmon_pid"; then + echoerr "[procmon] process monitor not running" + return 1 + fi + + if [ "$1" = "monitor_only" ]; then + echoerr "[procmon] stop monitor only. Children will be left behind." + kill -s TERM "$_procmon_pid" + wait "$_procmon_pid" + return 0 + else + echoerr "[procmon] stop process group. This will halt the script." + kill -s TERM "-$_pgid" + fi +} \ No newline at end of file diff --git a/src/utils.bash b/src/utils.bash new file mode 100644 index 0000000..da1b505 --- /dev/null +++ b/src/utils.bash @@ -0,0 +1,14 @@ +#!/usr/bin/env bash + +LIB_SRC=${LIB_SRC:-$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)} + +# shellcheck source=./config.bash +source "${LIB_SRC}/config.bash" + +clh_output_folder() { + echo "${OUTPUTS}/$1" +} + +echoerr() { + echo "$@" >&2 +} \ No newline at end of file diff --git a/test/bats b/test/bats new file mode 160000 index 0000000..941bf35 --- /dev/null +++ b/test/bats @@ -0,0 +1 @@ +Subproject commit 941bf35348987999cb510b17f4c59b67486aa818 diff --git a/test/test_helper/bats-assert b/test/test_helper/bats-assert new file mode 160000 index 0000000..035e99c --- /dev/null +++ b/test/test_helper/bats-assert @@ -0,0 +1 @@ +Subproject commit 035e99c84759427bf160a6613904e9d57f838ea9 diff --git a/test/test_helper/bats-support b/test/test_helper/bats-support new file mode 160000 index 0000000..d007fc1 --- /dev/null +++ b/test/test_helper/bats-support @@ -0,0 +1 @@ +Subproject commit d007fc1f451abbad55204fa9c9eb3e6ed5dc5f61 diff --git a/test/test_process_monitor.bats b/test/test_process_monitor.bats new file mode 100644 index 0000000..65bfb93 --- /dev/null +++ b/test/test_process_monitor.bats @@ -0,0 +1,46 @@ +setup() { + bats_require_minimum_version 1.12.0 + + export LIB_SRC="${BATS_TEST_DIRNAME}/../src" + load "${BATS_TEST_DIRNAME}/test_helper/bats-support/load.bash" + load "${BATS_TEST_DIRNAME}/test_helper/bats-assert/load.bash" + + source "${LIB_SRC}/process_monitor.bash" +} + +@test "should not start process monitor twice" { + assert clh_start_process_monitor + refute clh_start_process_monitor + assert clh_stop_process_monitor "monitor_only" +} + +@test "should not stop the process monitor if it wasn't started" { + refute clh_stop_process_monitor +} + +# @test "should keep track of process PIDs" { +# clh_get_tracked_pids +# assert [ ${#result[@]} -eq 0 ] + +# ( +# while [ ! -f "${OUTPUT_FOLDER}/stop" ]; do +# sleep 0.1 +# done +# ) & +# clh_track_last_background_job + +# ( +# while [ ! -f "${OUTPUT_FOLDER}/stop" ]; do +# sleep 0.1 +# done +# ) & +# clh_track_last_background_job + +# clh_get_tracked_pids +# assert [ ${#result[@]} -eq 2 ] + +# touch "${OUTPUT_FOLDER}/stop" + +# clh_get_tracked_pids +# assert [ ${#result[@]} -eq 0 ] +# }