After getting my program to listen and log Bluetooth Low Energy advertisements from Govee thermometers running reliably, I needed to figure out how to make the program automatically start when my Raspberry was rebooted. I was led down two paths to get things working, systemd unit files, and debian package files created with dpkg-deb.
The final file structure I came up with is visible in https://github.com/wcbonner/GoveeBTTempLogger but still can use some explanation as to what I did.
To create the debian package, I created a file structure under my source repository that mimicked what I wanted to put on the target system.
\GOVEEBTTEMPLOGGER\GOVEEBTTEMPLOGGER ├───DEBIAN │ control │ postinst │ postrm │ prerm │ ├───etc │ └───systemd │ └───system │ goveebttemplogger.service │ ├───usr │ └───local │ └───bin │ goveebttemplogger │ └───var └───log └───goveebttemplogger gvh507x.txt
I had decided I wanted my executable to be located in /usr/local/bin. It’s the file named goveebttemplogger. I wanted it to write log files into /var/log/goveebttemplogger/ and the easiest way to make sure that directory was created was to put a zero length file in that directory, gvh507x.txt.
The files in the DEBIAN directory are used by the dpkg-deb program when building the distributable package. More on those later.
To get the program configured to automatically run when the machine boots, and properly stop when it shuts down, I settled on the systemd unit files as the both the easiest and most reliable method. I’ve been around linux long enough to first think of /etc/rc.local manipulation, then script files for various runlevels in the /etc/init.d/ directories, and was amazed at both the power and ease of setting up to use the systemd unit files. The hardest part was figuring out what other services my program must have already started. I knew it was dependent on Bluetooth, but the specific services was a bit of a guess.
# Contents of /etc/systemd/system/goveebttemplogger.service [Unit] Description=GoveeBTTempLogger service After=bluetooth.target dbus-org.bluez.service network.target Requires=bluetooth.target KillSignal=SIGINT [Service] Type=simple Restart=always ExecStart=/usr/local/bin/goveebttemplogger -v 0 -l /var/log/goveebttemplogger/ [Install] WantedBy=multi-user.target
After creating that file in the specified location, I was able to issue the following commands to make systemd start the program.
sudo systemctl daemon-reload sudo systemctl enable goveebttemplogger.service sudo systemctl start goveebttemplogger.service
The most unique bit of my unit file is that I specifically want my program to be sent the SIGINT signal to kill it, since I will recognize that and flush the log files before exiting. The ExecStart line is the command line to run my program, which I’m also specifying the log directory as one of the parameters.
I had the systemd unit file and the initial DEBIAN/control file figured out pretty easily. I’d come across this https://linuxconfig.org/easy-way-to-create-a-debian-package-and-local-package-repository article which helped understanding the control file.
Package: GoveeBTTempLogger Version: 1.20200725-1 Section: custom Priority: optional Architecture: armhf Essential: no Installed-Size: 95 Maintainer: email@example.com Description: Listen and log Govee Thermometer Bluetooth Low Energy Advertisments Depends: libbluetooth3
What took me a while to figure out was how to get the systemctl commands to be run after the files were put in place by the package manager. There are four script commands, which I’m using three. preinst, postinst, prerm, and postrm. Each of them is a simple script and needs to be marked executable in the file system. They are each run at various stages by the package manager, Pre-Installation, Post-Installation, Pre-Removal, and Post-Removal.
#!/bin/sh # POSTINST script for goveebttemplogger echo "\033[36m HI I'M A POSTINST SCRIPT `date +"%s"` \033[39m" systemctl daemon-reload systemctl enable goveebttemplogger.service systemctl start goveebttemplogger.service exit 0
After installation of my program and the systemd unit file, I reload the systemd database, enable my service, and start my service.
#!/bin/sh # PRERM script for goveebttemplogger echo "\033[36m HI I'M A PRERM SCRIPT `date +"%s"` \033[39m" systemctl stop goveebttemplogger.service systemctl disable goveebttemplogger.service exit 0
Before removal of my program, I stop the service and disable the service.
#!/bin/sh # POSTRM script for goveebttemplogger echo "\033[36m HI I'M A POSTRM SCRIPT `date +"%s"` \033[39m" systemctl daemon-reload exit 0
After removal of my program, I reload the systemd database, to make sure it’s not got my unit file in its database any longer.
When I retrieve a copy of my code with the command git clone https://github.com/wcbonner/GoveeBTTempLogger I then have a subdirectory below the GoveeBTTempLogger that is also named GoveeBTTempLogger. That deeper directory is the structure that will be created into the package.
GoveeBTTempLogger/usr/local/bin/goveebttemplogger: goveebttemplogger.cpp mkdir -p GoveeBTTempLogger/usr/local/bin g++ -lbluetooth goveebttemplogger.cpp -o GoveeBTTempLogger/usr/local/bin/goveebttemplogger deb: GoveeBTTempLogger/usr/local/bin/goveebttemplogger GoveeBTTempLogger/DEBIAN/control GoveeBTTempLogger/etc/systemd/system/goveebttemplogger.service mkdir -p GoveeBTTempLogger/var/log/goveebttemplogger touch GoveeBTTempLogger/var/log/goveebttemplogger/gvh507x.txt chmod a+x GoveeBTTempLogger/DEBIAN/postinst GoveeBTTempLogger/DEBIAN/postrm GoveeBTTempLogger/DEBIAN/prerm dpkg-deb --build GoveeBTTempLogger
I made the very simple makefile above to both compile the code and build the debian package with the simple command of make deb. It produces the package ‘goveebttemplogger’ in ‘GoveeBTTempLogger.deb’.
I can then install the package and start it running with the command sudo apt-get install ./GoveeBTTempLogger.deb
I can stop and either remove it or purge it with the command sudo apt-get remove goveebttemplogger or sudo apt-get purge goveebttemplogger.