SIGN IN SIGN UP

fix(agent): Windows lifecycle lock and restart-mutex close the shutdown race

On Windows, OssecServiceStart() reports SERVICE_RUNNING before
local_start() finishes building the wmodules list and spawning module
threads, so the SCM can deliver SERVICE_CONTROL_STOP on its own thread
at any point during startup, with nothing serializing it against
local_start(). Depending on timing this let stop_wmodules() run
against an empty/half-built module list, let local_start() keep
spawning module threads after wm_shutdown_requested was already set,
or raced cur_module->win_thread being written and read unsynchronized.

A CRITICAL_SECTION (wm_lifecycle_lock) now makes "check the shutdown
flag, build the wodle config, spawn every module thread" and "set the
shutdown flag, walk the module list" mutually exclusive, held only
across the fast, non-blocking parts of each -- never across a
module's own Run(). Every Windows thread-based module entry point
(skthread, logcollector_thread, win_module_thread, execd's
win_exec_main) now checks startup_gate_wait_for_ready()'s result and
skips its start routine on STARTUP_GATE_SHUTDOWN_REQUESTED instead of
running it anyway.

Two more pieces close the rest of the race:

- CheckServiceRunning() treated only SERVICE_RUNNING as "running", so
  service-restart's wait-for-the-old-process-to-die loop gave up the
  instant the SCM reported SERVICE_STOP_PENDING -- long before the old
  process had actually finished stop_wmodules() -- and started a new
  process over it, racing the same SQLite files. It now treats
  anything other than SERVICE_STOPPED as still running, and the stop
  handler reports its checkpoint to the SCM every 2s while joining
  module threads so a slow shutdown doesn't read as hung.

- Concurrent/overlapping service-restart invocations each
  independently fell through os_stop_service()'s "already
  stopping"/"already stopped" soft-success cases into their own
  stop-wait-start sequence, with nothing coordinating which one's
  stop/start actually won -- observed live to leave the service
  permanently SERVICE_STOPPED with no self-recovery and no error
  logged anywhere. run_service_restart() now serializes on a named,
  cross-process mutex (Global\WazuhAgentServiceRestart), with a
  bounded ~60s wait and a loud error log if that's ever exceeded.

Issue: #38428
J
jr0me committed
91bfe51201a8e75d523b2fcc29b85fbd81b0dae5
Parent: 1e57485