Skip to content

Fix/telegram openrc service support - #130

Open
rvalitov wants to merge 3 commits into
SamNet-dev:mainfrom
rvalitov:fix/telegram-openrc-service-support
Open

Fix/telegram openrc service support#130
rvalitov wants to merge 3 commits into
SamNet-dev:mainfrom
rvalitov:fix/telegram-openrc-service-support

Conversation

@rvalitov

Copy link
Copy Markdown

Fixes #129

Problem

setup_telegram_service() wrote a systemd unit only, with no else branch, so on a host
without systemd (Alpine/OpenRC) mtproxymax telegram setup reported success while installing
nothing. The bot stayed dead — and because the bot also carries the "proxy down" alerts, the
whole alert channel was lost with no signal to the user.

setup_autostart() had the identical no-else bug, so "start on boot" was silently broken on
the same hosts.

The blind spot also made the management commands lie. On the reporter's Alpine box, with the
bot service genuinely stopped:

Command Before After
telegram status Telegram: ● Enabled Telegram: ● Enabled (service not running) + how to fix it
telegram disable prints Telegram disabled, leaves the daemon running actually stops it
telegram setup prints success, installs nothing installs and starts an OpenRC service

Changes

detect_init_system() is the single detection point, returning systemd | openrc | none. It
exists as a function so the test suite can override it, matching how the existing tests already
stub check_root and curl.

  • setup_telegram_service — three-way branch. systemd behaviour is unchanged. OpenRC gets
    /etc/init.d/mtproxymax-telegram supervised by supervise-daemon (respawn_delay=10,
    respawn_max=0), mirroring the unit's Restart=on-failure / RestartSec=10. A host with
    neither init system now warns loudly and returns nonzero, the way
    setup_replication_service already does.
  • setup_autostart — same treatment; the OpenRC script is a plain start/stop wrapper
    around the manager's own start/stop, mirroring Type=oneshot + RemainAfterExit=yes.
  • Update / disable / remove / menu toggle / uninstall — routed through the same detection
    via small telegram_*_service helpers, so they no longer report work they did not do.
  • telegram status — reports whether the service is actually running instead of echoing
    the settings flag.
  • SYSTEMD_DIR / INITD_DIR / RUNLEVELS_DIR — unit paths become injectable via the
    existing ${VAR:-default} idiom. This is what makes the service installers testable without
    root; the tests deliberately never run as root, and hardcoded /etc/systemd/system was the
    only reason these functions could not be covered.

A failed start is now reported rather than logging "service started" regardless.

Testing

New tests/test_telegram_service_openrc.sh — 54 assertions covering all three init systems,
the failure branches, and the service helpers. bash tests/test_telegram_service_openrc.sh,
no root required. Existing tests unaffected.

test_client_mss.sh and test_lxc_ram_and_resources.sh fail on check_root in a non-root
environment; this is pre-existing and reproduces identically on main.

Validated on the reporter's Alpine LXC (OpenRC 0.63.2, no systemd):

  • init script generated in the right place, registered in default, service starts
  • killing the bot process → supervise-daemon respawns it (~12s)
  • telegram disable genuinely stops it; telegram remove cleans up unit + runlevel + settings
  • re-install after removal is idempotent
  • telegram status correct in both running and stopped states

Notes for review

  • need net docker, not use docker. The daemon drives the proxy container and is the
    only scheduler for the periodic tasks (quota/expiry enforcement, sweep, proxy auto-restart),
    so a hard dependency fails loudly instead of starting a bot that can only emit bogus
    "proxy down" alerts. This is stricter than the systemd unit's Wants=, deliberately;
    it's commented in the generated unit so it doesn't get "fixed" back.
  • rc-update's exit status is not trusted. It returns nonzero when the service is already
    registered, so gating on it would warn on every re-install. openrc_enable_service()
    verifies the runlevel entry actually exists instead.
  • respawn_max=0 restarts on any exit, whereas Restart=on-failure does not restart after
    a clean exit. Kept, since the daemon only exits on error or a signal, and documented as a
    real difference between the backends. Worth noting it cuts both ways: systemd's default
    StartLimitBurst=5 gives up after 5 failures in 10s, while this retries indefinitely at one
    attempt per 10s.
  • Two pid files exist and are not interchangeable/run/<svc>.pid belongs to the
    supervision layer, while the daemon writes its own PID under INSTALL_DIR. Killing the
    first stops the supervisor, not the bot. Documented in the generated unit.

