Files
vdm/daemon/CMakeLists.txt
T
samiandClaude Sonnet 5 de748cc2fc daemon: fix startMode 'later' -32603 and isolate the single-instance lock by runtime dir
Two bugs blocking PROTO's live-veloxd conformance check.

1. tasks.start_mode's CHECK was ('auto','now','queue','manual') — not the
   contract's StartMode enum (['now','later','queue']) at all. 'later', a real
   documented value (the File Info dialog's Download Later button), hit the CHECK
   on every insert and surfaced as an unhandled -32603; 'auto'/'manual' were
   never contract values to begin with.

   Migration 0003 rebuilds tasks (SQLite can't ALTER a CHECK) with the contract's
   values, remapping existing rows by what they actually meant: 'auto' -> 'now'
   (eligible for the scheduler immediately), 'manual' -> 'later' (parked, matching
   StartMode's own "lands the task in paused" description). store_migrations_test
   covers the remap and that 'later' inserts clean while the retired spellings
   are rejected.

   dispatcher.cpp's on_download_add matched: default (absent startMode) is now
   'now' instead of the invented 'auto'; 'later' actually lands the task in
   `paused` (pause_reason 'user') instead of a dead 'manual' -> `new` branch that
   spec.startMode (typed as the 3-value enum) could never even reach.
   TaskRow::start_mode's in-memory default followed suit ('now').

2. main.cpp's single-instance guard bound an abstract socket named
   "velox-daemon-<euid>" — one name per user, system-wide. XDG_RUNTIME_DIR
   isolation never reached it: a leaked test veloxd held the lock for 4h40m and
   locked out every other isolated instance with the same euid (PROTO, EXT, the
   orchestrator), real daemon included.

   Extracted rpc/single_instance.{hpp,cpp} (was a static in main.cpp, untestable)
   and derived the abstract-socket name from a hash of the resolved runtime dir
   path instead of euid alone. The real per-user daemon is still unique (its
   runtime dir is unique to it); isolated instances pointed at their own runtime
   dirs now coexist. main() resolves the runtime dir before acquiring the lock
   (was the other way around). New single_instance_test covers same-dir refusal,
   different-dir coexistence, and release-on-close.

Verified against real veloxd binaries, not just unit tests: startMode: "later"
via a live download.add lands in `paused`; two veloxd with different runtime
dirs run concurrently, two with the same one and the second refuses with the
runtime dir named in the error. Full ctest: 39/39.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01GRDjHGgpYmMoPE2UFbe7pP
2026-09-11 17:04:02 +04:00

104 lines
4.5 KiB
CMake

# daemon/ produces the veloxd binary and the static libraries it is built from.
# Owned by lane DAEMON. Wired in by PKG via add_subdirectory(daemon) in the root file,
# guarded on this file existing.
#
# Layering (CLAUDE.md §3): depends on velox::core and velox::proto. No Qt. The engine
# (velox::core) is not linked yet — it arrives when sched/ and the task glue land. This
# drop is the RPC transports (Unix socket + loopback WebSocket), the SQLite store, and a
# dispatcher skeleton so the CLI and GUI have a real server to talk to.
if(NOT TARGET nlohmann_json::nlohmann_json)
find_package(nlohmann_json 3.11 REQUIRED)
endif()
find_package(Threads REQUIRED)
find_package(SQLite3 REQUIRED)
find_package(OpenSSL REQUIRED) # libcrypto: WebSocket accept hash, pairing token hash
# --- generated: migrations_embedded.hpp from src/store/migrations/*.sql ---------------
set(_mig_dir ${CMAKE_CURRENT_SOURCE_DIR}/src/store/migrations)
set(_mig_hdr ${CMAKE_CURRENT_BINARY_DIR}/generated/migrations_embedded.hpp)
file(GLOB _mig_srcs ${_mig_dir}/*.sql)
add_custom_command(
OUTPUT ${_mig_hdr}
COMMAND ${CMAKE_COMMAND} -DMIG_DIR=${_mig_dir} -DOUT=${_mig_hdr}
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed_migrations.cmake
DEPENDS ${_mig_srcs} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed_migrations.cmake
COMMENT "Embedding SQL migrations"
VERBATIM)
add_custom_target(veloxd_migrations_hdr DEPENDS ${_mig_hdr})
# --- veloxd_store — SQLite store, migrations, crypto helpers --------------------------
add_library(veloxd_store STATIC
src/util/crypto.cpp
src/store/sqlite.cpp
src/store/migrations.cpp
src/store/pairings.cpp
src/store/settings.cpp
src/store/tasks.cpp
src/store/categories.cpp
src/store/queues.cpp
src/store/segments.cpp
${_mig_hdr}
)
add_library(velox::daemon_store ALIAS veloxd_store)
target_include_directories(veloxd_store
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src
PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/generated
)
target_compile_features(veloxd_store PUBLIC cxx_std_23)
target_compile_options(veloxd_store PRIVATE -Wall -Wextra -Wpedantic -Werror)
target_link_libraries(veloxd_store PUBLIC SQLite::SQLite3 velox::proto nlohmann_json::nlohmann_json PRIVATE OpenSSL::Crypto)
# --- veloxd_fs — the saveDir/filename path-traversal boundary (security) -----------
# daemon/docs/safepath-adversarial.md is the spec; safepath_test.cpp is that table.
add_library(veloxd_fs STATIC src/fs/safepath.cpp)
add_library(velox::daemon_fs ALIAS veloxd_fs)
target_include_directories(veloxd_fs PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_compile_features(veloxd_fs PUBLIC cxx_std_23)
target_compile_options(veloxd_fs PRIVATE -Wall -Wextra -Wpedantic -Werror)
# --- veloxd_sched — the concurrency governor (pure; no engine link yet, see
# daemon/docs/deferrals.md D4) ---------------------------------------------------
add_library(veloxd_sched STATIC
src/sched/schedule_window.cpp
src/sched/governor.cpp
src/sched/scheduler.cpp
)
add_library(velox::daemon_sched ALIAS veloxd_sched)
target_include_directories(veloxd_sched PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_compile_features(veloxd_sched PUBLIC cxx_std_23)
target_compile_options(veloxd_sched PRIVATE -Wall -Wextra -Wpedantic -Werror)
target_link_libraries(veloxd_sched PUBLIC velox::proto velox::core veloxd_store veloxd_rpc nlohmann_json::nlohmann_json)
# --- veloxd_rpc — the RPC transports + dispatcher ------------------------------------
add_library(veloxd_rpc STATIC
src/rpc/runtime_dir.cpp
src/rpc/single_instance.cpp
src/rpc/event_loop.cpp
src/rpc/event_hub.cpp
src/rpc/uds_server.cpp
src/rpc/ws_frame.cpp
src/rpc/ws_handshake.cpp
src/rpc/ws_server.cpp
src/rpc/pairing.cpp
src/rpc/dispatcher.cpp
)
add_library(velox::daemon_rpc ALIAS veloxd_rpc)
target_include_directories(veloxd_rpc PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src)
target_compile_features(veloxd_rpc PUBLIC cxx_std_23)
target_compile_options(veloxd_rpc PRIVATE -Wall -Wextra -Wpedantic -Werror)
target_link_libraries(veloxd_rpc
PUBLIC velox::proto veloxd_store veloxd_fs nlohmann_json::nlohmann_json Threads::Threads
)
# --- veloxd — the daemon binary -------------------------------------------------------
add_executable(veloxd src/main.cpp)
target_compile_features(veloxd PRIVATE cxx_std_23)
target_compile_options(veloxd PRIVATE -Wall -Wextra -Wpedantic -Werror)
target_link_libraries(veloxd PRIVATE veloxd_rpc veloxd_sched)
if(VELOX_BUILD_TESTS AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt)
add_subdirectory(tests)
endif()