Skip to content

Commit

Permalink
SPK generic service support for DSM 6
Browse files Browse the repository at this point in the history
- Generic installer and start-stop-status scripts with logging
- Service configuration generation for tcp port
- User creation and run-as privilege file
- Share folder creation with group permissions
- Support for non service application package (non startable)
- Target both DSM 5 and 6
- demoservice package as usage example

Based on BenjV proposal #2904
  • Loading branch information
ymartin59 committed Nov 12, 2017
1 parent 2952f7b commit 7a44c5d
Show file tree
Hide file tree
Showing 16 changed files with 886 additions and 47 deletions.
4 changes: 2 additions & 2 deletions mk/spksrc.directories.mk
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
# * the copy target creates a staging area, in $(WORK_DIR)/staging/, known as $(STAGING_DIR).
# This staging dir does not contain the $(INSTALL_PREFIX)
# * Binaries and libraries are strip in $(STAGING_DIR)
# * The full content of $(STAGING_DIR) is packed, it will then be unpacked on the target in $(INSTALL_PREFIX)
# * The full content of $(STAGING_DIR) is packed, it will then be unpacked on the target in $(INSTALL_PREFIX)

PWD := $(shell pwd)

Expand All @@ -27,7 +27,7 @@ endif
STAGING_DIR = $(WORK_DIR)/staging

ifndef INSTALL_PREFIX
INSTALL_PREFIX = /usr/local
INSTALL_PREFIX = /var/packages/$(SPK_NAME)/target
endif

ifndef KERNEL_DIR
Expand Down
Empty file added mk/spksrc.service.config
Empty file.
310 changes: 310 additions & 0 deletions mk/spksrc.service.installer
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
#!/bin/sh

### TODO DSM 5 -> 6 upgrade PATH
# Cannot remove user (still in use or may be DSM service itself
# Try to fix existing user itself:
# - remove from "users" group
# - change default group to "package" (same UID/GID) if still "users"
# - change home folder to /var/packages/demoservice/target/

INST_LOG="/tmp/${SYNOPKG_PKGNAME}_install.log"
INST_ETC="/var/packages/${SYNOPKG_PKGNAME}/etc"
INST_VARIABLES="${INST_ETC}/installer-variables"
INST_VAR="${SYNOPKG_PKGDEST}/var"

# Optional FWPORTS file
FWPORTS_FILE="/var/packages/${SYNOPKG_PKGNAME}/conf/${SYNOPKG_PKGNAME}.sc"

# Versions lower then DSM 6 don't support an upgrade folder
if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
TMP_DIR="/var/tmp/${SYNOPKG_PKGNAME}/var"
else
TMP_DIR="${SYNOPKG_TEMP_UPGRADE_FOLDER}/var"
fi


# Source package specific variable and functions
SVC_SETUP=`dirname $0`"/service-setup"
if [ -r "${SVC_SETUP}" ]; then
. "${SVC_SETUP}"
fi

# Reload wizard variables stored by postinst
if [ -r "${INST_VARIABLES}" ]; then
. "${INST_VARIABLES}"
fi

# Expect user to be set from package specific variables
if [ -n "${USER}" -a -z "${USER_DESC}" ]; then
USER_DESC="User running $SYNOPKG_PKGNAME"
fi

# Default description if group name provided by UI
if [ -n "${GROUP}" -a -z "${GROUP_DESC}" ]; then
GROUP_DESC="SynoCommunity Package Group"
fi

# Extract share volume and share name from download location if provided
if [ -n "${SHARE_PATH}" ]; then
SHARE_VOLUME=$(echo "${SHARE_PATH}" | awk -F/ '{print "/"$2}')
SHARE_NAME=$(echo "${SHARE_PATH}" | awk -F/ '{print $3}')
fi


# Tools shortcuts
MV="/bin/mv -f"
RM="/bin/rm -rf"
MKDIR="/bin/mkdir -p"
LN="/bin/ln -nsf"
TEE="/usr/bin/tee -a"

### Functions library

# Invoke shell function if available
call_func ()
{
FUNC=$1
if type "$FUNC" | grep -q 'function' 2>/dev/null; then
echo "Invoke $FUNC" >> ${INST_LOG}
eval ${FUNC}
fi
}

