#!/bin/bash #$Id:$ SCRIPTVERSION="3.26" # Author: Adimarantis # License: GPL #Install script for signal-cli SIGNALPATH=/opt SIGNALUSER=signal-cli LIBPATH=/usr/lib SIGNALVERSION="0.13.2" #Check for latest valid version at https://github.com/AsamK/signal-cli/releases LIBRARYVERSION="0.40.1" #Check for latest valid version at https://github.com/exquo/signal-libs-build/releases #Make sure this matches the required version for signal-cli (see lib/libsignal-client-0.xx.x.jar version in signal-cli) LIBSIG=libsignal_jni.tgz SIGNALVAR=/var/lib/$SIGNALUSER DBSYSTEMD=/etc/dbus-1/system.d DBSYSTEMS=/usr/share/dbus-1/system-services SYSTEMD=/etc/systemd/system LOG=/tmp/signal_install.log TMPFILE=/tmp/signal$$.tmp DBVER=0.22 OPERATION=$1 JAVACMD=java JAVA_VERSION=21.0 JDK_PACKAGE=openjdk-21-jre-headless JAVA_NATIVE=yes #Check if Java 21 installation is available for this system JDK_VER=`apt-cache search --names-only $JDK_PACKAGE` if ! [ "$JAVA_HOME" = "" ]; then JAVACMD=$JAVA_HOME/bin/java JAVA_NATIVE=no fi if [ -e "/opt/java/bin/java" ]; then JVER=`/opt/java/bin/java --version | grep -m1 -o '[0-9][0-9]\.[0-9]'` if [ "$JVER" = "$JAVA_VERSION" ]; then JAVACMD="/opt/java/bin/java" export JAVA_HOME="/opt/java" JAVA_NATIVE=no fi fi JVER=`$JAVACMD --version | grep -m1 -o '[0-9][0-9]\.[0-9]'` if [ "$JDK_VER" == "" ]; then JAVA_NATIVE=no if [ "$JVER" != "$JAVA_VERSION" ]; then JAVA_NATIVE=download echo "Warning: No Java $JAVA_VERSION found - will try download of a Java $JAVA_VERSION package" JAVACMD="/opt/java/bin/java" fi fi #Get OS data if [ -e /etc/os-release ]; then source /etc/os-release cat /etc/os-release >$LOG else echo "Could not find OS release data - are you on Linux?" exit fi if grep -q docker /proc/1/cgroup; then echo "You seem to run in a docker environment." export LC_ALL=C export DEBIAN_FRONTEND=noninteractive USER=`id | grep root` if [ -z "$USER" ]; then echo "Docker Installation needs to run under root" exit fi DOCKER=yes if [ -n "$FHEMUSER" ]; then SIGNALUSER=$FHEMUSER fi #overide path so its in the "real" world SIGNALPATH=/opt/fhem fi # install_and_check() { #Check availability of tools and install via apt if missing TOOL=$1 PACKAGE=$2 echo -n "Checking for $TOOL..." WHICH=`which $TOOL` if [ -z "$WHICH" ]; then echo -n "installing ($PACKAGE)" apt-get -q -y install $PACKAGE >>$LOG WHICH=`which $TOOL` if [ -z "$TOOL" ]; then echo "Failed to install $TOOL" exit else echo "done" fi else echo "available" fi } install_by_file() { #Check availability of tools and install via apt if missing FILE=$1 PACKAGE=$2 echo -n "Checking for $FILE..." if ! [ -e "$FILE" ]; then echo -n "installing ($PACKAGE)..." apt-get -q -y install $PACKAGE >>$LOG if ! [ -e "$FILE" ]; then echo "Failed to install $FILE" exit else echo "done" fi else echo "available" fi } check_and_create_path() { #Check if path is available and create of not CHECK=$1 echo -n "Checking for $CHECK..." if ! [ -d $CHECK ]; then mkdir $1 if ! [ -d $CHECK ]; then echo "Failed to create $CHECK - did you run on sudo?" exit else echo "created" fi else echo "found" fi if ! [ -w $CHECK ]; then echo "Cannot write to $CHECK - did you start this script with sudo?" exit fi } check_and_compare_file() { #Check if a file exists and compare if its the same as our internal reference file CHECK=$1 COMPARE=$2 echo -n "Checking for $CHECK..." if [ -e $CHECK ]; then echo "found" diff $CHECK $COMPARE DIFF=`diff -q $CHECK $COMPARE` if ! [ -z "$DIFF" ]; then echo "$CHECK differs, update (Y/n)? " read REPLY if [ "$REPLY" = "y" ] || [ "$REPLY" = "Y" ]; then cp $COMPARE $CHECK echo "$CHECK updated" else echo "$CHECK left untouched" fi fi else cp $COMPARE $CHECK echo "$CHECK installed" fi } #Main part - do always, check basic system requirements like OS, packages etc - does not install any signal specific stuff ARCH=`arch` OSNAME=`uname` APT=`which apt` GLIBC=`ldd --version | grep -m1 -o '[0-9]\.[0-9][0-9]' | head -n 1` if [ $ARCH = "armv7l" ]; then ARCH="armhf" ARCHL="armv7-unknown-linux-gnueabihf.tar.gz" ARCHJ="arm" elif [ $ARCH = "x86_64" ]; then ARCH="amd64" ARCHL="x86_64-unknown-linux-gnu.tar.gz" ARCHJ="x64" elif [ $ARCH = "aarch64" ]; then BITS=`getconf LONG_BIT` if [ "$BITS" = "64" ]; then ARCH="aarch64" ARCHL="aarch64-unknown-linux-gnu.tar.gz" ARCHJ="aarch64" else ARCH="armhf" ARCHL="armv7-unknown-linux-gnueabihf.tar.gz" ARCHJ="arm" fi fi if [ $OSNAME != "Linux" ]; then echo "Only Linux systems are supported (you: $OSNAME), quitting" exit fi if [ -z "$APT" ]; then echo "Your system does not have apt installed, quitting" exit fi if [ -z "$OPERATION" ] || [ "$OPERATION" = "system" ] || [ "$OPERATION" = "install" ] || [ "$OPERATION" = "all" ]; then echo "This script will help you to install signal-cli as system dbus service" echo "and prepare the use of the FHEM Signalbot module" echo echo "Please verify that these settings are correct:" echo "Signal-cli User: $SIGNALUSER" echo "Signal-cli Install directory: $SIGNALPATH" echo "Signal config storage: $SIGNALVAR" echo "Signal version: $SIGNALVERSION (libsignal-client v$LIBRARYVERSION)" echo "System library path: $LIBPATH" echo "System architecture: $ARCH" echo "System GLIBC version: $GLIBC" echo "Using Java version: $JAVACMD" echo "Native Java $JAVA_VERSION $JAVA_NATIVE (current version:$JVER)" fi if [ ! $(id -u) = 0 ] ; then # the script needs to be executed with sudo (or root) echo echo "Please run $0 with sudo" exit 1 fi check_and_update() { check_and_create_path $LIBPATH check_and_create_path /etc/dbus-1 check_and_create_path $DBSYSTEMD check_and_create_path /usr/share/dbus-1 check_and_create_path $DBSYSTEMS check_and_create_path $SYSTEMD check_and_create_path /run/dbus if [ -n "$DOCKER" ]; then echo -n "Running in Docker, performing apt update/upgrade..." apt-get -q -y update apt-get -q -y upgrade echo "done" fi install_and_check apt-ftparchive apt-utils install_and_check wget wget install_and_check sudo sudo install_and_check haveged haveged install_and_check java default-jre install_and_check diff diffutils install_and_check dbus-send dbus install_and_check cpan cpanminus install_and_check zip zip install_and_check qrencode qrencode if [ -z "$BASH" ]; then echo "This script requires bash for some functions. Check if bash is installed." install_and_check bash bash echo "Please re-run using bash" exit fi #For DBus check a number of Perl modules on file level install_by_file /usr/include/dbus-1.0/dbus/dbus.h libdbus-1-dev install_by_file /usr/share/build-essential/essential-packages-list build-essential install_by_file /usr/share/doc/libimage-librsvg-perl libimage-librsvg-perl install_by_file /usr/share/perl5/URI.pm liburi-perl cat >$TMPFILE <>$LOG 2>>$LOG echo "done" fi echo -n "Checking user $SIGNALUSER ..." if id "$SIGNALUSER" &>/dev/null; then echo 'found' else adduser --disabled-password --gecos none $SIGNALUSER echo 'created' fi if [ "$JAVA_NATIVE" = "download" ]; then cd /opt if [ "$ARCHJ" = "arm" ]; then echo -n "Downloading private jdk21 build for armv7l platform..." JAVA_ARC=openjdk-21.0.35-armv7l-glibc2.28.tar.gz wget -qN https://github.com/bublath/FHEM-Signalbot/raw/main/java/$JAVA_ARC else echo -n "Downloading Java from adoptium..." JAVA_ARC=OpenJDK21U-jdk_$ARCHJ\_linux_hotspot_21.0.2_13.tar.gz wget -qN https://github.com/adoptium/temurin21-binaries/releases/download/jdk-21.0.2%2B13/$JAVA_ARC fi if [ -z $JAVA_ARC ]; then echo "failed" exit fi echo "successful" echo -n "Unpacking ..." tar zxf $JAVA_ARC rm -rf /opt/java mv jdk* java rm $JAVA_ARC echo "done" JAVA_NATIVE=no export JAVA_HOME=/opt/java fi JVER=`$JAVACMD --version | grep -m1 -o '[0-9][0-9]\.[0-9]'` if [ "$JAVA_NATIVE" = "yes" ] && [ "$JVER" != "$JAVA_VERSION" ]; then PKG=$(dpkg-query -W --showformat='${Status}\n' $JDK_PACKAGE | grep "install") if [ -z "$PKG" ]; then echo -n "Installing $JDK_PACKAGE..." apt-get -q -y install $JDK_PACKAGE >>$LOG else echo "$JDK_PACKAGE already installed but Java $JVER found. Please use" echo "sudo update-alternatives --config java" echo "to activate Java $JAVA_VERSION or set your JAVA_HOME environment variable" exit fi JVER=`$JAVACMD --version | grep -m1 -o '[0-9][0-9]\.[0-9]'` if [ "$JVER" = "$JAVA_VERSION" ]; then echo "done" else echo "failed" echo "Error: Installation of $JDK_PACKAGE not successful" echo "Please try manually and restart installation once successful" echo "sudo apt-get install $JDK_PACKAGE" exit fi fi #Final check JVER=`$JAVACMD --version | grep -m1 -o '[0-9][0-9]\.[0-9]'` if ! [ "$JVER" = "$JAVA_VERSION" ]; then echo "Error: Java $JVER found, but Java $JAVA_VERSION required" echo "Something went wrong that this script cannot resolved - please fix manually" exit else echo "Checking for Java $JAVA_VERSION...successful" fi } #Check, install the signal-cli package as system dbus #After this, signal-cli should be running and ready to use over dbus install_signal_cli() { IDENTSTR=libsignal_v$LIBRARYVERSION/libsignal_jni.so-v$LIBRARYVERSION-$ARCHL GETLIBS=1 echo -n "Checking/downloading native library..." wget -qN https://github.com/exquo/signal-libs-build/releases/download/$IDENTSTR -O /tmp/$LIBSIG if [ -s /tmp/$LIBSIG ]; then echo "done" else echo "error" echo "Could not find native lib $LIBRARYVERSION for $ARCHL" GETLIBS=0 exit fi check_and_create_path $SIGNALPATH check_and_create_path $SIGNALVAR NEEDINSTALL=0 echo -n "Checking for existing signal-cli installation..." if [ -x "$SIGNALPATH/signal/bin/signal-cli" ]; then echo "found" echo -n "Checking signal-cli version..." CHECKVER=`$SIGNALPATH/signal/bin/signal-cli --version` echo $CHECKVER if [ "$CHECKVER" = "signal-cli $SIGNALVERSION" ]; then echo "signal-cli matches target version...ok" else echo -n "Update to current version $SIGNALVERSION (y/N)? " read REPLY if [ "$REPLY" = "y" ]; then NEEDINSTALL=1 fi fi else echo "not found" NEEDINSTALL=1 fi if [ $NEEDINSTALL = 1 ]; then echo "Proceed with signal cli installation" stop_service cd /tmp echo -n "Downloading signal-cli $SIGNALVERSION..." wget -qN https://github.com/AsamK/signal-cli/releases/download/v$SIGNALVERSION/signal-cli-$SIGNALVERSION-Linux.tar.gz -O signal-cli-$SIGNALVERSION.tar.gz if [ ! -s signal-cli-$SIGNALVERSION.tar.gz ]; then #Try alternative name used in 0.12.3 wget -qN https://github.com/AsamK/signal-cli/releases/download/v$SIGNALVERSION/signal-cli-$SIGNALVERSION.tar.gz -O signal-cli-$SIGNALVERSION.tar.gz fi if [ -s signal-cli-$SIGNALVERSION.tar.gz ]; then echo "done" echo "Unpacking ..." cd $SIGNALPATH tar zxf /tmp/signal-cli-$SIGNALVERSION.tar.gz rm -rf signal mv "signal-cli-$SIGNALVERSION" signal if [ "$GETLIBS" = 1 ]; then cd /tmp tar zxf $LIBSIG echo "Updating native libs for $IDENTSTR" zip -u $SIGNALPATH/signal/lib/libsignal-client-*.jar libsignal_jni.so fi echo "done" rm -f /tmp/signal-cli-$SIGNALVERSION.tar.gz rm -f /tmp/$LIBSIG else echo "failed" exit fi fi #Updating ownership anyway - just if case chown -R $SIGNALUSER: $SIGNALVAR chown -R $SIGNALUSER: $SIGNALPATH/signal if [ -z "$DOCKER" ]; then #Don't do this in Docker environment cat >$TMPFILE < EOF check_and_compare_file $DBSYSTEMD/org.asamk.Signal.conf $TMPFILE cat >$TMPFILE <$TMPFILE </var/log/dbus.log 2>/var/log/dbus.err & fi echo -n "Waiting for dbus to become ready." WAIT="" while [ -z "$WAIT" ] do WAIT=`ps -eo pid,command | grep dbus-daemon | grep -v grep` echo -n "." sleep 1 done echo "running" SIGSERVICE=`ps -eo pid,command | grep $SIGNALVAR | grep -v grep` WAITCHECK="ps -eo pid,command | grep $SIGNALVAR | grep java | grep -v grep" if [ -z "$SIGSERVICE" ]; then cd $SIGNALPATH/signal/bin echo "Starting signal-cli daemon for Docker" sudo -u $SIGNALUSER ./signal-cli --config $SIGNALVAR daemon --system >/var/log/signal.log 2>/var/log/signal.err & WAITCHECK="grep -i dbus /var/log/signal.err" fi echo -n "Waiting for signal-cli to become ready." WAIT="" while [ -z "$WAIT" ] do WAIT=`$WAITCHECK` echo -n "." sleep 1 done echo "running" fi } test_device() { start_service echo -n "Checking installation via dbus-send command..." REPLY=`dbus-send --system --type=method_call --print-reply --dest="org.asamk.Signal" /org/asamk/Signal org.asamk.Signal.version` REP1=`echo $REPLY | grep $SIGNALVERSION` #REP2=`echo $REPLY | grep "boolean true"` if [ -n "$REP1" ]; then echo "success" # - "signal-cli running in standard registration mode" else echo "unexpected reply" echo $REPLY fi #if [ -n "$REP2" ]; then # echo "partial success - still running in -u mode - check $SYSTEMD/signal.service" #fi cat <$TMPFILE #!/usr/bin/perl -w use strict; use warnings; use Protocol::DBus::Client; my \$dbus = Protocol::DBus::Client::system(); \$dbus->initialize(); \$dbus->get_message(); my \$got_response; my @recipients=('$RECIPIENT'); my @att=(); \$dbus->send_call( path => '/org/asamk/Signal', interface => 'org.asamk.Signal', signature => '', body => undef, destination => 'org.asamk.Signal', member => 'version', )->then( sub { print "reply received\n"; } )->catch( sub { print "Error getting reply\n"; } )->finally( sub { \$got_response = 1; } ); \$dbus->get_message() while !\$got_response; EOF echo -n "Sending a message via perl Protocol::DBus..." perl $TMPFILE } remove_all() { #just in case paths are wrong to not accidentially remove wrong things cd /tmp echo "Warning. This will remove signal-cli and all related configurations from your system" echo "Your configuration will be archived to $HOME/signalconf.tar.gz" echo -n "Continue (y/N)? " read REPLY if ! [ "$REPLY" = "y" ]; then echo "Abort" exit fi stop_service echo "Archiving config" tar czf ~/signalconf.tar.gz $SIGNALVAR echo "Removing files" rm -rf $SIGNALVAR rm -rf $SIGNALPATH/signal rm -f $LIBPATH/libsignal_jni.so rm -f $LIBPATH/libzkgroup.so rm -f $DBSYSTEMD/org.asamk.Signal.conf rm -f $DBSYSTEMS/org.asamk.Signal.service rm -f $SYSTEMD/signal.service echo "Disabling services" if [ -z "$DOCKER" ]; then systemctl daemon-reload systemctl disable signal.service systemctl reload dbus.service else DBDAEMON=`ps -eo pid,command | grep dbus-daemon | grep -v grep` if [ -n "$DBDAEMON" ]; then echo "Stopping dbus daemon for Docker" ARRAY=($DBDAEMON) PID=${ARRAY[0]} kill $PID fi fi } if [ -z "$OPERATION" ] ; then echo "This will update system packages, install or uninstall signal-cli" echo echo "system : prepare required system package (except signal-cli)" echo "install : install signal-cli and setup as dbus system service" echo "test : run a basic test if everything is installed and registered correctly" echo "remove : Remove signal-cli and all configurations (will be archived)" echo "start : Start the signal-cli service (or respective docker processes)" echo "all : Run system, install, start and test (default)" echo OPERATION=all else echo "You chose the following option: $OPERATION" fi echo if [ -z "$OPERATION" ] || [ "$OPERATION" = "system" ] || [ "$OPERATION" = "install" ] || [ "$OPERATION" = "all" ]; then echo -n "Proceed (Y/n)? " read REPLY if [ "$REPLY" = "n" ]; then echo "Aborting..." exit fi fi if [ $OPERATION = "docker" ]; then OPERATION="" fi # Main flow without option: intall, register if [ -z "$OPERATION" ] || [ $OPERATION = "all" ] || [ $OPERATION = "system" ]; then check_and_update fi if [ -z "$OPERATION" ] || [ $OPERATION = "all" ] || [ $OPERATION = "install" ]; then install_signal_cli fi if [ -z "$OPERATION" ] || [ $OPERATION = "all" ] || [ $OPERATION = "test" ]; then test_device exit fi # Other options if [ $OPERATION = "remove" ]; then remove_all fi if [ $OPERATION = "start" ]; then start_service fi if [ $OPERATION = "backup" ]; then echo "Creating backup of all configuration files" stop_service rm signal-backup.tar.gz tar czPf signal-backup.tar.gz $SIGNALVAR $DBSYSTEMD/org.asamk.Signal.conf $DBSYSTEMS/org.asamk.Signal.service $SYSTEMD/signal.service start_service ls -l signal-backup.tar.gz fi if [ $OPERATION = "restore" ]; then if ! [ -e signal-backup.tar.gz ]; then echo "Make sure signal-backup.tar.gz is in current directory" exit fi echo "Are you sure you want to restore all signal-cli configuration files?" echo -n "Any existing configuration will be lost (y/N)? " read REPLY if ! [ "$REPLY" = "y" ]; then echo "Aborting..." exit fi stop_service echo -n "Restoring backup..." tar xPf signal-backup.tar.gz chown -R $SIGNALUSER: $SIGNALVAR echo "done" start_service fi rm -f $TMPFILE exit