Commit Graph
10 Commits
Author SHA1 Message Date
Divarion_D d1153ca787 chore: strip stray backslash from XC_VM string literals
Namespace-migration find/replace (XC_VM -> \XC_VM) leaked a backslash into
many plain string literals: CLI echo/log text, the MaxMind/GitHub outbound
User-Agents, the LB tarball temp path, process-match greps, the sysctl and
cron markers, the proxy ini section, and docblocks.

Most are cosmetic or self-consistent, but two carried upgrade hazards on
already-deployed servers, handled with migration guards:

- sysctl marker: writer + first-line check moved to `# XC_VM` together
  (one-time benign rewrite on upgrade).
- cron marker: switched to `# XC_VM` and added a purge to both root-crontab
  installers that drops lines with either the old `# \XC_VM` or the new
  `# XC_VM` marker before re-adding, so upgrades don't duplicate cron jobs.
  The xc_vm user crontab already does a full `crontab -r` rebuild.
2026-08-23 16:03:22 +03:00
Divarion_D b215d774ed refactor(streaming): drop dead XC_VMProxy[] process checks (ADR 0003 E, dead-code)
ProxyCommand was removed in E1, so no process is ever titled XC_VMProxy[<id>]
anymore — the process-name checks in ProcessManager::isMonitorAlive and
StreamProcess::stopStream that matched it can never be true. Remove the dead
references; the real titles (XC_VM[], LLOD[], Loopback[], the m3u8/ts patterns)
are untouched.

Not touched: the live.php non-proxy .ts chase-read (E2) and HLSGenerator
generateHLS / segment.php tmpfs-serving (E3) — those are the active
daemon-reachability fallback (daemon down, re-feed windows, and the
proxy/restreamer/llod2 types not yet canary-validated), so deleting them now
would remove the rollback. Gated on Phase D stability across all types.
2026-08-17 20:46:19 +03:00
Divarion_D 7545f9991c refactor(streaming): remove dead code orphaned by the daemon cutover
Audited the streaming subsystem for symbols with zero real callers (ruled out
dynamic dispatch: command-name strings, routes, #[ListensTo], self::/$this->
internal calls, bare-name string dispatch). Removals, each grep-verified across
all of src/:

- Whole class TS (src/Streaming/TimeshiftClient.php) — superseded by the inline
  TS byte-parsing in LLOD/Loopback; only ref left was a stale comment. Drops its
  2 require + 3 use lines too.
- StreamUtils::getTSInfo — 0 callers.
- SegmentReader::getLLODSegments — replaced by the LLOD-v3 daemon feed; 0 callers.
- ProcessChecker::isPIDRunning + isPIDsRunning (dead pair) — 0 callers; drops the
  now-orphaned CurlClient import.
- ProcessManager: checkPidFile + matchesCmdline (dead pair), killByPattern,
  countProcesses, currentPid, releaseCronLock — all 0 callers (acquireCronLock
  stays; locks self-release on exit).
- AsyncFileOperations: checkFilesExists, awaitFileExistsAdaptive, awaitFileModified,
  getCacheStats, filterExistingFiles — never-wired public helpers, 0 refs.

php -l clean on all touched files; make gates green; no phpstan-baseline entries
reference the removed symbols.
2026-08-16 21:17:32 +03:00
Divarion_D 6a97159c75 refactor(streaming): P2/E1 — delete legacy proxy path (ProxyCommand)
Phase E (ADR 0003), step 1: proxy live streams are now daemon-only. The
daemon owns the proxy puller (registered + off-air-probed in live.php), so
the legacy producer + relay are dead code and are removed:

- delete Cli/Commands/ProxyCommand.php (the XC_VMProxy producer).
- remove StreamProcess::startProxy and ProcessManager::startProxy (only
  caller was live.php).
- live.php: drop the startProxy branch in the process block and the
  AF_UNIX socket relay in the proxy delivery arm. A proxy stream whose
  daemon is unreachable ($rFanout false) now shows not-on-air — the
  keepalive restarts the daemon in ~2s — instead of falling back to the
  legacy producer.

