Service Support - SynoCommunity/spksrc GitHub Wiki
Generic service support in spksrc
Context
A third party application packaged for Synology often provide a web user interface and users expect to get simple access to it thanks to a service shortcut.
A packager may consider running such a user interface as root is a non-affordable security risk and will push effort to get it run as non-privileged user.
As maintainer facing a large upgrade, work on packages created with copy-paste pattern of many similar files are both time consumming and error prone; so generated files from generic support appear as a requirement.
Concept
Thanks to a small set of spk Makefile
variables, a package designer can
benefit from generic installer
and start-stop-status
scripts, with
possibility to adapt default behavior thanks to hooks as shell functions.
Provided support
- User creation (
SERVICE_USER
) and removal to run backgroup service process - Share folder creation based on wizard variable (
SERVICE_WIZARD_SHARE
) - Group creation for managing access to share folder (
SERVICE_WIZARD_GROUP
) - Service port (
SERVICE_PORT
) configuration for firewall and DSM shortcut - TLS certificate (
SERVICE_CERT
) integration with DSM's certificate management - Logging support in both
installer
andstart-stop-status
script - Hooks in package specific script (
SERVICE_SETUP
) - A specific
start-stop-status
script is provided for package withSTARTABLE=no
Support is enabled as far as a SERVICE_
variable is set in spk Makefile
.
Example
Package spk/demoservice
is provided as non-arch example of how to design your
package and wizard screens.
Replacement of defaults
If INSTALLER_SCRIPT
is set with a installer script, it replaces generic one.
If SSS_SCRIPT
is set with a specific start-stop script, it replaces generic one.
In any case, scripts/service-setup
is generated from package variables and
SERVICE_SETUP
, if provided.
Package migration
For package that has been built without this new support, refer to migration guidelines.
Reference documentation
Configuration
DSM UI integration
An application shortcut is available when both SPK_ICON
and SERVICE_PORT
are set in Makefile.
A single URL link is built from:
DISPLAY_NAME
DESCRIPTION
SERVICE_PORT
SERVICE_PORT_PROTOCOL
, defaults tohttp
SERVICE_URL
, defaults to/
SERVICE_PORT_ALL_USERS
, defaults totrue
Generated ${DSM_UI_DIR}/config
and icons are included in package.tgz
. If
not set in Makefile
, DSM_UI_DIR
default value is app
.
Setting NO_SERVICE_SHORTCUT
prevents this file generation, typically when
SERVICE_PORT
is not intended to be browsed.
For information, ADMIN_PORT
, ADMIN_PROTOCOL
and ADMIN_URL
are also
available to generate administration link visible from Package Center entry.
Service user creation
To prevent user account name collision when migrating from previous busybox
usage, a prefix is added to package name to create service account:
sc-USER
for DSM 6 privilege frameworksvc-USER
for DSM 5synouser
account
For usage in scripts, variable EFF_USER
contains effective account name with
prefix.
DSM 6 privilege
conf/privilege
is generated with username=sc-SPK_NAME
if SERVICE_USER
variable is set, by defaults to auto
.
In rare case package requires an alternate service account name, SERVICE_USER
value other than auto
can be used.
For DSM 5 compatibility, installer
script will create a system account with
synouser
but prefixed with svc-
. Script start-stop-status
invokes su
to start SERVICE_COMMAND
as non-priviliged user.
In case script start-stop-status
has to run as root
on DSM 6, package can
provide its own conf/privilege
file as far as CONF_DIR
variable is set.
Worker: service configuration for port
conf/resource/SPK_NAME.sc
is generated except if FWPORTS
is provided in the
Makefile. By default, it configures SERVICE_PORT
as tcp with port forwarding
enabled.
[SPK_NAME]
title="(SERVICE_PORT_TITLE:SPK_NAME)"
desc="(DISPLAY_NAME:SPK_NAME)"
port_forward="yes"
dst.ports="SERVICE_PORT/tcp"
If that default behaviour do not suit your need (for instance to declare two
ports), provide a package specific file relative path as FWPORTS
variable. File is then copied as conf/resource/SPK_NAME.sc
and handled by
installer
script as standard one.
Worker: service configuration for managed certificates integration
This feature is reverse-engineered and undocumented in Synology's Package Developer Guide. Starting with DSM 7.0, packages using it even fail to install because they are using a function intended "only for Synology packages". Therefore, this functionality is ignored when building a DSM >= 7.0 package.
A configuration block is added to conf/resource
if SERVICE_CERT
is provided
in the Makefile. The value must be one of the service names defined in the
FWPORTS
file, or the generated equivalent.
When a new certificate is assigned to the service by DSM (user change or
renewal), DSM can execute a script to cause the service to reload its
certificate. Such a script is service-specific and needs to be provided for
each package individually. The path given in the Makefile as
SERVICE_CERT_RELOAD
is relative to the package directory layout after
installation, so you need copy it to the STAGING_DIR
in an *_extra_install
target as appropriate.
Scripts
Both generic installer
and start-stop-status
scripts source service-setup
script which is generated aggregating following variables and SERVICE_SETUP
script if set in spk
Makefile
GROUP
and SHARE_PATH
can be set from wizard variable names declared in
Makefile variables SERVICE_WIZARD_GROUP
and SERVICE_WIZARD_SHARE
or
enforced in SERVICE_SETUP
script.
When an application requires a storage location, use SHARE_PATH
and
GROUP=sc-download
Open work-ARCH-TC/scripts/service-setup
to read generated content if
interested in.
Installer
SERVICE_SETUP
script can provide shell function invoked as hook when
installer
steps are run by DSM: service_preinst
, service_postinst
,
service_preuninst
, service_postuninst
, service_preupgrade
,
service_save
, service_restore
, service_postupgrade
.
All output of above service_* functions goes to the installer log file.
To notify the user and optionally abort the installer with exit 1
there are some validate_* function provided:
validate_preinst
, validate_preunint
and validate_preupgrade
. The user notificytion work
with DSM>=6 only.
For DSM 5 compatibility, scripts have to be designed to run with busybox
.
The installer
script and DSM package installer logs to /var/log/packages/${SPK_NAME}.log
.
Only for DSM<=5 the installer
script logs to /tmp/${SPK_NAME}_install.log
and if successfully
installed file is copied as /var/packages/${SPK_NAME}/target/var/${SPK_NAME}_install.log
.
By defaults on DSM 5, only var
folder is writable by service account. If
required, add required commands in service_postinst
shell function in package
specific SERVICE_SETUP
script.
Wizard variables are stored in
/var/packages/${SPK_NAME}/etc/installer-variables
so that they can be
retreived and used in uninstall or upgrade.
Service startup
start-stop-status
generic script starts SERVICE_COMMAND
as non-privileged
user, account name defaults to package name SPK_NAME
except if SERVICE_USER
is different from auto
.
Package status feedback relies on pid file
/var/packages/${SPK_NAME}/target/var/${SPK_NAME}.pid
available as PID_FILE
shell variable.
Service process is expected to fork in background and to provides PID_FILE
containing its own main process PID. Two means are available to do so:
-
if application supports such PID file generation,
service_postinst
shell function should configure service fromPID_FILE
variable -
PID_FILE
variable has to be provided as command argument inSERVICE_COMMAND
so that process writes down its own PID there, at least from shell special variable$!
Process standard output and error streams are aggregated into log file
/var/packages/${SPK_NAME}/target/var/${SPK_NAME}.log
which is readable thanks
to Package Center "View Log" package action.
Logging
Script is verbose by default. This behavior can be switched off setting
variables in SERVICE_SETUP
script:
SVC_NO_REDIRECT=y
prevents process streams to be collected in package logSVC_QUIET=y
prevents logging start/stop date and actionSVC_KEEP_LOG=y
prevent log content to be cleared before each startup
Advanced tuning options
Following options can be set in SERVICE_SETUP
script:
SERVICE_SHELL
can be used to replace default/bin/sh
on DSM 5 onlySVC_WAIT_TIMEOUT
is the delay for script to wait forPID_FILE
to appear at process startup (and disappear when stopping). Default value is 20 seconds
Advanced use cases
Run service process in background
In service-setup.sh
, set SVC_BACKGROUND=y
in addition to SERVICE_COMMAND
to get it started as background process (from shell thanks to &
)
In case process is not able to write its own PID in a file thanks to command
line option, then set also SVC_WRITE_PID=y
so that generic
start-stop-status
script generates PID_FILE
itself. This option only make
sense if SVC_BACKGROUND=y
is set.
Custom service process startup
In case application binary does not support background execution, a work-around
is to execute command from service_prestart
instead of setting
SERVICE_COMMAND
. This prevents to create an additional script to do so.
SERVICE_USER = auto
SERVICE_SETUP = src/service-setup.sh
STARTABLE = yes
In src/service-setup.sh
, replace generic command execution with shell
background execution and PID_FILE
generation. LOG_FILE
can be use to
collect stderr
and stdout
. Example from demoservice
:
service_prestart ()
{
# Replace generic service startup, fork process in background
echo "Starting python -m SimpleHTTPServer ${SERVICE_PORT} at ${SYNOPKG_PKGDEST}" >> ${LOG_FILE}
COMMAND="python -m SimpleHTTPServer ${SERVICE_PORT}"
if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
su ${EFF_USER} -s /bin/sh -c "cd ${SYNOPKG_PKGDEST}; ${COMMAND}" >> ${LOG_FILE} 2>&1 &
else
cd ${SYNOPKG_PKGDEST};
${COMMAND} >> ${LOG_FILE} 2>&1 &
fi
echo "$!" > "${PID_FILE}"
}
Run service process as root
In case application binary has to start as root
and forks to non-privileged
user from parameter (with setuid
syscall), it is possible to keep benefit of
SERVICE_USER
support.
CONF_DIR = src/conf
SERVICE_USER = auto
SERVICE_SETUP = src/service-setup.sh
STARTABLE = yes
Provide a specific src/conf/privilege
, using package name in sc-USER
,
defaults remain package
so that files are own by service user:
{
"defaults":{
"run-as": "package"
},
"username": "sc-USER",
"ctrl-script": [{
"action": "preinst",
"run-as": "root"
}, {
"action": "postinst",
"run-as": "root"
}, {
"action": "preuninst",
"run-as": "root"
}, {
"action": "postuninst",
"run-as": "root"
}, {
"action": "preupgrade",
"run-as": "root"
}, {
"action": "postupgrade",
"run-as": "root"
}, {
"action": "start",
"run-as": "root"
}, {
"action": "stop",
"run-as": "root"
}]
}
In SERVICE_SETUP
, execute application specific startup command:
service_prestart ()
{
COMMAND="${SYNOPKG_PKGDEST}/bin/appservice --background --user ${EFF_USER} --pidfile ${PID_FILE}"
# Run as root in both DSM 5 and 6
${COMMAND} >> ${LOG_FILE} 2>&1
}
start-stop-daemon
Service running thanks to BusyBox REMARKS: Since DSM 7 this is outdated and not supported anymore.
It is possible to replace generic start-stop-status
support by usage of
BusyBox start-stop-daemon
(for instance provided by Python package).
In package Makefile
, provide following variables:
SERVICE_EXE
is process executable absolute pathSERVICE_OPTIONS
is optional set of command line options to pass to process- Do not set
SERVICE_COMMAND