Out of scope: the replication sync service still has no OpenRC path. Giving it real
OpenRC/cron support is a separate change; its setup_replication_service correctly warns
today, so it is not silent, and fixing its restart path alone would be pointless since
nothing is ever installed to restart.

setup_telegram_service() wrote a systemd unit only, with no else branch, so on
a host without systemd (Alpine/OpenRC) `mtproxymax telegram setup` reported
success while installing nothing. The bot stayed dead, and because the bot also
carries the "proxy down" alerts, the entire alert channel was lost with no
signal to the user. setup_autostart() had the identical no-else bug, so
"start on boot" was silently broken on the same hosts.

Add detect_init_system() and branch on it:

- OpenRC hosts get /etc/init.d/mtproxymax-telegram supervised by
  supervise-daemon (respawn_delay=10, respawn_max=0), mirroring the systemd
  unit's Restart=on-failure / RestartSec=10, plus /etc/init.d/mtproxymax for
  autostart (plain start/stop, mirroring Type=oneshot + RemainAfterExit=yes).
- Hosts with neither init system now warn loudly and return nonzero instead of
  failing silently, matching what setup_replication_service already does.
- A failed start is reported instead of logging "service started" regardless.

Route the remaining service calls (update restart, telegram disable/remove,
menu toggle, uninstall) through the same detector, and make `telegram status`
report whether the service is actually running rather than echoing the settings
flag.

Unit paths become injectable via SYSTEMD_DIR/INITD_DIR, following the existing
${VAR:-default} idiom, so the service installers are testable without root.

Adds tests/test_telegram_service_openrc.sh covering all three init systems.
Reconcile the generated init script with the unit running in production on an
Alpine LXC:

- `need net docker` rather than `use docker`. The daemon drives the proxy
  container and is the only scheduler for the periodic tasks (quota/expiry
  enforcement, sweep, proxy auto-restart), so a hard docker dependency fails
  loudly instead of starting a bot that can only emit bogus "proxy down" alerts.
- Separate error_log (.err) from output_log so stderr does not interleave.
- start_pre tests ${command_args} via -f — the daemon is invoked through bash so
  it need not be executable — and names the exact command to run.

Also fixes a generation bug this surfaced: in the unquoted heredoc
${command_args} was expanded at generation time, to empty, so the guard would
have been written as `if [ ! -f "" ]`. Now escaped, with a test assertion
pinning it.
Review on a live Alpine LXC surfaced three issues.

1. `rc-update add ... || true` swallowed failures and then logged success, so a
   user could be told boot-autostart was enabled when it was not — the same
   class of silent lie this branch set out to fix. Adds openrc_enable_service(),
   which verifies the service actually landed in the runlevel instead of
   trusting rc-update's exit status (which is also ambiguous when the service is
   already registered). setup_autostart now returns nonzero when it fails, since
   enabling boot-autostart is the whole point of that function.

2. Two pid files exist with different meanings: /run/<svc>.pid belongs to the
   supervision layer while the bot daemon writes its own PID under INSTALL_DIR.
   Killing the first stops the supervisor, not the bot. Documented in the
   generated unit.

3. respawn_max=0 restarts on any exit, whereas systemd's Restart=on-failure
   does not restart after a clean exit. Kept the behaviour — the daemon only
   exits on error or a signal — but documented the divergence.

Also switches the runlevel check to -e rather than -L so the decision logic
stays verifiable on hosts that cannot create symlinks.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Telegram setup silently fails to start the bot on non-systemd systems (Alpine/OpenRC)

1 participant