log_step ()
{
date >> ${INST_LOG}
echo "Step $1. USER=$USER GROUP=$GROUP SHARE_PATH=${SHARE_PATH}" >> ${INST_LOG}
}

save_wizard_variables ()
{
$RM "${INST_VARIABLES}"
if [ -n "${GROUP}" ]; then
echo "GROUP=${GROUP}" >> ${INST_VARIABLES}
fi
if [ -n "${SHARE_PATH}" ]; then
echo "SHARE_PATH=${SHARE_PATH}" >> ${INST_VARIABLES}
fi
}

# Remove legacy user $USER from system and from groups it is member of
remove_user ()
{
if [ -n "${USER}" ]; then
# Check if user exists
if synouser --get "${USER}" &> /dev/null; then
echo "Removing user ${USER}" >> ${INST_LOG}
synouser --del "${USER}" >> ${INST_LOG} 2>&1
synouser --rebuild all
fi
fi
}

# Create syno group $GROUP with $USER as member
syno_group_create ()
{
echo "Creating group ${GROUP}" >> ${INST_LOG}
# Create syno group
synogroup --add "${GROUP}" "${USER}" >> ${INST_LOG}
# Set description of the syno group
synogroup --descset "${GROUP}" "${GROUP_DESC}" >> ${INST_LOG}
}

# Delete syno group $GROUP if empty
syno_group_remove ()
{
# Check if syno group is empty
if ! synogroup --get "${GROUP}" | grep -q "0:"; then
# Remove syno group
synogroup --del "${GROUP}" >> ${INST_LOG}
fi
}

# Sets recursive permissions for ${GROUP} on specified directory
# Usage: set_syno_permissions "${SHARE_FOLDER}"
set_syno_permissions ()
{
DIRNAME=$1
echo "Granting '$GROUP' group permissions on $DIRNAME" >> ${INST_LOG}
VOLUME=$(echo "$DIRNAME" | awk -F/ '{print "/"$2}')

# Walk up the tree and set traverse permissions up to VOLUME
while [ "${DIRNAME}" != "${VOLUME}" ]; do
# Set read/write permissions for GROUP
if [ ! "`synoacltool -get "${DIRNAME}"| grep "group:${GROUP}:allow:..x"`" ]; then
synoacltool -add "${DIRNAME}" "group:${GROUP}:allow:--x----------:---n" >> ${INST_LOG} 2>&1
fi
DIRNAME="$(dirname "${DIRNAME}")"
done
}

### Generic package behaviors

preinst ()
{
log_step "preinst"
call_func "service_preinst"

# Check volume exists
if [ -n "${SHARE_PATH}" ]; then
if [ ! -d "${SHARE_VOLUME}" ]; then
echo "Volume ${SHARE_VOLUME} does not exist." | $TEE ${INST_LOG}
exit 1
fi
fi

exit 0
}

