Libreoffice Online sans systemd
Le dimanche 26 août 2018 par Benjamin BoudoirJe ne suis pas un grand fan de systemd, aussi, j'utilise Devuan comme système principal.
La partie chiante c'est que maintenant, certains softs ne fournissent plus de script d'init sysV traditionnel (quand ils fournissent autre chose d'une image docker).
Récemment, j'ai découvert que Collabora fournissait une preview bien finie de LibreOfficle OnLine (LOOL, pour les intimes), avec une intégration à [Own|Next]Cloud. Parfait ? Pas vraiment.
Puisque les paquets Debian sont fournis... Avec une unit systemd.
Qu'à celà ne tienne, une petite doc pour installer tout ça.
Installation de Collabora
Je ne vais pas répéter la documentation officielle, simplement ajouter des choses sur ce qu'elle ne couvre pas (et fournir le script d'init, bien entendu)
loolwsd
Depuis quelque temps, le paquet du daemon loolwsd a une dépendance sur systemd. Voici comment le reconstruire de manière à pouvoir l'installer et suivre la publication depuis le repo :
#!/bin/sh
set -e
tmpdir=looltmpunpack
npkg=loolwsd-nosystemd.deb
cd /tmp
apt download loolwsd
mkdir $tmpdir
echo "Unpacking..."
dpkg-deb -R loolwsd_*_amd64.deb $tmpdir
echo "Remove systemd from dependancies"
sed -i 's/systemd, //' $tmpdir/DEBIAN/control
echo "Update version"
sed -ri 's/^(Version:.*)/\1-nsd/' $tmpdir/DEBIAN/control
dpkg-deb -b $tmpdir $npkg
echo "Upgrading..."
/etc/init.d/loolwsd stop
dpkg -i $npkg
/etc/init.d/loolwsd start
echo "Cleaning"
rm -r loolwsd*.deb $tmpdir
Il ne s'agit pas d'un paquet complet mais il est possible de faire les mises à jour du logiciel sans tout casser.
SSL
Il faut commencer par générer un certificat SSL, le mettre dans un endroit accessible à l'utilisateur lool.
Puis on modifie le fichier de configuration (ici, chaque fichier est un pem dans le dossier de configuration, c'est à adapter à votre configuration) :
<ssl desc="SSL settings">
<cert_file_path desc="Path to the cert file" relative="false">/etc/loolwsd/cert.pem</cert_file_path>
<key_file_path desc="Path to the key file" relative="false">/etc/loolwsd/key.pem</key_file_path>
<ca_file_path desc="Path to the ca file" relative="false">/etc/loolwsd/ca.pem</ca_file_path>
</ssl>
Cette partie est optionnelle si vous avez confiance dans le réseau entre votre proxy (Apache HTTPd ou NGinx, par exemple) et votre serveur websocket (loolwsd), loolwsd peut être démarré sans gérer son SSL avec l'option --disable-ssl.
Intégration côté loolwsd à [Next|Own]Cloud
Storage est la partie où on va pouvoir indiquer quels hôtes peuvent transférer des requêtes à LOOLWSD et avec quelle méthode.
Ici, c'est l'intégration [Own|Next]Cloud qui m'intéresse alors j'ai simplement besoin d'ajouter :
<storage desc="Backend storage">
<wopi desc="Allow/deny wopi storage. Mutually exclusive with webdav." allow="true">
<host desc="Regex pattern of hostname to allow or deny." allow="true">sub\.domain\.tld</host>
</wopi>
</storage>
Et voilà.
Init script
Je me basé sur le vieux skeleton de Debian pour pondre ceci :
#!/bin/sh
### BEGIN INIT INFO
# Provides: loolwsd
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: LOOL WSD
# Description: LibreOffice Online WebSocket Daemon
### END INIT INFO
# Author: Benjamin Boudoir <benjamin+sysv boudoir.name>
# Do NOT "set -e"
# PATH should only include /usr/* if it runs after the mountnfs.sh script
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="LibreOffice Online WebSocket Daemon"
NAME=loolwsd
PIDDIR=/var/run/$NAME
PIDFILE=$PIDDIR/$NAME.pid
DAEMON=/usr/bin/$NAME
DAEMON_ARGS="--version --o:sys_template_path=/opt/lool/systemplate --o:child_root_path=/opt/lool/child-roots --o:file_server_root_path=/usr/share/loolwsd --pidfile=$PIDFILE --daemon"
# If you don't use ssl on loolwsd side, uncomment this :
#DAEMON_ARGS="$DAEMON_ARGS --disable-ssl"
SCRIPTNAME=/etc/init.d/$NAME
USER=lool
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
# Read configuration variable file if it is present
[ -r /etc/default/$NAME ] && . /etc/default/$NAME
# Load the VERBOSE setting and other rcS variables
. /lib/init/vars.sh
# Define LSB log_* functions.
# Depend on lsb-base (>= 3.2-14) to ensure that this file is present
# and status_of_proc is working.
. /lib/lsb/init-functions
if [ ! -d "$PIDDIR" ]
then
mkdir "$PIDDIR"
chown ${USER}: "$PIDDIR"
fi
#
# Function that starts the daemon/service
#
do_start()
{
# Return
# 0 if daemon has been started
# 1 if daemon was already running
# 2 if daemon could not be started
start-stop-daemon --start --quiet --chuid $USER --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
|| return 1
start-stop-daemon --start --quiet --chuid $USER --pidfile $PIDFILE --exec $DAEMON -- \
$DAEMON_ARGS \
|| return 2
# Add code here, if necessary, that waits for the process to be ready
# to handle requests from services started subsequently which depend
# on this one. As a last resort, sleep for some time.
}
#
# Function that stops the daemon/service
#
do_stop()
{
# Return
# 0 if daemon has been stopped
# 1 if daemon was already stopped
# 2 if daemon could not be stopped
# other if a failure occurred
start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME
RETVAL="$?"
[ "$RETVAL" = 2 ] && return 2
# Wait for children to finish too if this is a daemon that forks
# and if the daemon is only ever run from this initscript.
# If the above conditions are not satisfied then add some other code
# that waits for the process to drop all resources that could be
# needed by services started subsequently. A last resort is to
# sleep for some time.
start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
[ "$?" = 2 ] && return 2
# Many daemons don't delete their pidfiles when they exit.
rm -f $PIDFILE
return "$RETVAL"
}
#
# Function that sends a SIGHUP to the daemon/service
#
do_reload() {
#
# If the daemon can reload its configuration without
# restarting (for example, when it is sent a SIGHUP),
# then implement that here.
#
start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $BINNAME
return 0
}
case "$1" in
start)
[ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
do_start
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
stop)
[ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
do_stop
case "$?" in
0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
esac
;;
status)
status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $?
;;
#reload|force-reload)
#
# If do_reload() is not implemented then leave this commented out
# and leave 'force-reload' as an alias for 'restart'.
#
#log_daemon_msg "Reloading $DESC" "$NAME"
#do_reload
#log_end_msg $?
#;;
restart|force-reload)
#
# If the "reload" option is implemented then remove the
# 'force-reload' alias
#
log_daemon_msg "Restarting $DESC" "$NAME"
do_stop
case "$?" in
0|1)
do_start
case "$?" in
0) log_end_msg 0 ;;
1) log_end_msg 1 ;; # Old process is still running
*) log_end_msg 1 ;; # Failed to start
esac
;;
*)
# Failed to stop
log_end_msg 1
;;
esac
;;
*)
#echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2
echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2
exit 3
;;
esac
:
Le packet ne va pas redémarrer loolwsd en cas de mise à jour (car un appel à systemd est hardcodé...). On peut contourner ce problème avec un fichier de configuraiton d'apt (/etc/apt/apt.conf.d/99-loolwsd-restart) :
DPkg::Pre-Install-Pkgs {"/usr/local/sbin/apt-helper-restart-loolwsd";};
DPkg::Post-Invoke {"/usr/local/sbin/apt-helper-restart-loolwsd";};
DPkg::Tools::Options::/local/sbin/apt-helper-restart-loolwsd::Version "2";
Et un script associé (/usr/local/sbin/apt-helper-restart-loolwsd) :
#!/bin/bash
##
## This script is run twice per apt invokation.
## The first time, it create a "canari", saying if loolwsd is one of the package involved
## The second time, it restart loolwsd if the canari say so
##
declare -gA version action ver_index
ver_index["VERSION 2"]=3
ver_index["VERSION 3"]=5
CANARI=/tmp/loolwsd-restart
# CTRL + C
trap "rm -vf $CANARI" SIGINT
# kill -15
trap "rm -vf $CANARI" SIGTERM
skip_options ()
{
while IFS= read -r line && [[ -n "$line" ]]
do
:
done
}
# No canari == first launch
if [ ! -f "$CANARI" ]
then
# check packages
IFS= read line
case $line in
"VERSION "[23])
ver_index="${ver_index[$line]}"
skip_options
while read -ra pack
do
version["${pack[0]}"]="${pack[$ver_index]}"
action["${pack[0]}"]="${pack[-1]}"
done
;;
*)
while read pack ver
do
version["$pack"]="$ver"
action["$pack"]="**CONFIGURE**"
done < <( (echo "$line"; cat ) | xargs -d '\n' dpkg-deb --show --showformat='${Package} ${Version}\n')
;;
esac
# No packages installed
[ -z "${!version[@]}" ] && echo "no" > "$CANARI" && exit 0
# Package installed
for i in "${!version[@]}"
do
# ... and loolwsd is one of them
if [ "$i" == "loolwsd" ] && [ "${action[$i]}" == "**CONFIGURE**" ]
then
echo yes > "$CANARI"
break
else
# ... and loolwsd is none of them
echo no > "$CANARI"
fi
done
# Canari == second invokation
elif [ $(cat "$CANARI") == "yes" ]
then
# If it contain "yes", we should restart loolwsd
/etc/init.d/loolwsd restart
rm "$CANARI"
else
# second invokation but no need to restart, deleting canari to avoid confusion
rm "$CANARI"
fi