From 10c043878fe7a75d3c7c5f426f1bdfc660cc378a Mon Sep 17 00:00:00 2001 From: j Date: Sun, 29 Mar 2026 16:22:00 +1300 Subject: [PATCH] Add flock-based locking to prevent concurrent service operations --- source/agent-remote/common.sh | 12 ++++++++++++ source/agent-remote/ds_install.sh | 4 ++++ source/agent-remote/ds_run.sh | 3 +++ 3 files changed, 19 insertions(+) diff --git a/source/agent-remote/common.sh b/source/agent-remote/common.sh index e003774..43ec598 100755 --- a/source/agent-remote/common.sh +++ b/source/agent-remote/common.sh @@ -180,5 +180,17 @@ _root_remove_tree() { docker run --rm -v "$abs_parent":/data alpine rm -rf "/data/$child" } +# Acquires an exclusive lock on a service directory to prevent concurrent operations. +# Uses flock() which is automatically released when the process exits (even on crash/SIGKILL). +_lock_service() { + local service_dir="$1" + local lockfile="${service_dir}/.deploy.lock" + mkdir -p "${service_dir}" + exec 200>"${lockfile}" + if ! flock -n 200; then + _die "Another operation is already running on this service. Aborting." + fi +} + # Load autocommands source "${AGENT_PATH}/datacommands_v2.sh" \ No newline at end of file diff --git a/source/agent-remote/ds_install.sh b/source/agent-remote/ds_install.sh index 4b03bf3..3194534 100755 --- a/source/agent-remote/ds_install.sh +++ b/source/agent-remote/ds_install.sh @@ -45,6 +45,10 @@ export TEMP_DIR="${2:-}" export DOCKER_CLI_HINTS=false SERVICE_DIR="${DROPSHELL_DIR}/services/${SERVICE}" + +# -- Acquire exclusive lock (released automatically on exit) -- +_lock_service "${SERVICE_DIR}" + LIVE_CONFIG="${SERVICE_DIR}/config" LIVE_TEMPLATE="${SERVICE_DIR}/template" STAGING_DIR="${SERVICE_DIR}/_staging" diff --git a/source/agent-remote/ds_run.sh b/source/agent-remote/ds_run.sh index 04dfbb9..d38d40b 100755 --- a/source/agent-remote/ds_run.sh +++ b/source/agent-remote/ds_run.sh @@ -77,6 +77,9 @@ export TEMPLATE_PATH="${DROPSHELL_DIR}/services/${SERVICE}/template" [[ -f "${DROPSHELL_DIR}/server_info.env" ]] || _die "Missing ${DROPSHELL_DIR}/server_info.env" [[ -d "${CONFIG_PATH}" ]] || _die "Service '${SERVICE}' does not exist on server (missing ${CONFIG_PATH})" +# -- Acquire exclusive lock (released automatically on exit) -- +_lock_service "${DROPSHELL_DIR}/services/${SERVICE}" + # -- Load template info (template defaults, loaded first) -- export TEMPLATE_INFO_ENV="${TEMPLATE_PATH}/template_info.env" if [[ ! -f "${TEMPLATE_INFO_ENV}" ]]; then