postinst ()
{
log_step "postinst"
save_wizard_variables

# Link for backward compatibility of binaries location
$LN "${SYNOPKG_PKGDEST}" "/usr/local/${SYNOPKG_PKGNAME}" >> ${INST_LOG}

# Add firewall config
if [ -r "${FWPORTS_FILE}" ]; then
echo "Installing service configuration ${FWPORTS_FILE}" >> ${INST_LOG}
servicetool --install-configure-file --package "${FWPORTS_FILE}" >> ${INST_LOG} 2>&1
fi

# DSM 5 specific operations
if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
# Create "normal" user
if ! cat /etc/passwd | grep "${USER}:x:" &> /dev/null; then
synouser --add "$USER" "" "$USER_DESC" 0 "" 0 >> ${INST_LOG} 2>&1
fi
fi

# Only if a group is provided via UI or set by script
if [ -n "$GROUP" ]; then
# Check if group already exists
if ! synogroup --get "$GROUP" &> /dev/null; then
# Group does not exist yet: create with user as member
syno_group_create
fi
if synogroup --get "$GROUP" &> /dev/null; then
# Check user already in group
if synogroup --get "$GROUP" | grep '^[0-9]:\[$USER\]' &> /dev/null; then
# Add user, not in group yet
MEMBERS="$(synogroup --get $GROUP | grep '^[0-9]' | sed 's/.*\[\([^]]*\)].*/\1/' | tr '\n' ' ')"
# The "synogroup --member" command clears all users before adding new ones
# so all the users must be listed on the command line
synogroup --member "$GROUP" "$MEMBERS" "$USER" >> ${INST_LOG}
fi
fi
# Not sure but invoked with hope DSM is updated
synogroup --rebuild all
fi

# Share management
if [ -n "${SHARE_PATH}" ]; then
echo "Configuring ${SHARE_PATH}" >> ${INST_LOG}
# Create share if does not exist
# !"#$%&’()*+,/:;<=>?@[]nˆ`{} |
if ! synoshare --get "${SHARE_NAME}" &> /dev/null ; then
synoshare --add "${SHARE_NAME}" "${SHARE_DESC}" "${SHARE_PATH}" "" "rw" "" 1 0 >> ${INST_LOG}
fi

# Add user/group permissions depending if GROUP is set in UI
if [ -n "$GROUP" ]; then
synoshare --setuser "${SHARE_NAME}" RW + "${USER}",@"${GROUP}" >> ${INST_LOG} 2>&1
else
synoshare --setuser "${SHARE_NAME}" RW + "${USER}" >> ${INST_LOG} 2>&1
fi
synoshare --build

$MKDIR "${SHARE_PATH}"
# Permissions
set_syno_permissions "${SHARE_PATH}"
if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
chgrp users "${SHARE_PATH}"
chmod g+rw "${SHARE_PATH}"
fi
fi

call_func "service_postinst"
$MKDIR "${INST_VAR}"
cp "${INST_LOG}" "${INST_VAR}"
if [ -n "${LOG_FILE}" ]; then
echo "Installation log: ${INST_VAR}/${SYNOPKG_PKGNAME}_install.log" >> ${LOG_FILE}
fi
chown -R ${USER}:root "${SYNOPKG_PKGDEST}/var" >> $INST_LOG 2>&1
exit 0
}

preuninst ()
{
log_step "preuninst"

if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then
# Remove firewall config
if [ -r "${FWPORTS_FILE}" ]; then
echo "Removing service configuration ${SYNOPKG_PKGNAME}.sc" >> ${INST_LOG}
servicetool --remove-configure-file --package "${SYNOPKG_PKGNAME}.sc" >> ${INST_LOG} 2>&1
fi
fi

call_func "service_preuninst"
exit 0
}

postuninst ()
{
log_step "postuninst"

if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then
# Remove link
$RM "/usr/local/${SYNOPKG_PKGNAME}" >> ${INST_LOG}

# Remove syno group if empty
if [ -n "${GROUP}" ]; then
if ! [ synogroup --get "${GROUP}" | grep -q "0:\[" > /dev/null ]; then
echo "Removing group ${GROUP}" >> ${INST_LOG}
synogroup --del "${GROUP}" >> ${INST_LOG} 2>&1
synogroup --rebuild all
fi
fi
# On DSM 5, remove user if still exists
if [ $SYNOPKG_DSM_VERSION_MAJOR -lt 6 ]; then
remove_user
fi
fi

call_func "service_postuninst"
if [ "${SYNOPKG_PKG_STATUS}" == "UNINSTALL" ]; then
$RM "${INST_VARIABLES}"
fi
exit 0
}

preupgrade ()
{
log_step "preupgrade"
call_func "service_preupgrade"

# Save some stuff
$RM "$TMP_DIR" >> ${INST_LOG}
$MKDIR "$TMP_DIR" >> ${INST_LOG}

call_func "service_save"

$MV "${TO_SAVE_DIR}/*" "$TMP_DIR" >> ${INST_LOG}
exit 0
}

postupgrade ()
{
log_step "postupgrade"
# Restore some stuff
$MV "$TMP_DIR" "$TO_SAVE_DIR" >> ${INST_LOG}

call_func "service_restore"

$RM "$TMP_DIR" >> ${INST_LOG}

call_func "service_postupgrade"
exit 0
}
Loading

0 comments on commit 7a44c5d

Please sign in to comment.