This removes the proxy fallback: the daemon is required for proxy delivery
(its own crash resilience is the keepalive). Non-proxy legacy stays for now.
PHPStan clean, 443 tests.

Refs ADR 0003 (Phase E).
2026-08-16 20:03:49 +03:00
Divarion_D 82a0cd0289 fix: harden null/missing-data access surfaced by panel error logs
Fixes five runtime errors seen in production panel logs (v2.3.9):

- HomeController: player home page fatally crashed (count(): null given)
  when content/tmdb_popular was absent or corrupt. Guard the read +
  unserialize, default movies/series to arrays, null-safe the counts.
- ShutdownHandler: "array offset on null" on every live session close
  when $rChannelInfo was null (vod/timeshift or early exit). Use !empty().
- ProcessManager::acquireCronLock: TOCTOU race — a competing cron removed
  the lock file between file_exists() and file_get_contents()/filemtime(),
  emitting "failed to open stream"/"stat failed". Read content+mtime once
  under @, and take the lock if the file vanished.
- AuthRepository::updateCodes: "foreach() argument must be array, null"
  when a code's whitelist JSON decoded to null. Cast to (array).
- admin/review.php: "Undefined array key extension" for URLs without an
  extension. Coalesce pathinfo()['extension'] to ''.
2026-08-15 23:42:16 +03:00
Divarion-D 552c4036c9 fix(cron): reap wedged watchdog instead of trusting its presence
cron:servers only checked whether a watchdog process existed. A watchdog
  blocked in poll() on a half-open MariaDB socket (CLOSE_WAIT) inside its DB
  ping still exists, so cron kept trusting it indefinitely: last_check_ago
  went stale and the panel marked the node offline while nginx, php-fpm,
  redis and active streams were all healthy. Restarting only the watchdog
  restored the heartbeat immediately.

  Treat an abnormally old watchdog as stale. A normal generation lives only
  a few seconds, so any process alive for more than 90s is killed and a
  fresh generation is started; a plain presence check alone would keep
  trusting the wedged one forever.

  Add ProcessManager::getProcessAge(), which derives age from the mtime of
  the /proc/PID directory (time() - filemtime) — the same figure as
  `ps -o etimes` without shelling out, consistent with the rest of the class
  reading /proc directly and with no HZ/CLK_TCK assumption. Reaping uses the
  existing ProcessManager::kill() rather than a raw `kill -9`.

  Recovery restarts only the watchdog; nginx, php-fpm, redis, ffmpeg and
  running streams are untouched.
2026-07-29 18:43:27 +03:00
Divarion-D b33e944cfd fix(cron): make daemon liveness checks precise so cron:servers actually revives them
Two false positives/negatives in process detection kept the panel's
self-healing loop dead on any real installation:

- isNginxRunning() looked for "nginx: master" only among xc_vm-owned
  processes, but the master runs as root on typical installs (workers run
  as xc_vm), so cron:servers/cron:streams bailed out with "XC_VM not
  running..." before reaching the daemon revival block. Now scans
  /proc/*/cmdline user-agnostically, same as RootSignalsCronJob.

- The per-daemon "is it alive" checks piped ps through grep by bare words:
  every live-stream ffmpeg carries -thread_queue_size/-max_muxing_queue_size
  in its command line, so the "queue" check matched any running stream and
  the encode queue daemon was never started while at least one channel was
  up — created channels sat at "0% DONE" until console.php queue was run by
  hand. All seven checks (signals, cache_handler, network, watchdog, queue,
  ondemand, scanner) now use ProcessManager::findProcessPIDs(), a /proc
  cmdline scan matching the daemon title (XC_VM[...]) or its exact
  console.php invocation. Kill branches use the same PID list — the old
  bare "ondemand"/"scanner" greps could kill an innocent ffmpeg whose
  source URL contained those words.
2026-07-10 19:36:15 +03:00
Divarion-D fc0fdb2f29 Fix documentation links in Reseller controllers and related classes to point to the correct GitHub repository URL. Remove unused directory registration code in autoload.php. 2026-06-25 20:32:18 +03:00
Divarion-D da291e20f8 chore(psr4): phase 3 (Core) — namespace remaining Core subdirs
Namespace the rest of Core, completing the Core layer:
- Backup, Boundary, Cache (CacheInterface/FileCache/RedisCache), Diagnostics,
  GeoIP (GeoIPService/MaxMindUpdater), Init (LegacyInitializer), Localization
  (Translator), Process (Thread/Multithread/ProcessManager), Updates
  (GitHubReleases), Util (NetworkUtils, AdminHelpers, Encryption, GeoIP,
  ImageUtils/ImageResizeService, Mobile_Detect, StreamUtils, SystemInfo,
  TimeUtils), Validation (InputValidator) → XcVm\Core\<Subdir>.
- Rename GithubReleases.php → GitHubReleases.php so the file matches its class
  name (PSR-4 is case-sensitive); fix the WebApiBootstrap require accordingly.
- Qualify built-ins (\Exception, \DateTime, \DateTimeZone, \Redis,
  \RuntimeException, \BadMethodCallException, ...) and still-global deps
  (\ServerRepository, \CacheReader, \DatabaseFactory, \BouquetService,
  \CategoryService, \FfmpegPaths, \StreamSorter, \XC_VM). LegacyInitializer's
  Domain calls stay \-qualified (the known Core→Domain exception).
- Add use to referrers; fix leading-backslash refs from earlier commits; fix
  string class refs class_exists('Translator'/'NetworkUtils'/...) → ::class.
  Remove the duplicate global 'use BoundaryInterface;' in MinistraModule.
- phpstan-baseline.neon regenerated.

Core is now fully namespaced (procedural constant/error/RequestGuard files stay
global by design; M3uParser/PhpM3u8 remain vendored). Verified: php -l clean;
PHPStan no errors; PHPUnit 295/295; leading-backslash re-sweep clean.
2026-06-24 22:35:33 +03:00
Divarion-D 1a296d0985 chore(psr4): phase 1 — rename 7 top-level dirs to PascalCase
Atomic case-rename of the seven class-holding source directories to match the
PSR-4 namespace casing, plus every consequent path reference. No code logic
changes — pure structural rename. (config/content/resources/signals/migrations/
ministra/storage/tmp/vendor/www/bin stay lowercase.)

- git mv: core→Core, domain→Domain, infrastructure→Infrastructure,
  streaming→Streaming, modules→Modules, cli→Cli, public→Public (module subdirs
  like Modules/plex stay lowercase; loaded by ModuleLoader, not Composer).
- PHP filesystem paths updated across src/ + tests/: require/include, glob/scandir
  bases, view includes, asset/nginx-alias paths, autoload.php registerDirectories(),
  ModuleLoader/ModuleManager module roots, console.php discovery dirs.
- src/composer.json PSR-4 vendored-lib paths → Core/Parsing/...; vendor/ regenerated.
- nginx.conf: docroot + SCRIPT_FILENAME → Public/. SCRIPT_NAME and the public-facing
  /streaming/*.php compat routes are URLs, left unchanged.
- Build/CI: Makefile LB_DIRS / LB_DIRS_TO_REMOVE / LB_FILES_TO_REMOVE PascalCased
  (closes the LB-archive privileged-leak blocker); phpstan.dist.neon paths/
  scanDirectories/excludePaths; phpunit.xml.dist coverage; phpstan-baseline.neon
  regenerated (294→294 errors, no new resolution failures).
- migrations/deleted_files.txt: old lowercase trees listed for client cleanup
  (assumes case-sensitive FS — the supported Linux target).

Verified: grep-gate 0 live lowercase-dir require/include in src+tests; php -l clean;
PHPUnit 292/292; PHPStan no errors; Composer first / XC_Autoloader last in SPL stack;
global classes resolve from the renamed dirs.
2026-06-24 20:10:34 +03:00