Under koha-sysv the daemon(1) wrapper captures each daemon's stdout and
stderr to /var/log/koha/INSTANCE/NAME-output.log and NAME-error.log.
The systemd units sent that output to the journal instead, so
zebra-*.log, indexer-*.log, es-indexer-*.log, worker-*.log and
sip-error.log silently stopped being written on migrated hosts, and
log locations differed between the two init packages.
Use StandardOutput=append: and StandardError=append: on those six
units so the files are identical whichever package supervises the
daemons. The shipped logrotate stanza uses copytruncate, which is safe
with append-mode descriptors. The journal still records systemd's own
messages about each unit, which is what matters when one fails to
start, and README.Debian shows the drop-in for sites that prefer the
journal. Plack and the Z39.50 responder already wrote their own files.
The README, NEWS.Debian entry and koha-common(8) text that described
the journal-only behaviour are updated accordingly.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
- koha_init_backend relied on "systemctl list-unit-files" exiting
non-zero when nothing matches. That behaviour is version dependent,
and on a systemd host running koha-sysv a false positive would send
every koha-* helper to units that do not exist. Test for the
koha-plack@.service unit file instead: unambiguous, and no systemctl
fork per call. The _KOHA_INIT_BACKEND cache is dropped; every caller
ran the function in a $(...) subshell so it never took effect anyway.
- The _sysv_worker and _sysv_z3950 code moved here from bash-only
scripts brought "((error_count++))" and "[[ ! $VAR ]]" into a
#!/bin/sh library that koha-disable, koha-enable, koha-remove,
koha-list and the init script source under dash. Use POSIX
equivalents.
- _sysv_restart_zebra and _sysv_restart_sip lost the explicit
"return 0" of the original quiet not-running branch, so a start
failure now aborted the caller's whole instance loop under set -e
(including the init script's restart across all instances). Restore
it.
- systemctl status is run with --no-pager from koha_service_ctl and
koha-systemd-ctl so the helpers never block on a pager.
- koha-plack --enable/--disable ran systemctl before editing the Apache
configuration, so a systemctl failure under set -e left the vhost
untouched. Do the systemd half after the Apache edits and warn
instead of aborting.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
- Order services Before= their instance target instead of After=. With
After=, koha@<inst>.target (and therefore koha.target) reported active
before a single daemon had started, so its state meant nothing to
anything ordered after Koha. koha@.target is likewise Before=koha.target.
- Add SyslogIdentifier=%i-koha-<service> to every unit so journal lines
can be filtered per instance and component (the template this series
removed already did this) and Documentation= pointing at each helper's
man page.
- koha-plack@: stop with SIGQUIT and a 30s timeout. Starman treats QUIT
as graceful shutdown and TERM as immediate; the SysV path used
QUIT/30/KILL/5, so the default SIGTERM cut in-flight requests on every
restart. Drop the redundant --user/--group starman flags, User=/Group=
already apply.
- koha-worker@, koha-worker-long_tasks@: KillMode=mixed with a 120s
timeout so the parent is signalled but a running forked job gets a
chance to finish before it is killed.
- koha-zebra@: remove every *socket file in the run directory rather
than the two default names, so sites with customised <listen> entries
get the same stale-socket protection.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Two problems in how the units consume koha-generate-env's output:
1. koha-z3950@.service passed ${Z3950_ADDITIONAL_OPTS}. In systemd unit
files "${VAR}" always expands to exactly one argument, and to a single
empty argument when the variable is empty; only unbraced "$VAR" is
word-split. So a configured value like "--add-item-status k -t 5"
reached z3950_responder.pl as one unknown option, and the default
empty value reached it as a "" listener address. Verified with
systemd-run: ${X}="--a --b" gives ["--a --b"], ${Y}="" gives [""],
$X gives ["--a"] ["--b"]. Use the unbraced form.
2. koha-indexer@.service hardcoded rebuild_zebra.pl -daemon -sleep,
ignoring ALTERNATE_INDEXER_DAEMON and INDEXER_PARAMS from
/etc/default/koha-common that the SysV path honours. koha-generate-env
now resolves INDEXER_DAEMON and INDEXER_PARAMS exactly like
koha-indexer and the unit runs them through /usr/bin/env, so any
configured indexer works.
koha-generate-env is also tightened up: numeric values are validated
and fall back to their defaults, every value is forced onto one line
without quote characters so the EnvironmentFile always parses, the
debug_mode test matches is_debug_mode, Z3950_ADDITIONAL_OPTS is read
from /etc/default/koha-common first (as before this patchset), the
Z39.50 config dir uses the same config.xml test as is_z3950_enabled,
the env dir is created with the instance's ownership if it does not
exist yet, and unknown services get an empty env file instead of a
chmod failure. The responder is invoked through /usr/bin/perl like the
other Perl daemons.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The compat unit declared BindsTo=koha.target and After=koha.target while
its ExecStop ran "systemctl stop koha.target". After= orders this unit's
stop before the target's, so the ExecStop waited on a job that could not
run until the ExecStop itself finished. Every "systemctl stop koha-common"
and every "systemctl restart koha-common" hung for TimeoutStopSec (90s
by default) and left the unit in failed state. An async --no-block stop
does not help either: the queued target stop replaces the start job the
restart transaction needs, and everything ends up stopped.
Drop the dependencies. The unit is now a plain oneshot with
RemainAfterExit that starts koha.target in ExecStart and stops it in
ExecStop, which is all the old "service koha-common" UX requires.
Verified with a replica set of user units: start, stop and restart of
the shim each complete in well under a second, restart gives every
service a new PID, and managing koha.target directly is unaffected.
The one cosmetic consequence is that the shim stays "active (exited)"
if koha.target is stopped behind its back; README.Debian says to look
at koha.target for real status.
Also sets SyslogIdentifier so its journal lines are identifiable.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
systemd 257 warns that these units point PIDFile at the legacy
/var/run directory. This patch updates the remaining /var/run/koha
paths to /run/koha to match the EnvironmentFile lines; /var/run is a
symlink to /run on Debian, so nothing changes at runtime.
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
koha-common.service had an unexplained one-line shell loop in
ExecStartPre. It's now a documented sync-es-indexers command in
koha-systemd-ctl that does the same thing: notice instances that
switched to Elasticsearch and start their indexers, like the SysV init
script did on every start.
https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=40901#c132
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
koha-common.service already declares BindsTo=koha.target and
After=koha.target. BindsTo= implies Requires=, so systemd will start
koha.target automatically when this compat unit starts and will wait for
it to be active before proceeding. The explicit
ExecStart=/bin/systemctl start koha.target
was therefore redundant, and calling systemctl from within a service body
is an anti-pattern (it makes an extra D-Bus round-trip back to PID 1 and
can deadlock in edge cases during early boot).
Replace with /bin/true so the oneshot succeeds immediately after the
ExecStartPre ES-indexer discovery step. ExecStop is kept as-is because
there is no declarative equivalent for "stopping this unit should stop
koha.target" — BindsTo= only propagates stops in the other direction.
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
I think the following systemd security measures are safe for Koha:
ProtectSystem=full
Sets /usr & /boot to read-only
ProtectHome=yes
Hides /home, /root and /run/user
NOTE: The hiding of /run/user through me off, but google says it's only for
interactive logins only. AFAIK the only thing that does that is
koha-shell which isn't a systemd thing so it is unaffected
PrivateTmp=yes
Private /tmp & /var/tmp
NOTE: AFACT nothing in Koha shares files via /tmp which feels like it
would be a bad thing to do anyway
NoNewPrivileges=yes
setuid binaries can't re-elevate
NOTE: This feels like the most important one!
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
PartOf=koha.target already cascades stop to the per-instance target
so StopWhenUnneeded is not needed and adds more complication that could
trip us up later.
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
30 seconds between restart attempts is a very long time especially for
AIP. With StartLimitIntervalSec at 60, and RestartSec at 5 we get 6
retries and what is hopefully a less visible downtime window for
the tcp services. For es-indexer it's not so big a deal but could be
an issue for cataloguers.
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Pair each Requires=koha-create-dirs@%i.service with a matching
After=, and drop the now redundant Before= list from
koha-create-dirs@.service.
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Add StartLimitIntervalSec=60 add StartLimitBurst=3 so a broken-at-
startup daemon stops looping at after three attempts.
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The SysV init script runs `koha-list --enabled --elasticsearch` at
every start. ExecStartPre on koha-common.service restores that.
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
zebrasrv removes its Unix sockets on clean exit but leaves them behind on
SIGKILL, OOM, or sysv to systemd swap, causing 'Address already in use' on
the next start. ExecStartPre rm makes the unit auto-fix itself.
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Remove the use of sip.enabled for systemd, keep for sysv
No other service using a .enabled file is controlled via systemd or sysv
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Documentation is wrong, -c doesn't work for z3950_responder.pl
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The koha-sip@.service unit gates startup on
/var/lib/koha/<inst>/sip.enabled, the per-instance "I want SIP on"
flag file. `koha-sip --enable` creates that flag in the same call
that runs `systemctl enable`; running a bare `systemctl enable
koha-sip@<inst>.service` only flips the systemd half, so the unit
ends up enabled-but-refusing-to-start.
With ConditionPathExists the failure mode is silent and the unit
ends up "enabled" at the systemd level but "Active: inactive"
(dead) with "Condition: start condition failed" and the journal
logs aren't a big help figuring out the problem.
This patch replaces the silent skip with a loud failure with a helpful message:
ERROR: SIP not enabled for instance <inst>. Use: koha-sip --enable <inst>
SIPconfig.xml is gated by AssertPathExists if it's missing the unit fails.
Either failure path lands the unit in failed state, surfaced by 'systemctl --failed'
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Adds PartOf=koha.target to koha@.target so that stopping or
restarting the global target propagates to all instance targets
and their services.
Ships a koha-common.service oneshot unit that delegates to
koha.target, so 'systemctl restart koha-common' works as the
drop-in replacement for the old 'service koha-common restart'.
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
The systemd units were missing most of the parameters that the SysV
init functions read from koha-conf.xml. This caused wrong worker
counts, missing log files, ignored batch sizes, and other config
being silently dropped.
This patch adds koha-generate-env, a helper script that reads
koha-conf.xml and writes an EnvironmentFile for each service.
Each unit runs it as ExecStartPre and then uses the variables.
Changes per unit:
- koha-plack@: workers, max-requests, access/error logs,
environment (development/deployment), per-instance psgi, -M FindBin
- koha-zebra@: loglevels, max_record_size, timestamp format
- koha-es-indexer@: batch_size
- koha-indexer@: INDEXER_TIMEOUT from /etc/default/koha-common
- koha-z3950@: config dir fallback to global, additional options
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
Replace per-service ExecStartPre=+koha-create-dirs (running as root
on every start of every service) with a single oneshot unit per
instance. Services declare Requires= on it, and the oneshot's Before=
ensures directories exist before any service starts.
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
- koha-z3950-responder: add systemd enable/disable to enable_z3950/disable_z3950
- koha-disable: add plack and es-indexer stop/disable
- koha-remove: add worker --disable after --stop
- koha-systemd-ctl: fix service name worker-long to worker-long_tasks
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This patch adds an elevated koha-create-dirs to the systemd units, so
that when the units (re)start, the directories are definitely present.
We found that PID files were missing and couldn't be created without
this patch.
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
We found a bug when running koha-create. The systemd unit name didn't
match expected, so we've moved the unit to match the koha functions
computed value.
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>
This patch introduces the koha-systemd package, which provides native
systemd service units for managing Koha instances. Each service type
(Plack, Zebra, SIP, Z3950, workers, indexers) is managed as a separate
systemd unit using instance templates.
The package includes:
- 8 service templates (plack, zebra, sip, z3950, worker, worker-long,
indexer, es-indexer)
- 2 target units (koha@.target for per-instance, koha.target for global)
- Helper script (koha-systemd-ctl) for managing services
All service units include:
- KOHA_HOME environment variable pointing to /usr/share/koha
- WorkingDirectory set to /var/lib/koha/%i
- Proper user/group permissions (%i-koha)
This allows granular control over individual services per instance and
leverages systemd features like dependency management, resource limits,
and journald logging.
Test plan:
1. Build and install the package
2. Enable services for an instance
3. Verify all services start correctly
4. Check journalctl logs show no errors
Signed-off-by: Tomás Cohen Arazi <tomascohen@theke.io>
Signed-off-by: Kyle M Hall <kyle@bywatersolutions.com>
Signed-off-by: Martin Renvoize <martin.renvoize@openfifth.co.uk>
Signed-off-by: Pedro Amorim <pedro.amorim@openfifth.co.uk>