From 8c7da1f224075e53f2f254b9efa015dbd386620b Mon Sep 17 00:00:00 2001 From: Alexandre Date: Thu, 25 Jul 2024 09:09:09 +0200 Subject: [PATCH] battybirdnet-pi --- battybirdnet-pi/CHANGELOG.md | 3 + battybirdnet-pi/Dockerfile | 205 + battybirdnet-pi/README.md | 159 + battybirdnet-pi/apparmor.txt | 66 + battybirdnet-pi/build.yaml | 6 + battybirdnet-pi/config.json | 126 + battybirdnet-pi/icon.png | Bin 0 -> 216488 bytes battybirdnet-pi/logo.png | Bin 0 -> 216488 bytes .../rootfs/custom-services.d/00-php_pfm.sh | 12 + .../rootfs/custom-services.d/01-avahi.sh | 9 + .../rootfs/custom-services.d/02-caddy.sh | 21 + .../rootfs/custom-services.d/02-nginx.sh | 6 + .../rootfs/custom-services.d/30-monitoring.sh | 92 + .../etc/cont-finish.d/savestreamdata.sh | 18 + .../rootfs/etc/cont-init.d/01-structure.sh | 87 + .../etc/cont-init.d/02-restorestreamdata.sh | 24 + .../rootfs/etc/cont-init.d/31-checks.sh | 54 + .../rootfs/etc/cont-init.d/33-mqtt.sh | 47 + .../rootfs/etc/cont-init.d/71-newfeatures.sh | 127 + .../etc/cont-init.d/81-modifications.sh | 66 + .../etc/cont-init.d/91-nginx_ingress.sh | 37 + .../rootfs/etc/cont-init.d/92-ssl.sh | 20 + .../rootfs/etc/cont-init.d/99-run.sh | 72 + .../rootfs/etc/nginx/includes/mime.types | 96 + .../etc/nginx/includes/proxy_params.conf | 16 + .../rootfs/etc/nginx/includes/resolver.conf | 1 + .../etc/nginx/includes/server_params.conf | 6 + .../rootfs/etc/nginx/includes/ssl_params.conf | 9 + .../rootfs/etc/nginx/includes/upstream.conf | 3 + battybirdnet-pi/rootfs/etc/nginx/nginx.conf | 78 + .../rootfs/etc/nginx/servers/ingress.conf | 47 + .../rootfs/helpers/birdnet_to_mqtt.py | 124 + .../rootfs/helpers/birdnet_to_mqtt.sh | 5 + .../rootfs/helpers/caddy_ingress.sh | 24 + .../rootfs/helpers/convert_list.php | 116 + .../rootfs/helpers/spectral_analysis.py | 70 + .../rootfs/helpers/spectral_analysis.sh | 62 + battybirdnet-pi/rootfs/helpers/systemctl3.py | 6851 +++++++++++++++++ battybirdnet-pi/rootfs/helpers/timedatectl | 72 + battybirdnet-pi/rootfs/helpers/views.add | 27 + battybirdnet-pi/stats.png | Bin 0 -> 1768 bytes battybirdnet-pi/updater.json | 8 + birdnet-pi/updater.json | 2 +- 43 files changed, 8873 insertions(+), 1 deletion(-) create mode 100644 battybirdnet-pi/CHANGELOG.md create mode 100644 battybirdnet-pi/Dockerfile create mode 100644 battybirdnet-pi/README.md create mode 100644 battybirdnet-pi/apparmor.txt create mode 100644 battybirdnet-pi/build.yaml create mode 100644 battybirdnet-pi/config.json create mode 100644 battybirdnet-pi/icon.png create mode 100644 battybirdnet-pi/logo.png create mode 100644 battybirdnet-pi/rootfs/custom-services.d/00-php_pfm.sh create mode 100644 battybirdnet-pi/rootfs/custom-services.d/01-avahi.sh create mode 100644 battybirdnet-pi/rootfs/custom-services.d/02-caddy.sh create mode 100644 battybirdnet-pi/rootfs/custom-services.d/02-nginx.sh create mode 100644 battybirdnet-pi/rootfs/custom-services.d/30-monitoring.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-finish.d/savestreamdata.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/01-structure.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/02-restorestreamdata.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/31-checks.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/33-mqtt.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/71-newfeatures.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/81-modifications.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/91-nginx_ingress.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/92-ssl.sh create mode 100644 battybirdnet-pi/rootfs/etc/cont-init.d/99-run.sh create mode 100644 battybirdnet-pi/rootfs/etc/nginx/includes/mime.types create mode 100644 battybirdnet-pi/rootfs/etc/nginx/includes/proxy_params.conf create mode 100644 battybirdnet-pi/rootfs/etc/nginx/includes/resolver.conf create mode 100644 battybirdnet-pi/rootfs/etc/nginx/includes/server_params.conf create mode 100644 battybirdnet-pi/rootfs/etc/nginx/includes/ssl_params.conf create mode 100644 battybirdnet-pi/rootfs/etc/nginx/includes/upstream.conf create mode 100644 battybirdnet-pi/rootfs/etc/nginx/nginx.conf create mode 100644 battybirdnet-pi/rootfs/etc/nginx/servers/ingress.conf create mode 100644 battybirdnet-pi/rootfs/helpers/birdnet_to_mqtt.py create mode 100644 battybirdnet-pi/rootfs/helpers/birdnet_to_mqtt.sh create mode 100644 battybirdnet-pi/rootfs/helpers/caddy_ingress.sh create mode 100644 battybirdnet-pi/rootfs/helpers/convert_list.php create mode 100644 battybirdnet-pi/rootfs/helpers/spectral_analysis.py create mode 100644 battybirdnet-pi/rootfs/helpers/spectral_analysis.sh create mode 100644 battybirdnet-pi/rootfs/helpers/systemctl3.py create mode 100644 battybirdnet-pi/rootfs/helpers/timedatectl create mode 100644 battybirdnet-pi/rootfs/helpers/views.add create mode 100644 battybirdnet-pi/stats.png create mode 100644 battybirdnet-pi/updater.json diff --git a/battybirdnet-pi/CHANGELOG.md b/battybirdnet-pi/CHANGELOG.md new file mode 100644 index 000000000..b1bd52ea9 --- /dev/null +++ b/battybirdnet-pi/CHANGELOG.md @@ -0,0 +1,3 @@ + +## 0.1 (28-04-2024) +- Initial build diff --git a/battybirdnet-pi/Dockerfile b/battybirdnet-pi/Dockerfile new file mode 100644 index 000000000..6b5ae0733 --- /dev/null +++ b/battybirdnet-pi/Dockerfile @@ -0,0 +1,205 @@ +#============================# +# ALEXBELGIUM'S DOCKERFILE # +#============================# +# _.------. +# _.-` ('>.-`"""-. +# '.--'` _'` _ .--.) +# -' '-.-';` ` +# ' - _.' ``'--. +# '---` .-'""` +# /` +#=== Home Assistant Addon ===# + +################# +# 1 Build Image # +################# + +ARG BUILD_VERSION +ARG BUILD_FROM +FROM ${BUILD_FROM} + +ENV DEBIAN_FRONTEND="noninteractive" \ + BIRDNET_USER="pi" \ + USER="pi" \ + PUID=1000 \ + PGID=1000 \ + HOME="/home/pi" \ + XDG_RUNTIME_DIR="/run/user/1000" \ + PYTHON_VIRTUAL_ENV="/home/pi/BirdNET-Pi/birdnet/bin/python3" \ + my_dir=/home/pi/BirdNET-Pi/scripts + +# Global LSIO modifications +ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_lsio.sh" "/ha_lsio.sh" +ARG CONFIGLOCATION="/config" +RUN chmod 744 /ha_lsio.sh && if grep -qr "lsio" /etc; then /ha_lsio.sh "$CONFIGLOCATION"; fi && rm /ha_lsio.sh + +SHELL ["/bin/bash", "-o", "pipefail", "-c"] +# hadolint ignore=DL3015,SC2016 +RUN \ + # Install dependencies + echo "Install dependencies" && \ + apt-get update -y && apt-get install curl gcc python3-dev git jq sudo php-mbstring procps -y && \ + \ + # Correct for systemctl + curl -f -L -s -S https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl3.py -o /bin/systemctl && \ + chmod a+x /bin/systemctl && \ + \ + # Change user to pi and create /home/pi + echo "setting users" && \ + if id abc >/dev/null 2>&1; then groupmod -o -g 101 abc && usermod -o -u 101 abc; fi && \ + groupadd --non-unique -g 1000 "$USER" && \ + useradd --non-unique --uid 1000 --gid 1000 -m "$USER" && \ + \ + # Ensure permissions + echo "setting permissions" && \ + echo "$USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \ + mkdir -p /home/"$USER"/.config/matplotlib && \ + chown -R "$USER":"$USER" /home/"$USER" && \ + git config --global --add safe.directory '*' && \ + \ + # Download installer + curl -f -L -s -S "https://raw.githubusercontent.com/rdz-oss/BattyBirdNET-Pi/main/newinstaller.sh" -o /newinstaller.sh && \ + chmod 777 /newinstaller.sh && \ + \ + # Use installer to modify other scripts + ####################################### + # Define file + sed -i "1a /./newinstallermod.sh" /newinstaller.sh && \ + echo '#!/bin/bash' >> /newinstallermod.sh && \ + # Remove all instances of sudo from all other scripts + echo 'for file in $(grep -srl "sudo" $HOME/BirdNET-Pi/scripts); do sed -i "s|sudo ||" "$file"; done' >> /newinstallermod.sh && \ + echo 'for file in $(grep -srl "my_dir" $HOME/BirdNET-Pi/scripts); do sed -i "s|\$my_dir|/config|" "$file"; done' >> /newinstallermod.sh && \ + # Disable pulseaudio + echo 'for file in $(grep -srl "pulseaudio --start" $HOME/BirdNET-Pi/scripts); do sed -i "/pulseaudio --start/d" "$file"; done' >> /newinstallermod.sh && \ + # Set permission + chmod +x /newinstallermod.sh && \ + \ + # Modify installer + ################## + # Avoid rebooting at end of installation + sed -i "/reboot/d" /newinstaller.sh && \ + # Use apt-get as without user action + sed -i "s|apt |apt-get |g" /newinstaller.sh && \ + # Ensure chmod + sed -i "/git clone/a chown -R 1000:1000 $HOME" /newinstaller.sh && \ + # Remove all instances of sudo from the newinstaller + sed -i -e "s|== 0|== 7|g" -e "s|sudo -n true|true|g" -e "s|sudo -K|true|g" /newinstaller.sh && \ + \ + # Execute installer + /./newinstaller.sh && \ + \ + # Install dateparser + $PYTHON_VIRTUAL_ENV /usr/bin/pip3 install dateparser && \ + \ + # Adapt for lsio usage of /app + if [ -d /app ]; then rm -r /app; fi && \ + ln -s /home/"$USER" /app && \ + chown -R "$USER":"$USER" /home/"$USER" /app && \ + \ + # Give access to caddy for files owned by the user, to allow files modification + groupmod -o -g 1000 caddy && usermod -o -u 1000 caddy && \ + \ + # Cleanup + apt-get clean all && \ + rm -rf /var/lib/apt/lists/* + +################## +# 2 Modify Image # +################## + +# Set S6 wait time +ENV S6_CMD_WAIT_FOR_SERVICES=1 \ + S6_CMD_WAIT_FOR_SERVICES_MAXTIME=0 \ + S6_SERVICES_GRACETIME=0 + +################## +# 3 Install apps # +################## + +# Add rootfs +COPY rootfs/ / + +# Uses /bin for compatibility purposes +# hadolint ignore=DL4005 +RUN if [ ! -f /bin/sh ] && [ -f /usr/bin/sh ]; then ln -s /usr/bin/sh /bin/sh; fi && \ + if [ ! -f /bin/bash ] && [ -f /usr/bin/bash ]; then ln -s /usr/bin/bash /bin/bash; fi + +# Modules +ARG MODULES="00-local_mounts.sh 00-smb_mounts.sh" + +# Automatic modules download +ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_automodules.sh" "/ha_automodules.sh" +RUN chmod 744 /ha_automodules.sh && /ha_automodules.sh "$MODULES" && rm /ha_automodules.sh + +# Manual apps +ENV PACKAGES="alsa-utils libasound2-plugins" + +# Automatic apps & bashio +ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_autoapps.sh" "/ha_autoapps.sh" +RUN chmod 744 /ha_autoapps.sh && /ha_autoapps.sh "$PACKAGES" && rm /ha_autoapps.sh + +################ +# 4 Entrypoint # +################ + +# Add entrypoint +ENV S6_STAGE2_HOOK=/ha_entrypoint.sh +ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_entrypoint.sh" "/ha_entrypoint.sh" + +# Entrypoint modifications +ADD "https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.templates/ha_entrypoint_modif.sh" "/ha_entrypoint_modif.sh" +RUN chmod 777 /ha_entrypoint.sh /ha_entrypoint_modif.sh && /ha_entrypoint_modif.sh && rm /ha_entrypoint_modif.sh + +# Avoid config.yaml interference +WORKDIR /config +#ENTRYPOINT ["/lib/systemd/systemd"] +#ENTRYPOINT [ "/usr/bin/env" ] +#CMD [ "/ha_entrypoint.sh" ] +#SHELL ["/bin/bash", "-o", "pipefail", "-c"] + +# Allow a dockerfile independent from HA +EXPOSE 80 +RUN mkdir -p /data /config + +############ +# 5 Labels # +############ + +ARG BUILD_ARCH +ARG BUILD_DATE +ARG BUILD_DESCRIPTION +ARG BUILD_NAME +ARG BUILD_REF +ARG BUILD_REPOSITORY +ARG BUILD_VERSION +LABEL \ + io.hass.name="${BUILD_NAME}" \ + io.hass.description="${BUILD_DESCRIPTION}" \ + io.hass.arch="${BUILD_ARCH}" \ + io.hass.type="addon" \ + io.hass.version=${BUILD_VERSION} \ + maintainer="alexbelgium (https://github.com/alexbelgium)" \ + org.opencontainers.image.title="${BUILD_NAME}" \ + org.opencontainers.image.description="${BUILD_DESCRIPTION}" \ + org.opencontainers.image.vendor="Home Assistant Add-ons" \ + org.opencontainers.image.authors="alexbelgium (https://github.com/alexbelgium)" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.url="https://github.com/alexbelgium" \ + org.opencontainers.image.source="https://github.com/${BUILD_REPOSITORY}" \ + org.opencontainers.image.documentation="https://github.com/${BUILD_REPOSITORY}/blob/main/README.md" \ + org.opencontainers.image.created=${BUILD_DATE} \ + org.opencontainers.image.revision=${BUILD_REF} \ + org.opencontainers.image.version=${BUILD_VERSION} + +################# +# 6 Healthcheck # +################# + +ENV HEALTH_PORT="80" \ + HEALTH_URL="" +HEALTHCHECK \ + --interval=5s \ + --retries=5 \ + --start-period=30s \ + --timeout=25s \ + CMD curl --fail "http://127.0.0.1:${HEALTH_PORT}${HEALTH_URL}" &>/dev/null || exit 1 diff --git a/battybirdnet-pi/README.md b/battybirdnet-pi/README.md new file mode 100644 index 000000000..c17b10825 --- /dev/null +++ b/battybirdnet-pi/README.md @@ -0,0 +1,159 @@ +# Home assistant add-on: battybirdnet-pi + +[![Donate][donation-badge]](https://www.buymeacoffee.com/alexbelgium) +[![Donate][paypal-badge]](https://www.paypal.com/donate/?hosted_button_id=DZFULJZTP3UQA) + +![Version](https://img.shields.io/badge/dynamic/json?label=Version&query=%24.version&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbattybirdnet-pi%2Fconfig.json) +![Ingress](https://img.shields.io/badge/dynamic/json?label=Ingress&query=%24.ingress&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbattybirdnet-pi%2Fconfig.json) +![Arch](https://img.shields.io/badge/dynamic/json?color=success&label=Arch&query=%24.arch&url=https%3A%2F%2Fraw.githubusercontent.com%2Falexbelgium%2Fhassio-addons%2Fmaster%2Fbattybirdnet-pi%2Fconfig.json) + +[![Codacy Badge](https://app.codacy.com/project/badge/Grade/9c6cf10bdbba45ecb202d7f579b5be0e)](https://www.codacy.com/gh/alexbelgium/hassio-addons/dashboard?utm_source=github.com&utm_medium=referral&utm_content=alexbelgium/hassio-addons&utm_campaign=Badge_Grade) +[![GitHub Super-Linter](https://img.shields.io/github/actions/workflow/status/alexbelgium/hassio-addons/weekly-supelinter.yaml?label=Lint%20code%20base)](https://github.com/alexbelgium/hassio-addons/actions/workflows/weekly-supelinter.yaml) +[![Builder](https://img.shields.io/github/actions/workflow/status/alexbelgium/hassio-addons/onpush_builder.yaml?label=Builder)](https://github.com/alexbelgium/hassio-addons/actions/workflows/onpush_builder.yaml) + +[donation-badge]: https://img.shields.io/badge/Buy%20me%20a%20coffee%20(no%20paypal)-%23d32f2f?logo=buy-me-a-coffee&style=flat&logoColor=white +[paypal-badge]: https://img.shields.io/badge/Buy%20me%20a%20coffee%20with%20Paypal-0070BA?logo=paypal&style=flat&logoColor=white + +_Thanks to everyone having starred my repo! To star it click on the image below, then it will be on top right. Thanks!_ + +[![Stargazers repo roster for @alexbelgium/hassio-addons](https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/.github/stars2.svg)](https://github.com/alexbelgium/hassio-addons/stargazers) + +![downloads evolution](https://raw.githubusercontent.com/alexbelgium/hassio-addons/master/BirdNET-Pi/stats.png) + +## About + +--- + +[battybirdnet-pi](https://github.com/Nachtzuster/BirdNET-Pi) is an AI solution for continuous avian monitoring and identification originally developed by @mcguirepr89 on github (https://github.com/mcguirepr89/BirdNET-Pi), whose work is continued by @Nachtzuster and other developers on an active fork (https://github.com/Nachtzuster/BirdNET-Pi) + +Features of the addon : +- Robust base image provided by [linuxserver](https://github.com/linuxserver/docker-baseimage-debian) +- Working docker system thanks to https://github.com/gdraheim/docker-systemctl-replacement +- Uses HA pulseaudio server +- Uses HA tmpfs to store temporary files in ram and avoid disk wear +- Exposes all config files to /config to allow remanence and easy access +- Allows to modify the location of the stored bird songs (preferably to an external hdd) +- Supports ingress, to allow secure remote access without exposing ports + +## Configuration + +--- + +Install, then start the addon a first time +Webui can be found by two ways : +- Ingress from HA (no password but some functions don't work) +- Direct access with , port being the one defined in the birdnet.conf. The username when asked for a password is `birdnet`, the password is the one that you can define in the birdnet.con (blank by default). This is different than the password from the addon options, which is the one that must be used to access the web terminal + +Web terminal access : uesrname `pi`, password : as defined in the addon options + +You'll need a microphone : either use one connected to HA or the audio stream of a rstp camera. + +Options can be configured through three ways : + +- Addon options + +```yaml +BIRDSONGS_FOLDER: folder to store birdsongs file # It should be an ssd if you want to avoid clogging of analysis +MQTT_DISABLED : if true, disables automatic mqtt publishing. Only valid if there is a local broker already available +LIVESTREAM_BOOT_ENABLED: start livestream from boot, or from settings +SPECIES_CONVERTER_ENABLED: true/false. if enabled, will create a new setting in the birdnet options where you can specify birds to convert. It will convert on the fly the specie when detected +PROCESSED_FOLDER_ENABLED : if enabled, you need to set in the birdnet.conf (or the setting of birdnet) the number of last wav files that will be saved in the temporary folder "/tmp/Processed" within the tmpfs (so no disk wear) in case you want to retrieve them. This amount can be adapted from the addon options +TZ: Etc/UTC specify a timezone to use, see https://en.wikipedia.org/wiki/List_of_tz_database_time_zones#List +pi_password: set the user password to access the web terminal +localdisks: sda1 #put the hardware name of your drive to mount separated by commas, or its label. ex. sda1, sdb1, MYNAS... +networkdisks: "//SERVER/SHARE" # optional, list of smb servers to mount, separated by commas +cifsusername: "username" # optional, smb username, same for all smb shares +cifspassword: "password" # optional, smb password +cifsdomain: "domain" # optional, allow setting the domain for the smb share +``` + +- Config.yaml +Additional variables can be configured using the config.yaml file found in /config/db21ed7f_battybirdnet-pi/config.yaml using the Filebrowser addon + +- Config_env.yaml +Additional environment variables can be configured there + +## Installation + +--- + +The installation of this add-on is pretty straightforward and not different in comparison to installing any other add-on. + +1. Add my add-ons repository to your home assistant instance (in supervisor addons store at top right, or click button below if you have configured my HA) + [![Open your Home Assistant instance and show the add add-on repository dialog with a specific repository URL pre-filled.](https://my.home-assistant.io/badges/supervisor_add_addon_repository.svg)](https://my.home-assistant.io/redirect/supervisor_add_addon_repository/?repository_url=https%3A%2F%2Fgithub.com%2Falexbelgium%2Fhassio-addons) +1. Install this add-on. +1. Click the `Save` button to store your configuration. +1. Set the add-on options to your preferences +1. Start the add-on. +1. Check the logs of the add-on to see if everything went well. +1. Open the webUI and adapt the software options + +## Integration with HA + +--- +### Apprise + +You can use apprise to send notifications with mqtt, then act on those using HomeAssistant +Further informations : https://wander.ingstar.com/projects/birdnetpi.html + +### Automatic mqtt + +If mqtt is installed, the addon automatically updates the birdnet topic with each detected species + +## Using ssl + +--- + +Option 1 : Install let's encrypt addon, generate certificates. They are by default certfile.pem and keyfile.pem stored in /ssl. Just enable ssl from the addon option and it will work. + +Option 2 : enable port 80, define your battybirdnet-pi URL as https. Certificate will be automatically generated by caddy + +## Improve detections + +--- + +### Gain for card + +Using alsamixer in the Terminal tab, make sure that the sound level is high enough but not too high (not in the red part) +https://github.com/mcguirepr89/BirdNET-Pi/wiki/Adjusting-your-sound-card + +### Ferrite + +Adding ferrite beads lead in my case to worst noise + +### Aux to usb adapters + +Based on my test, only adapters using KT0210 (such as Ugreen's) work. I couldn't get adapters based on ALC to be detected. + +### Microphone comparison + +Recommended microphones ([full discussion here](https://github.com/mcguirepr89/BirdNET-Pi/discussions/39)): +- Clippy EM272 (https://www.veldshop.nl/en/smart-clippy-em272z1-mono-omni-microphone.html) + ugreen aux to usb connector : best sensitivity with lavalier tech +- Boya By-LM40 : best quality/price +- Hyperx Quadcast : best sensitivity with cardioid tech + +Conclusion, using mic from Dahua is good enough, EM272 is optimal, but Boya by-lm40 is a very good compromise as birndet model analysis the 0-15000Hz range + +![image](https://github.com/alexbelgium/hassio-addons/assets/44178713/df992b79-7171-4f73-b0c0-55eb4256cd5b) + +### Denoise ([Full discussion here](https://github.com/mcguirepr89/BirdNET-Pi/discussions/597)) + +Denoise is frowned upon by serious researchers. However it does seem to significantly increase quality of detection ! Here is how to do it in HA : +- Using Portainer addon, go in the hassio_audio container, and modify the file /etc/pulse/system.pa to add the line `load-module module-echo-cancel` +- Go in the Terminal addon, and type `ha audio restart` +- Select the echo cancelled device as input device in the addon options + +### High pass + +Should be avoided as the model uses the whole 0-15khz range + +## Common issues + +Not yet available + +## Support + +Create an issue on github + +--- + diff --git a/battybirdnet-pi/apparmor.txt b/battybirdnet-pi/apparmor.txt new file mode 100644 index 000000000..660a9003f --- /dev/null +++ b/battybirdnet-pi/apparmor.txt @@ -0,0 +1,66 @@ +#include + +profile battybirdnet-pi_addon flags=(attach_disconnected,mediate_deleted) { + #include + + capability, + file, + signal, + mount, + umount, + remount, + network udp, + network tcp, + network dgram, + network stream, + network inet, + network inet6, + network netlink raw, + network unix dgram, + + capability setgid, + capability setuid, + capability sys_admin, + capability dac_read_search, + # capability dac_override, + # capability sys_rawio, + +# S6-Overlay + /init ix, + /run/{s6,s6-rc*,service}/** ix, + /package/** ix, + /command/** ix, + /run/{,**} rwk, + /dev/tty rw, + /bin/** ix, + /usr/bin/** ix, + /usr/lib/bashio/** ix, + /etc/s6/** rix, + /run/s6/** rix, + /etc/services.d/** rwix, + /etc/cont-init.d/** rwix, + /etc/cont-finish.d/** rwix, + /init rix, + /var/run/** mrwkl, + /var/run/ mrwkl, + /dev/i2c-1 mrwkl, + # Files required + /dev/fuse mrwkl, + /dev/sda1 mrwkl, + /dev/sdb1 mrwkl, + /dev/nvme0 mrwkl, + /dev/nvme1 mrwkl, + /dev/mmcblk0p1 mrwkl, + /dev/* mrwkl, + /tmp/** mrkwl, + + # Data access + /data/** rw, + + # suppress ptrace denials when using 'docker ps' or using 'ps' inside a container + ptrace (trace,read) peer=docker-default, + + # docker daemon confinement requires explict allow rule for signal + signal (receive) set=(kill,term) peer=/usr/bin/docker, + +} diff --git a/battybirdnet-pi/build.yaml b/battybirdnet-pi/build.yaml new file mode 100644 index 000000000..72df56b73 --- /dev/null +++ b/battybirdnet-pi/build.yaml @@ -0,0 +1,6 @@ +--- +build_from: + aarch64: ghcr.io/linuxserver/baseimage-debian:arm64v8-bookworm + amd64: ghcr.io/linuxserver/baseimage-debian:amd64-bookworm +codenotary: + signer: alexandrep.github@gmail.com diff --git a/battybirdnet-pi/config.json b/battybirdnet-pi/config.json new file mode 100644 index 000000000..04d889eaf --- /dev/null +++ b/battybirdnet-pi/config.json @@ -0,0 +1,126 @@ +{ + "arch": [ + "aarch64", + "amd64" + ], + "audio": true, + "backup": "cold", + "codenotary": "alexandrep.github@gmail.com", + "description": "A realtime acoustic bat & bird classification system for the Raspberry Pi 4/5 built on BattyBirdNET-Analyzer", + "devices": [ + "/dev/dri", + "/dev/dri/card0", + "/dev/dri/card1", + "/dev/dri/renderD128", + "/dev/vchiq", + "/dev/video10", + "/dev/video11", + "/dev/video12", + "/dev/video13", + "/dev/video14", + "/dev/video15", + "/dev/video16", + "/dev/ttyUSB0", + "/dev/sda", + "/dev/sdb", + "/dev/sdc", + "/dev/sdd", + "/dev/sde", + "/dev/sdf", + "/dev/sdg", + "/dev/nvme", + "/dev/nvme0n1p1", + "/dev/nvme0n1p2", + "/dev/mmcblk", + "/dev/fuse", + "/dev/sda1", + "/dev/sdb1", + "/dev/sdc1", + "/dev/sdd1", + "/dev/sde1", + "/dev/sdf1", + "/dev/sdg1", + "/dev/sda2", + "/dev/sdb2", + "/dev/sdc2", + "/dev/sdd2", + "/dev/sde2", + "/dev/sdf2", + "/dev/sdg2", + "/dev/sda3", + "/dev/sdb3", + "/dev/sda4", + "/dev/sdb4", + "/dev/sda5", + "/dev/sda6", + "/dev/sda7", + "/dev/sda8", + "/dev/nvme0", + "/dev/nvme1", + "/dev/nvme2" + ], + "image": "ghcr.io/alexbelgium/battybirdnet-pi-{arch}", + "ingress": true, + "ingress_stream": true, + "init": false, + "map": [ + "addon_config:rw", + "media:rw", + "share:rw", + "ssl" + ], + "name": "BattyBirdNET-Pi", + "options": { + "BIRDSONGS_FOLDER": "/config/BirdSongs", + "LIVESTREAM_BOOT_ENABLED": false, + "TZ": "Europe/Paris", + "certfile": "fullchain.pem", + "keyfile": "privkey.pem", + "ssl": false + }, + "panel_admin": false, + "panel_icon": "mdi:bird", + "ports": { + "80/tcp": null, + "8081/tcp": 8081 + }, + "ports_description": { + "80/tcp": "Optional : set to 80 to use caddy's automatic ssl", + "8081/tcp": "Web ui" + }, + "privileged": [ + "SYS_ADMIN", + "DAC_READ_SEARCH" + ], + "schema": { + "BIRDSONGS_FOLDER": "str?", + "LIVESTREAM_BOOT_ENABLED": "bool", + "MQTT_DISABLED": "bool?", + "MQTT_HOST_manual": "str?", + "MQTT_PASSWORD_manual": "password?", + "MQTT_PORT_manual": "int?", + "MQTT_USER_manual": "str?", + "PROCESSED_FOLDER_ENABLED": "bool?", + "SPECIES_CONVERTER_ENABLED": "bool?", + "TZ": "str?", + "certfile": "str", + "cifsdomain": "str?", + "cifspassword": "str?", + "cifsusername": "str?", + "keyfile": "str", + "localdisks": "str?", + "networkdisks": "str?", + "pi_password": "password", + "ssl": "bool" + }, + "services": [ + "mqtt:want" + ], + "slug": "battybirdnet-pi", + "tmpfs": true, + "udev": true, + "url": "https://github.com/alexbelgium/hassio-addons/tree/master/battybirdnet-pi", + "usb": true, + "version": "0.1", + "video": true +} diff --git a/battybirdnet-pi/icon.png b/battybirdnet-pi/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..39f0e4d5df0bcfed958456afcb24ec45231c847c GIT binary patch literal 216488 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

(%0T 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 `ffg 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ⅆein5s!yNF4&M01ZnEEG`{ii3l(BX2W*%nO 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 literal 0 HcmV?d00001 diff --git a/battybirdnet-pi/logo.png b/battybirdnet-pi/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..39f0e4d5df0bcfed958456afcb24ec45231c847c GIT binary patch literal 216488 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

(%0T zC4+0G*MQ(=Ri+0EQ1_imo=ZcFdl8NC*Q-qi#hyKkY{+wq) zwn6@>+RQw}rRB`CgS%N(xe6`lDPIk5_)D;2ROD5ealXfH2gTq!imxYx2WraMG z;Jy;CwqiQAJf(8xL0&P0@m}*`*DLI$qM`9$GZfF<4o2|ZXvvpIOJBu*MLZL(-?A8n z4XHV~Reild$!U9EL6~ddLOO#Qt+F&zPrGjlw_rq6xatwn;v&JxKpvuoS}$a6I%PAV z?B(uXZuZZ|c7xeo{S$s!!mSL-%)}wJ2KscP?0IzhJp6A$kwMEvD9kJ~64THjw z-a#zxDO!c>ciGv~Z{nhM&9C3;@u43A8=Pf~!3(le8P!n&!dlMv$!{-tD#+!k<)#w5 z5U!)rGA_L7R3S*sjrHCxPmevskaE|E;Pc#k>K;}96}w+i?NowrQ>=f8`hs~IOgS5@ zr!>bw_J_{&QxRgj|DTs)0rDbhvEd$JQM_9SacSQ33CF=_Ey;nOeW?;&g&gGBoa@2Q zyAts%1h`Qc{HP7YqRz8P;t}&24fg}d1_lOd1U#$|izfUGC&j{rtNppjC8tEo#6snh z_oc)EIse>rKz1&AFJiERMQjzSJ|EFqf<8>B4-^hKi$6Soc)@D{^Kx%&a8N6apiFgl z=fjDQi}*L-TdgqmQBPoB9z^CFbzgOLY>Mb{nS5ZR(r z;@WQ^2?&IkBV2ATNVCAhH7^HeAF$j0j{(8=^jM`@(F97U3CQ6-NRG=k`q@v7e`#VK zb|#`KX*!e+1i=F< z-k>&b=z7hhl8XP}Y3R_4@Wl+#QITj>6LqH8h}r@o+7;(PO}xqm1WUS;4m#zGI1it( zrMYxHr;e8TEjuEz_IFG*#%ZOj`XUE8w_A}fXLd$Gu=sb;I&IT}Q7uwYQGp zr-B-@3v=+nmlM0EB{iRyS|NVE!^d5T$o>0{LoKj_fXcUbb#;kAgK!`7y?~7$wa!DQ z6}aVO@n4kU@$|st@vq5%DwN=kzJW$o2^e)Q=o^Ql?Ge27Vf8PJUN< zCgDx{sDGCo;~68<5Q$1haHG)(D--G5WiUt6sdrEz*;SS4Sc zKDp7W`LGIW>_kJ8TS1=Nfk5}jbcXLT(Me*P5YDRUET5yzmZ!eSaM-_g+Cp8d{ZuYc zC$5(9GESK=*m9h@gCd2N3GdJK^#`LAWJFpVadlUg*VaBDSiYATI3Ab|k`{l@m6^?Q zWGe6BPIBT7VM#Myx!Y%2i)tr6Vxl_;p;zyvT=56TyQ2pZA34T-Bz-r9YB`;_%^nwY9ZzYzhqfI5q`rZd*f{qpcQ} zHha6fI3bnVM~!tD1D0{~hwmI~1+NGgY#^%^4u4#{4vaRV>WYW@#t-J-Kn55Nw2+j% z4^Rp&TM1&Oz5o7T!eJl{4GjwJ;(|nQY{01jf{XnOAYySx5bQJ@0xptlxKbtA0$T7R)FfO8FF6Ipwf(-?J7>~sut*_S)DCn)7`BpBWt*}8rfzbt2yJ<%d&nx5LW~>HIVEuS!5FY?hU2=r zy0}hE2n&E1LJ^(Yx7jTk{dRs(hldN{QbaBQ9lm5__A{ zb1iv36!ID$Nn(n3X+iVw+$^1M#A8>@KEb&~sXHRImo7+;LbIO?>B-LQ)C;`#S4|%4 zh@PQ&!jIjj!YP7#GC`8*)3DXkc@eI&i?!JY-fTr^Z zdRND%cYsvVizNCNJNqYolQV$$h(NEH$&+2yg&s!g()-}W7w+v}@s=kIS;_**R(O;T zZl;+}nXn&V&ub)}*A`V)>V{^wEBQ707x01DLDJ2gEo#rjFKy!yaqNc@N6T9pN!ed` z{!>Qcru`m$M~ca-i{|Ax$g?^P+Wzh?t?n1!?;i&k zuy!9ARt!4WYZ1rU#9e!-4aPFuL6hbTs|I@I<*y=$a`RVm zwZpL^fS-78%&0w~K{SGx-5>InR&dUl%uH6?eGStp6GEaI{tM<5TtNWN($}~%cn4-( z7ualYwqKxR_k{lQ0`HL_z^4mwp9CE+I6V9L7Z>k=F$NS3^5)^-169~v%4Ja7dt6K) zPr?;8$*`Y4@8Ku4_1o%N{&@B;yWd7)zh*%U=la>$*qCG8YTWe0?>fLlfMv#M(F0dI ztN{WETpEln%J)gQh^NdM+kq_2vg$0+A;n{l_(X$dHmi8+)!nG~Ug}gU?EW%Q-8ne| z?dSLLE;4n50VDOiB|juMiMLfcC`rp3+^SF}2}>PpuK!gJz+2j_rAltISzByXC#6-4 zTR|;diQN@ci44vm$3PmB|D#V$N#V3gLM>5udzXq0?xtTL%82@{rujd*snV4e#>a7koPyZpmxn<+D}rp1Yt!aQ(VL1KRciRZ%n0HIj$&TZ)MXo` zm9W_OGqorQ;hj-A)4CAPu*K@NF={b?6>+wX=F$(=BXUM3#z+*;GLJ9}zVOKF%a`UK zzBTZZ^>R&%IL}0=yG94}TXO{WUyXx_0}k{tpL&lMpIZd-H9)4EJ)(I8iSU{4wD4T@ zr$W5KpAowgyceyxP0P>l^U=+9x?%4b{9#yFTD~^uhw~EK)1V(meYl;q)hAKMlK8b6 z?gEgFufD*01 z>K@H9&obtaVBR2EEgd=oN_r55)1ANFM7$_BYV+ntDT~{R1hHuF+Z~ZhsW$fyEDG%| zIh8ZHGde$qd(8Eu&CxipLm_NjPby+6#a z)<~x1=2|Awsd@CmXR=ky1)0`eL2?JJ(ayN?S7u)Uqc?sbTU94XyHdIKW!@eC+ar%d zFEIK+R20m>GHwJcp(d=4qh5zRp4L(E;CK8>y_jV-aLMx22~@8CM2053*o&52rndj5 z7gMazdtz^Acg)@^d}A{P{{UzL04w&i6D_kHGN*qp@bkxvq%?l%dyO1nh!$?lj=J_wI-F|x(%1^wMi z6)Z)#nFqZ7;HbR}Qk1u$zwP6xk6r=}3av?UYaemFZJ;!P)OK900Hg=uoGdH27EoAQ zrua_gy9ohMNnFFlEWwLcm`y-X6kPD&|I4**$-s3E0D@WvyBWv{?Z+z0%Xf&3MD1(5 z>~Layz9OWk6rN&A*Srg4+Yo$#Y&flDTpIk?yN7amV~d_RDYMD>I-o%&N|tISU=7jj zuG)gB2Igk-ZpwRP(rVoqKmT4gCDEHY#YSI1douYK`gO8VVr4$t@o4rgO@dLHziB@h zxF7o_i;6MGwssC*cZJhTZNtQQhPLgQMjK$kK%U!I(sn%iX$z1nF3!joji+wSn!<$O z;;7IhqM+qyiE3;=hd;hyAu5~}Tjs)#z4NM7#{GH1%BS{6*GSZ>OX%O%u`FbuXBe3d zBTn)twU*BXB?S(7^Q1_2zN+JSky(AQr?=a_-%+5=AxoVj?2CWp!&b{mns1hE7g|#4 zCKg}QWQ*v8w#(fAg{LRgG;oO@X+y5GaD$eky5v=Bmeej4!t6{gxfBXSkIl5_0!NK~o z(V)j@wbDtM)`MQc@fPt0{a&O=q(uBZgQ@i4`SKZKglj@ozd=d3X<3^>_x(knLN< zM9lqn)D%R;Nymcm|FPdf&-UL?_kZBkDb;?Xv^EyIl}XO-&|I{Oq_Qxf3^U45UX#-A zB{cPam8MXx!S89bl7Gk4bkWcil`ML4g8VHzcVp4Cj4y_C;BhvOE-iH*-g_p^^D4Ho z%;{3RBcB9-!{^d1Y&JYPE0GlB#j0E;BxLJelra3>VOJfPwxHafNsf;Z#^WTuQ6kr| z1ee`b&>xUD0CW~8&}gvhnU#Andwa^U>*X$^4WVWe9$TJ-vQ@!Dy4NP?Jzx7!r-Yj89<5a&oxQFh)*wCE$xGZSoDm9Z#V?~@SYo4fsE~X7T#1A`QjYXWc4ih~ znydv~rTPxjF!SLz>96nGUkDgd8BbpgqZ?c120L zO=NLk-wfBlp5ed0o}Udc6Of>uvM$3Atzif3#E~|hG%gv8Ec=7f`mc;a=z<|JQfkXT zgvfqevqz9Xa_(}XQOygs50_hfbbC+Hm7U;3S4iXrvfR2CL#Cq3Umv>5I^r= z4_>t5K2ZPQ!qIV&0JQ5k$vH5+AU}huEL^QTSaV7=Nf_a9S+^z62*1N`gtv5;HcGF zYmS9S9M?hy8V6kL$zH;iB>XZcP1N@>mX^@31cw_NHQ{Fv+IEiXJ$qgk;=XDXMazZo zEb&3^=2{xF+G0s7c<&G<`6X;zIz9y{C`k&2IOYERYExMAHd=zbyat%I96#`hg{&$l zD6ICGf=!jn!AFH@3Tg8>Xo=2F#y6Q*Bl-IcRGr=)mjj*d@@Rd8qeM^SEGtBxR+ ztU^K7j;C9KT)9=7yzcdl@H6@5dkkDRKBA(Zo80ivPXDX6LD~*=FeD?d;HCd`SWwp&auDyRtyv`F(-2bET?UsfsjGWGUw{PFZ<%7Vt!17Ik zOLE;6Ci9pnco@8n$F#{>6H2L8W!W_fx0X^Y;*%hq4$+FOOCAzAKLX?{1;*+Zn5PP$uy5!qpH2Kcgq zu}G{c7yEDJw;B~R=*xw#o$Bp|5;qlMPc}-Bc~Zlh)IygAp+yQO1fgGBeP5vR^Nwvu$sU=mQngnleEHNyA^x znQpWmp=hW#UCv9wC!U`D5pQb_Z;EjIYnkx)ik42+;?@*PyQ21y?2*soP$R(z%wy;U zZ}`oVq*kVNM-Wl!xr&M9t7;nk6s$cR4`mZ;akey`&&0gxcrNYUST|@Ib#ds(_wO+kOfJe0RE=}+w`jqb5=F!1$9*$Vj#o+GL+UX8GlK)rntGp|o+$kF zBaBGf2_wJWdKC&4Mrt=>f6qOj^L&L$RR)hQtWDleO4-C=erifUfvEBQ*yGNU zPSKlXG}AH;Z%FUB59I}JDj@mCpU~?aM>?5zH^=<$aW95MP~e!ePO9MG1DrSTYI=3s zH|5bxAhwP(F&LbX=6w7O&w{K_EW^fM)Q zKB_KX{8X~1KZ7q><2c^w`ggftd`emqX~f$lRZPa_DpEQg@wscpMoOVSR?=CQPL-9n z=2=E&HJ+#VQ=^+z=f~+>c>4B?bE9Y?Rw)I-9`Y*}*@o5Ugi~-U6D@)#@hBr1}PB`lVJx8ZW|6x0Zw6?R6cxGIq5Do{YqZw65P;O?t(_l26`sv7XwcxwH&&=l$0ko#CM^QNA!G3o0?A2;@}6%S z)47F-&Mv$Eku^;G{G-rVN}nS!Z$^uBkuugm`GD6%@BKLv3#MoqYU=CzdM`-H!pVzD zLO9ve^R@+7c!ubhk=}&LvIWfV85XM8jInpSOVX7QE^I%}z(0yhws9w6xr%KzXY?Z0 zqQIB=tQ6W_vdP~bL#S-87`98*Ifhcx*R~MdXL006Za*|6>i10&J+EY2qO>`Ii~)s| zS|DlYNSAW0Kh-1D-47_i1Aki2OJj(z&db(V2_zaJB8wk`NRUu>Vo zOE=y!rlZ$3CmQTx>kcvw&P1B!Mp!ZxvbhosmB3 zqc4sgQ!+gq2t;UTBI$KZjsF_?y{-LJ7IQ z(2cz=KO~s65wAtq^X29;=eK-uGn0If^t7eu!oOG+S{FL3 z78SXL@N)o3_LmI`bp^SJ1Rg<0AguGC67!Pf0I2iL)X#<+(YSUrwaNdwp*}Pp$$=G@ zGl_@x8eG_bv*88_&~@P2H7p5YWSnVei#+PV2ZhTb0*%EUY^pej6b2O?swa?_iX)lf zeuom!v{@L8;Hd@m*B@^rj5vUE0BsEX>o%va;9KyU;DA(6g82*~V2 z+_!9X-{-6g@I$UR!+ZuC6a0Cebp(yI+x_n#qciF==tHtb+`YZ$tTT{T3B&fC=~)n- zRLI)V;xcB3b;5p>HaPfTIrHoNG>=oZj6Lv*I}vEk-39$oGixHWt)4*gni{!sNg#e?qx<+i78AR0VSuNqdnj8Q-(* zS`D-1d31eTZ8zP!EKMfqLJgv7H&MDl-*H)A657c7hrnFb>Y<`pj3~O=wie_{cjFRd z(AnwAcHIBkSPMr>FH?33i8a(|V5V3mapl}F;0=WuF>JHqA|KyLakg>+kmL$ZC3+A(UgA(jKccVF~v9HIcO{H!64?iFwfx>kvc%Om4OGQfyjn=(R zErgx;SVCjf@u&fiIGCu_oZ={3-@;n+0ICAXg69&$LI##tTp{=~ zw%xir$yq8>F_qFyO!>H>2HA?zADBv7cX#w0XNxiyGH1C|zFV@8jm>4~4`bPq!5So3 z*I|c4BWE-Qtc1QC!c3kVh-G9;5-=coMU?$AJSLplKJUj%)C-iR8PQoyN`6S1I1zCv zp`dI5v9i58JuUdtXxjC-zl@0eG?;J1VlsHwWcjizyyCAZUpyjkDwlUP%b!|)q~lq{ z?;JXP5izTHmW@nVypG%XLPPSZN0l9F)o)))zTS0k z&`^-KJyq()ew&=KIaIhK;P1GTKM7gIu61e#1Lig~DM6}RwX$0zLugg7^!Y24NW>3f zX<6B~u&VPSKMFY~awykCw1GIM-at&}g=yI+Z{C)QW=(_fcsAggT3aymQzH=jRh--X z3e0goJ(=iT@3BTyQ5_tnf{RjC_7#xPwT_dz{cU zJpzMmq7t8s-Khukr>3Vfkqkj}z)EMf+{D>wr`%fbt1&pffr%L#8QCEB0!|em$l_;I z+yI}-*N4B?5RvAitKtZN73Tnx%b(-($-0WA+87se2x)4&p&$uYJl#kvhr}X)A>iZ2 z6&k>X2Cb=p-7fA04sQnRnN?g^uuDut7<>!vY6*)%zfjqi> zahAL_I4It*&vzH0QpQi201F3@hky#he2t@*-~(~m&@$F7G|=f8bdRZ#7X9@H)|}Os zP=_~Wvr~X<+jrl2hYFM)3JyUS>V0_Z?QLGmPJ5Z{DXtoOEXejtE5hm?h<^FQ_tqx& z5Jw;~R6FOZ^WfsPQOk{moMA5|)nqCK`d0Gr#eu7(-v#?x)`*$wx{0mPxstjJ`r;JSqh?$z zZSXvZcD~HIn~oFMZQkHHYk^r1RY&M+qD=lv8WMd^GAMK$k)+U-5l)x=2$!#WK7btCJ z9Yh6qdP@i9Yn^phT|n|WQHs@>3oPUV)Ti%@CXITd>qX8-i7~ zj_nI;R6)*m8`P{l*1MfR&T(=edqmG)%%@a8g{%O|>}jsx%T4)MPU>B9gDa6&b==e< z2*NXB1tOIP>|`iLZ{3(u)hjyb5A%FfK4>`b*MsyE$oI0H3I)XH$-~Xj-RXf!Nrhb< zN9*>3G+1$B9evAPwEer%shrql$MfL{{P%<82c)u))VBsuWXp&vo^1H@>ZP*+9(^zJ zUR^#`Abd$WjQO)*?zTenT3oF*`aajRxNVNxyr;0pQMo4JuDbnYAyILY5G6MXb*MasL6I3{WMw-i9y| zn&pW`e}!Jn`G3IOOfRO3mk68-}UaF;eP|Ufz!Q#`2FzP?F@uKA5M~Pfv?QTH&M?F zZ|{`zmtD1Pp5|B87n$IX^ZUa3_bB)H5C25b>)3j@{e(8g!(bi)LM&puqXP`Uvxs^4 zES^H-S3wU>Xb&(wuG09jFusVV9cW#x|k^rOGUBJx=h;QmbX2zLP592qCK5RclEs-5E>hqOJVD(9}~r+5nd(_ zQ+#(>N3Yluo!u~3x>L|@fNiTtb=#LIrzThx|-DT{iFFEA}y1IjiVodRR2PkcrYzo><||Nb`^3zYiRoN!$?! zM3W@-1R03R{CL3uRqm*%+qbo~eaFjIf`=7~T_EUj$RMH(fVne3tm9Yd;3KP|!Z`Vm57_d@TLhBWEkC zB5=j|F`buQty`hK)dS5)3}v2=mR?x_Z~uZ-h9@y!6iS_lcEQb8j|Vw&&)gi>I+IT1 zAQco!53)7*H3$aWw-HP_;vqeV5AiLv2$f$ zBq^^?nm^lJv*lI)m0`kB`%;uIW>5N_b_<>JqNnVsFJ=O3_r}I4PpNd2|HluMfr-#+ z0yYb~gvBNq_Sil_Fnbos=F#Q$B;Q2UDd%Z+N}nej2%u@j*VKg7i7BAnelg36o%3q+ z)=ed17Q7V^&cv<~t?ILJl3)s~TQEATwFRs(VDehICP}=!dG5Bv95A`9`I_g%nk9 zb&6-rXmEm&uKATO%=fnCq}i#n4HDkK(&z!RTdfluBRWh0yCe3Hf;6(TQwzI>Z0*tJ zW-p*OFeKqZN6IGGKKu%Re>rxoF}w0_?+C{t!*>-@Q*q%T&}zt=e5AqM;oBbc@o4o! z&Xj!MA+CqC(LTrh>ae<9PHGz9(Z#J$#VaQx=5@Kj(+edOcTGnZ9$`Gj7P+o_4FG{QL`L zt1dbn)RU3Ad4~n42aNCWsM2_Dt~HQ%fcgZeFx>f8CkY;Ez@tef<3lcZTi$0Rdkg^b zhp@(;I&MD)G8JFlU1`k6^6E;NYix?^{^)P%`_XH`Az$|-WEqZ%zc+^_j^5K2tV z*hn#B=^0eCW*U%rB_78?%-zl`fG8`dLNOCg7GFulX&Kwxyu0PPH)@gIqeE{RKQrF< z9KR4j6Krq0N~=yEXOM=6X&tJLB2WAG8q!;4H-hVwFwNnMs^)L{80u+`vz8Ex9Ha@U z1?eG^`Q^{c-U%+*7@~=CANRYLC_m-4zSNHW6vTIMe=-e^R6rx(TUQI?pFKP?gbuy^ zR`$3m`5i$vn8V9h)hgk|>vV@=Gm^*SudAvvRSOGd{rWw0iLy*chH{q@&vcU_n@z1# zoP`o@)$*GKJp*pi<_%3v7EqJZBzE|+`QT;hz2UZF62<-2xQfPIGd?Bf$%7f@=KW*m zibn7C=A)4($6ucu&*j2j@+9OMyM7dH<9;o`@vqW9yH>MMZ=T!ZfHCBAWeG2ajR-A` zLO)6o4@EJ>;{EMidbfMevPN?oy?;wYA$CYmE5D0^zSL>y_&VB1yT(gLvYm#UnRpdB1%ceej^0>?$=Cxys1odK)A;@ ziwY>%gCJ#FL&SyM=#%`n`7>h3Z-jBE&RQ=7ioK0_z!SYmaw+QOYck@2N1>|df7%yQ zC+@V#+Xn8OrPeIoCroiD_gq*wcOtytiF}b|6<&$5%HR&uqRiDT?z@_;bJtVrQaFl< z)EbXm<}vcRfUb?ICEg-N#8xb+dzLiwM&2hmv+4U5m+bF_h_5^cG{l3vkuaGs1l3y7 z-Rl>udVwzLJ(-%XY_{JfkuA+RlCGZ(gM)14s{(h=3b;-%dxMKCEzlH7q62uX3mhY0 zF9G=&ml)1&)4c24jO$zmwGnO@0gwI4%F4*ghfRx&OW?niQ&8Bo@H!0dTdcu_OgQp; zjTHZ%7T__AK+yChR4%cTXHfwPSX?PH@V}l*6Yvb?RzY|c)YyBzguHR94WDsNdA~oq z@q*IZO+XYkCZpPQb#&%N^f8u!cua|1mkFqfYT)KcsxMeuU;eD6Dhx~%xx{|xiZI=Gnh{GGn7YrEty2*E zE);2|ZDeK15g7G!e(w@rY6eqd68t@ZU8!@QTxYQxKiY}VkrI2TdmLs#JIiNPo>E`+ z#4NLrL*uKzBfZx6>*~M0ifDBb(fkuVH6uvJef7P$wP9Pf9j#_wS}{x-4exlJVR z>B{;ZT&)gjeh8;}M~8UkZNYPKf#V%B(6D7})Vx#vuyKnbGlL{`^X#;G;(60{;a_M$ zE#K=4I=y%L_g>S%qVAJJ;cdt4TN*Qze(_?Yv6b$a+DSB8to9w@4{3RcryLEtO>E z5GOm7M2_8^4(=?)#3V%X`R2>&s=VsA5-ecx#nQ0^tY{A9_+#6VdC_+rxYCWd-1`a1 zN#`A&Fwd6kUssOn{Mgni?iy45=`=*vxk5_d9h=RHNN*k~8z0^*iI^Jd8%sRIJlQ-gjllLs45UyPv)w$z^VbxV`vU*w0<={@63&DsUBP|wtDt|S#7vwHb;n!rd z%0;b7XN$GnX6?OqGa@=@dgOr}jOMtQc0g7c$mf40r&@K!gfZtBB$k zcl_S)E2CAIO3?1CQz~okJ6#J0h)1t7F&?(v^O+`EE1q?=u&@{ft%XD~W_iF#3U|4J z)dik1*jS+O3L@V-Pw^;^6VLW(;MZ``MzHqc(hZpze}S7Bijv;-K-oKP57{H24RyZ{ z+BkU3;hu(&@NJLK`rn?iwICAl-<(|ZWM>cB{1d~k8O(~wodh`>ELZY=+utUiI68jk zR0vp5ri)zzO*W9d?@XZugX9}az7#ukIZ9B57UJCQe#uaJI>p+0?f7D=Av~%pD=y3! z5XSDpcWD%tgtWK%yBgX`@A*HNb(7w%ve)z6pn|uPO0>++#ARDUXrGWe43>PTFw5!N zgEH_eB_WTg4Jur~3j7=&-oM8kYH&Uh*kVuiEjW5gw)*K5A)gW#T1|JmxDOZQ1n1z8 z#ftLPm$f4g<~;B3vTxj4{z`I?ZF&A2quDX(sX;A@F{AzAChMwVYeVPf!An=ns?@m| zFAgs*F0${WA9RvS+Ej#j)Q_k#Wf+8L+d5elBeXRqOt~8*?-cTpJ0?=Ty(!g>3MTKS zco|bf=`LZ($VMsa9=(=v`{ING!a07gKSPg#Q8p;kB@*nPoOdZP0=+3Z>ol6aYDA;NPUd)?%e@! zu*cz#CKV%(TD!&qX_HrfcyC-oM!fw@v!;_*k#D7ayjD1NXp1AyMb=JB#UhB2Z;|0F zqS^M?0xy=v&=3P;>e|$8%Wg4AJwH*S>q3*dd8P8GWiCyzR)YEDlb#KO(LJi@8!eq| zur<}2q;$%ntyt-WCD}c%r^Jpr=+^B9=!Xnescw6elD`*x&#+$lt#@D`-FS~QxOOjo zuhmkh(VVE2w2W5jvnJro@`%@#)dGJL^FmlZ%f2&N%T2goD5^F;{-_S_ZNpBhem#y> z_oSqPh4ROw>M;caEeJYhro`~WTT?BKf9`g443sJNlGV4*RsqUcJVadbt)@rdj>-ioy>{K0WFM%VwF?0Tg zXAV0^zJ>X;6%Eoj%cSb?XmhADg>*I?7@M7vAWYC0lXbr73^o_A_K6wm^XLuSAfbH- z#|5NI%=Ncf)wwAGa0(?7u!K|@mD*l=II%6}FzwZ(kF|^VgEa{1zy8B> zs`MTj8Vbtwyv!IFlxAFQRe(wNzeyR%WivaGTybF36D&-~J{RO#_u79NY%^nr4@4 zRtSmt_>Mf^Kt6TGdG$e%vwHHLO?!~fB-Q_WeZg99#$iTcH#LKJ>Rt%%uyorKws)6? zZ(b+fznv`l&2{cKsA6!XhGSzMlUYCi*8O@gjVt0Dywo!WQz+`hVm5lQpO_p_P8o#F+Q zo&H8IXxh^W;tch-E&MwM+%_jzywwu&H$6(k=&=hJMul?ZGp!3ap*4`uxL9xy$Fv4_ z`Wbtsv~k*F)+(4}Y4sSNlaYGP@ph(#8*^NA&TGY!R4PXiXA;MbJ2flZeA?xMVk&*X`5%`BKWpJ3Gy9cPOTUta z5=rryFtqz62ZJl)$;~X5gkU7zeWs|}=R*xHNRbUTB1z{gFtqHQF-n{yI+bM-f2A}A z9~+YMpc&J!4GemV&W-s`3*d2xR9kzTn`qZZH(bGzsK@fZ3ZTm^OP2n_l1XUAK@!*Cz3fXyWt z(%?bUGmn#Kd3fB#b6}w{7Jk06@@}#Y{F?9b;jsr@BBT!uL-E7>+<2+z3=A3Bxw&_M z15wGJn*R%P3b^kfWaQyB1cuLqBk?PaX^~$;0gQq^}`pa-f6c5k6#D!@QSQL z!3oI5r#C;R`Y6#XloQ?pnb=U{$OBoEw!KT19VQr{c@@G4DW#^TH29ZE2Cf~Z!fP2P zkFD%xh3)4qzL% zdvAHL@gg6iOu_#5@7#ihunGS@4&aU!Y8?z~DOiFRR_(cMkT@p|yS&c^Cq`md7HY{JOec~b?ALi0?s zbFLP-l()xiu&j*6YXa0^GFZs6rZ1#Ln-MNBQ5tF;`HtCmpN@Q)5Ytx zS#pSn0WqB(_9B|2_SKmZ!?drERrecF)oT9Zct=d;`HdZ56s?UcV}|X{jtgW!2Ll#o!?jZkb+##4boH{4a}m(iiMziCl^x5tTS< z<5!ptxz5-Hmn?ApaDA`9t-La+8??zFAu$H>9=Nt)7tEHoNw*|+vltp21ejKiwhdDb zcQI5Pz%JhSR_x`kfIsZ=VW1}i4OZFM3NABIUJMr?cx^JE4@efEZjLT*<5E{)O6O;Q z?H^LFE<=tpB$#R!8{$rdvpvWR0TVtTmJdW(OqwV6@*tP-gJ1y64*a?~_1qI~(lvmB z0@DSmRveT8J0!tC^+*R;bWe1uc9)7#5nMetOw^F$g!999gYEi%q?@X2>NpeSrQ?4d z{dpaB4vUHpSVivU&aZ_W+CuU0Qa~$#h|TYasMWR5AhY26BSkXrsL%QGzMJ=5$7$2N zi8xJ*A6%7Fzl-KR39}P2H1E!_;&@&ZFET8%A1D~9w}a+Jo4Suu7o&w%rTg`lbMVuV z+A%k*-`v$I^}&|s_j^2$6f>>lHv?zU>RTOIvWP8=_pS6I84^U)uyYMzjd;1IO%Cg! z)c2~TX6|kbgGQQm)Ngcid)Lg*%GCWpqC07hnAyJQIJBY$Q-#+#c?8L1zSdUdjrzEn zAhBk%JN%6I4aw!2a<;y)k&Kn*ah6J(CdsJB6DX-szEW}0Yt)>{Tvys*ZpMqd1U6_m zt;6b(fDX^l&nR^=6NO9>viu^5-e@o$U22!nM~LocZul&_$X15!bFN;Vr&~85LZbP2 zFvUtQ*)3)5T|`-iEk)N&OkLRa&dZU71$zx0g#>n+N9~6Vr}MJSM}H#Al~YuFJjdJB zxyG0KIWpxXB3dB2C0c<<3|zJ=-&6TP0Bi>{L%M_H`;YL?)<8wj&NR;4vL8|S_N?qI zU=P}4=9rt-BR=GZEBpu7)%KSI=yH+-HtN_ZR}t*Kt^ zm=`ZLEhD<5)i^M5KE77a#3_PaJ-BDK#gR7tt({={iz8~x^!10J_<{I^Km+(fgHGf) zr2=v&N(Ua{31FkSy#$M11y*(#%=KRS^rkaHzJQB?FIu%FdO=!-NvdZFUn5)gd{#AY z-x47c*#KD2J(mKPJ_5P8ae7NwvNKlcCKa+P_!Uw!QSg=%jkrI-1rjgM8}Ulx(Eb{* zD>;A9zeAH(Y&mvrHeT-{vG#>UyfnRYZ4QzzZgq+}kt6kqxO*&r<3n4)ky4tt(9(aTn<~*` zK-nLzqC|K&6Xgb;R=)|28#d@9%jH?HY(zzc*cDMH(_AqYxEBD0}4hJfH9N z`{%y8FI{q+^M1Xa&&TXw_B|~YOkz5KbAJKKJ$Q6WPMOWg<(rVqE0($^AZooHDqfl) zbUyrU&pu?R+bI?lm95Q-=7mRsx(qOdfsRu*UOJm_BM-@VwDXTP=6e* zNJ@4m8JG<`*Ixcjx0+%R&-r}cKQw5hW~wcS)T*9tGKTCf;&K^Q9Gbl+Si zX_I8Y>hD83)xdQNgvC=s#xlfliV|V+#HJDS5=9oWG~*`tgga=smaa0eO2kHtU)YuC zXPK1{;JZe37!I2f#%r zw)m!=olG&R&d|k4jLU^fT*$o;#*_;d(HfPNTB`VrQN?zr0gYs)jWZhLi7M-w zZ)GF$)E@msW0NvQT{v=mSOd}`p?R5eLg`ZX4(83pY$vX}nCCcyRPPUGH%=Z@B2M+0 zhHWOB%X^J|(KFa$_4xI5FhmbGzOlicR+j+g1F$?V$cw$5Y1x^)Y~=f~fqBGhS)5*= za(6!h*fTi2NfTogb~0605GD8G-z2nc&*s`(&ObaSd%T{reH<*qb$Ep^pd`0%>_02e z%m2xMF(Fy_URML+D4gAJn}gF&$>S|!rGgZ;<;Eo$6a+(TO1|!z+5Ip$+Twn zk23v3Ya2+$UTG*fdcu&XLnKQl`3Pu*{EC)qa>Pijam&Wb5UIRb#_WH|I zV@929yg>$jHFW!EPi?XbewC5c#yS-b39r0}7^7Ia6WYA7Xlz`1Y%}ZIS#~XsC4YDg zW>q8V=PfL-87$ov6eqnOZ!cx#c}M3_kttmxBLJSXD0)yPWQY|n-kLw_d{=??Gv8@E z54|}1RUXQPctlP-E3=-$>2dSeplNACN+H{CE8u=X2O0M9%i^Wa-%Lv!AS9{u zwY~U-1Wm^&Y9^fz>m~P}V7vXOurGqFrPMYn!$wLfuzIMIx;*LlY=QKd3rzSqbE$PXP*Ovj0e44IM5eg~+m)>3#SUNmK>m3Wxv%DuJYf z0DE{mZ7C+p6}hA z4$*HBK&=|FxtN*O>k3gJmnHer^cCUe1`XH7M*ZmWR;%&^I8h+39CR6^0R=wDiXM;_ z0xgC|*GVm|&>sg~s^c%#+I&Fud{XS?4*0IYR5Ado3;FouJ#DA7Bf=|6e4*xp87&1y znQt)hAO-b6VF_ONv=3`i1TXSVZX^}La5Slk3|qE;^zQ)M9{#F$jr^T@6vGug z2~9v0AZY+prKJ4MF+79jT6Kz8W?xqKX{i(@7=3OQm;~L4$0E-oI3lIS={GCLJ|WH4 zi!swI(P^fi>azftljxICT`x%TlwG2TPCL5#HP^1}nzQlIXb9bm@s+azN&Sy(oX=}= zyEQH~=FhS-bs)2NQBCD(=GnfcZ<#+@Z9lY5&3$MV#C+G>pcL(syp1@dT#(8%90y1T zFdS)`x_iRc9u)p93cJyGMiVuxY;MxsIZ*wSW+CO)2{#Gv znCC*-t20pQ;Zi@I*3;ATqc-|zInCyBX!+5H?n52R+2c*~w)Cx7q8@MYa8+!8ToWg5A#!dN$O`)pMw`rK#L{RV8tdfP$Gu70Vk z>E^xZi-()DzQ$5YT1cP7vSELWx9#2$`m#=k!TQr&QTD_aU!SV5evRh6A5%)@$wf~! z%~`#vQ`E$8%`z?TX;c5ZPj+7h^Ke{xpB-5&^_rWci{;D)<2C@Hj(Uo#w~}z6xcfv> z?Hc|hCKNwVg*i9Sy>s&zd(}+b-NbCRZIJ0b*ICs1$UWBIC!GqcpCh;$MJ=mMmADqe zv67W^RtYg=Z;u{T@VO(_nB)>WPI^>&JaUkNz^}jED&QZi0|$VEf7{Gj_>Am3s!kQP zO^XYm{cw0i&RD#4GO8RDC>h;2hs{ILBt%iu#J(1>6%hcnG@rw=t4`4kiL@y6ks8>B)LCuU8igA`tOG`Y)F z;VZ%kkT7sCEx${d2j3KSiXuTk4tNFLL)-?;7OcriKTZ|ggyc~s5wORT+LuVhFMu_y z9l`>k_P0+CHkb3~^%EE960|`aTGVegXH=JORuL(kPd&Z1v#|JiMp}Va}Z^88~=^hwSqIUToH8hY%kqMHTLx={!w~ zArT)P!OS=Oe%6Z%7mJFDUOykg_?XkPqbAC{$vwoph^3#`;ww+=0>SXH$mIy@DZkxR zOT+q@ZsV8t&6u2vu>4?Q9q_8HB|ZpfURS{POt#-SVoZ-<;Ls$DpnZ(4C8qGZXp~OR z9XEZxKOpW(?G&qPytLTqe`EYyTC%OQ2!(~bR@`kbb@1y{jqKj6q4Rlxt^M2px)J|N z7Sc)I6CWin=z)Ng+~L#v#%3`9r=)E+!o>~!KJQ7fUYvZi>)6dB9ESEZ+2ZUl^|)HC z)IeA5Jz6AZaA+d(b~f za%IZ{8&c3@W0A0XY2y7jgRhXDs{VD>59=P@=1B`J0d+R@*LF`fhKN~{{0ks=1Jg1b z?oH31?{ED0@uQ)Azq%b00qSm*c9TAhZjJvv=O0O}Ou?YCJ7V@*l93_!`0{9FY{wJB z#Hbw(dZ^=KP7pnrq^J_Fn)Ooou_)U@L`oY?*vKet6;~3*o{RyPJo~I&K zfTwqp{pF=NJMGy@^wHqGDk7QY`VJlicOXcNPXyIRV`CwclW(FhIc{Fizk zfcKUu9+Xe{`Cx5r7b5yXJ=9?+JlddsXyuQI9Z-RY>Ts8%#UUY*IUdv#;MHgZYubLK zjU@B6dBNRxSI-6Hc<1SZbxle&MKbXS;i|jk#I8i`K(VXy-E>0o>-AtW0b_}U2fh>@ zIZ#UpgVfCe{h&M}MB{lcA8zzA90KYW2~(?KmQU^3JRK2>*`G8(Pz7WNWYZlhqjM;R z=*GM+rIAfxGV-p{SfquQ8>!2lXuMLy(Eq@oY)Dy+@(@GIjcA^}lx4M|h*W9T$#$*z z^rvC+R=FN+4Z?*j&^u-O;O|MdYxRti(ob@(c7ODYoC^(dsNGk+me?ijH`ZwXF?5J4 z{k=#Ba#d^G%|)42<2So}K$Z|MTi-xd-XC83-59g}{7!SSw+;X8`t`>=<)5vZHsU>c z6Im*Tz?b5s1(LSstDh;?SZci~YNnyObfz&)>y9R|>2;Auq@iMKgX)W4dv*NX6W#UW zL1T1^c7D&Y$P&JoZvT#xCl1nfQdz}Ae--02Ql7QJWnqG8JO2>BlUPoyIs1e&yf)xN zlhPZz;ceYH)lm)sMWL~tX0H$8^>0874p?)>N2S-GD=8@%K<)-`gy_!&G~P;*PF`ve zda#h`)8{T%BK)kat&N*5L9uUT`*Zuht*@l}euH%1AO7Wx-rx-82Tcm@auJdq^KkU$ zYR~CbiV4@htjXODZmAa@HZ-=bb1??>@o4fYKXhT_vAxT$pCw*ypJ}IuzZn)V#w zyrrI~tdPZgyU}7!GhjM_Urvl(Vz^t0_3Wx3-b)=DY*Rvru_|V16)Q zs5~F6>+zZOQN_qoYdo%cAZ!~~-U?PKn6qJkdldozReIOuvmB%b#cb^kx-hF!QRty0 zv2E@R%IE(uXzJ)?kP?GSpaKO_opu>fKSnMq_qR}OQml>Btazex|0vR7D<*6Tn`1A4 zERH1ITSg`8g5g^Q1>Bu26el*hA6KgC7%fC9nJ?<(<%3!g+LxY@L<4@=AaMjy&rVG1 zAJqXM3t^Ck0RZ&5EA9W5+kHXc1Tjj0!F5+nQKY_Hh7SwuLXe^dL&}pO&yZo{=&8(3 z7*4bdL2Y=s)&mZZK2ZE9fBPkUBy;EyrI5L zV?@@SVovz>uH+$<)%3H_a?z6IYV%TGkVVv8NI(V^qSW;|EfK$+uGEOgVWv}4G&Gy6 z#}qm{)i1Kd6PZR>WI|N=R6^)G`Y?n2y$u`vE69K5Q$Lhk(f^3PGNXo;&US9oolw8p zI;l>~aT&j{!iM$28|BeI@3kL4J?&x~L*|LGw5k5NLVZgY$xnHNJH)X$yImD4%NkEr z_^vw7G@2+8!dNCHHu+>Ei`jL|mZ^r)?VevnD>b46sNxgX4^v~$;VTdojWl<4JY(+c zh=f{?@r21du&}C8S5qq*=^lP(g}I(b!Hb~zNIB>Ghv7|orf0xHP{e89vmIm|NYv}^ zW@y8%0T*+qTL6+VdI!+v0<(%+e}8_&3bQ6lN=ccKYNKBs0n4zwc|Up2d~X4IVAtCB z_6DN=NhLGkByj?t^1l!wH&Hj2K#+0#rO_n|B@2pgEAPxb0t zQec_p+a7COTk%a{6@hsBeCweU0u8TGT5qoFXs)X_oewu%2z5OtU`PT2XsH?Ex zUWAObHuK4~JFYauQtcu)HO+TMb&YEVnHJ@m;z5^jYC(l`3+4l7kZu-#@p8pr%Kbh# zq>DX6sIK-)OWFTM0o+QHS2&pg3fmG6MBYMNXz-|0d%H{@QxV70ok0^&;utRXe-^}4`wGIRgVof9P7 z;MDo+;+b#&Q!FSdG>~Q!-59h}| zz#4WPf_{5Jd7MV4oe_jx%%-WO>z9G#8UTiVS;d?#2BABILb?HvYLIF^j>=-qWQ-ni z-03OgxX>{WqJh>M9|SFmz=^il1IoO|G2$KR!`GB=2INV^>Z>C$&ed zRo=zHw5HD+{uo|bj@ukcPC@={i20;sC6)1ygxNgn27%-fuz}Zk+dg%9l3wE+7sBf%})cOim|pAE;-^- z3$)qr)bf8Jo_ydTE*f?GgWktM|A}GWy9Pwy`T;gWt4Fpeca{`)@_*@ORip5Rm6w-) z0bc0%^OfVRD<#JOCW;>){s3Vjln^*-RAQ#=FJj3IQOt9!F^>jyI3ERD#ti+d5pZ*? zcExVOQy6Yl`o>8fr)n&H-EWgj=uwCWFRvz3_3+!4O(jXzBnPR5vfF|Etr+br+Ud+Y z$;2}Ql;=)3AyR6DF3KbsXOE^J-%*Z=M)IwmeC-zygY(o)W7mv-D%nqUH7)3(+pLrM ze}Yly$FXIH#pzKF2FZW>0*-Sa@P}1Y!!2CsJu5TPQj@hm-^c3(3Qjn(Sk~ ziC(;|tlfUngG{^lxiqd&1qRfybAM>Wp%cNDjR z$N@+|VCG2K{V)k^49VO7h>7J&)0ZsxXb;bCi0XB~tU$5zZ8_(oJMSSf@6jrmhrwWp ziO3QC`b31fhA3V4IQq>qW<$>{r07>zyAg)J*+);r23rKP#)E?52@+4XYW|7uMia8o zc{>*m$m7J+jrMDx_XW26I6dr@dgAuqX?SEwip0-TZ-3l`8{hZFjR8=tyn1NL{Me&J zLHHy-B4t@ETEMUa zzOOhUIJEkaHrOTR3?Z5Mh?}>Za2?vtPZ(X?>C|773GF$;aRE3nFhp@ai-o)1St9n_ zc`^NaJtZy%v{+c0K$HZBc$EI0+7t0+?JcRCiTE_!73xbgV^rJRb*W0RdyDA2cYJID zJ_iZg<7De2VX=}Ux?IQaf~l&z4xi30+7wCFXeut`5-!Ee&-z}&3#ag77)^R68wYSG zLKSwXIWcmf-*p5@W7M5DriIk;dI#D`@fIz98Updv1jSe4nXZzhC)Zbd-K2A<`u3miU|df;KPih?_BygaAXxb08`ujKuL6m%}X*6aW^@rTzW=U%(ygU1_&S z-rIrp@tJ@9!EJwcJ@)dWcN3#`e=ex+w7#$yZ;e+{l}g?|3#gu-kw>OwL%aT~6VU@{Q9N$9OnAz*UK8Cm^MiYVho@-UtSx2#x&It>jJj{#g>oA5Ru|A@ zwslV1Y;UT?^(HfSlsP?#4NUc$+mQ&$3@)>=$-FV0kHCL_U<^#+;j>lOXrupwPl$V@ZNpB}T8>M!ghfH?hDlRiydfwGNI>vJc*SkyBeeKzUu znJTk#%l__;PD+#yPELD5gih(3FOEJf-0*H<8Im2I=BO)%taACOP_UsSK_;A!D5hHl zV0|l0ZLJfR$w&N-&8g~t#sO?Wwa})bPW0nR%Aj-vqSsWr&+#!ThQMhrPSZxEwIaH} zwXYVOOt7jpUlBSk`0(RP)lH_ndzzMA0c_m~ubtP*MMFp5oq`*>dJn)q(gSVtQDpbA zja3tuepx060>ZT~Lni^8;Tuk|O&8t#va||Szt(Na!oHu|u#_^|J9b zcwLHI^o7sfwhg24m>bYI0>Ibb&(4m1(e=j&jlxV55xI&(i2aOAWqas5ZjYV;$f4N8 z?=x-A5F6oFZ$8k)Z3~SjApHec2rw!f{j+3QpwDa!xF;no4a{RWDm6uDA>aF?<_WFV z=D^JHYQb|(<OJq*$p_N%|WyimtYAACNTTI$^rW3Z7mU-4XCvYRQ$V*%vshZ5D@^2<*>UhwlQ5 z0~4hwm5oOezKZ|d9v2@?j_HBa3gA3!!R7_n_YSVMb)its#iU4pTuguN&ey9MAyTG% zLBgC-R{2RFb&gb6?k;*b*zv+sL(St}{P{}rhiBEU#m(~gb1?t-5!w>2g~lZF98-yM zS!fQ@A}|k8r}SR-EYx56atUPuY498sDSBw7w|h1OtF4s57J;bm6NYuTJG2GH7qiIt zmIp#<)g>tDbn(dvK+`m|JE?mfh;1+=FsZ-Z19f>oO+k|G!;ab-;ci=)PNvOXM-E3A zeKxVsrgx$~yQEr*ep)5`|Y`t)V{8Tl~YsCazHfKFc6ayo% zXWpXGK>xTjOZ)h2?rC0+7-GftpG+~a8iU-Ap;SBt{dm#!Gk)EM%V#}5-B!N8>GtPP z*icK)9Ow0!;&Pmd=Bd=v$0Y%!k)OE&{uLDS7Agdv=)G`)+<5JaQ6UmsN_Aku>9@mK zH4!shDXZR;q6gzVlTz9;5yL*jQA%P8#ip2>+?|tnEpgw?lMU)HdmmJQ?2Y;=Jd@$K zL5|_*WxrI0-qlIqMxqKgd=>wye+A|p&evO)J}m`C+z5we4}AI%&E97?_EEk&D9ia`P{VA=LHBnvcn70$wp4_X4q zq#6LJ29K^dJ00ki=5wME#qhEp`cYF;ySu|ftd{wxmeJ`ks09g6R;^Px9!0-`^nJ)l z1H@1ghxK93AIWbZe%5@bpH=o;^o!OC&$}<6BeJjg`HuQIXWymh1@Qh}6xB*xZ+LNAtozv6vuw7LQB> z%QuPD3)WlCzv%>TV{g!U=0<%uPShe`6pgPjo!e33?_826l`MU92Vr!GUg|h<{iL() z;v$_eMYu<4#xwU_5jU2qmXmGfO^oYj5Q^weM0HEi$;#V|B}3>o@28a^Qn)V!`~UVi z@zLrP0x{{&g)Z9Q$u7ND{wZZOZoxsGisbb&>Z-EhOS(8}(?Es3*`wl9z~@s_CZ!yG zs54id44M9{ClGXqTh!UE9cphDekGY5a_!^^vQsi^QmrIz9|RQdF&2?OeV1ol+ca}! z=_kh6+^+gPjc$DEJ()v>jr-|O5}wtf=eyauyNIXD5;Xc>4ZlZy*AK}Hebt3+wt zSWZbO8gSyo8s^#-v^d!1G&)o&$Aowk5>FD*|tVIh= z3Srs7WYJDowQ`r`QI>7yS&8?Cqbvpnns{fyjf>q`P9@!UubQ%{@D@Fm=P7m!uQt@K z^mRt$^7WSwPY{UPRb?Ns*SO1_7@oEG880lT5ZwPp2^Z1emT1m|8HVT+6DK{)=c-^8 zW3YJarByT8vi_8N)5?c8we+~9kzNa2JQ&|#&0U!i7b zQESJKXG}dyC2hMm)J#*HHX!I-zNqGrdzC5P=GYhDZ+I*K>;rX5wua}NFoBR==;JOG za9nv+LP>W(^c5bPf<0aA^x(L*QDfDp?52U9mgxD8H#Ah-Ag{jOE!ZfhYXXi{&9shJ zpO^PSBysQDSMCk}FzTR51%1TJ1+0OkxLU*1YX5}zSblhVT&&{zwU{Gq4W&)heyQNg zQw-z5qk^+9VL)jA4|6MBK#xtbI}#i20@L&YUP!fyOo`$}g0{06iu1t0Pt$E4kbQW5 z9Rk{teH6{@tQe;Nbre!#m9%rkq z?i`TP=|Cz1Vlm&)&ri~@7!QjH)9lB^=*^q6)=2S|bDtTJd?Hs}+1pq{n=h2jM zF=H`s(7J4tlT{@m8~x4(jrx4v;<@1bqrlCIlya{%IVUD9#bDIkq^PK(el5!KktHP^ zXOJF%6`~jMC0L)4MQr5UBcZmOxtHMK$~SR;>Y^){q4cO(#{d3$gRn3$fNUf+k0Kg* zjq_6ajHYxc(5(vAC)g&R1%N6JN(C-SHD1$_`1j!M-6V>94*Rdxl%XyPMYazS|B2X* ziysQE#ja9Y2pS1YR6H|SAp{HseMcYv%9Ae6R*KzCC$?{|zV>ysq)tX-D2!_pwLo0) zMX?i4p?S64Ig*TyjAHT7D=%+XslV(TRijerYSg&&>Ws&5s@W2f=0tzRSV(q&8Iq#? z^*G!6a!&#R@{r$gDW;sZm zKfguocU!6OMVUbQZ_=``^zah~EWPG*%_hn2#^IWa!L7@kc#MmblP` z-(-CLZHAL@D|@@SopE>ndLN8MX*Z$|rtY^htNqmQvApiSZ4kX_5L`-aCc?J%Sy1fL z-Xn_VzI?Z@ly52ICv10|wNd1cx2x{uf)JEBQtADvpw-Ob3-6G(ACz{TrHB`>bPy{F zrR%_#4jbQ2ckPssb+ySgZOSsgtBo*op&vGJsX5wm5H3`I_4z>{b2kg~lBaS974tmF z5c@@mlgQ$LDZk;$?9cink*L#fo(qWz8!FD}>Cpo%OTf{rKoWM5rO)-Y?7M2JT;cmG z_2^w6H#+9q13Guc1d<1QZt&+yquc8A0#&IQjT7ifYmfvnCBL8`H~365ECgbT3S4PJ z;y18(TJGxYl8a}Yqh#52vqX4kHK~w=+pnuS7uqN8bUMjYL_B}?Y_5Z`gVI9;Zc-@Q zy2{1v4fCgp$&Gq6I@_1XtsE$E*OeHNS|q5|J)zYqXp6O=QDl-Ug28}93qZD~GPv*< z9b~yvDQ@F!!zqpUs)UyvF8-c_jLSHBVUf+hmXY7u0$+S>_2!~gtvtV@GsOcQ!2yDM zeBO40GquqprC`9=VLP`G%NaACy}dfrraQVg=*{ttvr`A;GZ0D*^8eM-srLcr(Rv5b zE>FOO4E?=EC^+pT=CBDv454rhkREl=_`VvjG~j#I4zNjk85W(Nzqi{283ds91G(8M z1QwD?L;ysBvw=59Ya`s>e+bOdt1z&WJcui$a72*gLL}f3PKiso04%8Tfh%GCd&}4B z1NCk#AI8ICD?fa=AdzcqcJlTS3-rN5nOXY`DU4P?3w|tNCfP>P*E#N(ebDtuP)opR z6|iWDZeNw*+FhHMs`OUWdeVGRJctV)JCLC-1m7p%q33wj&DIqZ2K^1BP!O8^h*i#F$aw|g5mzMIjrXwJ zGDe)uksyn%7|Acdl-jd&vRp?@;se-C0$oyKZ|tr?c-AR;Dn?zW<2}2>(`N;3!*|_@ z&z=Y&&UhGZT~J!a%&UscJ;nz7PHgVnkP{vnx0h_GRN{_&1Lp6`-Ru}-;?Wyv?>l4y z$QA8=KL2>)4V^nw>{fbP03!RdBb;FFvlfaffpNtVZE|5lfA!ou5tN5-8QFwzUZ>s9 zC+|X3Z*Aytkv&j_j>y(-bBUumGwdb0Q|ZUw4$&9x1@t{Xw-mF3+&%rqsRTDbbC26d zCY2VZec$1UdG^RU21bzsK8WTlD@-^*MY-yrDhNl+~S*%Kf4JE%tBz zyQ``TbG)|auU(+!P^%CB&OR_-QykZ{;@en-~rUnmN+;#y!^M~p`mgp zQ_}m6ZDl!PqSI-;?LD6s%Y8$MUJJpN2;pCZRD$P&FI8PHM1FIzr}F0-pwL6==fY@P zdHNh~7aI?ojiyAQcw!!gO%^t={Ag$`&5~yZoHY>L2;WCl^blTmrjJd=?+jWW)RScT?}Q=&&Q z8G5%m+**JMJ+fHg?Jc-)=jZ2@c_|dy22-lO%n{Dz_Z41;Xx&Um>1!<`4|&)VD+d8e z^K@O66=W976q$rh_1P!GJMWJ^i8Cm%#GcFJne@3*V@-_R6+&; z(J2u&m`h9&R0xzsXNm>DH5~kj!mf@{Gi^xOqQ`z*5!<$uNY$u?P2#&n)(F{hB)J(n z7qU+-o#h~@l4I+KS8u(-m@cOg@5QB9O)!}~$Ny$3Di%%xi=yl6p39!JPV%SPOAdB` zY=9iMTX4Vz|NbH?M}q#~=z;E9HELbY590r4jjmO9xN_x%=Ojd~WVDg|ULi8e{~+Z6 zNKZGQE$uV_s4ytv<>d`k`RF8*=NbiVf}jkameT5qStn=yp>DbFy0WeUJ`w3$ntYk4)D)H=GnR8yvt0E(_5MUyyi>tSb@e#NKK=n?G~%XLy}uud0~_>nnEM`#ChBZ>-?X z(E63}C|{O?w{|^z6yg23J+Gg8XPL~kz$^qyUG{4bhIcUCk_DysFfs)4gB&ebG!vVg z&7C~SvZ#6{k}f297&4<)bUN<5SFABubhHp2+K9xMB2IlcnwMgpB8sFp|IYj}N8R+y z%vsBT;aP9D&r21j+s)G2^Piiw@$%)*1A?)a(3Lxyl(t%TfCPUS`TUmf7yD18PWoq` z)*vG|m!S^n&FL&diQL$jFLJn2kDJs&e90LtQ<(R89th94T-uP^3?tDMBUR>Xof(Kp z0biQcA{FfdmgbCwL4U7t{khFfC#Ton279~eZ!JE$ySw~|lJct(TZ9M{VyX$$j%+)* zu>N*ybs#mh0Vz!?)+QOyH4Yjy4vrEM65ctd>3;bKIbMK#Ka#Cr>x8d? zvRkNQWa$$_kY6B0I+LqT$D1=Er@t2wdNSkxoPBwok)}7Hv+Hj5*=3V}8cqBxTVUr| zf;U;u-JQ{Tdf^U=`bsY%+v~bHC!@32v@4fn5O)Zozvzwh3KYbsJUcqqeT8QxUuSe$ z2IfV{G3%v@CY@Q}Gl(ty+!j(uiIg5lWp#&as$aq}^gc#RqDYn)8Eq@9c`UKN5$(6V z^jGj=S7?VAp+^r@Wu$m;)COc>bOvY^jxV5?zG>s>%5A(3kZUwI%^n7ahI+wx0V*QU z22?0%m&5P%>HB^=x5vucWFmL1;~;(V&i&E{#4-+A&D*a_tB+#e=$Q5>lY&av>F$|T ztp57-9@Spo1lu`DzL3l1%V_q1&$wTx`9TBqE9+};ZD z(D@=UZ9{n;BxWNuO*_n*mRpKaQc_UDK(bTb$$bT79{?|t`a9tNLF8hy2<4QF{f-U} zq=;M6`cnNKlG*zgL8MhIf7#J-&z_W;K+0`dTbqK2gJ0*3_P{2EL!+Pv1P*dvIAq`j zz`4*KM_~!3rDGm+2+SpxRo_t(o(J|{&y=R=myt3t$LIN1Pa4dcE>*z;XKif_(ND^i zoguOacQnVY2`lp|O4Qk6imcL-#FG|V$6$LYJJ<-B-o<2AG#Kzf^(PN4*=oB`?(^nO zKbcg-xM_t)1f@F5To(wfcu$;9&q=<0grEEPmF&AyG|i#7;<`!`Z%POEmBbQi3Xx>Z z51KRPRqywGF;P~RXkvFtf;$qIuig`IyMKg?5b-LTt0kz?Hm1~UWDK`Ee_K~8WFrnPIhoP9~;Uv)?P^n*<)-9dMRWthHojnVV}~Hh{hVz<2lIc1(&s5Vt7UQ@X2Jj z{6}|o&KM2zMT{~Mo|)XC+winl<#}-e{|M_hr!sDY-cML0q=%nrM)W;=pFSv-tiF*< z7mL)c^eai9!hO@dCMO`(EqZs>zZS=P*`pYa***45{j!a=k{@OP?K!6_R$U%kV(z{9 zGmtxr<4m1-1k*oV+>chaUGcQ}(I^^+)HDpWr8uhr+6`ms!sq-$0~wmB|Eussf~2gwD`!{9%+PsE&^AA_f_U0N%&-Zr8{ z4bc+g{q=v%IH3v24Ku!q*8l;4Q|QEyXNT^e65tganT3}FF>MQ*?}n_>YCwfdvOuro z4tZV$`~sLPk{%wml&`nt3rr!2rFalh|4D5j5K{tw0bq(160r>z4Johqh*2clh$LbF zKr?C{rdQI*HB@H6w+o02=TGe#4RBEcffM;%h`%1T6HZ3dJ{4$FJ zDO~7~Ch>Lu7s4O1E#}PAWFh^#pavs-xp7>DOlVvHFN`lleAzq}y7@Sp(U8pWIZl=S zS1Fn*`!JK8BF32aGr8u#i;I!CBUaxpp7J-fR9SJWII64Pl|wif4ugsto5P$&>LSto z#Ipa^-O%6nwH`>XByWQTs+F!|n6E?q+enq|pO^1{jkH-5nUr;1K>QiMO12MS!$!&0 z(@u&k=Jmy$wmR21!Ge$zEYv!DPZPEM9)4_W*JEVfGX0*wHI4BH72&1xL)hdZjnDCR zRSOuT!Gg!~LFU=N2VNHk8{VujH2=&bG+am1Ra(Uu4l7&~*2hK))ug*-AiiNs47E;2 z(BbKJm}iS#JQ|2K9}S)BR(cTVZ_sqU#JhFcpxEw`_Bhj6v4V}HXaviwuu1Cdj<@Wc zoyU&2pDaQraHm;=C;*Lz zc?If}Nuz5!ufj^e69aGD3pO1TEA{g-(Hx!6-Ra zReB1XkC+Yx+Y=T<>^5pCHH6G7OijW0p?8d|Zk%vt8^LFfQ7lt;>T@H@+QQ zr1^}dHOmvw^iZmWR&hvy>!p*4{}T!8x3{blYnPVAf$Vca1x3VOM3h;eVLl_~>1{<; z1e@vpgih+YSN#-0n-W79ZN0({>UFJZ4dHw5uXhYq53SvL@q3n#6!Yt$yxH3MvPmV6 zz7%U@1%Lshc(fruB`V^XCo1!Jg4Xh)7Fh(3t4Ov#Mp;shdqwUqk4KSldEqpJTk6E$ zLTaAxBzMgVN1y+u`gH}w(|cUtVf$KIfB%q2D38z`U`|8o42O$1RPTO+ZY|h}0etz6 z=3%_l>ahJi#hXs9yh;~la#9jxoKT$=R{pj(>q-)2G9x~e8DzYaq@Ik1rfKC>1aA^j zOREA6QwQ&2%gdLIpke}hG#n2=t4sig0iX$97WoFO*4N{HgcL{NVaPKZ(S+Y&}K?`eSs$A*}YB<5n2JqLri?Ssc^2J%B)8VI zW}9#Ul{3s$n1?DbJlh-G5Or9z{W4$%*Pxl(g=A$`Vc5PWF_NK@@>Rgj`W5e-YJ|*y zk?d`S;aIMN(i+8a_L^G_x55n8QIije+DT8UpRtryv6YeGyS(vI<~&pZ6jS9M)}7!T z{Ps^r!5tAb6pffAOs1>FG8mO+hnLUS4#^?wVtMlh7LItbIiboHR(jI92@SA}`?_Tp zNg*AinrVo%Ch(S8;-l$|b}1ecRUwOdyBhow+w=U#<9&S~*2x49%@Mez;@U}+NtMv2 ze;IIi$Q^u+KKh&h%`r@l3JbITeqfQU0pC81o#5bWuZ=cGaV<@}KK;v3#3D+OT9>k( za{sYx7pMYwDRJDq2{Gp+IB7-8<;1A4Pr{k|w*$J`*#;XK^hAF4mchxY2qK z40t%{>kT%mVHCHA>FU;tRg^8O4-dzi`{6pXu}G?SV}#MS25wKLt~`E*+*2OiV7}6XKm)VPfgd-cIQ-w#F%oD>z|I0~V;*G#83V|-FtWS| zTE6$ci~7Imun!Q*S$JgS-vn+Kz#KrMs!;H;)tZ&n1y|J$JEK7c-`+r$CKT3BkTMhM z>h3pE-hPo-L*;bZ5UAAVwzl7>6JhH{4~-~*NdYHz?HBSCEE-iC3;%4G#@W;}n$-~G z2Vmzd(k0S6zdd&8#su8?5U@saLO`Gk$rl0gO=MI4M8kkO2}S{P<_(Z@Zzr2abdgYy zQ)&9s2d3I4&*Dkt~xXV&1f z0xK8kr&Qd3lXmJ~F^IfGMJ(J-+kOAdw)+`;oZTr09m??LKLd5%XGieEHP_Y6;vU0< z{QQ*nYoo!S#RKI2|R`ZfCZ5|1g+RLIEjK!*Pj_mP_FQvK(K!a$1_Dp#Qc{YG^c`e zZW@MYa(e1b+CcVGZ6=pGlHA6n4;!{W{tX_#8tcj1ns_8=?IbOx>G@4Fl2w3XJ3oM? zK&Q9K>kQkk_H*7X{|R#-ERbvOj;2PJP>0k7{Z3j2%WpV`uEls5*X6Q!iJ+`kTX8>JD9c)AxL?A9OMa zCHmQV*_VkOyPhi)9eL-Bu{n8y8N;8QU$~qPjAfNA9&T@X|0MQZBZ^hRdkDg?An|5E z-y_)v@l|i1 z|6lJS<6`Cw7L9t2!Wc+Nm=#lhpp@Psm%1tkUr;mPs|vfRMuIA=PAZFhh!h5#9j;?uD`0K;L~l? z!qrJvGThiGZLB9CBk-WGDoY}nj+QRRukc(9l#~X43Qc*!QdxmkyLr;^Np6E%$^r-R@|gUJY;U2^N7MA@*mQB(zz2Yx)B0}E6fWX2X^b#DVXN4k)UM&*8K z#H*JtS3Y^8K3Q_}baM8y4dSQWfA+`SDoc$5&J}2;9vfyFGBJ9pRbdLxaKr>lakZe<`0`t5R!`G77reUR zHwWlLetuRDS?_?Fg_YKY;s!u0Ke~?qI74baW{Y&cbH&k-R8tHS(Ci5-ow{($ds_T| z6TT!P;Kz@qq{tfh75Dv~X~-Vdv(qxI-+8T4RzLrhZ%Z%U1&OQ;Q{_l5?( z6*EbD$q_Q&HMFr>vUi}Qkek=*-wzE9#5y|iNi40SPs5Lt|D6SF4(HNO^ zNOPdY$3hX=QucuJ+l$KevK=yVY4GzwSd3c70NV~Zk#MO@?`)uX(#J+q5zrGuYZDZO zb&{%jCTfrOYo{vIx5VKF+5QqR@W0ClHephHCy;3U5hM3>_a~isL|AEWtf4^&1XXrG zmfvB95fzvRp1YtUl!WkjnkJZYmPs*_#|N!X^N#!14*L@yF#T6ox6VZk4ExWAa_6sq zS^u>S3(~?}X?p+p%A({Fw2ugC=UPYbPKR?`Q#!!skGO?r(O7c$P3OKb8B+ z!Y1f7yhosT2mb(@0q>oT=d+6zd==>Om_q9{*)T3l(OQ+755ZlDmDS?ScPGsp#ex00 zel!{d(fU7Sy9Ncmja_{pr^Q+}C#b}a+9J;z>OPU!7eL$>OOl-_Rf{T=ASS}imu*4x zu_%!tDzVU4q&t-jQ!@5B1~*QtlAELncxsu8 z>}3CvL2?ZA+9|;`geWZb!Xc>)zb&-h7&J8sp;ZS_1GUW;PBJM|^?bcBVC`r4c@iqo zk2YC#2kaE8fl79}MS(M#B3b=Wg~0s4gaTxT)zQa3 zSf+r^`AB=O^d&vl8#S#(3pQjdn(US>j-n007D1}VJNQ!HyHbN*9I+>(sgj=saXbj@ zs(&xWEq-V4Jtt60JpG!zgW5?5<~nmMfoRSOr|`o543cU(a>p^6@iC*t=xJxHBDW9i zAaXIS=&?4jQyJy#LdmMn+6F)nX&+;%9Kddc7^KmzR|~%tPKju?5bH}GbZ%&3;@alj zzBAxI(^N!Ppw-pX(&7mRx2p&%J;LA`o0B}MIagtXibZY3Wmzsl?5ec&5_6vAEF;2;sekz;=@326HT;rW7%8QmGRMq*X(83lH(z> z)Pw8cCO=(*Y9^R(o`#wAAq=}dan4=QM<4Cr6PXH zmF&%I=fQn-#6(6D|Is3hs>xKCHJw5`RJ&V~S6!a+U%Pc`sU};Ym}T<+(=W&9pCTgb6xMr0Eun0I-HOW{QZ)b2sJ>dP>SQC=!q+EsYMV0TXuThXVr9s;G(_&TIK8#p+JLq2P>Q_>)Y# z#;_#xAN~RvI7|x1YuFQnPSA7Yx$|N76!P+O{#G^BQuh4wUCJ=KDB@pK|7bo-u|U6U z4e$y;9PC!yZ`~r+{lH3d;zJ`+RG+>g#TZkVNTrN?EI#-26{-?sfprQ4V`h{O&cCq} zRqGWIKo41$Iy|>J3$OO>i!N*2s9s8FLwed<*S!7)SV4yP<1R$1s8>=^_v9Ae%dJBF zL^Wuige&ph#^C(}P_Dw2K*$hxl`)9P%nJq(_e*}dmn+o!IXDnk8vu!dKlO%pw>5~u z)T$EUt&lg9A!K) zfrBMdR>6W^;pOVh`ByCgVLKnh{+|}$_3fL7MMG@D8H=VH-=Wyy zWMye&!#d&2?oa+c^)kvLgQ0n1f%IT76fmKbY=*^tkq_k~92Skv&gTY--`7JYiU%Xg8Hxsb3+vP95Y7T|fNns3RuZ4wkFeCxHiupkH>4BSH6 zu#yv5KfwL+qw{q5R#sP6Ke)CkawZ*LxWyUn0TCvSK%nq$SZ?&0f6&hl*N=8~I zU*|HS=Vw2Ao!?(uk6ZF7-_sX-th6s*_gFh3!U@wf4M_4Vp0hSjlj3BuY=Z|Q86B<* z3?(Zi3oAZ7U;C@BZnaLF)%vAHW7SXX09CD5Q85oQ{9V(MB(D?}Sy-lp&7B;a6ug1< zAZ|c8RfHaK)Y;q=!MH9K|2h?*^}CRQt9+#W5iE4uF6mymPs9a0F50D;U1E+r!*ZvN z;-Nuk95y>gvv@+fiUj+>&)~3|fst7ZZ~WKb%)fYdM()TvGJ=$+EoqIx?ZW?ae!eSB zZ?fPUt)`Azq?z+#6xOA*lCjbR8)OlQ;CZE%T1?AIq4t++Ik8c{%yjkx`gKYC$}lY^ zO(|7(f1vUyLJC2f_R(ZhKzHe&@FZO+>NEa7jp$|G!Zn0D{o4FD%IlN_FcEVZ)MaqI zg}eqRz8WB52|0W?Stsl zoKB^@>w;74JVRAm^c1;mC_VZV850>Q2C*WpVcmR27msC|ZkegtM2RL7`9p?@9b=oe0^D*HX-mkFY&z$$a zel3C;Gb;eewK!jZ!`d5wy?nHk_*IDr^lJf<*b^(*z(GVzBnxV$ep-uX2K*L5lR#M! zSZcgL?FXCuS(wU!@LJD+9cov%;8L&(xbvb1<6 zer)aeEbtXPkP-hi&S8hKJht85qq!u3xqMky6;u+GYZi4k4RguxItUYXaPxSM+4Y3TB-2A7@#XP1#JGcm!^@bCUS+2yU4F+WD;$&d*UZaAH~Tga z=~eM9RI4P-P?{+(rGBV|FJHHiV$f~Zi{0KB{BLe zUB^(iVo$@@xNH4Qs$sBweRKVWShuzgD8*wOta$zFMP|S60z+UOz{> zCay0!UiQI%UF^$vnE`ptB!M`Qy}>($9#=0sF{1sJ(#ebG&rRB)O8@A1{UH*!dE>$5 z$cxFO%cQz$j_)^i2dSBvb*tV2@)hvxbCx@rR{ax?lm$!#Mn@L?rX*Z>bVi60mopXE zhN(h1#%9C!5AR%>Q2^5`O~JP;Hri8tDXXQehVBluu@a+keGa137@GN46^+$@s!gfX zeV{$KqAb{&G~_6tVB;#y_ASns!kzj=ZhI=H@P#e=Nngr9$v+L7#-W)4x1 zyA6^!)Up;^NLV*Nu(Xn9ESuoTJI{D%6*$*D4d1D!44O>W+R3llp*6bF)$ zXn=PH53=l~XGkt2PtM)vgwkiv)Eqs_f!PvUqS^u_+yBu_J?;)bXtN8zGLlB8xMgz3ECw@Q4Li+V1N0! zzmn!2X>qJfRDJOU z)`>%vavmuA4u?ngEO92wX=c;!Su!+3-}92+f6zeg<>oeM9+ho2dK;(?Zd30b z)pU2M=kYB|+0HNCZ6jU}P>_7CoK0-4-Q@LR+o7JRN*3t#ZDhsqhS8p*Qb@?AIyXko zRe!XAHWa^-)n$M1g=Ot@WY4{-Ssc}K7&=q_1}(&rB!hk|(ryxQt-JoYXfSA+R=A-0>=6K3nciVyg*4PgRIGa%Jf7sQ%eD%vUjWhJ@?kr_ zWhz@Ze=pN2%i`*Sve)To4rj8gDw}s$f2%H}l`GY63DTtEeqbX>t@>KmO!5^Qsc=jO z)}kVaEHiHlWwHpq-?-!O0Mfd+qfuUgcQJaA#;C5>a76*rN4~Pf%OmupRqa|FBvW+! zwA*-kt<5LtZx$0Dg4Zz$VHdlY>D?3fzCCGKW2AP(#Mx!i&U+0KULm^yyyTx0V7$$H z%Z7>sHMX>?GXQx98?WZdlT*u0wlSG^v*?N)=jfs}>hJe{t^Vq_SCxJA7PPzJvYZod z*FZd*cznuqs5ATGN#LvIwl+2eh!&xW=`EX{o-X_F;RC1?;vZ-fRe~HIl4;U2Hl9Ts z?dL$(+2f-=;U$2|58Ze}*BbA6yPoWCWF;JLf6CeLggw9^`p+GRss-W%{)+1?8kZK} z%MrD}?5q7D1cB63Le$L63~*N-JHcsTlVXqJ@NBTEz|*mtrzTw{UOws zmX+BR`CFaxaak7Kl*JWwIM&5O^V2*r)erK-bExXm4l82Gf9lRz0G~WY>V-*DNaSCv zcQkO^_OnL`Vf%H}m@B6^J_=unjTbZg@9#2)G1bw^3dguk-oYC_y!evlLYMW6fM-hN zVZWbMwbvOLBhNCr(~z(nJ=xIZX(d@p>5`nLmNt)ir>@-xHM;-Mf?ZVk3`nKr zzk7Rk3=YW}68N>w++1VnQV}!8wjs#~DQjlwieF?DLPRq4lUh{xU%$2qc+x;26%HXn zkO`FX?hX_idBbb7`=3ZVOf(=?3-^6bp?!<{za$G@c^8yXjcO?BEhXWCjXHkjvrpUl z&ibe1F3sCh&t_3<*hi4ONqP@=sUd2cld=>3<;(54@QB(GRWkbSV{q@jcq%~>3-}AP z8+{LWCjH=)DzI=WGPVbV=0^{Y1Itu08XyDqV0O3D4L}5+FVDZ52jyErX8~lAuu0l) z&;KX$yrIDn9?3d~SHHw3068Ioj}#=jhi$E`5NN*N30r>)D0l!sWdAv+3ZZ2obD+>z zhhlZu)5}aNr6=yTNDwruicwp+hT@_quZm4Ga}uksfx1CZT1|+pfO*-mxb?MPaD|^! z%bWU;UzPKBKszMfG0n^Kl1st8#m=fYg`tH({`iUI{=FMPdZIeq33C)0Bn|IJru z1pE@RMk0#hsnFfC#bwc(T8LDF@X8y7qx(Z>eNvGhxSQ&KbF@eme`3S2C+&rfexR2bH1pNHa$%P~xW}jGe&iQ*gZqY)`Cb!e zOw?5tRBzN)Mx1)gwI^9cP@2*curh4@QC4mxMM)sJv|>Y&_+v#?yx9E-g8o9CY2}u1 zy+U|&*8ME)NXB1z9W`@t#+X9xd97*o@j`2d!}BiO(^1SJ%wncD0!Bsn#m`dY7wqeg z(sgFHXmX);_@mDw&^vOQVsm;|6Fh!8zM{9E_#xMP3&(&08TMLj&KUz9_)zp3za#r~D>69-lty9l`;E#N z$`HCB=|q7w1c^F}s+qYT{w5=%xQV#-pjZwJr^fM|NdpAiJBh-PLdkSDu9Pj&TU9ir zKv?lbDE{E>%lKxrqakrCqf@3fY*AJ!LSZ)OcD#`dWrJcyUDgjbi%AI-AatiL zjyFF=rjKo?^HEq2#8tBMcmMZ3O6|JzM;K7e3vLB*n)gEQ%5t(eRSn~M_Xq!IIg?Sr zF?uzWUyK{AP#LHcR0?j#fQ-f4)pa0Jh{k~7lj-+svO<1JN?hqIxo6Z_r(Sn3s38%8 zSy6Iq@x$8;fxqsw0sv~V3E?gP{4z~JOb{{g2VS9rj8dFqmDSn(<@9F%xdyB*Nka@u z10@DO4iO@!T;CTX>0|QDI7@dLyK!;aCA0%#%4wnSSB_9#>jp#y@_JcOkqH<44HN%B zY#k&PUZ2~aY&(l4ahn>?FiKbR4Fx_g!it1DU1LVPV#ejw+Yvb0Kb3+U55{PpS1bM$ zwy;9kPrKQ?hG7bxmqyefoB}{i58(fTA#Ix2Sq7kx#D)QF;ly=VuBt6!Vq%E#$SA_dIXd9y<{GFR=@eX5LY-nvRMa=QT<6435S7Lfg`u+~IDA4_H~s&j_OlD2)y zcdQO*rZRZ1t;UkGv2f@lGkr>!EBA)^1)b~3O{3P1%?z(n?v4B3t_n8lzs7`dpWJ@M zwCG-K3X6`q{v;R9Ad<}+i5aJRTw^3?LgtG*mCjqnO}hPlAJ`)jom>C0lci?$J)R_) zAbq#`td41je<#;X%~yG{NM_{j-=8R%+xDX!SL$+DpN-f4^LAHeOz~0j>4{Ruq4+PuY#Z)OKBswwBjtPZd`!_%s~zfuCSt_gD(fB|e;Q%cvX^=s;Bt z#A>|#$RG?al!<3vqkhAn48pIFFP5JnM(uR@r*#_$#losW4Z!zPE;TIZJPE~0L_!hy zT{8dvXShp$Leq5ns+sjK3rdyQR0{^J$>-gynu={Qabn1m(J!`#F1-rU!XjVG9}999 zJB>5*vNsLsgmuq}sj%4KP9@4rCB>hKz07h>>~R%(m^~Z(z`(Y^NoVHcpQxX9MM`hk zR-;5{&*|+$pWzPNV~M{O{WL#%p~G7xJyE7k#(_fs-Je>RcF~-i>@0;B%F(>Tez)l( zDDS2}{3&!mkt^!z61FC;WBnGzlMabW(lmKW|+*wGF zwCS*K1IgCldAsM=LBd5`#-EG8GEQt>7BrfVv)2RpFPIr2HD(Q>hc^~M!+V~;zr*zc z2i;RFhxK0-UAbw}+-2D0;G-QmoqrTKZ%u073AK1~iV%M8bas5PNe}OIasAh?J|O5 z8~6&2VS6Ray-H3l;rc_{TQ`G3;zm=+6RGiC(K`R^e?3~RJuuhtb23xROSTM@zwGXm z+FY$G5zKdu&Wb!I*(%|cXLkukteqeb!FBDb>D7@-(;|gGhyN{i27S)Y*X)}{j5j#s z>Zn-W%9Q%k3fpn=y^njEf01|W8;WC1`tGXF9d zH^26K*Moh=tW(-ysu0K}q$kN2d0!#0 ztjfgStbDBLLF<#bviL@J|NY8JSO0r*BzXSGoCs08hI=XOPw)*}CSo;s+ zR4TCX=S^!clif@evmv&udUUDL+eco2y0#dqO#i#jz?l& z1uWQno)21M87Y(g@q&A8ebe zN-J{VHHS9LpU>9SZmUOgLemLLdM)LXQ8;U7XyKB|o5vEk` zO-93v{J$ooTW~a=g@QFW*dRd4C0ah79ga~Ku?0&jLE%qp(i0*JhpTeBJNe?T7o(hh z1S#;iC3lkx@sh7Cg!!E#T(OxXnC0H=bAcuv;$spFZ|aMa;{&j|hWPy630x%(5`E5% zQ^v-|4iHK8vk2pUj+=H)SD5jWYTdI{W32Nro!;=AMX1Ap)!UM4mA?tD4B{* zBdm{}AUn%{lR6S*_lJ$vGp_f~tv~^^w-ShJ zJz5mVwLnMPF?;*o-&ON^+2SAXpcDL5=;%zx?(9J_gB?Z;dAs2)9K_}?p?95FaZ-D&P3g{qyeo5VAWjZG zN07~Z1&2RCU%`t5Nw>G3JXM7sw9QSnpXEn?-bEU~M(vhPe|=F%PdsSpQs#RQ!4O|V zxhJO^Br7^ef(rl8^j~AC`KKbjt?+>1+5nc^OKBeIV%qXN;jQyNS@;|w7{yun9>N%j zYG`3{#SyoK1Nm~?XW+x&I2;sPAaI8`R(`BPi1G_-BoRUnC6_78LlC6v>#p|~ptIi7 z#qk6(H{h$HxQgX_AU`O)tsrR55z{~>Hz-9tJ*RZrnd*qUi()N*^oHd-881G6mV%@& z8s?8ng){a4DFnT+Q!Q!!<=2rR^o}6er!8F1>4t15p^5S}8IMw~Ebdbf$)sxA4`vj@ zoj>IC;zcd!#s_gXP`yLuik*sXubJ?WznFiCq?1WN(#E(slT5LHdVqH8c3GUKDP48K z&v!e@R`|VpPXc}$Ouwn&JKAX=Z8Vp9Oh_OEPuP+4Ie!yvO;uKtCM_7+C5^w3&!Sj- zP6FG&VQh7lbsZ$mE<$v1m3L?j9FVe39Z&P>ktoIg^9xdg%> zl_LSzBsS|om>w(^ART>1C1UOI$V{|k@==A>f5@)Ru0nIh_R6+kKHEXXoS%xsNsPp&|&xMb62z2hnOc@rL5Ble6IcGk>1G3BI z)b~usa9GcE+EYJizzI2$DwaO)(Nz@RZY#QLJi#J+4+_Bg+g7gS=j1ej#T&`!e&MK4 zt`-o8mCNQ=`z8?9uvQgtsgahEJf+1{0I6b0gA5~_)Cjz8F%kimDE}MNd}>9thO|13 z?0Xm-C{D>Sq3S@O!7Y9%x`8RwDI{bD1ofh!YpOUI9%R;ASG$hCNtb7TyU~_01?A$r zWjW=?`(=dDX4iS?4f?+ou1k35HTq(y@9>4+0L&iF=%^TwW4ZY>?T8gL{Z}I$Y9>*J zqr?0Jzozo`xMseb!>fod$RYqY=u{6IJWc(c+&ne(J%H#8grqx&Q27uBmxqk>%BRJe zuCtK?bu6+K?c*OAx=Pw;>2Jcv1MzGddB@&DLc~3=3>=R%MZSCXQMd9JVQMquUU}TQKX*y zv#dJ@O!r8Usm25qoJ zz}Dp)GjYm+UdaL5#m@~fjZMSn9|O(-jB=qwB1`R>**du{sa&b0!%^i7qQH3^|0f$@1@C`_YU$FD%@UfEYl3Mw$oZ^6ZX8 zJYi9f49~k(>+(i76{|y*V>qVLF)3X8__bI{;u510ihNwzIc2UB#|CeLgO{u$??1;i90AwiPo1IDG3`i@+xgW zbj<*H@%IN0sA|97STI=dbN1k27&`tS?VKA}m#I}?h3;bG(zuYUJqVM~ZpTLWP7z(W zy8+$ex>biNObzUgST`*N=*988M0+RzU9V$A1FiUS-~mpyfz)p2j^-*sMP z@s86H%;#UJG00^aeN4sABHbBvr1OHrJRW>1@OHqdw1Es+xU++K7R1aSDRV#@qu+qq zSEUqgyd)e6j0ExiVyyf3@9ymp6d3+{SaOge744?o0+_OTYS97Z#Z(i4@)>F1S(S%k zCExkxL7*StnV>HvDlh0!!Rs>@DKK(o+Y3;nP%c!BrduaplMu_sDg(*^9Ln9y0!K=Q zC*XJeIif%^Z<2XG*+}xzLKKMYoa=WkYy1$FGi`hWl5@ATRk?$>`Dj+oHL)(#N8|KG z@Jawy@Z+g)GsX~k4R&qIsluN<`TSWBenuoxX652|cI%-a`a?=`^2m^T|p89@g_M&R3bD5^9v2`fkn%p84|m zGk4B|!sW!>z;MEO{0&?{*EyE&sPPX!IB=J2hDZ`Ey0TCy_wWDAD-|+(H_2ctJe%P$ zF1$pLKIB!gzGcxcCQL4M%{}TJLp*__laCLdwfO7TFZPZ{f!$*_8Guk$`NQypVaL95 z^f9-GB28z`s$y5Jc%NsFb^=L+`Cs9NO#;`b;L)cwHY+y51uIq%`^@66I2)QK9+R0n zc)c=eP76gc%#Wgy&yY#(?SQr;#5w)2Y#e$vdpY9 zrtaqaS<0KANp)NqD>X&CSFI6BI0Q|PjlgF%gy3ABgI{b}#VV;g#tW%y zqFB@~;ta`D8$mvd&T1^qRuk+!+GOfdllFQq=+qE3-0R&_ml)ZvHfJ;}5yD2ZlLdt% zNWb+sIC?hz6W^zlQy;Bd_2i_okBciRc~Wt;QO`x2 z*S88bau*hI@ltkWng3~5;{170n3D9n76Q)UfoctQdKT60A1k|>S4}C@rAX6_3dznr z2<_Snn5V>NTJ);NkW>h{O8sU>^IR(uv}O-u5$&E{2t%X%P~y7L-Af@^2L>4J7V2dr zPg+-uC(;|+XFYMD@VyBJJ-3PZ8rML%+A+E>a~g+Bx=Alw z1P=+(&j}>yFM9212 zy~D0YadIH2igzHkYNkJd4YY>Y+0$1gJHDxzHcdCfr>=kbCm!mWi0BchIm*i}bJCo` zcfmYlnFx6U=U5Zq{chq%+vb!4LnQ_f!jBL-tWr@+b%1nD6vQV_8tWO$IPz#D=6X&{ z%c-ce!pA&OS!siqg+OlFnDvDijUw2Ne)WXAjFRqU@v`FEMa4L4RF$p0izBYWD zn_fe>R2MmV6eEGvjmOjbE`s7N=u`04|npL9NLYmc+;ug-&1k*|SIB>MhrQ4s9H}R!HhH~_lV48hwh0&~x83t_z&=O5sslIu`Gu3s{pE>ZR{m>CbW^!*n` zu66_XlU9l2xRb#hq<-}P3$8I=)0t(4Iw(kZUbQu2N8pB4PpO%5DS$~3%?`i&w<9A2 zSOVPm_QzwuIeznnk1A(U=LK$B2k9nBXf8Pk$`{BNT{`(zPYe788nC{+QuVnb) zA|GAH@19fDFw@WBKv zY{Pzr8$T+B`Z1Z^n^>ulYH02$Eyf*`_$YqJvBIBT|C)$8MbdIO3WHYq;bl!G-7JV# z|DeyQjt*DClKB6T<6@B!U~v2;I1ulFn2RJGkVc|#Mo|`j{>hn>a6#jEii)7K5?A7x z?R|)&mR2UgyQ$GtQ~-=pPBy;>s{LK2sNsxrwsK2v8ch;i*+p^z$t|i%z|-*B|o-(IQpx zXtK^hEcT)KJH=~Yng3m%|4^TA1$69&AkG08`7h=c)f ze~=5Hbl2r9(XR%>6%oD#D3XvM*-1?3XR>jBflVKu%6k0xDe<%bK1NoS%^& zm+sp6&|4oK0ptN-4of3^YN$~`Zy6X1b}fNhfICS!7QqY4z}TrVDu-g_-xUK*W0ZBb zUWzgQ0wvv8Y5~&mjHC9`_!Ha^j+bJHX(zwqfn-j8ez3X%iKBa->yixxfn=v?Ca~+; z;kkKUe5U@7a>3XLlA#Yk479O?E?bIqYFA;pDx@Pa{ z4eK9!qUrpjDkH3Gr#{}TO(YF^M_%eJ(|k8kTZlpyDT1K?sJ$rRK9!w&diLkvfgp$9 zPD-9Qqy10F3qh05jBYvBHiqxXA?_OD9IyMOC^2d8F;FyIddb$ncS(3EnXmXx(YCLZ zG)!veJqE`b`^W!_^9^F8R^jB&#v^yQo#dS*^2Tae+;Go^v@$#TpNU40d^9WMdBBoE z|FEO)BI&9-3m-p^A**+#BVRy)_@xnEiaSM5jl4>_r1<4p^W@a8ycM&N9zr~z^v<6l z8447-WX^-gyHI@g>IAQ&@xwPaASceHpK26=LkDNKTb+`Lty^t3bLRe)l11W2a#B8m zb>9eK1Vxx%aE_m|c$I({eZ@8_C?m#yl z--|!D(WAA~pSC`p?4_LCX$A*2V6Zz70b~RF!9$1%RPZpon4)66AB+C2=dowU6RP&R zh|%ZSDk;nB81)K z#PgLoF9@3`SeS_2nnWcC|3v+%GBt82eV@*8y1!GXQE=sc{@AX1hG3;g#V9!6`Nk8b z`A}am+LSYB_J2yL_v)hf|Jy+F_fRehx2uw>M;TFyh^|^+0gj&2E zRk3D5qSjYR?-Lo_HHP+}PXNsbXuNYDDCkXmQ%c>#SbyVfHpFL7=NeXB|I8I0FC!!4 z^6J{31FH@8)-1<|$awmDgQjrXOI zk!0-PB8=O_`xZx6K?H7B#?legwPT9n>h``;`+*rZ3}iG3BSs=gpzglePjF8PH| ze7wE?1rpN)VM(wIpeWRcxeiuYvSoS+sCot|nH`3)(KN&k(7KmbNLwPRe!`0l)rJ%y zN`akBoR?e4=14p8#96z9HY79ki7ksVD|*BK6kDUJu7e89jXGAJEFNLmuc&7bgk?F_ z*~N>nRfQJGB(=PazHWgu9cXs4#2RuW(N}ql2O$ye)*Q4kFk5!SWbM!xt~FnvtEk4E zek`faLT&(|nY4cbL3OC7(C|ojU;p1}0kxt6eJM9?t}BdB)}+n*Med3YkqQf)7pQ!% zU+vL6&Q$Z!bxmX_2LEsS3F+`wSY!G2w+yua9w}cjG<`4qPxr{E`}r*z6&BGynM;Mw ze4zUK&DK3g{*L54nE|c)#`l1`24eqdVFztDbN3X zn@r+r|GiVlu4w~HyL zD~j%1iHY7qmT25cd{|pV%^!yJtX#{3$Oa-|=9%i)+aNkDE1bM&(}M1^$9HsZeMsx- z3+S`YjR7;UbR7O6^!-i!0r0kkh`~cA?dIT5*BuWr=6!%yKH6N!KD@kaJ+J7Hwsm@M z@SgI?;Z~e+_^R2-TZmFPSO7yw>WhHy!#pxg_2KKDX^`v{yj>7_LhQc{6W~p;gq?io zO~JwEWtD0HepAU{dPcgiRt&NjFyR%1y}qa zY-><_K`sIxRF{UzpMTJWBz@aGW$(i1IT_Vu@XoF*){LjHjmZu771Hoj{>0(p3U33u z0unXRX*pJId85=)9931aOH^7RGInR$<$lYnbXKIeBIq)p`np3S`>pFaD8MziR?#Wv zU6m${X69(q7mLjJ{(Bgkl zd6;gw7+}vrm&|#rE9+1jaFc;h{iPd^Bl$bAy34?w%opeiD@n)fXr~(ab=%&YF=`NU zx66fq4QTpNKD9;@{KT|UF1p}W(FUDto>u+_T)X+Qf&pY*+I;6m@UX{jyLiQr2Vz3* zTHt7Lz9Jf+(go!*Fx}+D_w;08Qwyc@QIJ-(H>z8ZQGU&{fn>vk|S8t!)q{ zx-U;=wCS|p^$l1;Lf;@C`iP&GdQeCT@*Vh7AjSTs|GN2`n)wrw(On?|IYNt1B|JUM z2x0~90ZF8CC9>)z9t)y2;5Y|d$z;MKg>lk3hq=nd%x>Z~GZrOV8OZC{Q}~uX`*e2F zS<~+F=QZN93Zh(2m;YJ|mnJ&)C8r@8+E}Qk-lc&6S+&_7qx`MYN|ce&i{FL?aqQs> zD23?d6&5K&T!h;;cM!LPC)!<43361GvoWgLf?bEM72}xw7SpO zfnaVZd;Bf@6~iq8V^s;2t$Q^x*xS_G_M+dzB1F6Hnih5JoSBKn!UYytp$Losk<&oqFo7stgtSF2AgK<;*-aUaEZQi>#1MQF-plyK(j?4mkAdGVl7C* z8B@&i!#&({`Ql=rBKYr#KHXl8bX#&xso0Z5%1IXWI2z zU}zgq!%5?HYM(g%;mib5O~2C82bt%b{whFe-e+{vRalh&(RhN?K)hDe==&{wTy3*Y zy3Mg+Xpx^g$`>{DSgwfvO-p4~o`ptBIhO}xu^*P*PXR^Fe&@<+rHadIb#wnjb^`=8 zqcKSuF&{x9ndf7!BR`r%=T)V4brxMRpQ!PCjDWUUtk$Ex%Naw7mCroJo>^%5KSv$b zAYEE29)HL{n_IeJdIubj^ga@&&I9R1G*0(aW|bn?*MGyut!rj33;V1?S9p&GS#|4*0D7 z`n4}FGGF|Dl=n3M`ri;0)&`-Gh1%et;3qWQ4dKbV^W~-hot0t zv>hSM?g&%I(5-(jjd}RzZ}2gUz&%fNjN2y0;4;Uc5YR|oC$9}0QjiO-b}`#v1~k6J zB+t*EbD^>5K&%z46KtcxH=|6*vNTd(z^n!&JRtTd3Pi<@!JvUJK0Gfvp%v9dFZ0%< z47lFb7~YWAL{;&caBXarv&>2VpkKru4?=EVK`#ullk3qQ6DpclMGlvdFt?sRn5LdnIvvOG!q;0xt;%L2HMo~F$8#dw&K(Z5y>+$oGOn>Nq9&3L&i zK$FZOQlmDX#P;2i30T}1wX5QlB*IkmLLjUe`tPJ)=qyxsW~ZeNA7hjmjvZ4#l%D$? z&r@jr3v9Qhl^?URSUb%Yx${Ic7Y3BmDjFgVxe8yte~moE-qxNN(i@TgFrM*@^{>R9 zeni1?|1BdA7W(DiCsb4omPpr!U;5?hO#T^4RmoHe12ruc(7GrEU@NfowJNHy~jimh!}uOXG@StHHQd*9u-B_%Sw zi^yqSICC|n)Z(vL!|gs<#4>DKpv&UG0m|b4v#&cLXqM{Ui1qkgcHnZJA|@Z*~eU>d+m!n)Z|T@V;Z@ zxQ}Ncgrc&%{K=DupK+r3dmF?`p^xu>1l@9SDti9%r5EsZfcNfR))*`cc+d+0KP^DF z!iDEGlA6o~5h}8m_D!U=3tiduu^j4?x3Jj}DdMpKg_H~alm`CZ%JTE~amttUiyy=p>NScamt|jVw0-NXfNF zAjkz~YSLX6fU!MY=^bJYOh|_hOuxSX$dJAfoUV@35if+1oi@4f&Zw8~Bqe7p6~Fqm z=s(6}FKo{r*h5d|2EF@-S=GFX?rCMYPg}QTR`t)lE|64 z5q8nByy~G#y>4-lwVB?M?a%x4JPCSh#nNmi^(8N3t1GIi-Yd<~sST(s$q-`pbauFh zDyVkF1IhVnYA)a$J8n~*Q>T~v&ihJ(8v^5b934}{5|WR|&>GY+_7o~)b0~CC4cZwo zDx-J4F`>2Hk&phmIf8mqGY?&g4H}IcWwUI&a`>&Zg0E55hQVF3LYv7`ubRe2uo>ML z%vtRhd`q^{4&-c9_Z#y zBH#`_d42uD<*}w$TYZN*`+t*s!-O_RWX5vCwsPijd8x@i0T3`n9@V|dWURFLPr|Y; zXk;Y+?o#%n{E*ekiOR~#XGC9h!3kvfAN&LR76>uV&t^bh6yXfiJ0!h4`1N9tMAi_? zko?YGi!o&*rWx<0*>B>K;xwtkPGTTVf7tmx-|wZcBI*)~Quuv^twx%rd%{nIvg^|6 zWSz`=|CI1_>u37EntdlN1Eraf@B4UaT521C0cOn+$aE=nz98%)=b%mf!^@9RpQXfw zW`YBgJy_^5$CK<}zChX2PJvggXhYu?yK$<#mifXYb%^v>`YMYNU8Yx^C`43DaHQVH zp<|Nw<%6scfA(^ErRp{0<$XbU3{ ze303OQgHZZ>6a;Sl$K{k_~uWz%l?T_%%3XNmsEDxJ~C_3q)rVVp8a5=TyxQpg2kxz zvBy&bTy@j^cY@<;Ick=>HSrqGitGaIZEbEab9QUwvv|^}X%|=PnTK=!zg&RfmoM@B zeyrrocdAO@#gJ&m$%D5Bz-Hy&U8FnnQFO$FONgXQev5QqK^+UW@Q1}T988|ozx`uh;8ab+xrv)EFzPcI*OlNz%6F$6R1YWJkj z^cMUUee_0tF`7W~NiE$-W1u;DNRNNS-IZJT6)E8@Z-4%xTT4>X^6)H$mT>kv6L$rl zhn}dyc{*=?#5(gSU9aY7T%Y-RalC;?!HcKU4>wB(-05jt`e9Jr6~hojBjODLq$lZ>9$Kr z929Ka&(zHk-aPeFSAu+*^;*|-7I&={n5-v;ywJ2xH6B8tYttz+{vBb;GJI%M-vW5ZeRWGoM%mVOQ!A!q-OBu!WBAU zSZ_h@5PA!#sX3ADXvyHX{%?}uv~aGCK#{)G8Qx;OGuk#2Ez%e{X_D)BAC?leXigi} z*9yt(YREx^4K0c8(-Al4HR(&(+7_LZgL!4K^7NO^?ojr+t151ecDr(ebufn1YZ_1%^u4vvlq?Q-IE z+XbFa4JX*Oav1n0JSMKts8>+p(c~)yW#ObaV(T4{1?710^X4jyH}Uev$>Ki5vL3z4 zZhf8avro#)dA5t|*{O1p+lA)MzT4!B4)+1B1ok&&5xH9c1pTZD?92A7-qzevlpSt6 zc&>zq;WVJHZ&pmdjyQXCe#x|H05ddwyXK4<)9P~tH;!@qC9cr~@_cwzL&qwMX~{{U zlZRB^^4~BKgC2Kt2zKqbzrU9m|8~PQ0lj5~ugT&&x!B2ui4GrC!rq^L+Y>HMCCr z5F3N%ZvY$tq)jmJf;H!#ObH_sRrE{4=-_O4x2C-K$F*^EwTOyiTc*g1O$*4=y5s1l zncvl`8P(!FAlPXH%s@dn%$bgcE6vNf@}Gop!aFN8sZtrGjygrHJzia`F}+T0X2Ka2 z?@H=54v?M6(&~@*+)qxM%Zc?8Qh;RlG~`0YFXy_E1Te~wbw9KB;`h23My}jO6}wm+ z#n1k;_jGpD7o=RR%R4z2oF;Y^J@f8zSHbF@xcCo*wJ<#p*7uKUYaRD0K!Q|AH5KWN z_{RU$r0V<$-F*4cQ+V>+$1O-Ev+rnM!QRY?WQmy<89KKaXVKRZZZyrQ#5GCk7*_zX zicR%odSW#!~T)HSZV) znvm?hykbo0o@8MI`Th>JIZsdA2eF~2r1I%28aJ}2YETjaqqOJxM_}zJo`U1 zop(6Z|Nq9%;n*XyB;(*1oybZ?#xb&z)j>%piclHJ=2%7cDtimZEL18p86|tSWMqfz z{d=Cz_xk8Kx@9JJHEtuOHEZjr)K6y&d2B0AJtz4Yiz=DCom^ z0MxYr-`%;w?yjm@IH22hHwwi4?ykcDO`qkrIa>FhI)yiJKh;{;r2^B=zgd^s5_hottPwn=gdC$EOGTn`7C~v19#36I^mu1#x$8>Bz9; zeMSnq1P)zF3UR<7m6}c^kz;dojq=DeH`r;DX`7j2t*Kqi@e$hXMIUY@GL1E(Z9Xtf z&p&#vzr;6(Om0kB(6e-R7(2=f6wtIhqPtbLaOpPFM+56dC1V;~I0u#u?@7Vsd0fFf z(ynuPtu7LS6f5A5nfyIUHp|p9%OQn8aWj)oFkV${ta<(V4#+@o#-<|!$C zg)q}q?elt#Zv;ZQNkc8J zQnP~XlqP1-AdkX|;P_agO#Y~YM2*#r&xke>M}f5%iNO}n-T zQ#hGuGTcMqCX7QTAgRwe)}#ved`1Nl51>fVXM!eHEyf*+W6{)3AoH=ZbzOm^9h&+> zkWRxQSQp9N7can#udI3n?C1tgNkJUp8WtY5@LOj*A3%vO!gr=C<7KaBI(O@VBpJ&E zx|%@{(g$G4SMtHFKF7B0mL%XDR{fkkJ!j7cfOoD@l~DfrYN!HoMSDs++4JU6(S4*X z8P9ArVu=2FWH-74C2}QIC{Xbo#RvyCvbCDxHr;PtdAD8P#If?j;Hl8p2q(o|MhObU z2=(h4vd`_(W&WopDS4w>gI^~DaEb)fS7c$ezAz`>c2-29)f>G{E%&snWiz40r4D1b zPk&Q)k#70pb1xNbDjS<7NQJgyKP$|nVlwN3@4Zlp z8KL}DqNjOSlIS;tx4|s3HYyzHe^8}I*&}5vx+UrjCrt^unew6v=V+`2eg}-K?Vm6V z3`rk}DdwTAcC$1w$|NI?Bqt6d*szF`w4q?s@%a|~O8RDqNW|st;-?l4X9NfW<8pQ} zj&?CGWy4gio!CETM=p`6Lmei6>lo0XdG7?tN8wuhhk z4ODLI`O9|>mY4+`kf5rT%H4lR2lKGRFy@=KazsFt)lRlwg&bn!4)& z1&*Cf75kqn)<6DUS)ZGm^9FXfS;^oW?7Kh(IsF%t&cPDTO)qusdeAwp`lo?KSNKx+ zUJ5aNlsHrK+%_2V*wLs)zpT-kS~40ngYk>}lA^#JAz(@1W7Fgc$5##aETVXlW{@V9sZ?B*-Ot=m4k?u#6Tk1sPrVkn)jRV z&I>}wxfH*}gB-9rsD4H9mKc2&BGs3N7Ft&)D{D`X zT?|5{OLm!EW30Aa`To$BI$RCxA(nU9wNq14UV`lk5}#+$`B`~uALLo(Ou2ZI_z4*M zcDdUWb=6TJ2Tk5_Zyf@JJ5*As;e2EyAkQq}WI7nxQU(*} zKL*n0^?k^|U;|mxtY2mKI6zI<5a2M-yd9!6YJU!5;1{mgJbE-#vinX;s15BBg)yw& zu=wEF3pb>;vAd)yO|V#=#Zg)}+Ic8&16U32&0uLB1wuyMjMDN;Qpw=)h_hqTGBUR_ z^zB3?&3XkcKVy=>KV}F#xd#=w=5T5v61SLbRr{Z~Ld(G7#HR@8h%D{3ClyD76F8ur zV2e7cd^}Quy17lYNQ)D5`%uBg8eaX?wo>w~eYuy__m*A;5NEqn zZxwRPXi3nCv_&X;50qXrE3-X)RVOrc!8pmEMYQ!(@i2po)0z?Tf|IbId=TL#yubxwzqkCV|*oPYjVe9-iV8A1@7T`<@U>Ux6^ z;iglH5LKtwpE2aKmqwUWjM#|bk{H#BwIY8vnPEup3tBlx@ygA@ss_))&xS@sl|8M( zXtZ40%mN$Nru5beNxFp!+Eq*`f!4Qz_Uo!VVI1N4{bRmi>T3z-8|iihqmJ)m%Y$j( zsg4T0{G>To-x_UC_xx#Au`}Nz-q!G4F6_r``oZJb7elsnX4*@x@`M+R(GRILNo73C z>-XDEiN2!28=Oq`n(DRwH-33x*dM_qgQHSI3fjW!MhM4R55YeGN%Znivg@iQ2fbe# zACUhPO6ZV$_EcYZyq5V}34)D0y`h*5JQs7jQhfCnaU6;h-(IclKy=dot$ei^~2-6<_`g{f70~yqMP; zepUzbX5YZ2Piko{9d(M1N(lN%2h9|*1TYiZdv!3R{LN6{0rT0c|ocK&5MzQ zG-4qCZ%iISf#Y7L06k?0;@X9asmSvHbfF4czxET#*Fj`8y9H*jWx5+GU?#bNQZu}t zqas~$F72zgD{(Efc*V%QRaN@q3A>imN|l>|0Lh2Rmc-F&42_m+>X4i_&`Ml~miyt* z`vxr82i9^2AozpGfA+j4x=^X0(i%@6+4$bLc)MOmgi;$J$1iawoedfNRM3TIa&>!) zJHZpyJa3SO0WwTri+1Ca(tPMrKj-*D?NX>6&_`!l7DhBSMUvv+TLo9tVD@+h0@evo z^e)FEO__jxL5c`KM@I!o<9eu|WO^_ANg8?w9zjwD9a7#P(viX*@HCUU<{=rn)Rl4J z(eGvo_{7{-*S?CNgHDGZJWQUjJ%@jpd1 z77u6(AYymr(Cf%aIJZM+vwrzaD6jpe6%znARyu)p0Qy32uZ-j_L_o z3h<7thuEFt+_9UxW*zYAI4Z`RFSk#}Ikm&qw@Vsl3HCUk0rBf@3x&{}G}9u}7`$}1 zK%%fHQ@wWhr-zdm?wqKUa)idSUT6Fq(+9xED^F&uJub@+4e|ka+H2F24aPVTtOMcE zdRcB?G;bZ)W`urp(SKKV zY>bM5rOl`KIID&_O}?=zTJ0hmhPf?*T#&#=Equ4R$ht_fPtG7xqU#pmb>qCfvvlUE zt*d!c&`;-8qJu-IS1|+x@k{m*M?gk#RPY~5Z3i~KVXt@iPCR=ij(z4keEZ29%FZKS zG3B#%5}m0B^)IK>7*c=tx?d`*^NRMAky6jemvXk)_t|eCpay({{=7s}-Q33sMyujO zloYp;Sy1)+r@EqRE`CbSj?*y8QlV#PodwhsoO(PO0z3l?8|lrTx8etw(Y*96;@skT)`Lvt;ni=`@rUmnyhfqQO&qw7*;^hg3F5rmN4;L z97U{#h3<3MZ?DW6_1ir@?_`vu@3DdP&C2T+BbHWvJ6)|0z+)31QqHiiLK+NUSq|5NjabM)Dh&`dK?eH{0 z;$;8o0ULfj<=+SStr2E(ft6s|e1sg_tWEuvArRD)%w)Sy0O>A zhgk;{X;0QF{INVS-$<70VBY;~SONRPYAB+%wzdL9EjW79Kl>ljk!4dlVtv^Y5aU$s)&pQlHf^#D_3YsZuZ{CXMT98NBS0dvMsbwR)f+6*^Ay@zO+) zO(xeTwZR}eoqvo^iGkF}tj(ZeZ#f^$te0J6Gbc;DKXlz2!iq*8|MciDop7#OVG@g8 zXzX4;^ErhQZram+!ymHz?cyUkEZu^5gVeB}Q7Yes7m!TuGhN)Er*|FpxXAbPj9$W2 z6Ub5bvD*1=&Pk8p<01-1V=yj^XZhd3GGlr%$E!_N9<(`p$NF-d-r5btZj#aL1uX+b zkTQi?^3`l>R^#k9l*9w-*V!^NkvU#ID4=Ju?FDI@tyUvx?t+@TF24;tTqdVp@ z(_2iveLG_q;>h{dF=RAyWmfTFvV5iO${Bl>%H?ZR<>4WeR-fDvn0c-TPOO7AgPdr6 zw3nf(9?L0;b~PK84<| z|MuqoyM+N`@O_7Rpo+H1)tUH?spU*N^O^9Sn1O}R6P95XwXV04&U@sodd(D8X^m6-+dE8`aH% z&jksX2ne10B$q(IW<;Wryc!h#XzsDPf2S{!SAEgP?1HusZrvuq&y129sS3kh38_-p zV>Fq9qpAv;$!`G9@{=lKJgcVooTw{4$|Y7IUgKh1V%Oz=1oE2<+K-t(^|@#$u(EL~ z%{T5-;cEPaII1QKpf~16`xLllAAD2j zPr~V+&c}q{Yep+TeacW#`hsdePb-o>M{Zg)ktVibxNz%ajpJyo(AVcWrlr^4(|j*p zoZDu<_uTqxO}D*G;<m>d18c&(UQ)QEeGwurs9PBv-8F!) z1r5DqFof_;WkRdfGD2Y)1r+wWq0htevS&a;6RJ+lT6tc!m~z% z31)}^1+SpyWA!1lGTrfZi0tMnGN&g|`4j$Z_Xa9hDv18ELR~GMi0wty#j2-xvhn$R z{y`r;$;X3*ok|log%T^DfDyM2E+jl39ZKl;Y+o@Xx;GCYL>ye`C}W~4Q5Ie#>1a9N zXm9CoZ}|;4B)$S6%{9kx`^3z(pI)~P1-4+*TKJ;EqE>2~nONzed@yGa07oS>Ue$mq zt|LX67J{fgiAitN@zJl5HA=W+XcjL`k8V1v8BTkmiBg|&GCEhXK})Tm+JfmJ*Lwx_ zpnE~~^uir@`0x|njyjF4uC88<)p5Vh@Zl*<@5kWZf+PLbDb`P91Ut+e&UMI7o_Uym z0=b28kA2Fn%GVW&dS26}6W2%-p{F^wex_&~bNs1!i5wl0moeCf-+4L}D9{Th>??Tn zbq76bw&F0_3A{tps>it#J}OW9(6B&K7y#e~T?57a*NaLif~c@&n0fp<^)t`2Ko}=K zhovJ1UHTMupn?0s6V%o2$%Q-dGf!YQ`_u1%V%MH5hJ#F6e$`Vm%tj~TDY|4#aDJ`u zuL z(>}-PwMWW&g0|-7=0;j`sa&VdK`MWL>+%s20G*wSeFc}E0W=>pp?WLoS-4= z0#+Ro>5x`}zp^zx6sBiYL4{N4vLU~E%m^~ha6<)Yc{nZ8^`@(xo==u9)cR~n5+q6-{; zEjo6cM!XP;#AH}dEi#rxFSJXsAAVeJR%@=nj%fF(9g}@%z1zLoK&$Qg!{SUfgQI~= zCCuMOc}9(t-vj;f^Ntr&Ixh50ak=>O1_WJhDRB*BUR zlzbmo(^rrv%6Q&H*1ZLz&EXv>i)2G$X0uIyc~xwF9=9##SoxulODl|{&?xlOjt*`r0`uE-g^W#FB@^HJEHolmk3afmcaZe|djT4m*oDd1S!})MsX4T7Nj~Mc z$8u*#3t;-rTDPz6UC?Q+(Jfqe8VaMORcv6*1n8LS29F-yA=+?^4g>Y$|8X|jw z-fzPs%|bG{(v;V@mC^64$l|_1?L+5h`;3gq0RpNRFI9TF7K;=*MY<*KX&?5B!nOFX zD3<$JdQ=a75?4k;c~zE6ba^IaHC^7u{5Ld9%p4mh$hXtRx+`$QgXV&u!d;q6!r&Gq zxkF#@s~|+~{JCYD-T^r2LDe9hkR{mbfC>vx|DUzS64RY+|uYDPnHJ=uqGc$knG;V&1 z^Nsd@?2fIiZD& zao^`YymB(Cg%?2_aYBf2Q;j<;#E~k~ z_F-R~gT;wWve)bjjEIWnmc=uhkKmpG++b z_st@6+L22cw5TiU8~g}Uyu|~M`M^W6EO}d-xulQhVrOIne$Wpk_~7S!3X17W0U@Sj*cue4mHzCniZALmO-{<~e#$Nip6(@o7+L43LfS2zpE1h9|tvGWAJbu4z; z5u&jWGKJ0L+x|Z2#KXH=@N^roDsp8;*G=-!tRsXG*4v^XU5UkkYlFH$v~qm2ZQVH= zuXt;8a}NHkhfL1W@Vh6b3GspP9?(6CNN|BJ<92JlqAIm0st}e}eM0DH@HA&P+^$}U zr^2UD1qarcm%TKSOU02<+gGSKwq5HRZ9*l7IT>a2*ccG>2gr~7QtDjtzV^bk@Vm?%WkdI^6RUiPtA54!vD)*@SLhj$1{4$F=@u41bDZ)NhF%R}b zAm5P)Ct38!_V&dUaJ#|+#!}{v#x)Xs9eLrC=S4D2Vq^MstRpoonL=r4shm3p)Dj1M zkbVAzg#D6c0A3jAxj_4J=X(o45SVURFB1yW-dq&(k_tv=VWVYZ=?UI)kp> zz4F3DA63FJ981Kf))B=;pZ%(*!v8vj18)ub-dxT;L0*I-3;SWJ#2U|WPNXP(g6i6d zR?&$VokKh!(lan?jsqWmF5dNWW{OVvJ0#-ywkCFKTYgA_nv6=!`t)z!m0&F9#l!ov z-&$lx-0!XRp(372slA#f-^j#2r)&ssATKpGm+`-*dZRorz^;YMCstSH=;z$S4@nG7 z8s~Vc%b339M#@ZF+$=lJWB>f4IN7^K+a6^%RA^GTmT#z4&LAbjF+hh@|2{+(AUy7> z++wIkH?5%z?_jB4qRcs~sf_f{f0rJ;ufJ32vEl|xj6Hw+rUihgzGZSuaRtF&bmSq&<=@+RIOx^-&{9$*;2`$He}k-Y+ShkI zEP>0wI@v-856!i6(icCC$Az1IW}{@c3%p5TLgn|Vqvwd2M3=#!Hy-Ut6ON?WX=)1W z50IF`S8nU?S{lv0P`F-Z+SXc67%IV@)3o?x3fuk*zV>(5H+u!H7voz+!tnCW#y!3| z=W1B8rCUsrX|>8GC#f^DUC8egxr(X2?phf{U*4OLLGUCO^n{6}yLe?9MQy^h zGeof>G4gmmy!xl-%6CKZwGF{;b)>@n6FPvfGZ0lg>q?2%4u!q@HQ@ZFsSc87KYdL; zyV=8Yz3drCwj`3>b4%|Ipe4!!#9{NVN5i+XR`pKcKmw(v6r@tY42MoI*xz{fu%o00 z0Na(&PzyzHrl9PQB9!y)wHB`dE5D=AJE7iaau_h`^M+%zd*Pj%$B*|5{uT8eRozjE z_Mg`!Wmv?5gUU3*7@AfPlujd+H)-_IDzm2vzfW|hNfI!h&v9uCMTVaIih0a>Dv_mw zOjJ(Np07*$kw%1KuJ&r>-g?=)zgy+y^#h994NE411}yYKFH4KbsN-iInhhQu9o0Wr zu7Mkq8TRZBK8bmXQH=7;nKQ6!sJn>lP;elC!-6m|HPr&xt}h0~^+u{66ZN*R0ji)f2dFLd5l5ym`8@HJ9cg|FMb$5d|K5jKUy(WDpmM6H$`h|P&NPadw zp))0}!}h~OL@J8kK*vbwn7cUzF4(TPg++s=g-g<8Ql|BG3pzuk+{S><&R%C+w7=Ih zR%q&Mi$pu+1OgG>(0}OQzGs@JbWg|dBFCR7rfXB@r%o`5I?U3^D`V z#3J^~y+>@TMo((_WoTv}IsYko5M|oz{z$Q#orPe^IK(8W`%Sv&54lvwr}p>TUD(TZ z2F{1}hpj1}eolC?Hmn%-7W0mgQJoI&J>wNl#27tZJz^7<_%{`qUuPhkRMg*~kMAX8 zdP;@Yn2K7waZ75CZgBYT-?R60_ZAiwtim+&c)^MqOu=c6So^f#Q%F2i>6LvCCRg}X zX)0za%#&gsrpgTmTXNP2QO4FZ^7P=#KF!G0WE`_7c_6`TWcJsKiZp8?i$L-nnS<=^ zB=TcAqKkF9wQ*s0RTN#(2rg7;bbH^mCJY`y(fCebnnkZVmA3Ks`{GH(B)TBUe^-^w z+A0lm2K*mjeHDy@(T`JDve|lCxAudYCwYJy%)YBsULxB{ddlM7hdcD*HabxBc^z*8QSgcD}*F&I>mh_CNj-j;m*q`G88uOnS54Ud2gMQoYjH-Uf`;%~GPZGH->TJW zwRTa30%lsbNwF^MZr;uj2}c2x;)7M*qnBU_{gz;$d>8JJ@CNidT+8yf- zu>U}zbCXt)iQvyMkigH z%s~~$&OrQ0&`9Q`>5x_%%@Mm;${J>^jU7_j35>s}Af4%^bzS&c;S_<*44q(dqiBY$ z`$@N%v1OJ@LMRNuGYeA%rP4Qe;YSJ|lO>4DRJQT>IH_>ZA6`T=ZCecw4Mm^OwnuiJ zs94S!Cb@?8tCEtE8a{x4AFY|U2WvmL2f^9~s`WMrFJ9Q9#L05IK1>-3jSjrz1NIXw z;zepMV*a6MSl76z#~nluB)HraGE!UsvVfx8?f#!)p+c?G982k){eQVgHTP(DKn8;cRyV@`Euj_n8W zjPB-w)Dn+$wAAO)?~i!?)Z+e4qY!B^!KqzolBv~mMOyB!(%;-jQ*u3!@Nn8F$y{&rl#BWn(OahBu|6cUkPia0A`F!pUxt#o#*U!Z#KvC07 zRCpEj>l4#6yBya5{QaUg=gvBu$|Z8386@ut2=`svjFqfTKfS8@ea6jj+=#xH>`A61 zQFKyXEjsprYt(#kv;3vAEy}m*`dM;`eE)a_)8F#Qpz@=%{f<``;hG|_e*AGO$AQ3n zT2yj~Ek3I130u5VTAjkJ99(U-iLW z3EnASAtPiHHPpze%Hz-8u812iS-bGhYUB~44|WObGP!*7nR|4RuI!x51r-&KHm40b zG;V@ZAs_}hg0aM_B?=1P9p4@NKykNbv#O+D$@q7}n43Ps^C@n+RSi|7($OrVbY%czTMz_)Kd|NQ#{ zG7J$N6zs|@vbN2eGMct|~ zzqB~xb@%<_%?L+Jgtp*=G+_njyIQqDlMU~_t<|9=Jj)&My4QKTleowQGqGB+f7V1j z4Kn`a#C!Z@-#Pi|`OeP(YFe2Q%(0fX@N}}E%WD+3+@ZSCrAp+oyAmcr2c|0k1}?(f z!V&K;`TWbl8Xmi0M~A9L{D~Oenq43{Y+p3&e`)v*eXLp6%*}}qM{V0X@P7rxFW_D1 z{veo)Jw<(~(9LLW)Ks03QH$8j<&8Nz&qjyLw$Y@n{4DiRq5IP5;uUhmo7^<%5~c0r zF5Mb0O6q)4)9qz6xOGf+DYOG6ALmRfH&*_8z|ib)4&HWLiu|9nD51($$q0pGmY6f@ znpw*fm3SJ8(&6^vBdbpR;`i9a^o_GWW$_DLG)*R^OhFm*{L9zxbnW!<(Aq+(piWSa z7t8RArYi;;06Ivj^-8V9{ni4jy`I4ULWp(!n}~ef>2Ulh>bH0*K9Q@!_-=^_;-Evh zE_#*3He-D9gW-9I?&V1Q*+RcR?e$)FBIWXKjXBhOuck}+J2;pG3QvlLhSo})Mc+(m z-3G1G+voTZXqHrQ%sj0_=XWs1s`E;F`cwd2BPEK0=FCB_w#tq&W|>JhT@YU+5Elm9 zeg&_hswWXZI6Il?IYCp=c;m*6(SyA$AZmXD_{weY)g`2XBM%;;M@NT#q+4Z~>5rCI z(3(k0*rdYhfgJV7#pNad^UtT~l|mLJU~)1CO@rv#%6wkodX}D&+-ITejcAfCpwL~f z351``7yfe*GqcFr9s`)uUopvyMoUW%70zeqFTu^P1GleLL?*d}R2TF!#rvA%Mi+DJ zF}`9Mr`!&VZPLQgpncf}T)1QqNdhla;qNv(o!v<2%$p#JDHB{}-Uc}Z6Tt0fIen#8 z%EYM)!Tr0S)r~0vTN>e;lSWj1uN-6cUfIyI zu_{T=Nd5u*xo=DF^3#()Cf6@4I6@Xq`d}-a+fxX!UfYde!X@qAbXGPR)e>y%u9W?A znKzuTI(hX-(QN2R>+PQjre}uGk~)RhP6J223ZRtMXCZ9%GZg-o*eDG*xtWq*cE`@_ z^WVW%K8}R1^{-~vhN|zZy)=D?;Z9uI%b!MefQap?mOowm>13{$ox;NPtTWb`ZYOea zG<5e=Y%CO*>yJj&>{Fy#MhxKo`Rjz)Vm12J8et*Qfyv*zY#JCecK= z$ASI2o#ecQRuI$Kl!T5J7*ncz5&FsYo$|2_O>eJk2+q!n#H6L20$ztS)id2(L=WPT zttL=QSXGI~v4yEBvF;j_uVk@02$YOn|2U%&?v@7sMMM743GW)SnEW&}#82yJ^LP;7 zPqCOvuZ7?l+Nx;-r6tQ>t|f6^&Wx)r`FQs64UEdX6Ju?zrXrSnfcXY76IcGlga7wb z{n`&h*4Yp48Ag{HquM5=?>Mbl8%}SQvNt~FY+%whIyS2j1YHy$?MGQ6Q1ITqE<5{P zC}l>ulIkk!7@ZDc*fiYf`~{nHfO)aY@5bBJ9_w_?&=dM4W63ZnMG=Poqb*X@E?^^E z8+6tBxIE3}^Jy7#!T2`Ecp>-Z7rIT$z;rg~+KFCAksHHR_)99J9+!!NDSxq&|joLU-2Zo+Y&Gh8<32P&#QSy0S zLvvb_Eb`UkA?{5I9|a(5{YIq(8Wrkhnat(gv1GDgk|eMsjkDOv(G^*KHIPS$vSbA{ zWpAaFAXDQl(@QK)EK{{Nh`%h4M@B0HNE~~G{DINJ)MY!CJ7(OZ)1~&bR zJ43Qn0b3aXu&piu2l_hqvgZ?Ms2O|Suz%ZUI?3$LpU6iF+{aomcR5n+-BqI=aF{f! zmUU1dLev>L&{TqvimD;#%PgS@qE8A;PgTFGFgc}(x;d*p_?#$@i_E4vjWd9)B$$e2 z1Y}T)MuF|quJ?(bYM8A-8@TbpfM53?C~S9#B?K7)CSZ7ZtD!_I!@6l!iiP&C42#5) zjqnQDYXl?F7`OYlGXmYbH^~z{MT=wf3d&E@4ds+GqG+tW;ZyB$kP%QWF%a3(d#x64 z>B*aad;VrDT)X6%dUP}-YjWlzrO#Sd0A(&0df|VY zLMs6phQ_z1CAF#KqrB`d*Vf#4RUbh+C4k1kTL7#kpX=1&f{pqmY9K6vxRQ~PaSh~B z*iArxIqV8Ru(=L~^ETdq@1&|Z7-)m3jwuG(X4!z6|6Z^m!&E zu#(nU2cmG_@)x`FXPmqC&1Ri31zfoRZ|0||_*7ttZ^qa5YY8ufl^j~^;c{xl7Ui#$JlRgW2 zZZe4i`f8ZU$F-V6oC3#dRTRqE*q`g4e*5lS>dDp6^=o!63m8<{_*&H%dDoUEUyk%L zU&cdyh3?CHwS4J=mqItmx{l#wLb<-TbmX;Bc3RQPDD!~KE)?vG6g&BdYStgOGYf=b7BA1iV$q$j(kJ7&{*9!7A%GHOdKI zyztE|+4ydR7xGyd54^;= zlLB24aCa}kpxiALb~Q6(b^5$`Z8hfyij9{R7MA@#^ZdUTz!Bz>pAmWGNtG`vcMv*_ z7?t4S@J-``d-ta}>8W$^w+F^GE}LqzS$7vdVk(WZejpnQ2SLN$gviJq%`&g9QY2XVzfPFds3Q-l+FClJq~o%zT6d5In2 zoQZM1hGNDE!kGeFY4!M#N5BK7bi5cCm`aKK8Am~+VOe-$m(%Al;vus(;UA}0X{r7F zTt{fQ+1!OX`^Y}E%!oxxhFopE;?^}cApiSA$2V!fqrwO;s&CSSAd>&3X;bLPc&u7>J(ND~8t&E?S|LIbcx;##rbL zw!8DgRR{n^q{08P5UbDnY;UJrt!0HB{EkfO#~*L{?60-~KKL8RT_iIr{QQ*GI;EfK z3H`W!6Cf!(YGQ#Ci1@|b-Lbvs_dm+BiZ{IdC_ltj@{w1VmcX^!uS=rJ71TE;sg&*S z-ZGudD*!isr;yugKQdguDPxfBR+|nIGW?Jpxw}vQ-GS4YB;cL;wf&ePSQX `ffg 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ⅆein5s!yNF4&M01ZnEEG`{ii3l(BX2W*%nO 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 literal 0 HcmV?d00001 diff --git a/battybirdnet-pi/rootfs/custom-services.d/00-php_pfm.sh b/battybirdnet-pi/rootfs/custom-services.d/00-php_pfm.sh new file mode 100644 index 000000000..fcdd6e875 --- /dev/null +++ b/battybirdnet-pi/rootfs/custom-services.d/00-php_pfm.sh @@ -0,0 +1,12 @@ +#!/usr/bin/with-contenv bash +# shellcheck shell=bash + +# Correct /config permissions after startup +chown pi:pi /config + +# Waiting for dbus +until [[ -e /var/run/dbus/system_bus_socket ]]; do + sleep 1s +done +echo "Starting service: php pfm" +exec /usr/sbin/php-fpm* -F diff --git a/battybirdnet-pi/rootfs/custom-services.d/01-avahi.sh b/battybirdnet-pi/rootfs/custom-services.d/01-avahi.sh new file mode 100644 index 000000000..1963c3d25 --- /dev/null +++ b/battybirdnet-pi/rootfs/custom-services.d/01-avahi.sh @@ -0,0 +1,9 @@ +#!/usr/bin/with-contenv bashio +# Waiting for dbus +until [[ -e /var/run/dbus/system_bus_socket ]]; do + sleep 1s +done + +echo "Starting service: avahi daemon" +exec \ + avahi-daemon --no-chroot diff --git a/battybirdnet-pi/rootfs/custom-services.d/02-caddy.sh b/battybirdnet-pi/rootfs/custom-services.d/02-caddy.sh new file mode 100644 index 000000000..511299480 --- /dev/null +++ b/battybirdnet-pi/rootfs/custom-services.d/02-caddy.sh @@ -0,0 +1,21 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash + +# Dependencies +sockfile="empty" +until [[ -e /var/run/dbus/system_bus_socket ]] && [[ -e "$sockfile" ]]; do + sleep 1s + sockfile="$(find /run/php -name "*.sock")" +done + +# Correct fpm.sock +chown caddy:caddy /run/php/php*-fpm.sock +sed -i "s|/run/php/php-fpm.sock|$sockfile|g" /helpers/caddy_ingress.sh +sed -i "s|/run/php/php-fpm.sock|$sockfile|g" /etc/caddy/Caddyfile +sed -i "s|/run/php/php-fpm.sock|$sockfile|g" "$HOME"/BirdNET-Pi/scripts/update_caddyfile.sh + +# Update caddyfile with password +/."$HOME"/BirdNET-Pi/scripts/update_caddyfile.sh &>/dev/null || true + +echo "Starting service: caddy" +/usr/bin/caddy run --config /etc/caddy/Caddyfile diff --git a/battybirdnet-pi/rootfs/custom-services.d/02-nginx.sh b/battybirdnet-pi/rootfs/custom-services.d/02-nginx.sh new file mode 100644 index 000000000..08b3e2bd2 --- /dev/null +++ b/battybirdnet-pi/rootfs/custom-services.d/02-nginx.sh @@ -0,0 +1,6 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +set -e + +echo "Starting service: nginx" +nginx diff --git a/battybirdnet-pi/rootfs/custom-services.d/30-monitoring.sh b/battybirdnet-pi/rootfs/custom-services.d/30-monitoring.sh new file mode 100644 index 000000000..ff9ae0487 --- /dev/null +++ b/battybirdnet-pi/rootfs/custom-services.d/30-monitoring.sh @@ -0,0 +1,92 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash + +echo "Starting service: throttlerecording" +touch "$HOME"/BirdSongs/StreamData/analyzing_now.txt + +# variables for readability +srv="birdnet_recording" +analyzing_now="." +counter=10 +set +u +# shellcheck disable=SC1091 +source /config/birdnet.conf 2>/dev/null + +# Ensure folder exists +ingest_dir="$RECS_DIR/StreamData" + +# Check permissions +mkdir -p "$ingest_dir" +chown -R pi:pi "$ingest_dir" +chmod -R 755 "$ingest_dir" +ingest_dir="$(readlink -f "$ingest_dir")" || true +mkdir -p "$ingest_dir" +chown -R pi:pi "$ingest_dir" +chmod -R 755 "$ingest_dir" + +function apprisealert() { + # Set failed check so it only runs once + touch "$HOME"/BirdNET-Pi/failed_servicescheck + NOTIFICATION="" + STOPPEDSERVICE="
Stopped services: " + services=(birdnet_analysis + chart_viewer + spectrogram_viewer + icecast2 + birdnet_recording + birdnet_log + birdnet_stats) + for i in "${services[@]}"; do + if [[ "$(sudo systemctl is-active "${i}".service)" == "inactive" ]]; then + STOPPEDSERVICE+="${i}; " + fi + done + NOTIFICATION+="$STOPPEDSERVICE" + NOTIFICATION+="
Additional informations: " + NOTIFICATION+="
Since: ${LASTCHECK:-unknown}" + NOTIFICATION+="
System: ${SITE_NAME:-$(hostname)}" + NOTIFICATION+="
Available disk space: $(df -h "$(readlink -f "$HOME/BirdSongs")" | awk 'NR==2 {print $4}')" + if [ -n "$BIRDNETPI_URL" ]; then + NOTIFICATION+="
Access your battybirdnet-pi" + fi + TITLE="BirdNET-Analyzer stopped" + "$HOME"/BirdNET-Pi/birdnet/bin/apprise -vv -t "$TITLE" -b "${NOTIFICATION}" --input-format=html --config="$HOME/BirdNET-Pi/apprise.txt" +} + +while true; do + sleep 61 + + # Restart analysis if clogged + ############################ + + if ((counter <= 0)); then + latest="$(cat "$ingest_dir"/analyzing_now.txt)" + if [[ "$latest" == "$analyzing_now" ]]; then + echo "$(date) WARNING no change in analyzing_now for 10 iterations, restarting services" + /."$HOME"/BirdNET-Pi/scripts/restart_services.sh + fi + counter=10 + analyzing_now=$(cat "$ingest_dir"/analyzing_now.txt) + fi + + # Pause recorder to catch-up + ############################ + + wavs="$(find "$ingest_dir" -maxdepth 1 -name '*.wav' | wc -l)" + state="$(systemctl is-active "$srv")" + + bashio::log.green "$(date) INFO ${wavs} wav files waiting in $ingest_dir, $srv state is $state" + + if ((wavs > 100)); then + bashio::log.red "$(date) WARNING too many files in queue, pausing $srv" + sudo systemctl stop "$srv" + sudo systemctl restart birdnet_analysis + if [ -s "$HOME/BirdNET-Pi/apprise.txt" ]; then apprisealert; fi + elif [[ "$state" != "active" ]]; then + bashio::log.yellow "$(date) INFO started $srv service" + sudo systemctl start $srv + sudo systemctl restart birdnet_analysis + fi + + ((counter--)) +done diff --git a/battybirdnet-pi/rootfs/etc/cont-finish.d/savestreamdata.sh b/battybirdnet-pi/rootfs/etc/cont-finish.d/savestreamdata.sh new file mode 100644 index 000000000..215b7f9fd --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-finish.d/savestreamdata.sh @@ -0,0 +1,18 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash + +if [ -d "$HOME"/BirdSongs/StreamData ]; then + bashio::log.fatal "Container stopping, saving temporary files" + + # Stop the services in parallel + systemctl stop birdnet_analysis & + systemctl stop birdnet_recording + + # Check if there are files in StreamData and move them to /data/StreamData + mkdir -p /data/StreamData + if [ "$(ls -A "$HOME"/BirdSongs/StreamData)" ]; then + mv -v "$HOME"/BirdSongs/StreamData/* /data/StreamData/ + fi + + bashio::log.fatal "... files safe, allowing container to stop" +fi diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/01-structure.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/01-structure.sh new file mode 100644 index 000000000..39d3019cd --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/01-structure.sh @@ -0,0 +1,87 @@ +#!/command/with-contenv bashio +# shellcheck shell=bash +set -e + +############### +# SET /CONFIG # +############### + +echo " " +bashio::log.info "Ensuring the file structure is correct :" + +# Define structure +echo "... creating default files" +touch /config/include_species_list.txt # Should be null +for files in apprise.txt exclude_species_list.txt IdentifiedSoFar.txt disk_check_exclude.txt confirmed_species_list.txt blacklisted_images.txt; do + if [ ! -f /config/"$files" ]; then + echo "" > /config/"$files" + fi +done + +# Get BirdSongs folder locations +BIRDSONGS_FOLDER="/config/BirdSongs" +if bashio::config.has_value "BIRDSONGS_FOLDER"; then + BIRDSONGS_FOLDER_OPTION="$(bashio::config "BIRDSONGS_FOLDER")" + echo "... BIRDSONGS_FOLDER set to $BIRDSONGS_FOLDER_OPTION" + mkdir -p "$BIRDSONGS_FOLDER_OPTION" || bashio::log.fatal "...... folder couldn't be created" + chown -R pi:pi "$BIRDSONGS_FOLDER_OPTION" || bashio::log.fatal "...... folder couldn't be given permissions for 1000:1000" + if [ -d "$BIRDSONGS_FOLDER_OPTION" ] && [ "$(stat -c '%u:%g' "$BIRDSONGS_FOLDER_OPTION")" == "1000:1000" ]; then + BIRDSONGS_FOLDER="$BIRDSONGS_FOLDER_OPTION" + else + bashio::log.yellow "BIRDSONGS_FOLDER reverted to /config/BirdSongs" + fi +fi + +# Create BirdSongs folder +echo "... creating default folders ; it is highly recommended to store those on a ssd" +mkdir -p "$BIRDSONGS_FOLDER"/By_Date +mkdir -p "$BIRDSONGS_FOLDER"/Charts + +# If tmpfs is installed, use it +if df -T /tmp | grep -q "tmpfs"; then + echo "... tmpfs detected, using it for StreamData and Processed to reduce disk wear" + mkdir -p /tmp/StreamData + mkdir -p /tmp/Processed + rm -r "$HOME"/BirdSongs/StreamData + rm -r "$HOME"/BirdSongs/Processed + sudo -u pi ln -fs /tmp/StreamData "$HOME"/BirdSongs/StreamData + sudo -u pi ln -fs /tmp/Processed "$HOME"/BirdSongs/Processed +fi + +# Permissions for created files and folders +echo "... set permissions to user pi" +chown -R pi:pi /config /etc/birdnet "$BIRDSONGS_FOLDER" /tmp +chmod -R 755 /config /config /etc/birdnet "$BIRDSONGS_FOLDER" /tmp + +# Save default birdnet.conf to perform sanity check +cp "$HOME"/BirdNET-Pi/birdnet.conf "$HOME"/BirdNET-Pi/birdnet.bak + +# Symlink files +echo "... creating symlink" +for files in "$HOME/BirdNET-Pi/birdnet.conf" "$HOME/BirdNET-Pi/blacklisted_images.txt" "$HOME/BirdNET-Pi/scripts/birds.db" "$HOME/BirdNET-Pi/BirdDB.txt" "$HOME/BirdNET-Pi/scripts/disk_check_exclude.txt" "$HOME/BirdNET-Pi/apprise.txt" "$HOME/BirdNET-Pi/exclude_species_list.txt" "$HOME/BirdNET-Pi/include_species_list.txt" "$HOME/BirdNET-Pi/IdentifiedSoFar.txt" "$HOME/BirdNET-Pi/confirmed_species_list.txt"; do + filename="${files##*/}" + if [ ! -f /config/"$filename" ]; then + if [ -f "$files" ]; then + echo "... copying $filename" && sudo -u pi mv "$files" /config/ + else + touch /config/"$filename" + fi + fi + if [ -e "$files" ]; then rm "$files"; fi + sudo -u pi ln -fs /config/"$filename" "$files" || bashio::log.fatal "Symlink creation failed for $filename" + sudo -u pi ln -fs /config/"$filename" /etc/birdnet/"$filename" || bashio::log.fatal "Symlink creation failed for $filename" +done + +# Symlink folders +for folders in By_Date Charts; do + echo "... creating symlink for $BIRDSONGS_FOLDER/$folders" + rm -r "$HOME/BirdSongs/Extracted/${folders:?}" + sudo -u pi ln -fs "$BIRDSONGS_FOLDER"/"$folders" "$HOME/BirdSongs/Extracted/$folders" +done + +# Permissions for created files and folders +echo "... check permissions" +chmod -R 755 /config/* +chmod 777 /config + +echo " " diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/02-restorestreamdata.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/02-restorestreamdata.sh new file mode 100644 index 000000000..61fca2bc6 --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/02-restorestreamdata.sh @@ -0,0 +1,24 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash + +# Check if there are files in "$HOME"/BirdSongs/StreamData and move them to /data/StreamData +if [ -d /data/StreamData ] && [ "$(ls -A /data/StreamData/)" ]; then + + bashio::log.warning "Container was stopped while files were still being analysed, restoring them" + + # Copy files + if [ "$(ls -A /data/StreamData)" ]; then + mv -v /data/StreamData/* "$HOME"/BirdSongs/StreamData/ + fi + echo "... done" + echo "" + + # Setting permissions + chown -R pi:pi "$HOME"/BirdSongs + chmod -R 755 "$HOME"/BirdSongs + + # Cleaning folder + rm -r /data/StreamData + +fi + diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/31-checks.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/31-checks.sh new file mode 100644 index 000000000..47ffa5b77 --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/31-checks.sh @@ -0,0 +1,54 @@ +#!/command/with-contenv bashio +# shellcheck shell=bash +set -e + +###################### +# CHECK BIRDNET.CONF # +###################### + +echo " " +bashio::log.info "Checking your birndet.conf file integrity" + +# Set variables +configcurrent="$HOME"/BirdNET-Pi/birdnet.conf +configtemplate="$HOME"/BirdNET-Pi/birdnet.bak + +# Extract variable names from config template and read each one +grep -o '^[^#=]*=' "$configtemplate" | sed 's/=//' | while read -r var; do + # Check if the variable is in configcurrent, if not, append it + if ! grep -q "^$var=" "$configcurrent"; then + # At which line was the variable in the initial file + bashio::log.yellow "...$var was missing from your birdnet.conf file, it was re-added" + grep "^$var=" "$configtemplate" >> "$configcurrent" + fi + # Check for duplicates + if [ "$(grep -c "^$var=" "$configcurrent")" -gt 1 ]; then + bashio::log.error "Duplicate variable $var found in $configcurrent, all were commented out expect for the first one" + awk -v var="$var" '{ if ($0 ~ "^[[:blank:]]*"var && c++ > 0) print "#" $0; else print $0; }' "$configcurrent" > temp && mv temp "$configcurrent" + fi +done + +################ +# CHECK AMIXER # +################ + +# If default capture is set at 0%, increase it to 50% +# current_volume="$(amixer sget Capture | grep -oP '\[\d+%]' | tr -d '[]%' | head -1)" 2>/dev/null || true +# current_volume="${current_volume:-100}" + +# Set the default microphone volume to 50% if it's currently at 0% +# if [[ "$current_volume" -eq 0 ]]; then +# amixer sset Capture 70% +# bashio::log.warning "Microphone was off, volume set to 70%." +# fi + +############## +# CHECK PORT # +############## + +if [[ "$(bashio::addon.port "80")" == 3000 ]]; then + bashio::log.fatal "This is crazy but your port is set to 3000 and streamlit doesn't accept this port! You need to change it from the addon options and restart. Thanks" + sleep infinity +fi + +echo " " diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/33-mqtt.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/33-mqtt.sh new file mode 100644 index 000000000..2d6516a73 --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/33-mqtt.sh @@ -0,0 +1,47 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +set -e + +if bashio::services.available 'mqtt' && ! bashio::config.true 'MQTT_DISABLED' ; then + bashio::log.green "---" + bashio::log.blue "MQTT addon is active on your system! battybirdnet-pi is now automatically configured to send its ouptut to MQTT" + bashio::log.blue "MQTT user : $(bashio::services "mqtt" "username")" + bashio::log.blue "MQTT password : $(bashio::services "mqtt" "password")" + bashio::log.blue "MQTT broker : tcp://$(bashio::services "mqtt" "host"):$(bashio::services "mqtt" "port")" + bashio::log.green "---" + bashio::log.blue "Data will be posted to the topic : 'birdnet'" + bashio::log.blue "Json data : {'Date', 'Time', 'ScientificName', 'CommonName', 'Confidence', 'SpeciesCode', 'ClipName', 'url'}" + bashio::log.blue "---" + + # Apply MQTT settings + sed -i "s|%%mqtt_server%%|$(bashio::services "mqtt" "host")|g" /helpers/birdnet_to_mqtt.py + sed -i "s|%%mqtt_port%%|$(bashio::services "mqtt" "port")|g" /helpers/birdnet_to_mqtt.py + sed -i "s|%%mqtt_user%%|$(bashio::services "mqtt" "username")|g" /helpers/birdnet_to_mqtt.py + sed -i "s|%%mqtt_pass%%|$(bashio::services "mqtt" "password")|g" /helpers/birdnet_to_mqtt.py + + # Copy script + cp /helpers/birdnet_to_mqtt.py /usr/bin/birdnet_to_mqtt.py + cp /helpers/birdnet_to_mqtt.sh /custom-services.d + chmod 777 /usr/bin/birdnet_to_mqtt.py + chmod 777 /custom-services.d/birdnet_to_mqtt.sh +elif bashio::config.has_value "MQTT_HOST_manual" && bashio::config.has_value "MQTT_PORT_manual"; then + bashio::log.green "---" + bashio::log.blue "MQTT is manually configured in the addon options" + bashio::log.blue "battybirdnet-pi is now automatically configured to send its ouptut to MQTT" + bashio::log.green "---" + bashio::log.blue "Data will be posted to the topic : 'birdnet'" + bashio::log.blue "Json data : {'Date', 'Time', 'ScientificName', 'CommonName', 'Confidence', 'SpeciesCode', 'ClipName', 'url'}" + bashio::log.blue "---" + + # Apply MQTT settings + sed -i "s|%%mqtt_server%%|$(bashio::config "MQTT_HOST_manual")|g" /helpers/birdnet_to_mqtt.py + sed -i "s|%%mqtt_port%%|$(bashio::config "MQTT_PORT_manual")|g" /helpers/birdnet_to_mqtt.py + sed -i "s|%%mqtt_user%%|$(bashio::config "MQTT_USER_manual")|g" /helpers/birdnet_to_mqtt.py + sed -i "s|%%mqtt_pass%%|$(bashio::config "MQTT_PASSWORD_manual")|g" /helpers/birdnet_to_mqtt.py + + # Copy script + cp /helpers/birdnet_to_mqtt.py /usr/bin/birdnet_to_mqtt.py + cp /helpers/birdnet_to_mqtt.sh /custom-services.d + chmod +x /usr/bin/birdnet_to_mqtt.py + chmod +x /custom-services.d/birdnet_to_mqtt.sh +fi diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/71-newfeatures.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/71-newfeatures.sh new file mode 100644 index 000000000..96398d9cf --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/71-newfeatures.sh @@ -0,0 +1,127 @@ +#!/command/with-contenv bashio +# shellcheck shell=bash +set -e + +################ +# ADD FEATURES # +################ + +echo " " +bashio::log.info "Adding optional features" + +# Denoiser +#if bashio::config.true "DENOISER_ANALYSIS_ENABLED"; then +# sed -i "s|ar 48000|ar 48000 -af \"arnndn=m=sample.rnnn\"|g" "$HOME"/BirdNET-Pi/scripts/birdnet_recording.sh +# sed -i "s|ar 48000|ar 48000 -af afftdn=nr=30:nt=w:om=o|g" "$HOME"/BirdNET-Pi/scripts/birdnet_recording.sh +#fi + +# Add species conversion system +############################### +if bashio::config.true "SPECIES_CONVERTER_ENABLED"; then + echo "... adding feature of SPECIES_CONVERTER, a new tab is added to your Tools" + touch /config/convert_species_list.txt + chown pi:pi /config/convert_species_list.txt + sudo -u pi ln -fs /config/convert_species_list.txt "$HOME"/BirdNET-Pi/ + sudo -u pi ln -fs /config/convert_species_list.txt "$HOME"/BirdNET-Pi/scripts/ + # Not useful + sed -i "/exclude_species_list.txt/a sudo -u pi ln -fs /config/convert_species_list.txt $HOME/BirdNET-Pi/scripts/" "$HOME"/BirdNET-Pi/scripts/clear_all_data.sh + sed -i "/exclude_species_list.txt/a sudo -u pi ln -fs /config/convert_species_list.txt $HOME/BirdNET-Pi/scripts/" "$HOME"/BirdNET-Pi/scripts/install_services.sh + # Modify views.php if not already done + if ! grep -q "Converted" "$HOME"/BirdNET-Pi/homepage/views.php; then + # Add button + # shellcheck disable=SC2016 + sed -i '/Excluded Species List/a\ ' "$HOME"/BirdNET-Pi/homepage/views.php + # Flag to indicate whether we've found the target line + found_target=false + # Read the original file line by line + while IFS= read -r line; do + if [[ $line == *"if(\$_GET['view'] == \"File\"){"* ]]; then + found_target=true + fi + if $found_target; then + echo "$line" >> "$HOME"/BirdNET-Pi/homepage/views.php.temp + fi + done < "$HOME"/BirdNET-Pi/homepage/views.php + # Remove the extracted lines from the original file + # shellcheck disable=SC2016 + sed -i '/if(\$_GET\['\''view'\''\] == "File"){/,$d' "$HOME"/BirdNET-Pi/homepage/views.php + # Add new text + cat "/helpers/views.add" >> "$HOME"/BirdNET-Pi/homepage/views.php + cat "$HOME"/BirdNET-Pi/homepage/views.php.temp >> "$HOME"/BirdNET-Pi/homepage/views.php + # Clean up: Remove the temporary file + rm "$HOME"/BirdNET-Pi/homepage/views.php.temp + fi + + # Add the converter script + if [ ! -f "$HOME"/BirdNET-Pi/scripts/convert_list.php ]; then + mv -f /helpers/convert_list.php "$HOME"/BirdNET-Pi/scripts/convert_list.php + chown pi:pi "$HOME"/BirdNET-Pi/scripts/convert_list.php + chmod 664 "$HOME"/BirdNET-Pi/scripts/convert_list.php + fi + + # Change server + if ! grep -q "converted_entry" "$HOME"/BirdNET-Pi/scripts/server.py; then + sed -i "/INTERPRETER, M_INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST/c INTERPRETER, M_INTERPRETER, INCLUDE_LIST, EXCLUDE_LIST, CONVERT_LIST = (None, None, None, None, None)" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/global INCLUDE_LIST, EXCLUDE_LIST/c\ global INCLUDE_LIST, EXCLUDE_LIST, CONVERT_LIST, CONVERT_DICT" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/exclude_species_list.txt/a\ CONVERT_DICT = {row.split(';')[0]: row.split(';')[1] for row in CONVERT_LIST}" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/exclude_species_list.txt/a\ CONVERT_LIST = loadCustomSpeciesList(os.path.expanduser(\"~/BirdNET-Pi/convert_species_list.txt\"))" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "s|entry\[0\]|converted_entry|g" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "s|if converted_entry in|if entry\[0\] in|g" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/for entry in entries/a\ converted_entry = entry[0]" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/for entry in entries/a\ else :" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/for entry in entries/a\ log.info('WARNING : ' + entry[0] + ' converted to ' + converted_entry)" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/for entry in entries/a\ converted_entry = CONVERT_DICT.get(entry[0], entry[0])" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/for entry in entries/a\ if entry[0] in CONVERT_DICT:" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/for entry in entries/a\ if entry[1] >= conf.getfloat('CONFIDENCE'):" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "/converted_entry in INCLUDE_LIST or len(INCLUDE_LIST)/c\ if ((converted_entry in INCLUDE_LIST or len(INCLUDE_LIST) == 0)" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "s| d = Detection| d = Detection|g" "$HOME"/BirdNET-Pi/scripts/server.py + sed -i "s| confident_detections| confident_detections|g" "$HOME"/BirdNET-Pi/scripts/server.py + fi +fi || true + +# Enable the Processed folder +############################# + +if bashio::config.true "PROCESSED_FOLDER_ENABLED" && ! grep -q "processed_size" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py; then + echo "... Enabling the Processed folder : the last 15 wav files will be stored there" + # Adapt config.php + sed -i "/GET\[\"info_site\"\]/a\ \$processed_size = \$_GET\[\"processed_size\"\];" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\$contents = file_get_contents/a\ \$contents = preg_replace\(\"/PROCESSED_SIZE=\.\*/\", \"PROCESSED_SIZE=\$processed_size\", \$contents\);" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i
" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i

Processed folder management

" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i " "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i \"/>" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i
" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i Processed is the directory where the formerly 'Analyzed' files are moved after extractions, mostly for troubleshooting purposes.
" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i This value defines the maximum amount of files that are kept before replacement with new files.
" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i
" "$HOME"/BirdNET-Pi/scripts/config.php + sed -i "/\"success\"/i\
" "$HOME"/BirdNET-Pi/scripts/config.php + # Adapt birdnet_analysis.py - move_to_processed + sed -i "/log.info('handle_reporting_queue done')/a\ os.remove(files.pop(0))" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ while len(files) > processed_size:" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ files.sort(key=os.path.getmtime)" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ files = glob.glob(os.path.join(processed_dir, '*'))" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ os.rename(file_name, os.path.join(processed_dir, os.path.basename(file_name)))" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ processed_dir = os.path.join(get_settings()['RECS_DIR'], 'Processed')" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\def move_to_processed(file_name, processed_size):" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ " "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + # Adapt birdnet_analysis.py - get_processed_size + sed -i "/log.info('handle_reporting_queue done')/a\ return 0" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ except (ValueError, TypeError):" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ return processed_size if isinstance(processed_size, int) else 0" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ processed_size = get_settings().getint('PROCESSED_SIZE')" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ try:" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\def get_processed_size():" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/log.info('handle_reporting_queue done')/a\ " "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + # Modify calls + sed -i "/from subprocess import CalledProcessError/a\import glob" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/from subprocess import CalledProcessError/a\import time" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + # Modify main code + sed -i "/os.remove(file.file_name)/i\ processed_size = get_processed_size()" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/os.remove(file.file_name)/i\ if processed_size > 0:" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/os.remove(file.file_name)/i\ move_to_processed(file.file_name, processed_size)" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/os.remove(file.file_name)/i\ else:" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py + sed -i "/os.remove(file.file_name)/c\ os.remove(file.file_name)" "$HOME"/BirdNET-Pi/scripts/birdnet_analysis.py +fi || true + +echo " " diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/81-modifications.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/81-modifications.sh new file mode 100644 index 000000000..0b8372cfc --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/81-modifications.sh @@ -0,0 +1,66 @@ +#!/command/with-contenv bashio +# shellcheck shell=bash +set -e + +################ +# MODIFY WEBUI # +################ + +echo " " +bashio::log.info "Adapting webui" + +# Remove services tab +echo "... removing System Controls from webui as should be used from HA" +sed -i '/>System Controls/d' "$HOME"/BirdNET-Pi/homepage/views.php + +# Remove services tab +echo "... removing Ram drive from webui as it is handled from HA" +sed -i '/Ram drive/{n;s/center"/center" style="display: none;"/;}' "$HOME"/BirdNET-Pi/scripts/service_controls.php +sed -i '/Ram drive/d' "$HOME"/BirdNET-Pi/scripts/service_controls.php + +# Correct services to start as user pi +echo "... correct services to start as pi" +for file in $(find "$HOME"/BirdNET-Pi/templates/birdnet*.service -print0 | xargs -0 basename -a) livestream.service chart_viewer.service chart_viewer.service spectrogram_viewer.service; do + if [[ "$file" != "birdnet_log.service" ]]; then + sed -i "s|ExecStart=|ExecStart=/usr/bin/sudo -u pi |g" "$HOME/BirdNET-Pi/templates/$file" + fi +done + +# Send services log to container logs +echo "... send services log to container logs" +for file in $(find "$HOME"/BirdNET-Pi/templates/birdnet*.service -print0 | xargs -0 basename -a) livestream.service chart_viewer.service chart_viewer.service spectrogram_viewer.service; do + sed -i "/Service/a StandardError=append:/proc/1/fd/1" "$HOME/BirdNET-Pi/templates/$file" + sed -i "/Service/a StandardOutput=append:/proc/1/fd/1" "$HOME/BirdNET-Pi/templates/$file" +done + +# Avoid preselection in include and exclude lists +echo "... avoid preselecting options in include and exclude lists" +sed -i "s|option selected|option disabled|g" "$HOME"/BirdNET-Pi/scripts/include_list.php +sed -i "s|option selected|option disabled|g" "$HOME"/BirdNET-Pi/scripts/exclude_list.php + +# Correct log services to show /proc/1/fd/1 +echo "... show container logs in /logs" +sed -i "/User=pi/d" "$HOME/BirdNET-Pi/templates/birdnet_log.service" +sed -i "s|birdnet_log.sh|cat /proc/1/fd/1|g" "$HOME/BirdNET-Pi/templates/birdnet_log.service" + +# Make sure config is correctly formatted. +echo "... caddyfile modifications" +#Correct instructions +caddy fmt --overwrite /etc/caddy/Caddyfile +#Change port to leave 80 free for certificate requests +sed -i "s|http://|http://:8081|g" /etc/caddy/Caddyfile +sed -i "s|http://|http://:8081|g" "$HOME"/BirdNET-Pi/scripts/update_caddyfile.sh +#Remove default file that blocks 80 +if [ -f /etc/caddy/Caddyfile.original ]; then rm /etc/caddy/Caddyfile.original; fi + +# Improve webui paths to facilitate ingress +echo "... correcting webui paths" +sed -i "s|/stats|/stats/|g" "$HOME"/BirdNET-Pi/homepage/views.php +sed -i "s|/log|/log/|g" "$HOME"/BirdNET-Pi/homepage/views.php + +# If port 80 is enabled, make sure it is still 80 +if [ -n "$(bashio::addon.port 80)" ] && [ "$(bashio::addon.port 80)" != 80 ]; then + bashio::log.fatal "The port 80 is enabled, but should still be 80 if you want the automatic ssl certificates generation to work" +fi + +echo " " diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/91-nginx_ingress.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/91-nginx_ingress.sh new file mode 100644 index 000000000..d2749a17b --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/91-nginx_ingress.sh @@ -0,0 +1,37 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +set -e + +################# +# NGINX SETTING # +################# + +declare ingress_interface +declare ingress_port +declare ingress_entry + +# Variables +ingress_port=$(bashio::addon.ingress_port) +ingress_interface=$(bashio::addon.ip_address) +ingress_entry=$(bashio::addon.ingress_entry) + +# Quits if ingress not active +if [ -z "$ingress_entry" ]; then exit 0; fi + +echo " " +bashio::log.info "Adapting for ingress" +echo "... setting up nginx" +sed -i "s/%%port%%/${ingress_port}/g" /etc/nginx/servers/ingress.conf +sed -i "s/%%interface%%/${ingress_interface}/g" /etc/nginx/servers/ingress.conf +sed -i "s|%%ingress_entry%%|${ingress_entry}|g" /etc/nginx/servers/ingress.conf + +echo "... ensuring restricted area access" +echo "${ingress_entry}" > /ingress_url +sed -i "/function is_authenticated/a if (strpos(\$_SERVER['HTTP_REFERER'], '/api/hassio_ingress') !== false && strpos(\$_SERVER['HTTP_REFERER'], trim(file_get_contents('/ingress_url'))) !== false) { \$ret = true; return \$ret; }" "$HOME"/BirdNET-Pi/scripts/common.php + +echo "... adapt Caddyfile for ingress" +chmod +x /helpers/caddy_ingress.sh +/./helpers/caddy_ingress.sh +sed -i "/sudo caddy fmt --overwrite/i /./helpers/caddy_ingress.sh" "$HOME"/BirdNET-Pi/scripts/update_caddyfile.sh + +echo " " diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/92-ssl.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/92-ssl.sh new file mode 100644 index 000000000..8b6b05c24 --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/92-ssl.sh @@ -0,0 +1,20 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash +set -e + +############### +# SSL SETTING # +############### + +if bashio::config.true 'ssl'; then + bashio::log.info "Ssl is enabled using addon options, setting up nginx" + bashio::config.require.ssl + certfile=$(bashio::config 'certfile') + keyfile=$(bashio::config 'keyfile') + sed -i "2a\ tls /ssl/${certfile} /ssl/${keyfile}" /etc/caddy/Caddyfile + sed -i "s|http://:8081|https://:8081|g" /etc/caddy/Caddyfile + sed -i "s|http://:8081|https://:8081|g" "$HOME"/BirdNET-Pi/scripts/update_caddyfile.sh + sed -i "/https:/a tls /ssl/${certfile} /ssl/${keyfile}" "$HOME"/BirdNET-Pi/scripts/update_caddyfile.sh +fi + +echo " " diff --git a/battybirdnet-pi/rootfs/etc/cont-init.d/99-run.sh b/battybirdnet-pi/rootfs/etc/cont-init.d/99-run.sh new file mode 100644 index 000000000..8ac46778e --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/cont-init.d/99-run.sh @@ -0,0 +1,72 @@ +#!/command/with-contenv bashio +# shellcheck shell=bash +set -e + +############## +# SET SYSTEM # +############## + +echo " " +bashio::log.info "Setting password for the user pi" +echo "pi:$(bashio::config "pi_password")" | sudo chpasswd +echo "... done" + +echo " " +bashio::log.info "Starting system services" + +# Set TZ +if bashio::config.has_value 'TZ'; then + TIMEZONE=$(bashio::config 'TZ') + echo "... setting timezone to $TIMEZONE" + ln -snf /usr/share/zoneinfo/"$TIMEZONE" /etc/localtime + echo "$TIMEZONE" >/etc/timezone +fi || (bashio::log.fatal "Error : $TIMEZONE not found. Here is a list of valid timezones : https://manpages.ubuntu.com/manpages/focal/man3/DateTime::TimeZone::Catalog.3pm.html") + +# Correcting systemctl +echo "... correcting systemctl" +mv /helpers/systemctl3.py /bin/systemctl +chmod a+x /bin/systemctl + +# Correcting systemctl +echo "... correcting datetimectl" +mv /helpers/timedatectl /usr/bin/timedatectl +chmod a+x /usr/bin/timedatectl + +# Correct language labels +export "$(grep "^DATABASE_LANG" /config/birdnet.conf)" +# Saving default of en +cp "$HOME"/BirdNET-Pi/model/labels.txt "$HOME"/BirdNET-Pi/model/labels.bak +# Adapt to new language +echo "... adapting labels according to birdnet.conf file to $DATABASE_LANG" +/."$HOME"/BirdNET-Pi/scripts/install_language_label_nm.sh -l "$DATABASE_LANG" + +echo "... starting cron" +systemctl start cron + +# Starting dbus +echo "... starting dbus" +service dbus start + +# Starting journald +# echo "... starting journald" +# systemctl start systemd-journald + +# Starting services +echo "" +bashio::log.info "Starting battybirdnet-pi services" +chmod +x "$HOME"/BirdNET-Pi/scripts/restart_services.sh +"$HOME"/BirdNET-Pi/scripts/restart_services.sh + +if bashio::config.true LIVESTREAM_BOOT_ENABLED; then + echo "... starting livestream" + sudo systemctl enable icecast2 + sudo systemctl start icecast2.service + sudo systemctl enable --now livestream.service +fi + +# Correct the phpsysinfo for the correct gotty service +gottyservice="$(pgrep -l "gotty" | awk '{print $NF}' | head -n 1)" +echo "... using $gottyservice in phpsysinfo" +sed -i "s/,gotty,/,${gottyservice:-gotty},/g" "$HOME"/BirdNET-Pi/templates/phpsysinfo.ini + +echo " " diff --git a/battybirdnet-pi/rootfs/etc/nginx/includes/mime.types b/battybirdnet-pi/rootfs/etc/nginx/includes/mime.types new file mode 100644 index 000000000..7c7cdef2d --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/nginx/includes/mime.types @@ -0,0 +1,96 @@ +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/svg+xml svg svgz; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/webp webp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + + font/woff woff; + font/woff2 woff2; + + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.oasis.opendocument.graphics odg; + application/vnd.oasis.opendocument.presentation odp; + application/vnd.oasis.opendocument.spreadsheet ods; + application/vnd.oasis.opendocument.text odt; + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsx; + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docx; + application/vnd.wap.wmlc wmlc; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} diff --git a/battybirdnet-pi/rootfs/etc/nginx/includes/proxy_params.conf b/battybirdnet-pi/rootfs/etc/nginx/includes/proxy_params.conf new file mode 100644 index 000000000..924ba949e --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/nginx/includes/proxy_params.conf @@ -0,0 +1,16 @@ +proxy_http_version 1.1; +proxy_ignore_client_abort off; +proxy_read_timeout 86400s; +proxy_redirect off; +proxy_send_timeout 86400s; +proxy_max_temp_file_size 0; + +proxy_hide_header X-Frame-Options; +proxy_set_header Accept-Encoding ""; +proxy_set_header Connection $connection_upgrade; +proxy_set_header Upgrade $http_upgrade; +proxy_set_header Host $http_host; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +proxy_set_header X-Forwarded-Proto $scheme; +proxy_set_header X-NginX-Proxy true; +proxy_set_header X-Real-IP $remote_addr; diff --git a/battybirdnet-pi/rootfs/etc/nginx/includes/resolver.conf b/battybirdnet-pi/rootfs/etc/nginx/includes/resolver.conf new file mode 100644 index 000000000..70f4982b9 --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/nginx/includes/resolver.conf @@ -0,0 +1 @@ +resolver 127.0.0.11 ipv6=off; diff --git a/battybirdnet-pi/rootfs/etc/nginx/includes/server_params.conf b/battybirdnet-pi/rootfs/etc/nginx/includes/server_params.conf new file mode 100644 index 000000000..09c06543e --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/nginx/includes/server_params.conf @@ -0,0 +1,6 @@ +root /dev/null; +server_name $hostname; + +add_header X-Content-Type-Options nosniff; +add_header X-XSS-Protection "1; mode=block"; +add_header X-Robots-Tag none; diff --git a/battybirdnet-pi/rootfs/etc/nginx/includes/ssl_params.conf b/battybirdnet-pi/rootfs/etc/nginx/includes/ssl_params.conf new file mode 100644 index 000000000..6f1500599 --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/nginx/includes/ssl_params.conf @@ -0,0 +1,9 @@ +ssl_protocols TLSv1.2; +ssl_prefer_server_ciphers on; +ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:DHE-RSA-AES256-SHA; +ssl_ecdh_curve secp384r1; +ssl_session_timeout 10m; +ssl_session_cache shared:SSL:10m; +ssl_session_tickets off; +ssl_stapling on; +ssl_stapling_verify on; diff --git a/battybirdnet-pi/rootfs/etc/nginx/includes/upstream.conf b/battybirdnet-pi/rootfs/etc/nginx/includes/upstream.conf new file mode 100644 index 000000000..6dc04d8b4 --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/nginx/includes/upstream.conf @@ -0,0 +1,3 @@ +upstream backend { + server 127.0.0.1:80; +} diff --git a/battybirdnet-pi/rootfs/etc/nginx/nginx.conf b/battybirdnet-pi/rootfs/etc/nginx/nginx.conf new file mode 100644 index 000000000..fb597811c --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/nginx/nginx.conf @@ -0,0 +1,78 @@ + +# Run nginx in foreground. +daemon off; + +# This is run inside Docker. +user root; + +# Pid storage location. +pid /var/run/nginx.pid; + +# Set number of worker processes. +worker_processes auto; + +# Enables the use of JIT for regular expressions to speed-up their processing. +pcre_jit on; + +# Write error log to Hass.io add-on log. +error_log /proc/1/fd/1 error; + +# Load allowed environment vars +env HASSIO_TOKEN; + +# Load dynamic modules. +include /etc/nginx/modules/*.conf; + +# Max num of simultaneous connections by a worker process. +events { + worker_connections 8192; +} + +http { + include /etc/nginx/includes/mime.types; + + # https://emby.media/community/index.php?/topic/93074-how-to-emby-with-nginx-with-windows-specific-tips-and-csp-options/ + server_names_hash_bucket_size 64; + gzip_disable "msie6"; + gzip_comp_level 6; + gzip_min_length 1100; + gzip_buffers 16 8k; + gzip_proxied any; + gzip_types + text/plain + text/css + text/js + text/xml + text/javascript + application/javascript + application/x-javascript + application/json + application/xml + application/rss+xml + image/svg+xml; + proxy_connect_timeout 1h; + + log_format hassio '[$time_local] $status ' + '$http_x_forwarded_for($remote_addr) ' + '$request ($http_user_agent)'; + + access_log /proc/1/fd/1 hassio; + client_max_body_size 4G; + default_type application/octet-stream; + gzip on; + keepalive_timeout 65; + sendfile on; + server_tokens off; + tcp_nodelay on; + tcp_nopush on; + + map $http_upgrade $connection_upgrade { + default upgrade; + '' close; + } + + include /etc/nginx/includes/resolver.conf; + include /etc/nginx/includes/upstream.conf; + + include /etc/nginx/servers/*.conf; +} diff --git a/battybirdnet-pi/rootfs/etc/nginx/servers/ingress.conf b/battybirdnet-pi/rootfs/etc/nginx/servers/ingress.conf new file mode 100644 index 000000000..3fd68901e --- /dev/null +++ b/battybirdnet-pi/rootfs/etc/nginx/servers/ingress.conf @@ -0,0 +1,47 @@ + server { + listen %%interface%%:%%port%% default_server; + include /etc/nginx/includes/server_params.conf; + include /etc/nginx/includes/proxy_params.conf; + + proxy_buffering off; + auth_basic_user_file /home/pi/.htpasswd; + + location /log { + # Proxy pass + proxy_pass http://localhost:8082; + } + + location /stats { + # Proxy pass + proxy_pass http://localhost:8082; + } + + location /terminal { + # Proxy pass + proxy_pass http://localhost:8082; + } + + location / { + # Proxy pass + proxy_pass http://localhost:8082; + + # Next three lines allow websockets + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Correct base_url + proxy_set_header Accept-Encoding ""; + sub_filter_once off; + sub_filter_types *; + sub_filter /spectrogram %%ingress_entry%%/spectrogram; + sub_filter /By_Date/ %%ingress_entry%%/By_Date/; + sub_filter /Charts/ %%ingress_entry%%/Charts/; + sub_filter /todays %%ingress_entry%%/todays; + sub_filter href=\"/ href=\"%%ingress_entry%%/; + sub_filter src=\"/ src=\"%%ingress_entry%%/; + sub_filter hx-get=\"/ hx-get=\"%%ingress_entry%%/; + sub_filter action=\"/ action=\"%%ingress_entry%%/; + } + +} diff --git a/battybirdnet-pi/rootfs/helpers/birdnet_to_mqtt.py b/battybirdnet-pi/rootfs/helpers/birdnet_to_mqtt.py new file mode 100644 index 000000000..63c679e55 --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/birdnet_to_mqtt.py @@ -0,0 +1,124 @@ +#! /usr/bin/env python3 +# birdnet_to_mqtt.py +# +# Adapted from : https://gist.github.com/deepcoder/c309087c456fc733435b47d83f4113ff +# Adapted from : https://gist.github.com/JuanMeeske/08b839246a62ff38778f701fc1da5554 +# +# monitor the records in the syslog file for info from the birdnet system on birds that it detects +# publish this data to mqtt +# + +import time +import re +import dateparser +import datetime +import json +import logging +import paho.mqtt.client as mqtt +import subprocess + +# Setup basic configuration for logging +logging.basicConfig(level=logging.INFO) + +# this generator function monitors the requested file handle for new lines added at its end +# the newly added line is returned by the function +def file_row_generator(s): + while True : + line = s.readline() + if not line: + time.sleep(0.1) + continue + yield line + +# mqtt server +mqtt_server = "%%mqtt_server%%" # server for mqtt +mqtt_user = "%%mqtt_user%%" # Replace with your MQTT username +mqtt_pass = "%%mqtt_pass%%" # Replace with your MQTT password +mqtt_port = %%mqtt_port%% # port for mqtt + +# mqtt topic for bird heard above threshold will be published +mqtt_topic_confident_birds = 'birdnet' + +# url base for website that will be used to look up info about bird +bird_lookup_url_base = 'http://en.wikipedia.org/wiki/' + +# regular expression patters used to decode the records from birdnet +re_high_clean = re.compile(r'(?<=^\[birdnet_analysis\]\[INFO\] ).*?(?=\.mp3$)') + +syslog = open('/proc/1/fd/1', 'r') + +def on_connect(client, userdata, flags, rc, properties=None): + """ Callback for when the client receives a CONNACK response from the server. """ + if rc == 0: + logging.info("Connected to MQTT Broker!") + else: + logging.error(f"Failed to connect, return code {rc}\n") + +def get_bird_code(scientific_name): + with open('/home/pi/BirdNET-Pi/scripts/ebird.php', 'r') as file: + data = file.read() + + # Extract the array from the PHP file + array_str = re.search(r'\$ebirds = \[(.*?)\];', data, re.DOTALL).group(1) + + # Convert the PHP array to a Python dictionary + bird_dict = {re.search(r'"(.*?)"', line).group(1): re.search(r'=> "(.*?)"', line).group(1) + for line in array_str.split('\n') if '=>' in line} + + # Return the corresponding value for the given bird's scientific name + return bird_dict.get(scientific_name) + +# this little hack is to make each received record for the all birds section unique +# the date and time that the log returns is only down to the 1 second accuracy, do +# you can get multiple records with same date and time, this will make Home Assistant not +# think there is a new reading so we add a incrementing tenth of second to each record received +ts_noise = 0.0 + +#try : +# connect to MQTT server +mqttc = mqtt.Client('birdnet_mqtt') # Create instance of client with client ID +mqttc.username_pw_set(mqtt_user, mqtt_pass) # Use credentials +mqttc.connect(mqtt_server, mqtt_port) # Connect to (broker, port, keepalive-time) +mqttc.on_connect = on_connect +mqttc.loop_start() + +# call the generator function and process each line that is returned +for row in file_row_generator(syslog): + # bird found above confidence level found, process it + if re_high_clean.search(row) : + + # this slacker regular expression work, extracts the data about the bird found from the log line + # I do the parse in two passes, because I did not know the re to do it in one! + + raw_high_bird = re.search(re_high_clean, row) + raw_high_bird = raw_high_bird.group(0) + + # the fields we want are separated by semicolons, so split + high_bird_fields = raw_high_bird.split(';') + + # build a structure in python that will be converted to json + bird = {} + + # human time in this record is in two fields, date and time. They are human format + # combine them together separated by a space and they turn the human data into a python + # timestamp + raw_ts = high_bird_fields[0] + ' ' + high_bird_fields[1] + + #bird['ts'] = str(datetime.datetime.timestamp(dateparser.parse(raw_ts))) + bird['Date'] = high_bird_fields[0] + bird['Time'] = high_bird_fields[1] + bird['ScientificName'] = high_bird_fields[2] + bird['CommonName'] = high_bird_fields[3] + bird['Confidence'] = high_bird_fields[4] + bird['SpeciesCode'] = get_bird_code(high_bird_fields[2]) + bird['ClipName'] = high_bird_fields[11] + + # build a url from scientific name of bird that can be used to lookup info about bird + bird['url'] = bird_lookup_url_base + high_bird_fields[2].replace(' ', '_') + + # convert to json string we can sent to mqtt + json_bird = json.dumps(bird) + + print('Posted to MQTT : ok') + + mqttc.publish(mqtt_topic_confident_birds, json_bird, 1) diff --git a/battybirdnet-pi/rootfs/helpers/birdnet_to_mqtt.sh b/battybirdnet-pi/rootfs/helpers/birdnet_to_mqtt.sh new file mode 100644 index 000000000..54045d508 --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/birdnet_to_mqtt.sh @@ -0,0 +1,5 @@ +#!/usr/bin/with-contenv bashio +# shellcheck shell=bash + +echo "Starting service: mqtt automated publish" +"$PYTHON_VIRTUAL_ENV" /usr/bin/birdnet_to_mqtt.py &>/proc/1/fd/1 diff --git a/battybirdnet-pi/rootfs/helpers/caddy_ingress.sh b/battybirdnet-pi/rootfs/helpers/caddy_ingress.sh new file mode 100644 index 000000000..76fda30e1 --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/caddy_ingress.sh @@ -0,0 +1,24 @@ +#!/bin/bash +# shellcheck shell=bash + +# Get values +source /etc/birdnet/birdnet.conf + +# Create ingress configuration for Caddyfile + cat << EOF >> /etc/caddy/Caddyfile +:8082 { + root * ${EXTRACTED} + file_server browse + handle /By_Date/* { + file_server browse + } + handle /Charts/* { + file_server browse + } + reverse_proxy /stream localhost:8000 + php_fastcgi unix//run/php/php-fpm.sock + reverse_proxy /log* localhost:8080 + reverse_proxy /stats* localhost:8501 + reverse_proxy /terminal* localhost:8888 +} +EOF diff --git a/battybirdnet-pi/rootfs/helpers/convert_list.php b/battybirdnet-pi/rootfs/helpers/convert_list.php new file mode 100644 index 000000000..f4259d341 --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/convert_list.php @@ -0,0 +1,116 @@ + + + +

This tool will allow to convert on-the-fly species to compensate for model errors. It SHOULD NOT BE USED except if you know what you are doing, instead the model errors should be reported to the owner. However, it is still convenient for systematic biases that are confirmed through careful listening of samples, while waiting for the models to be updated.

+ +
+
+ +

Specie to convert from :

+ + + +

+

Specie to convert to :

+ + + + +
+
+ +
+
+ +
+
+ +

+ +
+
+ +
+
+

Converted Species List

+ + +
+
+ +
+
+ + + + diff --git a/battybirdnet-pi/rootfs/helpers/spectral_analysis.py b/battybirdnet-pi/rootfs/helpers/spectral_analysis.py new file mode 100644 index 000000000..5a8351a44 --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/spectral_analysis.py @@ -0,0 +1,70 @@ +import numpy as np +import scipy.io.wavfile as wavfile +import matplotlib.pyplot as plt +import os +import glob +import sys # Import the sys module + +from utils.helpers import get_settings + +# Dependencies /usr/bin/pip install numpy scipy matplotlib + +# Define the directory containing the WAV files +conf = get_settings() +input_directory = os.path.join(conf['RECS_DIR'], 'StreamData') +output_directory = os.path.join(conf['RECS_DIR'], 'Extracted/Charts') + +# Ensure the output directory exists +if not os.path.exists(output_directory): + os.makedirs(output_directory) + +# Check if a command-line argument is provided +if len(sys.argv) > 1: + # If an argument is provided, use it as the file to analyze + wav_files = [sys.argv[1]] +else: + # If no argument is provided, analyze all WAV files in the directory + wav_files = glob.glob(os.path.join(input_directory, '*.wav')) + +# Process each file +for file_path in wav_files: + # Load the WAV file + sample_rate, audio_data = wavfile.read(file_path) + + # If stereo, select only one channel + if len(audio_data.shape) > 1: + audio_data = audio_data[:, 0] + + # Apply the Hamming window to the audio data + hamming_window = np.hamming(len(audio_data)) + windowed_data = audio_data * hamming_window + + # Compute the FFT of the windowed audio data + audio_fft = np.fft.fft(windowed_data) + audio_fft = np.abs(audio_fft) + + # Compute the frequencies associated with the FFT values + frequencies = np.fft.fftfreq(len(windowed_data), d=1/sample_rate) + + # Select the range of interest + idx = np.where((frequencies >= 150) & (frequencies <= 15000)) + + # Calculate the saturation threshold based on the bit depth + bit_depth = audio_data.dtype.itemsize * 8 + max_amplitude = 2**(bit_depth - 1) - 1 + saturation_threshold = 0.8 * max_amplitude + + # Plot the spectrum with a logarithmic Y-axis + plt.figure(figsize=(10, 6)) + plt.semilogy(frequencies[idx], audio_fft[idx], label='Spectrum') + plt.axhline(y=saturation_threshold, color='r', linestyle='--', label='Saturation Threshold') + plt.xlabel("Frequency (Hz)") + plt.ylabel("Amplitude (Logarithmic)") + plt.title(f"Frequency Spectrum (150 - 15000 Hz) - {os.path.basename(file_path)}") + plt.legend() + plt.grid(True) + + # Save the plot as a PNG file + output_filename = os.path.basename(file_path).replace('.wav', '_spectrum.png') + plt.savefig(os.path.join(output_directory, output_filename)) + plt.close() # Close the figure to free memory diff --git a/battybirdnet-pi/rootfs/helpers/spectral_analysis.sh b/battybirdnet-pi/rootfs/helpers/spectral_analysis.sh new file mode 100644 index 000000000..fb6378af1 --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/spectral_analysis.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +# Performs the recording from the specified RTSP stream or soundcard +source /etc/birdnet/birdnet.conf + +# Read the logging level from the configuration option +LOGGING_LEVEL="${LogLevel_BirdnetRecordingService}" +# If empty for some reason default to log level of error +[ -z "$LOGGING_LEVEL" ] && LOGGING_LEVEL='error' +# Additionally if we're at debug or info level then allow printing of script commands and variables +if [ "$LOGGING_LEVEL" == "info" ] || [ "$LOGGING_LEVEL" == "debug" ];then + # Enable printing of commands/variables etc to terminal for debugging + set -x +fi + +[ -z "$RECORDING_LENGTH" ] && RECORDING_LENGTH=15 +[ -d "$RECS_DIR"/StreamData ] || mkdir -p "$RECS_DIR"/StreamData + +filename="Spectrum_$(date "+%Y-%m-%d_%H:%M").wav" + +if [ ! -z "$RTSP_STREAM" ];then + # Explode the RSPT steam setting into an array so we can count the number we have + RTSP_STREAMS_EXPLODED_ARRAY=("${RTSP_STREAM//,/ }") + + while true;do + + # Initially start the count off at 1 - our very first stream + RTSP_STREAMS_STARTED_COUNT=1 + FFMPEG_PARAMS="" + + # Loop over the streams + for i in "${RTSP_STREAMS_EXPLODED_ARRAY[@]}" + do + # Map id used to map input to output (first stream being 0), this is 0 based in ffmpeg so decrement our counter (which is more human readable) by 1 + MAP_ID="$((RTSP_STREAMS_STARTED_COUNT-1))" + # Build up the parameters to process the RSTP stream, including mapping for the output + FFMPEG_PARAMS+="-vn -thread_queue_size 512 -i ${i} -map ${MAP_ID}:a:0 -t ${RECORDING_LENGTH} -acodec pcm_s16le -ac 2 -ar 48000 file:${RECS_DIR}/StreamData/$filename " + # Increment counter + ((RTSP_STREAMS_STARTED_COUNT += 1)) + done + + # Make sure were passing something valid to ffmpeg, ffmpeg will run interactive and control our loop by waiting ${RECORDING_LENGTH} between loops because it will stop once that much has been recorded + if [ -n "$FFMPEG_PARAMS" ];then + ffmpeg -hide_banner -loglevel "$LOGGING_LEVEL" -nostdin "$FFMPEG_PARAMS" + fi + + done +else + if pgrep arecord &> /dev/null ;then + echo "Recording" + else + if [ -z "${REC_CARD}" ];then + arecord -f S16_LE -c"${CHANNELS}" -r48000 -t wav --max-file-time "${RECORDING_LENGTH}"\ + --use-strftime "${RECS_DIR}"/StreamData/"$filename" + else + arecord -f S16_LE -c"${CHANNELS}" -r48000 -t wav --max-file-time "${RECORDING_LENGTH}"\ + -D "${REC_CARD}" --use-strftime "${RECS_DIR}"/StreamData/"$filename" + fi + fi +fi + +# Create the spectral analysis +"$PYTHON_VIRTUAL_ENV" "$HOME"/BirdNET-Pi/scripts/spectral_analysis.py diff --git a/battybirdnet-pi/rootfs/helpers/systemctl3.py b/battybirdnet-pi/rootfs/helpers/systemctl3.py new file mode 100644 index 000000000..c29ba2d37 --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/systemctl3.py @@ -0,0 +1,6851 @@ +#! /usr/bin/python3 +# type hints are provided in 'types/systemctl3.pyi' +from __future__ import print_function +import threading +import grp +import pwd +import hashlib +import select +import fcntl +import string +import datetime +import socket +import time +import signal +import sys +import os +import errno +import collections +import shlex +import fnmatch +import re +from types import GeneratorType + +__copyright__ = "(C) 2016-2024 Guido U. Draheim, licensed under the EUPL" +__version__ = "1.5.8066" + +# | +# | +# | +# | +# | +# | +# | +# | +# | +# | +# | +# | +# | + +import logging +logg = logging.getLogger("systemctl") + + +if sys.version[0] == '3': + basestring = str + xrange = range + +DEBUG_AFTER = False +DEBUG_STATUS = False +DEBUG_BOOTTIME = False +DEBUG_INITLOOP = False +DEBUG_KILLALL = False +DEBUG_FLOCK = False +DebugPrintResult = False +TestListen = False +TestAccept = False + +HINT = (logging.DEBUG + logging.INFO) // 2 +NOTE = (logging.WARNING + logging.INFO) // 2 +DONE = (logging.WARNING + logging.ERROR) // 2 +logging.addLevelName(HINT, "HINT") +logging.addLevelName(NOTE, "NOTE") +logging.addLevelName(DONE, "DONE") + +def logg_debug_flock(format, *args): + if DEBUG_FLOCK: + logg.debug(format, *args) # pragma: no cover +def logg_debug_after(format, *args): + if DEBUG_AFTER: + logg.debug(format, *args) # pragma: no cover + +NOT_A_PROBLEM = 0 # FOUND_OK +NOT_OK = 1 # FOUND_ERROR +NOT_ACTIVE = 2 # FOUND_INACTIVE +NOT_FOUND = 4 # FOUND_UNKNOWN + +# defaults for options +_extra_vars = [] +_force = False +_full = False +_log_lines = 0 +_no_pager = False +_now = False +_no_reload = False +_no_legend = False +_no_ask_password = False +_preset_mode = "all" +_quiet = False +_root = "" +_show_all = False +_user_mode = False +_only_what = [] +_only_type = [] +_only_state = [] +_only_property = [] + +# common default paths +_system_folders = [ + "/etc/systemd/system", + "/run/systemd/system", + "/var/run/systemd/system", + "/usr/local/lib/systemd/system", + "/usr/lib/systemd/system", + "/lib/systemd/system", +] +_user_folders = [ + "{XDG_CONFIG_HOME}/systemd/user", + "/etc/systemd/user", + "{XDG_RUNTIME_DIR}/systemd/user", + "/run/systemd/user", + "/var/run/systemd/user", + "{XDG_DATA_HOME}/systemd/user", + "/usr/local/lib/systemd/user", + "/usr/lib/systemd/user", + "/lib/systemd/user", +] +_init_folders = [ + "/etc/init.d", + "/run/init.d", + "/var/run/init.d", +] +_preset_folders = [ + "/etc/systemd/system-preset", + "/run/systemd/system-preset", + "/var/run/systemd/system-preset", + "/usr/local/lib/systemd/system-preset", + "/usr/lib/systemd/system-preset", + "/lib/systemd/system-preset", +] + +# standard paths +_dev_null = "/dev/null" +_dev_zero = "/dev/zero" +_etc_hosts = "/etc/hosts" +_rc3_boot_folder = "/etc/rc3.d" +_rc3_init_folder = "/etc/init.d/rc3.d" +_rc5_boot_folder = "/etc/rc5.d" +_rc5_init_folder = "/etc/init.d/rc5.d" +_proc_pid_stat = "/proc/{pid}/stat" +_proc_pid_status = "/proc/{pid}/status" +_proc_pid_cmdline= "/proc/{pid}/cmdline" +_proc_pid_dir = "/proc" +_proc_sys_uptime = "/proc/uptime" +_proc_sys_stat = "/proc/stat" + +# default values +SystemCompatibilityVersion = 219 +SysInitTarget = "sysinit.target" +SysInitWait = 5 # max for target +MinimumYield = 0.5 +MinimumTimeoutStartSec = 4 +MinimumTimeoutStopSec = 4 +DefaultTimeoutStartSec = 90 # official value +DefaultTimeoutStopSec = 90 # official value +DefaultTimeoutAbortSec = 3600 # officially it none (usually larget than StopSec) +DefaultMaximumTimeout = 200 # overrides all other +DefaultRestartSec = 0.1 # official value of 100ms +DefaultStartLimitIntervalSec = 10 # official value +DefaultStartLimitBurst = 5 # official value +InitLoopSleep = 5 +MaxLockWait = 0 # equals DefaultMaximumTimeout +DefaultPath = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" +ResetLocale = ["LANG", "LANGUAGE", "LC_CTYPE", "LC_NUMERIC", "LC_TIME", "LC_COLLATE", "LC_MONETARY", + "LC_MESSAGES", "LC_PAPER", "LC_NAME", "LC_ADDRESS", "LC_TELEPHONE", "LC_MEASUREMENT", + "LC_IDENTIFICATION", "LC_ALL"] +LocaleConf="/etc/locale.conf" +DefaultListenBacklog=2 + +ExitWhenNoMoreServices = False +ExitWhenNoMoreProcs = False +DefaultUnit = os.environ.get("SYSTEMD_DEFAULT_UNIT", "default.target") # systemd.exe --unit=default.target +DefaultTarget = os.environ.get("SYSTEMD_DEFAULT_TARGET", "multi-user.target") # DefaultUnit fallback +# LogLevel = os.environ.get("SYSTEMD_LOG_LEVEL", "info") # systemd.exe --log-level +# LogTarget = os.environ.get("SYSTEMD_LOG_TARGET", "journal-or-kmsg") # systemd.exe --log-target +# LogLocation = os.environ.get("SYSTEMD_LOG_LOCATION", "no") # systemd.exe --log-location +# ShowStatus = os.environ.get("SYSTEMD_SHOW_STATUS", "auto") # systemd.exe --show-status +DefaultStandardInput=os.environ.get("SYSTEMD_STANDARD_INPUT", "null") +DefaultStandardOutput=os.environ.get("SYSTEMD_STANDARD_OUTPUT", "journal") # systemd.exe --default-standard-output +DefaultStandardError=os.environ.get("SYSTEMD_STANDARD_ERROR", "inherit") # systemd.exe --default-standard-error + +EXEC_SPAWN = False +EXEC_DUP2 = True +REMOVE_LOCK_FILE = False +BOOT_PID_MIN = 0 +BOOT_PID_MAX = -9 +PROC_MAX_DEPTH = 100 +EXPAND_VARS_MAXDEPTH = 20 +EXPAND_KEEP_VARS = True +RESTART_FAILED_UNITS = True +ACTIVE_IF_ENABLED=False + +TAIL_CMDS = ["/bin/tail", "/usr/bin/tail", "/usr/local/bin/tail"] +LESS_CMDS = ["/bin/less", "/usr/bin/less", "/usr/local/bin/less"] +CAT_CMDS = ["/bin/cat", "/usr/bin/cat", "/usr/local/bin/cat"] + +# The systemd default was NOTIFY_SOCKET="/var/run/systemd/notify" +_notify_socket_folder = "{RUN}/systemd" # alias /run/systemd +_journal_log_folder = "{LOG}/journal" + +SYSTEMCTL_DEBUG_LOG = "{LOG}/systemctl.debug.log" +SYSTEMCTL_EXTRA_LOG = "{LOG}/systemctl.log" + +_default_targets = ["poweroff.target", "rescue.target", "sysinit.target", "basic.target", "multi-user.target", "graphical.target", "reboot.target"] +_feature_targets = ["network.target", "remote-fs.target", "local-fs.target", "timers.target", "nfs-client.target"] +_all_common_targets = ["default.target"] + _default_targets + _feature_targets + +# inside a docker we pretend the following +_all_common_enabled = ["default.target", "multi-user.target", "remote-fs.target"] +_all_common_disabled = ["graphical.target", "resue.target", "nfs-client.target"] + +target_requires = {"graphical.target": "multi-user.target", "multi-user.target": "basic.target", "basic.target": "sockets.target"} + +_runlevel_mappings = {} # the official list +_runlevel_mappings["0"] = "poweroff.target" +_runlevel_mappings["1"] = "rescue.target" +_runlevel_mappings["2"] = "multi-user.target" +_runlevel_mappings["3"] = "multi-user.target" +_runlevel_mappings["4"] = "multi-user.target" +_runlevel_mappings["5"] = "graphical.target" +_runlevel_mappings["6"] = "reboot.target" + +_sysv_mappings = {} # by rule of thumb +_sysv_mappings["$local_fs"] = "local-fs.target" +_sysv_mappings["$network"] = "network.target" +_sysv_mappings["$remote_fs"] = "remote-fs.target" +_sysv_mappings["$timer"] = "timers.target" + + +# sections from conf +Unit = "Unit" +Service = "Service" +Socket = "Socket" +Install = "Install" + +# https://tldp.org/LDP/abs/html/exitcodes.html +# https://freedesktop.org/software/systemd/man/systemd.exec.html#id-1.20.8 +EXIT_SUCCESS = 0 +EXIT_FAILURE = 1 + +def strINET(value): + if value == socket.SOCK_DGRAM: + return "UDP" + if value == socket.SOCK_STREAM: + return "TCP" + if value == socket.SOCK_RAW: # pragma: no cover + return "RAW" + if value == socket.SOCK_RDM: # pragma: no cover + return "RDM" + if value == socket.SOCK_SEQPACKET: # pragma: no cover + return "SEQ" + return "" # pragma: no cover + +def strYes(value): + if value is True: + return "yes" + if not value: + return "no" + return str(value) +def strE(part): + if not part: + return "" + return str(part) +def strQ(part): + if part is None: + return "" + if isinstance(part, int): + return str(part) + return "'%s'" % part +def shell_cmd(cmd): + return " ".join([strQ(part) for part in cmd]) +def to_intN(value, default = None): + if not value: + return default + try: + return int(value) + except: + return default +def to_int(value, default = 0): + try: + return int(value) + except: + return default +def to_list(value): + if not value: + return [] + if isinstance(value, list): + return value + if isinstance(value, tuple): + return list(value) + return str(value or "").split(",") +def commalist(value): + return list(_commalist(value)) +def _commalist(value): + for val in value: + if not val: + continue + for elem in val.strip().split(","): + yield elem +def int_mode(value): + try: return int(value, 8) + except: return None # pragma: no cover +def unit_of(module): + if "." not in module: + return module + ".service" + return module +def o22(part): + if isinstance(part, basestring): + if len(part) <= 22: + return part + return part[:5] + "..." + part[-14:] + return part # pragma: no cover (is always str) +def o44(part): + if isinstance(part, basestring): + if len(part) <= 44: + return part + return part[:10] + "..." + part[-31:] + return part # pragma: no cover (is always str) +def o77(part): + if isinstance(part, basestring): + if len(part) <= 77: + return part + return part[:20] + "..." + part[-54:] + return part # pragma: no cover (is always str) +def path44(filename): + if not filename: + return "" + x = filename.find("/", 8) + if len(filename) <= 40: + if "/" not in filename: + return ".../" + filename + elif len(filename) <= 44: + return filename + if 0 < x and x < 14: + out = filename[:x+1] + out += "..." + else: + out = filename[:10] + out += "..." + remain = len(filename) - len(out) + y = filename.find("/", remain) + if 0 < y and y < remain+5: + out += filename[y:] + else: + out += filename[remain:] + return out + +def unit_name_escape(text): + # https://www.freedesktop.org/software/systemd/man/systemd.unit.html#id-1.6 + esc = re.sub("([^a-z-AZ.-/])", lambda m: "\\x%02x" % ord(m.group(1)[0]), text) + return esc.replace("/", "-") +def unit_name_unescape(text): + esc = text.replace("-", "/") + return re.sub("\\\\x(..)", lambda m: "%c" % chr(int(m.group(1), 16)), esc) + +def is_good_root(root): + if not root: + return True + return root.strip(os.path.sep).count(os.path.sep) > 1 +def os_path(root, path): + if not root: + return path + if not path: + return path + if is_good_root(root) and path.startswith(root): + return path + while path.startswith(os.path.sep): + path = path[1:] + return os.path.join(root, path) +def path_replace_extension(path, old, new): + if path.endswith(old): + path = path[:-len(old)] + return path + new +def get_exist_path(paths): + for p in paths: + if os.path.exists(p): + return p + return None + +def get_PAGER(): + PAGER = os.environ.get("PAGER", "less") + pager = os.environ.get("SYSTEMD_PAGER", "{PAGER}").format(**locals()) + options = os.environ.get("SYSTEMD_LESS", "FRSXMK") # see 'man timedatectl' + if not pager: pager = "cat" + if "less" in pager and options: + return [pager, "-" + options] + return [pager] + +def os_getlogin(): + """ NOT using os.getlogin() """ + return pwd.getpwuid(os.geteuid()).pw_name + +def get_runtime_dir(): + explicit = os.environ.get("XDG_RUNTIME_DIR", "") + if explicit: return explicit + user = os_getlogin() + return "/tmp/run-"+user +def get_RUN(root = False): + tmp_var = get_TMP(root) + if _root: + tmp_var = _root + if root: + for p in ("/run", "/var/run", "{tmp_var}/run"): + path = p.format(**locals()) + if os.path.isdir(path) and os.access(path, os.W_OK): + return path + os.makedirs(path) # "/tmp/run" + return path + else: + uid = get_USER_ID(root) + for p in ("/run/user/{uid}", "/var/run/user/{uid}", "{tmp_var}/run-{uid}"): + path = p.format(**locals()) + if os.path.isdir(path) and os.access(path, os.W_OK): + return path + os.makedirs(path, 0o700) # "/tmp/run/user/{uid}" + return path +def get_PID_DIR(root = False): + if root: + return get_RUN(root) + else: + return os.path.join(get_RUN(root), "run") # compat with older systemctl.py + +def get_home(): + if False: # pragma: no cover + explicit = os.environ.get("HOME", "") # >> On Unix, an initial ~ (tilde) is replaced by the + if explicit: return explicit # environment variable HOME if it is set; otherwise + uid = os.geteuid() # the current users home directory is looked up in the + # # password directory through the built-in module pwd. + return pwd.getpwuid(uid).pw_name # An initial ~user i looked up directly in the + return os.path.expanduser("~") # password directory. << from docs(os.path.expanduser) +def get_HOME(root = False): + if root: return "/root" + return get_home() +def get_USER_ID(root = False): + ID = 0 + if root: return ID + return os.geteuid() +def get_USER(root = False): + if root: return "root" + uid = os.geteuid() + return pwd.getpwuid(uid).pw_name +def get_GROUP_ID(root = False): + ID = 0 + if root: return ID + return os.getegid() +def get_GROUP(root = False): + if root: return "root" + gid = os.getegid() + return grp.getgrgid(gid).gr_name +def get_TMP(root = False): + TMP = "/tmp" + if root: return TMP + return os.environ.get("TMPDIR", os.environ.get("TEMP", os.environ.get("TMP", TMP))) +def get_VARTMP(root = False): + VARTMP = "/var/tmp" + if root: return VARTMP + return os.environ.get("TMPDIR", os.environ.get("TEMP", os.environ.get("TMP", VARTMP))) +def get_SHELL(root = False): + SHELL = "/bin/sh" + if root: return SHELL + return os.environ.get("SHELL", SHELL) +def get_RUNTIME_DIR(root = False): + RUN = "/run" + if root: return RUN + return os.environ.get("XDG_RUNTIME_DIR", get_runtime_dir()) +def get_CONFIG_HOME(root = False): + CONFIG = "/etc" + if root: return CONFIG + HOME = get_HOME(root) + return os.environ.get("XDG_CONFIG_HOME", HOME + "/.config") +def get_CACHE_HOME(root = False): + CACHE = "/var/cache" + if root: return CACHE + HOME = get_HOME(root) + return os.environ.get("XDG_CACHE_HOME", HOME + "/.cache") +def get_DATA_HOME(root = False): + SHARE = "/usr/share" + if root: return SHARE + HOME = get_HOME(root) + return os.environ.get("XDG_DATA_HOME", HOME + "/.local/share") +def get_LOG_DIR(root = False): + LOGDIR = "/var/log" + if root: return LOGDIR + CONFIG = get_CONFIG_HOME(root) + return os.path.join(CONFIG, "log") +def get_VARLIB_HOME(root = False): + VARLIB = "/var/lib" + if root: return VARLIB + CONFIG = get_CONFIG_HOME(root) + return CONFIG +def expand_path(path, root = False): + HOME = get_HOME(root) + RUN = get_RUN(root) + LOG = get_LOG_DIR(root) + XDG_DATA_HOME=get_DATA_HOME(root) + XDG_CONFIG_HOME=get_CONFIG_HOME(root) + XDG_RUNTIME_DIR=get_RUNTIME_DIR(root) + return os.path.expanduser(path.replace("${", "{").format(**locals())) + +def shutil_chown(path, user, group): + if user or group: + uid, gid = -1, -1 + if user: + uid = pwd.getpwnam(user).pw_uid + gid = pwd.getpwnam(user).pw_gid + if group: + gid = grp.getgrnam(group).gr_gid + os.chown(path, uid, gid) +def shutil_fchown(fileno, user, group): + if user or group: + uid, gid = -1, -1 + if user: + uid = pwd.getpwnam(user).pw_uid + gid = pwd.getpwnam(user).pw_gid + if group: + gid = grp.getgrnam(group).gr_gid + os.fchown(fileno, uid, gid) +def shutil_setuid(user = None, group = None, xgroups = None): + """ set fork-child uid/gid (returns pw-info env-settings)""" + if group: + gid = grp.getgrnam(group).gr_gid + os.setgid(gid) + logg.debug("setgid %s for %s", gid, strQ(group)) + groups = [gid] + try: + os.setgroups(groups) + logg.debug("setgroups %s < (%s)", groups, group) + except OSError as e: # pragma: no cover (it will occur in non-root mode anyway) + logg.debug("setgroups %s < (%s) : %s", groups, group, e) + if user: + pw = pwd.getpwnam(user) + gid = pw.pw_gid + gname = grp.getgrgid(gid).gr_name + if not group: + os.setgid(gid) + logg.debug("setgid %s for user %s", gid, strQ(user)) + groupnames = [g.gr_name for g in grp.getgrall() if user in g.gr_mem] + groups = [g.gr_gid for g in grp.getgrall() if user in g.gr_mem] + if xgroups: + groups += [g.gr_gid for g in grp.getgrall() if g.gr_name in xgroups and g.gr_gid not in groups] + if not groups: + if group: + gid = grp.getgrnam(group).gr_gid + groups = [gid] + try: + os.setgroups(groups) + logg.debug("setgroups %s > %s ", groups, groupnames) + except OSError as e: # pragma: no cover (it will occur in non-root mode anyway) + logg.debug("setgroups %s > %s : %s", groups, groupnames, e) + uid = pw.pw_uid + os.setuid(uid) + logg.debug("setuid %s for user %s", uid, strQ(user)) + home = pw.pw_dir + shell = pw.pw_shell + logname = pw.pw_name + return {"USER": user, "LOGNAME": logname, "HOME": home, "SHELL": shell} + return {} + +def shutil_truncate(filename): + """ truncates the file (or creates a new empty file)""" + filedir = os.path.dirname(filename) + if not os.path.isdir(filedir): + os.makedirs(filedir) + f = open(filename, "w") + f.write("") + f.close() + +# http://stackoverflow.com/questions/568271/how-to-check-if-there-exists-a-process-with-a-given-pid +def pid_exists(pid): + """Check whether pid exists in the current process table.""" + if pid is None: # pragma: no cover (is never null) + return False + return _pid_exists(int(pid)) +def _pid_exists(pid): + """Check whether pid exists in the current process table. + UNIX only. + """ + if pid < 0: + return False + if pid == 0: + # According to "man 2 kill" PID 0 refers to every process + # in the process group of the calling process. + # On certain systems 0 is a valid PID but we have no way + # to know that in a portable fashion. + raise ValueError('invalid PID 0') + try: + os.kill(pid, 0) + except OSError as err: + if err.errno == errno.ESRCH: + # ESRCH == No such process + return False + elif err.errno == errno.EPERM: + # EPERM clearly means there's a process to deny access to + return True + else: + # According to "man 2 kill" possible error values are + # (EINVAL, EPERM, ESRCH) + raise + else: + return True +def pid_zombie(pid): + """ may be a pid exists but it is only a zombie """ + if pid is None: + return False + return _pid_zombie(int(pid)) +def _pid_zombie(pid): + """ may be a pid exists but it is only a zombie """ + if pid < 0: + return False + if pid == 0: + # According to "man 2 kill" PID 0 refers to every process + # in the process group of the calling process. + # On certain systems 0 is a valid PID but we have no way + # to know that in a portable fashion. + raise ValueError('invalid PID 0') + check = _proc_pid_status.format(**locals()) + try: + for line in open(check): + if line.startswith("State:"): + return "Z" in line + except IOError as e: + if e.errno != errno.ENOENT: + logg.error("%s (%s): %s", check, e.errno, e) + return False + return False + +def checkprefix(cmd): + prefix = "" + for i, c in enumerate(cmd): + if c in "-+!@:": + prefix = prefix + c + else: + newcmd = cmd[i:] + return prefix, newcmd + return prefix, "" + +ExecMode = collections.namedtuple("ExecMode", ["mode", "check", "nouser", "noexpand", "argv0"]) +def exec_path(cmd): + """ Hint: exec_path values are usually not moved by --root (while load_path are)""" + prefix, newcmd = checkprefix(cmd) + check = "-" not in prefix + nouser = "+" in prefix or "!" in prefix + noexpand = ":" in prefix + argv0 = "@" in prefix + mode = ExecMode(prefix, check, nouser, noexpand, argv0) + return mode, newcmd +LoadMode = collections.namedtuple("LoadMode", ["mode", "check"]) +def load_path(ref): + """ Hint: load_path values are usually moved by --root (while exec_path are not)""" + prefix, filename = "", ref + while filename.startswith("-"): + prefix = prefix + filename[0] + filename = filename[1:] + check = "-" not in prefix + mode = LoadMode(prefix, check) + return mode, filename + +# https://github.com/phusion/baseimage-docker/blob/rel-0.9.16/image/bin/my_init +def ignore_signals_and_raise_keyboard_interrupt(signame): + signal.signal(signal.SIGTERM, signal.SIG_IGN) + signal.signal(signal.SIGINT, signal.SIG_IGN) + raise KeyboardInterrupt(signame) + +_default_dict_type = collections.OrderedDict +_default_conf_type = collections.OrderedDict + +class SystemctlConfData: + """ A *.service files has a structure similar to an *.ini file so + that data is structured in sections and values. Actually the + values are lists - the raw data is in .getlist(). Otherwise + .get() will return the first line that was encountered. """ + # | + # | + # | + # | + # | + # | + def __init__(self, defaults=None, dict_type=None, conf_type=None, allow_no_value=False): + self._defaults = defaults or {} + self._conf_type = conf_type or _default_conf_type + self._dict_type = dict_type or _default_dict_type + self._allow_no_value = allow_no_value + self._conf = self._conf_type() + self._files = [] + def defaults(self): + return self._defaults + def sections(self): + return list(self._conf.keys()) + def add_section(self, section): + if section not in self._conf: + self._conf[section] = self._dict_type() + def has_section(self, section): + return section in self._conf + def has_option(self, section, option): + if section not in self._conf: + return False + return option in self._conf[section] + def set(self, section, option, value): + if section not in self._conf: + self._conf[section] = self._dict_type() + if value is None: + self._conf[section][option] = [] + elif option not in self._conf[section]: + self._conf[section][option] = [value] + else: + self._conf[section][option].append(value) + def getstr(self, section, option, default = None, allow_no_value = False): + done = self.get(section, option, strE(default), allow_no_value) + if done is None: return strE(default) + return done + def get(self, section, option, default = None, allow_no_value = False): + allow_no_value = allow_no_value or self._allow_no_value + if section not in self._conf: + if default is not None: + return default + if allow_no_value: + return None + logg.warning("section {} does not exist".format(section)) + logg.warning(" have {}".format(self.sections())) + raise AttributeError("section {} does not exist".format(section)) + if option not in self._conf[section]: + if default is not None: + return default + if allow_no_value: + return None + raise AttributeError("option {} in {} does not exist".format(option, section)) + if not self._conf[section][option]: # i.e. an empty list + if default is not None: + return default + if allow_no_value: + return None + raise AttributeError("option {} in {} is None".format(option, section)) + return self._conf[section][option][0] # the first line in the list of configs + def getlist(self, section, option, default = None, allow_no_value = False): + allow_no_value = allow_no_value or self._allow_no_value + if section not in self._conf: + if default is not None: + return default + if allow_no_value: + return [] + logg.warning("section {} does not exist".format(section)) + logg.warning(" have {}".format(self.sections())) + raise AttributeError("section {} does not exist".format(section)) + if option not in self._conf[section]: + if default is not None: + return default + if allow_no_value: + return [] + raise AttributeError("option {} in {} does not exist".format(option, section)) + return self._conf[section][option] # returns a list, possibly empty + def filenames(self): + return self._files + +class SystemctlConfigParser(SystemctlConfData): + """ A *.service files has a structure similar to an *.ini file but it is + actually not like it. Settings may occur multiple times in each section + and they create an implicit list. In reality all the settings are + globally uniqute, so that an 'environment' can be printed without + adding prefixes. Settings are continued with a backslash at the end + of the line. """ + # def __init__(self, defaults=None, dict_type=None, allow_no_value=False): + # SystemctlConfData.__init__(self, defaults, dict_type, allow_no_value) + def read(self, filename): + return self.read_sysd(filename) + def read_sysd(self, filename): + initscript = False + initinfo = False + section = "GLOBAL" + nextline = False + name, text = "", "" + if os.path.isfile(filename): + self._files.append(filename) + for orig_line in open(filename): + if nextline: + text += orig_line + if text.rstrip().endswith("\\") or text.rstrip().endswith("\\\n"): + text = text.rstrip() + "\n" + else: + self.set(section, name, text) + nextline = False + continue + line = orig_line.strip() + if not line: + continue + if line.startswith("#"): + continue + if line.startswith(";"): + continue + if line.startswith(".include"): + logg.error("the '.include' syntax is deprecated. Use x.service.d/ drop-in files!") + includefile = re.sub(r'^\.include[ ]*', '', line).rstrip() + if not os.path.isfile(includefile): + raise Exception("tried to include file that doesn't exist: %s" % includefile) + self.read_sysd(includefile) + continue + if line.startswith("["): + x = line.find("]") + if x > 0: + section = line[1:x] + self.add_section(section) + continue + m = re.match(r"(\w+) *=(.*)", line) + if not m: + logg.warning("bad ini line: %s", line) + raise Exception("bad ini line") + name, text = m.group(1), m.group(2).strip() + if text.endswith("\\") or text.endswith("\\\n"): + nextline = True + text = text + "\n" + else: + # hint: an empty line shall reset the value-list + self.set(section, name, text and text or None) + return self + def read_sysv(self, filename): + """ an LSB header is scanned and converted to (almost) + equivalent settings of a SystemD ini-style input """ + initscript = False + initinfo = False + section = "GLOBAL" + if os.path.isfile(filename): + self._files.append(filename) + for orig_line in open(filename): + line = orig_line.strip() + if line.startswith("#"): + if " BEGIN INIT INFO" in line: + initinfo = True + section = "init.d" + if " END INIT INFO" in line: + initinfo = False + if initinfo: + m = re.match(r"\S+\s*(\w[\w_-]*):(.*)", line) + if m: + key, val = m.group(1), m.group(2).strip() + self.set(section, key, val) + continue + self.systemd_sysv_generator(filename) + return self + def systemd_sysv_generator(self, filename): + """ see systemd-sysv-generator(8) """ + self.set(Unit, "SourcePath", filename) + description = self.get("init.d", "Description", "") + if description: + self.set(Unit, "Description", description) + check = self.get("init.d", "Required-Start", "") + if check: + for item in check.split(" "): + if item.strip() in _sysv_mappings: + self.set(Unit, "Requires", _sysv_mappings[item.strip()]) + provides = self.get("init.d", "Provides", "") + if provides: + self.set(Install, "Alias", provides) + # if already in multi-user.target then start it there. + runlevels = self.getstr("init.d", "Default-Start", "3 5") + for item in runlevels.split(" "): + if item.strip() in _runlevel_mappings: + self.set(Install, "WantedBy", _runlevel_mappings[item.strip()]) + self.set(Service, "Restart", "no") + self.set(Service, "TimeoutSec", strE(DefaultMaximumTimeout)) + self.set(Service, "KillMode", "process") + self.set(Service, "GuessMainPID", "no") + # self.set(Service, "RemainAfterExit", "yes") + # self.set(Service, "SuccessExitStatus", "5 6") + self.set(Service, "ExecStart", filename + " start") + self.set(Service, "ExecStop", filename + " stop") + if description: # LSB style initscript + self.set(Service, "ExecReload", filename + " reload") + self.set(Service, "Type", "forking") # not "sysv" anymore + +# UnitConfParser = ConfigParser.RawConfigParser +UnitConfParser = SystemctlConfigParser + +class SystemctlSocket: + def __init__(self, conf, sock, skip = False): + self.conf = conf + self.sock = sock + self.skip = skip + def fileno(self): + return self.sock.fileno() + def listen(self, backlog = None): + if backlog is None: + backlog = DefaultListenBacklog + dgram = (self.sock.type == socket.SOCK_DGRAM) + if not dgram and not self.skip: + self.sock.listen(backlog) + def name(self): + return self.conf.name() + def addr(self): + stream = self.conf.get(Socket, "ListenStream", "") + dgram = self.conf.get(Socket, "ListenDatagram", "") + return stream or dgram + def close(self): + self.sock.close() + +class SystemctlConf: + # | + # | + # | + # | + # | + # | + # | + # | + # | + def __init__(self, data, module = None): + self.data = data # UnitConfParser + self.env = {} + self.status = None + self.masked = None + self.module = module + self.nonloaded_path = "" + self.drop_in_files = {} + self._root = _root + self._user_mode = _user_mode + def root_mode(self): + return not self._user_mode + def loaded(self): + files = self.data.filenames() + if self.masked: + return "masked" + if len(files): + return "loaded" + return "" + def filename(self): + """ returns the last filename that was parsed """ + files = self.data.filenames() + if files: + return files[0] + return None + def overrides(self): + """ drop-in files are loaded alphabetically by name, not by full path """ + return [self.drop_in_files[name] for name in sorted(self.drop_in_files)] + def name(self): + """ the unit id or defaults to the file name """ + name = self.module or "" + filename = self.filename() + if filename: + name = os.path.basename(filename) + return self.module or name + def set(self, section, name, value): + return self.data.set(section, name, value) + def get(self, section, name, default, allow_no_value = False): + return self.data.getstr(section, name, default, allow_no_value) + def getlist(self, section, name, default = None, allow_no_value = False): + return self.data.getlist(section, name, default or [], allow_no_value) + def getbool(self, section, name, default = None): + value = self.data.get(section, name, default or "no") + if value: + if value[0] in "TtYy123456789": + return True + return False + +class PresetFile: + # | + # | + def __init__(self): + self._files = [] + self._lines = [] + def filename(self): + """ returns the last filename that was parsed """ + if self._files: + return self._files[-1] + return None + def read(self, filename): + self._files.append(filename) + for line in open(filename): + self._lines.append(line.strip()) + return self + def get_preset(self, unit): + for line in self._lines: + m = re.match(r"(enable|disable)\s+(\S+)", line) + if m: + status, pattern = m.group(1), m.group(2) + if fnmatch.fnmatchcase(unit, pattern): + logg.debug("%s %s => %s %s", status, pattern, unit, strQ(self.filename())) + return status + return None + +## with waitlock(conf): self.start() +class waitlock: + # | + # | + # | + def __init__(self, conf): + self.conf = conf # currently unused + self.opened = -1 + self.lockfolder = expand_path(_notify_socket_folder, conf.root_mode()) + try: + folder = self.lockfolder + if not os.path.isdir(folder): + os.makedirs(folder) + except Exception as e: + logg.warning("oops, %s", e) + def lockfile(self): + unit = "" + if self.conf: + unit = self.conf.name() + return os.path.join(self.lockfolder, str(unit or "global") + ".lock") + def __enter__(self): + try: + lockfile = self.lockfile() + lockname = os.path.basename(lockfile) + self.opened = os.open(lockfile, os.O_RDWR | os.O_CREAT, 0o600) + for attempt in xrange(int(MaxLockWait or DefaultMaximumTimeout)): + try: + logg_debug_flock("[%s] %s. trying %s _______ ", os.getpid(), attempt, lockname) + fcntl.flock(self.opened, fcntl.LOCK_EX | fcntl.LOCK_NB) + st = os.fstat(self.opened) + if not st.st_nlink: + logg_debug_flock("[%s] %s. %s got deleted, trying again", os.getpid(), attempt, lockname) + os.close(self.opened) + self.opened = os.open(lockfile, os.O_RDWR | os.O_CREAT, 0o600) + continue + content = "{ 'systemctl': %s, 'lock': '%s' }\n" % (os.getpid(), lockname) + os.write(self.opened, content.encode("utf-8")) + logg_debug_flock("[%s] %s. holding lock on %s", os.getpid(), attempt, lockname) + return True + except IOError as e: + whom = os.read(self.opened, 4096) + os.lseek(self.opened, 0, os.SEEK_SET) + logg.info("[%s] %s. systemctl locked by %s", os.getpid(), attempt, whom.rstrip()) + time.sleep(1) # until MaxLockWait + continue + logg.error("[%s] not able to get the lock to %s", os.getpid(), lockname) + except Exception as e: + logg.warning("[%s] oops %s, %s", os.getpid(), str(type(e)), e) + # TODO# raise Exception("no lock for %s", self.unit or "global") + return False + def __exit__(self, type, value, traceback): + try: + os.lseek(self.opened, 0, os.SEEK_SET) + os.ftruncate(self.opened, 0) + if REMOVE_LOCK_FILE: # an optional implementation + lockfile = self.lockfile() + lockname = os.path.basename(lockfile) + os.unlink(lockfile) # ino is kept allocated because opened by this process + logg.debug("[%s] lockfile removed for %s", os.getpid(), lockname) + fcntl.flock(self.opened, fcntl.LOCK_UN) + os.close(self.opened) # implies an unlock but that has happend like 6 seconds later + self.opened = -1 + except Exception as e: + logg.warning("oops, %s", e) + +SystemctlWaitPID = collections.namedtuple("SystemctlWaitPID", ["pid", "returncode", "signal"]) + +def must_have_failed(waitpid, cmd): + # found to be needed on ubuntu:16.04 to match test result from ubuntu:18.04 and other distros + # .... I have tracked it down that python's os.waitpid() returns an exitcode==0 even when the + # .... underlying process has actually failed with an exitcode<>0. It is unknown where that + # .... bug comes from but it seems a bit serious to trash some very basic unix functionality. + # .... Essentially a parent process does not get the correct exitcode from its own children. + if cmd and cmd[0] == "/bin/kill": + pid = None + for arg in cmd[1:]: + if not arg.startswith("-"): + pid = arg + if pid is None: # unknown $MAINPID + if not waitpid.returncode: + logg.error("waitpid %s did return %s => correcting as 11", cmd, waitpid.returncode) + waitpid = SystemctlWaitPID(waitpid.pid, 11, waitpid.signal) + return waitpid + +def subprocess_waitpid(pid): + run_pid, run_stat = os.waitpid(pid, 0) + return SystemctlWaitPID(run_pid, os.WEXITSTATUS(run_stat), os.WTERMSIG(run_stat)) +def subprocess_testpid(pid): + run_pid, run_stat = os.waitpid(pid, os.WNOHANG) + if run_pid: + return SystemctlWaitPID(run_pid, os.WEXITSTATUS(run_stat), os.WTERMSIG(run_stat)) + else: + return SystemctlWaitPID(pid, None, 0) + +SystemctlUnitName = collections.namedtuple("SystemctlUnitName", ["fullname", "name", "prefix", "instance", "suffix", "component"]) + +def parse_unit(fullname): # -> object(prefix, instance, suffix, ...., name, component) + name, suffix = fullname, "" + has_suffix = fullname.rfind(".") + if has_suffix > 0: + name = fullname[:has_suffix] + suffix = fullname[has_suffix+1:] + prefix, instance = name, "" + has_instance = name.find("@") + if has_instance > 0: + prefix = name[:has_instance] + instance = name[has_instance+1:] + component = "" + has_component = prefix.rfind("-") + if has_component > 0: + component = prefix[has_component+1:] + return SystemctlUnitName(fullname, name, prefix, instance, suffix, component) + +def time_to_seconds(text, maximum): + value = 0. + for part in str(text).split(" "): + item = part.strip() + if item == "infinity": + return maximum + if item.endswith("m"): + try: value += 60 * int(item[:-1]) + except: pass # pragma: no cover + if item.endswith("min"): + try: value += 60 * int(item[:-3]) + except: pass # pragma: no cover + elif item.endswith("ms"): + try: value += int(item[:-2]) / 1000. + except: pass # pragma: no cover + elif item.endswith("s"): + try: value += int(item[:-1]) + except: pass # pragma: no cover + elif item: + try: value += int(item) + except: pass # pragma: no cover + if value > maximum: + return maximum + if not value and text.strip() == "0": + return 0. + if not value: + return 1. + return value +def seconds_to_time(seconds): + seconds = float(seconds) + mins = int(int(seconds) / 60) + secs = int(int(seconds) - (mins * 60)) + msecs = int(int(seconds * 1000) - (secs * 1000 + mins * 60000)) + if mins and secs and msecs: + return "%smin %ss %sms" % (mins, secs, msecs) + elif mins and secs: + return "%smin %ss" % (mins, secs) + elif secs and msecs: + return "%ss %sms" % (secs, msecs) + elif mins and msecs: + return "%smin %sms" % (mins, msecs) + elif mins: + return "%smin" % (mins) + else: + return "%ss" % (secs) + +def getBefore(conf): + result = [] + beforelist = conf.getlist(Unit, "Before", []) + for befores in beforelist: + for before in befores.split(" "): + name = before.strip() + if name and name not in result: + result.append(name) + return result + +def getAfter(conf): + result = [] + afterlist = conf.getlist(Unit, "After", []) + for afters in afterlist: + for after in afters.split(" "): + name = after.strip() + if name and name not in result: + result.append(name) + return result + +def compareAfter(confA, confB): + idA = confA.name() + idB = confB.name() + for after in getAfter(confA): + if after == idB: + logg.debug("%s After %s", idA, idB) + return -1 + for after in getAfter(confB): + if after == idA: + logg.debug("%s After %s", idB, idA) + return 1 + for before in getBefore(confA): + if before == idB: + logg.debug("%s Before %s", idA, idB) + return 1 + for before in getBefore(confB): + if before == idA: + logg.debug("%s Before %s", idB, idA) + return -1 + return 0 + +def conf_sortedAfter(conflist, cmp = compareAfter): + # the normal sorted() does only look at two items + # so if "A after C" and a list [A, B, C] then + # it will see "A = B" and "B = C" assuming that + # "A = C" and the list is already sorted. + # + # To make a totalsorted we have to create a marker + # that informs sorted() that also B has a relation. + # It only works when 'after' has a direction, so + # anything without 'before' is a 'after'. In that + # case we find that "B after C". + class SortTuple: + def __init__(self, rank, conf): + self.rank = rank + self.conf = conf + sortlist = [SortTuple(0, conf) for conf in conflist] + for check in xrange(len(sortlist)): # maxrank = len(sortlist) + changed = 0 + for A in xrange(len(sortlist)): + for B in xrange(len(sortlist)): + if A != B: + itemA = sortlist[A] + itemB = sortlist[B] + before = compareAfter(itemA.conf, itemB.conf) + if before > 0 and itemA.rank <= itemB.rank: + logg_debug_after(" %-30s before %s", itemA.conf.name(), itemB.conf.name()) + itemA.rank = itemB.rank + 1 + changed += 1 + if before < 0 and itemB.rank <= itemA.rank: + logg_debug_after(" %-30s before %s", itemB.conf.name(), itemA.conf.name()) + itemB.rank = itemA.rank + 1 + changed += 1 + if not changed: + logg_debug_after("done in check %s of %s", check, len(sortlist)) + break + # because Requires is almost always the same as the After clauses + # we are mostly done in round 1 as the list is in required order + for conf in conflist: + logg_debug_after(".. %s", conf.name()) + for item in sortlist: + logg_debug_after("(%s) %s", item.rank, item.conf.name()) + sortedlist = sorted(sortlist, key = lambda item: -item.rank) + for item in sortedlist: + logg_debug_after("[%s] %s", item.rank, item.conf.name()) + return [item.conf for item in sortedlist] + +class SystemctlListenThread(threading.Thread): + def __init__(self, systemctl): + threading.Thread.__init__(self, name="listen") + self.systemctl = systemctl + self.stopped = threading.Event() + def stop(self): + self.stopped.set() + def run(self): + READ_ONLY = select.POLLIN | select.POLLPRI | select.POLLHUP | select.POLLERR + READ_WRITE = READ_ONLY | select.POLLOUT + me = os.getpid() + if DEBUG_INITLOOP: # pragma: no cover + logg.info("[%s] listen: new thread", me) + if not self.systemctl._sockets: + return + if DEBUG_INITLOOP: # pragma: no cover + logg.info("[%s] listen: start thread", me) + listen = select.poll() + for sock in self.systemctl._sockets.values(): + listen.register(sock, READ_ONLY) + sock.listen() + logg.debug("[%s] listen: %s :%s", me, sock.name(), sock.addr()) + timestamp = time.time() + while not self.stopped.is_set(): + try: + sleep_sec = InitLoopSleep - (time.time() - timestamp) + if sleep_sec < MinimumYield: + sleep_sec = MinimumYield + sleeping = sleep_sec + while sleeping > 2: + time.sleep(1) # accept signals atleast every second + sleeping = InitLoopSleep - (time.time() - timestamp) + if sleeping < MinimumYield: + sleeping = MinimumYield + break + time.sleep(sleeping) # remainder waits less that 2 seconds + if DEBUG_INITLOOP: # pragma: no cover + logg.debug("[%s] listen: poll", me) + accepting = listen.poll(100) # milliseconds + if DEBUG_INITLOOP: # pragma: no cover + logg.debug("[%s] listen: poll (%s)", me, len(accepting)) + for sock_fileno, event in accepting: + for sock in self.systemctl._sockets.values(): + if sock.fileno() == sock_fileno: + if not self.stopped.is_set(): + if self.systemctl.loop.acquire(): + logg.debug("[%s] listen: accept %s :%s", me, sock.name(), sock_fileno) + self.systemctl.do_accept_socket_from(sock.conf, sock.sock) + except Exception as e: + logg.info("[%s] listen: interrupted - exception %s", me, e) + raise + for sock in self.systemctl._sockets.values(): + try: + listen.unregister(sock) + sock.close() + except Exception as e: + logg.warning("[%s] listen: close socket: %s", me, e) + return + +class Systemctl: + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + # | + def __init__(self): + self.error = NOT_A_PROBLEM # program exitcode or process returncode + # from command line options or the defaults + self._extra_vars = _extra_vars + self._force = _force + self._full = _full + self._init = _init + self._no_ask_password = _no_ask_password + self._no_legend = _no_legend + self._now = _now + self._preset_mode = _preset_mode + self._quiet = _quiet + self._root = _root + self._show_all = _show_all + self._only_what = commalist(_only_what) or [""] + self._only_property = commalist(_only_property) + self._only_state = commalist(_only_state) + self._only_type = commalist(_only_type) + # some common constants that may be changed + self._systemd_version = SystemCompatibilityVersion + self._journal_log_folder = _journal_log_folder + # and the actual internal runtime state + self._loaded_file_sysv = {} # /etc/init.d/name => config data + self._loaded_file_sysd = {} # /etc/systemd/system/name.service => config data + self._file_for_unit_sysv = None # name.service => /etc/init.d/name + self._file_for_unit_sysd = None # name.service => /etc/systemd/system/name.service + self._preset_file_list = None # /etc/systemd/system-preset/* => file content + self._default_target = DefaultTarget + self._sysinit_target = None # stores a UnitConf() + self.doExitWhenNoMoreProcs = ExitWhenNoMoreProcs or False + self.doExitWhenNoMoreServices = ExitWhenNoMoreServices or False + self._user_mode = _user_mode + self._user_getlogin = os_getlogin() + self._log_file = {} # init-loop + self._log_hold = {} # init-loop + self._boottime = None # cache self.get_boottime() + self._SYSTEMD_UNIT_PATH = None + self._SYSTEMD_SYSVINIT_PATH = None + self._SYSTEMD_PRESET_PATH = None + self._restarted_unit = {} + self._restart_failed_units = {} + self._sockets = {} + self.loop = threading.Lock() + def user(self): + return self._user_getlogin + def user_mode(self): + return self._user_mode + def user_folder(self): + for folder in self.user_folders(): + if folder: return folder + raise Exception("did not find any systemd/user folder") + def system_folder(self): + for folder in self.system_folders(): + if folder: return folder + raise Exception("did not find any systemd/system folder") + def preset_folders(self): + SYSTEMD_PRESET_PATH = self.get_SYSTEMD_PRESET_PATH() + for path in SYSTEMD_PRESET_PATH.split(":"): + if path.strip(): yield expand_path(path.strip()) + if SYSTEMD_PRESET_PATH.endswith(":"): + for p in _preset_folders: + yield expand_path(p.strip()) + def init_folders(self): + SYSTEMD_SYSVINIT_PATH = self.get_SYSTEMD_SYSVINIT_PATH() + for path in SYSTEMD_SYSVINIT_PATH.split(":"): + if path.strip(): yield expand_path(path.strip()) + if SYSTEMD_SYSVINIT_PATH.endswith(":"): + for p in _init_folders: + yield expand_path(p.strip()) + def user_folders(self): + SYSTEMD_UNIT_PATH = self.get_SYSTEMD_UNIT_PATH() + for path in SYSTEMD_UNIT_PATH.split(":"): + if path.strip(): yield expand_path(path.strip()) + if SYSTEMD_UNIT_PATH.endswith(":"): + for p in _user_folders: + yield expand_path(p.strip()) + def system_folders(self): + SYSTEMD_UNIT_PATH = self.get_SYSTEMD_UNIT_PATH() + for path in SYSTEMD_UNIT_PATH.split(":"): + if path.strip(): yield expand_path(path.strip()) + if SYSTEMD_UNIT_PATH.endswith(":"): + for p in _system_folders: + yield expand_path(p.strip()) + def get_SYSTEMD_UNIT_PATH(self): + if self._SYSTEMD_UNIT_PATH is None: + self._SYSTEMD_UNIT_PATH = os.environ.get("SYSTEMD_UNIT_PATH", ":") + assert self._SYSTEMD_UNIT_PATH is not None + return self._SYSTEMD_UNIT_PATH + def get_SYSTEMD_SYSVINIT_PATH(self): + if self._SYSTEMD_SYSVINIT_PATH is None: + self._SYSTEMD_SYSVINIT_PATH = os.environ.get("SYSTEMD_SYSVINIT_PATH", ":") + assert self._SYSTEMD_SYSVINIT_PATH is not None + return self._SYSTEMD_SYSVINIT_PATH + def get_SYSTEMD_PRESET_PATH(self): + if self._SYSTEMD_PRESET_PATH is None: + self._SYSTEMD_PRESET_PATH = os.environ.get("SYSTEMD_PRESET_PATH", ":") + assert self._SYSTEMD_PRESET_PATH is not None + return self._SYSTEMD_PRESET_PATH + def sysd_folders(self): + """ if --user then these folders are preferred """ + if self.user_mode(): + for folder in self.user_folders(): + yield folder + if True: + for folder in self.system_folders(): + yield folder + def scan_unit_sysd_files(self, module = None): # -> [ unit-names,... ] + """ reads all unit files, returns the first filename for the unit given """ + if self._file_for_unit_sysd is None: + self._file_for_unit_sysd = {} + for folder in self.sysd_folders(): + if not folder: + continue + folder = os_path(self._root, folder) + if not os.path.isdir(folder): + continue + for name in os.listdir(folder): + path = os.path.join(folder, name) + if os.path.isdir(path): + continue + service_name = name + if service_name not in self._file_for_unit_sysd: + self._file_for_unit_sysd[service_name] = path + logg.debug("found %s sysd files", len(self._file_for_unit_sysd)) + return list(self._file_for_unit_sysd.keys()) + def scan_unit_sysv_files(self, module = None): # -> [ unit-names,... ] + """ reads all init.d files, returns the first filename when unit is a '.service' """ + if self._file_for_unit_sysv is None: + self._file_for_unit_sysv = {} + for folder in self.init_folders(): + if not folder: + continue + folder = os_path(self._root, folder) + if not os.path.isdir(folder): + continue + for name in os.listdir(folder): + path = os.path.join(folder, name) + if os.path.isdir(path): + continue + service_name = name + ".service" # simulate systemd + if service_name not in self._file_for_unit_sysv: + self._file_for_unit_sysv[service_name] = path + logg.debug("found %s sysv files", len(self._file_for_unit_sysv)) + return list(self._file_for_unit_sysv.keys()) + def unit_sysd_file(self, module = None): # -> filename? + """ file path for the given module (systemd) """ + self.scan_unit_sysd_files() + assert self._file_for_unit_sysd is not None + if module and module in self._file_for_unit_sysd: + return self._file_for_unit_sysd[module] + if module and unit_of(module) in self._file_for_unit_sysd: + return self._file_for_unit_sysd[unit_of(module)] + return None + def unit_sysv_file(self, module = None): # -> filename? + """ file path for the given module (sysv) """ + self.scan_unit_sysv_files() + assert self._file_for_unit_sysv is not None + if module and module in self._file_for_unit_sysv: + return self._file_for_unit_sysv[module] + if module and unit_of(module) in self._file_for_unit_sysv: + return self._file_for_unit_sysv[unit_of(module)] + return None + def unit_file(self, module = None): # -> filename? + """ file path for the given module (sysv or systemd) """ + path = self.unit_sysd_file(module) + if path is not None: return path + path = self.unit_sysv_file(module) + if path is not None: return path + return None + def is_sysv_file(self, filename): + """ for routines that have a special treatment for init.d services """ + self.unit_file() # scan all + assert self._file_for_unit_sysd is not None + assert self._file_for_unit_sysv is not None + if not filename: return None + if filename in self._file_for_unit_sysd.values(): return False + if filename in self._file_for_unit_sysv.values(): return True + return None # not True + def is_user_conf(self, conf): + if not conf: # pragma: no cover (is never null) + return False + filename = conf.nonloaded_path or conf.filename() + if filename and "/user/" in filename: + return True + return False + def not_user_conf(self, conf): + """ conf can not be started as user service (when --user)""" + if conf is None: # pragma: no cover (is never null) + return True + if not self.user_mode(): + logg.debug("%s no --user mode >> accept", strQ(conf.filename())) + return False + if self.is_user_conf(conf): + logg.debug("%s is /user/ conf >> accept", strQ(conf.filename())) + return False + # to allow for 'docker run -u user' with system services + user = self.get_User(conf) + if user and user == self.user(): + logg.debug("%s with User=%s >> accept", strQ(conf.filename()), user) + return False + return True + def find_drop_in_files(self, unit): + """ search for some.service.d/extra.conf files """ + result = {} + basename_d = unit + ".d" + for folder in self.sysd_folders(): + if not folder: + continue + folder = os_path(self._root, folder) + override_d = os_path(folder, basename_d) + if not os.path.isdir(override_d): + continue + for name in os.listdir(override_d): + path = os.path.join(override_d, name) + if os.path.isdir(path): + continue + if not path.endswith(".conf"): + continue + if name not in result: + result[name] = path + return result + def load_sysd_template_conf(self, module): # -> conf? + """ read the unit template with a UnitConfParser (systemd) """ + if module and "@" in module: + unit = parse_unit(module) + service = "%s@.service" % unit.prefix + conf = self.load_sysd_unit_conf(service) + if conf: + conf.module = module + return conf + return None + def load_sysd_unit_conf(self, module): # -> conf? + """ read the unit file with a UnitConfParser (systemd) """ + path = self.unit_sysd_file(module) + if not path: return None + assert self._loaded_file_sysd is not None + if path in self._loaded_file_sysd: + return self._loaded_file_sysd[path] + masked = None + if os.path.islink(path) and os.readlink(path).startswith("/dev"): + masked = os.readlink(path) + drop_in_files = {} + data = UnitConfParser() + if not masked: + data.read_sysd(path) + drop_in_files = self.find_drop_in_files(os.path.basename(path)) + # load in alphabetic order, irrespective of location + for name in sorted(drop_in_files): + path = drop_in_files[name] + data.read_sysd(path) + conf = SystemctlConf(data, module) + conf.masked = masked + conf.nonloaded_path = path # if masked + conf.drop_in_files = drop_in_files + conf._root = self._root + self._loaded_file_sysd[path] = conf + return conf + def load_sysv_unit_conf(self, module): # -> conf? + """ read the unit file with a UnitConfParser (sysv) """ + path = self.unit_sysv_file(module) + if not path: return None + assert self._loaded_file_sysv is not None + if path in self._loaded_file_sysv: + return self._loaded_file_sysv[path] + data = UnitConfParser() + data.read_sysv(path) + conf = SystemctlConf(data, module) + conf._root = self._root + self._loaded_file_sysv[path] = conf + return conf + def load_unit_conf(self, module): # -> conf | None(not-found) + """ read the unit file with a UnitConfParser (sysv or systemd) """ + try: + conf = self.load_sysd_unit_conf(module) + if conf is not None: + return conf + conf = self.load_sysd_template_conf(module) + if conf is not None: + return conf + conf = self.load_sysv_unit_conf(module) + if conf is not None: + return conf + except Exception as e: + logg.warning("%s not loaded: %s", module, e) + return None + def default_unit_conf(self, module, description = None): # -> conf + """ a unit conf that can be printed to the user where + attributes are empty and loaded() is False """ + data = UnitConfParser() + data.set(Unit, "Description", description or ("NOT-FOUND " + str(module))) + # assert(not data.loaded()) + conf = SystemctlConf(data, module) + conf._root = self._root + return conf + def get_unit_conf(self, module): # -> conf (conf | default-conf) + """ accept that a unit does not exist + and return a unit conf that says 'not-loaded' """ + conf = self.load_unit_conf(module) + if conf is not None: + return conf + return self.default_unit_conf(module) + def get_unit_type(self, module): + name, ext = os.path.splitext(module) + if ext in [".service", ".socket", ".target"]: + return ext[1:] + return None + def get_unit_section(self, module, default = Service): + return string.capwords(self.get_unit_type(module) or default) + def get_unit_section_from(self, conf, default = Service): + return self.get_unit_section(conf.name(), default) + def match_sysd_templates(self, modules = None, suffix=".service"): # -> generate[ unit ] + """ make a file glob on all known template units (systemd areas). + It returns no modules (!!) if no modules pattern were given. + The module string should contain an instance name already. """ + modules = to_list(modules) + if not modules: + return + self.scan_unit_sysd_files() + assert self._file_for_unit_sysd is not None + for item in sorted(self._file_for_unit_sysd.keys()): + if "@" not in item: + continue + service_unit = parse_unit(item) + for module in modules: + if "@" not in module: + continue + module_unit = parse_unit(module) + if service_unit.prefix == module_unit.prefix: + yield "%s@%s.%s" % (service_unit.prefix, module_unit.instance, service_unit.suffix) + def match_sysd_units(self, modules = None, suffix=".service"): # -> generate[ unit ] + """ make a file glob on all known units (systemd areas). + It returns all modules if no modules pattern were given. + Also a single string as one module pattern may be given. """ + modules = to_list(modules) + self.scan_unit_sysd_files() + assert self._file_for_unit_sysd is not None + for item in sorted(self._file_for_unit_sysd.keys()): + if "." not in item: + pass + elif not modules: + yield item + elif [module for module in modules if fnmatch.fnmatchcase(item, module)]: + yield item + elif [module for module in modules if module+suffix == item]: + yield item + def match_sysv_units(self, modules = None, suffix=".service"): # -> generate[ unit ] + """ make a file glob on all known units (sysv areas). + It returns all modules if no modules pattern were given. + Also a single string as one module pattern may be given. """ + modules = to_list(modules) + self.scan_unit_sysv_files() + assert self._file_for_unit_sysv is not None + for item in sorted(self._file_for_unit_sysv.keys()): + if not modules: + yield item + elif [module for module in modules if fnmatch.fnmatchcase(item, module)]: + yield item + elif [module for module in modules if module+suffix == item]: + yield item + def match_units(self, modules = None, suffix=".service"): # -> [ units,.. ] + """ Helper for about any command with multiple units which can + actually be glob patterns on their respective unit name. + It returns all modules if no modules pattern were given. + Also a single string as one module pattern may be given. """ + found = [] + for unit in self.match_sysd_units(modules, suffix): + if unit not in found: + found.append(unit) + for unit in self.match_sysd_templates(modules, suffix): + if unit not in found: + found.append(unit) + for unit in self.match_sysv_units(modules, suffix): + if unit not in found: + found.append(unit) + return found + def list_service_unit_basics(self): + """ show all the basic loading state of services """ + filename = self.unit_file() # scan all + assert self._file_for_unit_sysd is not None + assert self._file_for_unit_sysv is not None + result = [] + for name, value in self._file_for_unit_sysd.items(): + result += [(name, "SysD", value)] + for name, value in self._file_for_unit_sysv.items(): + result += [(name, "SysV", value)] + return result + def list_service_units(self, *modules): # -> [ (unit,loaded+active+substate,description) ] + """ show all the service units """ + result = {} + active = {} + substate = {} + description = {} + for unit in self.match_units(to_list(modules)): + result[unit] = "not-found" + active[unit] = "inactive" + substate[unit] = "dead" + description[unit] = "" + try: + conf = self.get_unit_conf(unit) + result[unit] = "loaded" + description[unit] = self.get_description_from(conf) + active[unit] = self.get_active_from(conf) + substate[unit] = self.get_substate_from(conf) or "unknown" + except Exception as e: + logg.warning("list-units: %s", e) + if self._only_state: + if result[unit] in self._only_state: + pass + elif active[unit] in self._only_state: + pass + elif substate[unit] in self._only_state: + pass + else: + del result[unit] + return [(unit, result[unit] + " " + active[unit] + " " + substate[unit], description[unit]) for unit in sorted(result)] + def list_units_modules(self, *modules): # -> [ (unit,loaded,description) ] + """ [PATTERN]... -- List loaded units. + If one or more PATTERNs are specified, only units matching one of + them are shown. NOTE: This is the default command.""" + hint = "To show all installed unit files use 'systemctl list-unit-files'." + result = self.list_service_units(*modules) + if self._no_legend: + return result + found = "%s loaded units listed." % len(result) + return result + [("", "", ""), (found, "", ""), (hint, "", "")] + def list_service_unit_files(self, *modules): # -> [ (unit,enabled) ] + """ show all the service units and the enabled status""" + logg.debug("list service unit files for %s", modules) + result = {} + enabled = {} + for unit in self.match_units(to_list(modules)): + if self._only_type and self.get_unit_type(unit) not in self._only_type: + continue + result[unit] = None + enabled[unit] = "" + try: + conf = self.get_unit_conf(unit) + if self.not_user_conf(conf): + result[unit] = None + continue + result[unit] = conf + enabled[unit] = self.enabled_from(conf) + except Exception as e: + logg.warning("list-units: %s", e) + return [(unit, enabled[unit]) for unit in sorted(result) if result[unit]] + def each_target_file(self): + folders = self.system_folders() + if self.user_mode(): + folders = self.user_folders() + for folder1 in folders: + folder = os_path(self._root, folder1) + if not os.path.isdir(folder): + continue + for filename in os.listdir(folder): + if filename.endswith(".target"): + yield (filename, os.path.join(folder, filename)) + def list_target_unit_files(self, *modules): # -> [ (unit,enabled) ] + """ show all the target units and the enabled status""" + enabled = {} + targets = {} + for target, filepath in self.each_target_file(): + logg.info("target %s", filepath) + targets[target] = filepath + enabled[target] = "static" + for unit in _all_common_targets: + targets[unit] = None + enabled[unit] = "static" + if unit in _all_common_enabled: + enabled[unit] = "enabled" + if unit in _all_common_disabled: + enabled[unit] = "disabled" + return [(unit, enabled[unit]) for unit in sorted(targets)] + def list_unit_files_modules(self, *modules): # -> [ (unit,enabled) ] + """[PATTERN]... -- List installed unit files + List installed unit files and their enablement state (as reported + by is-enabled). If one or more PATTERNs are specified, only units + whose filename (just the last component of the path) matches one of + them are shown. This command reacts to limitations of --type being + --type=service or --type=target (and --now for some basics).""" + result = [] + if self._now: + basics = self.list_service_unit_basics() + result = [(name, sysv + " " + filename) for name, sysv, filename in basics] + elif self._only_type: + if "target" in self._only_type: + result = self.list_target_unit_files() + if "service" in self._only_type: + result = self.list_service_unit_files() + else: + result = self.list_target_unit_files() + result += self.list_service_unit_files(*modules) + if self._no_legend: + return result + found = "%s unit files listed." % len(result) + return [("UNIT FILE", "STATE")] + result + [("", ""), (found, "")] + ## + ## + def get_description(self, unit, default = None): + return self.get_description_from(self.load_unit_conf(unit)) + def get_description_from(self, conf, default = None): # -> text + """ Unit.Description could be empty sometimes """ + if not conf: return default or "" + description = conf.get(Unit, "Description", default or "") + return self.expand_special(description, conf) + def read_pid_file(self, pid_file, default = None): + pid = default + if not pid_file: + return default + if not os.path.isfile(pid_file): + return default + if self.truncate_old(pid_file): + return default + try: + # some pid-files from applications contain multiple lines + for line in open(pid_file): + if line.strip(): + pid = to_intN(line.strip()) + break + except Exception as e: + logg.warning("bad read of pid file '%s': %s", pid_file, e) + return pid + def wait_pid_file(self, pid_file, timeout = None): # -> pid? + """ wait some seconds for the pid file to appear and return the pid """ + timeout = int(timeout or (DefaultTimeoutStartSec/2)) + timeout = max(timeout, (MinimumTimeoutStartSec)) + dirpath = os.path.dirname(os.path.abspath(pid_file)) + for x in xrange(timeout): + if not os.path.isdir(dirpath): + time.sleep(1) # until TimeoutStartSec/2 + continue + pid = self.read_pid_file(pid_file) + if not pid: + time.sleep(1) # until TimeoutStartSec/2 + continue + if not pid_exists(pid): + time.sleep(1) # until TimeoutStartSec/2 + continue + return pid + return None + def get_status_pid_file(self, unit): + """ actual file path of pid file (internal) """ + conf = self.get_unit_conf(unit) + return self.pid_file_from(conf) or self.get_status_file_from(conf) + def pid_file_from(self, conf, default = ""): + """ get the specified pid file path (not a computed default) """ + pid_file = self.get_pid_file(conf) or default + return os_path(self._root, self.expand_special(pid_file, conf)) + def get_pid_file(self, conf, default = None): + return conf.get(Service, "PIDFile", default) + def read_mainpid_from(self, conf, default = None): + """ MAINPID is either the PIDFile content written from the application + or it is the value in the status file written by this systemctl.py code """ + pid_file = self.pid_file_from(conf) + if pid_file: + return self.read_pid_file(pid_file, default) + status = self.read_status_from(conf) + if "MainPID" in status: + return to_intN(status["MainPID"], default) + return default + def clean_pid_file_from(self, conf): + pid_file = self.pid_file_from(conf) + if pid_file and os.path.isfile(pid_file): + try: + os.remove(pid_file) + except OSError as e: + logg.warning("while rm %s: %s", pid_file, e) + self.write_status_from(conf, MainPID=None) + def get_status_file(self, unit): # for testing + conf = self.get_unit_conf(unit) + return self.get_status_file_from(conf) + def get_status_file_from(self, conf, default = None): + status_file = self.get_StatusFile(conf) + # this not a real setting, but do the expand_special anyway + return os_path(self._root, self.expand_special(status_file, conf)) + def get_StatusFile(self, conf, default = None): # -> text + """ file where to store a status mark """ + status_file = conf.get(Service, "StatusFile", default) + if status_file: + return status_file + root = conf.root_mode() + folder = get_PID_DIR(root) + name = "%s.status" % conf.name() + return os.path.join(folder, name) + def clean_status_from(self, conf): + status_file = self.get_status_file_from(conf) + if os.path.exists(status_file): + os.remove(status_file) + conf.status = {} + def write_status_from(self, conf, **status): # -> bool(written) + """ if a status_file is known then path is created and the + give status is written as the only content. """ + status_file = self.get_status_file_from(conf) + # if not status_file: return False + dirpath = os.path.dirname(os.path.abspath(status_file)) + if not os.path.isdir(dirpath): + os.makedirs(dirpath) + if conf.status is None: + conf.status = self.read_status_from(conf) + if True: + for key in sorted(status.keys()): + value = status[key] + if key.upper() == "AS": key = "ActiveState" + if key.upper() == "EXIT": key = "ExecMainCode" + if value is None: + try: del conf.status[key] + except KeyError: pass + else: + conf.status[key] = strE(value) + try: + with open(status_file, "w") as f: + for key in sorted(conf.status): + value = conf.status[key] + if key == "MainPID" and str(value) == "0": + logg.warning("ignore writing MainPID=0") + continue + content = "{}={}\n".format(key, str(value)) + logg.debug("writing to %s\n\t%s", status_file, content.strip()) + f.write(content) + except IOError as e: + logg.error("writing STATUS %s: %s\n\t to status file %s", status, e, status_file) + return True + def read_status_from(self, conf): + status_file = self.get_status_file_from(conf) + status = {} + # if not status_file: return status + if not os.path.isfile(status_file): + if DEBUG_STATUS: logg.debug("no status file: %s\n returning %s", status_file, status) + return status + if self.truncate_old(status_file): + if DEBUG_STATUS: logg.debug("old status file: %s\n returning %s", status_file, status) + return status + try: + if DEBUG_STATUS: logg.debug("reading %s", status_file) + for line in open(status_file): + if line.strip(): + m = re.match(r"(\w+)[:=](.*)", line) + if m: + key, value = m.group(1), m.group(2) + if key.strip(): + status[key.strip()] = value.strip() + else: # pragma: no cover + logg.warning("ignored %s", line.strip()) + except: + logg.warning("bad read of status file '%s'", status_file) + return status + def get_status_from(self, conf, name, default = None): + if conf.status is None: + conf.status = self.read_status_from(conf) + return conf.status.get(name, default) + def set_status_from(self, conf, name, value): + if conf.status is None: + conf.status = self.read_status_from(conf) + if value is None: + try: del conf.status[name] + except KeyError: pass + else: + conf.status[name] = value + # + def get_boottime(self): + """ detects the boot time of the container - in general the start time of PID 1 """ + if self._boottime is None: + self._boottime = self.get_boottime_from_proc() + assert self._boottime is not None + return self._boottime + def get_boottime_from_proc(self): + """ detects the latest boot time by looking at the start time of available process""" + pid1 = BOOT_PID_MIN or 0 + pid_max = BOOT_PID_MAX + if pid_max < 0: + pid_max = pid1 - pid_max + for pid in xrange(pid1, pid_max): + proc = _proc_pid_stat.format(**locals()) + try: + if os.path.exists(proc): + # return os.path.getmtime(proc) # did sometimes change + return self.path_proc_started(proc) + except Exception as e: # pragma: no cover + logg.warning("boottime - could not access %s: %s", proc, e) + if DEBUG_BOOTTIME: + logg.debug(" boottime from the oldest entry in /proc [nothing in %s..%s]", pid1, pid_max) + return self.get_boottime_from_old_proc() + def get_boottime_from_old_proc(self): + booted = time.time() + for pid in os.listdir(_proc_pid_dir): + proc = _proc_pid_stat.format(**locals()) + try: + if os.path.exists(proc): + # ctime = os.path.getmtime(proc) + ctime = self.path_proc_started(proc) + if ctime < booted: + booted = ctime + except Exception as e: # pragma: no cover + logg.warning("could not access %s: %s", proc, e) + return booted + + # Use uptime, time process running in ticks, and current time to determine process boot time + # You can't use the modified timestamp of the status file because it isn't static. + # ... using clock ticks it is known to be a linear time on Linux + def path_proc_started(self, proc): + # get time process started after boot in clock ticks + with open(proc) as file_stat: + data_stat = file_stat.readline() + file_stat.close() + stat_data = data_stat.split() + started_ticks = stat_data[21] + # man proc(5): "(22) starttime = The time the process started after system boot." + # ".. the value is expressed in clock ticks (divide by sysconf(_SC_CLK_TCK))." + # NOTE: for containers the start time is related to the boot time of host system. + + clkTickInt = os.sysconf_names['SC_CLK_TCK'] + clockTicksPerSec = os.sysconf(clkTickInt) + started_secs = float(started_ticks) / clockTicksPerSec + if DEBUG_BOOTTIME: + logg.debug(" BOOT .. Proc started time: %.3f (%s)", started_secs, proc) + # this value is the start time from the host system + + # Variant 1: + system_uptime = _proc_sys_uptime + with open(system_uptime, "rb") as file_uptime: + data_uptime = file_uptime.readline() + file_uptime.close() + uptime_data = data_uptime.decode().split() + uptime_secs = float(uptime_data[0]) + if DEBUG_BOOTTIME: + logg.debug(" BOOT 1. System uptime secs: %.3f (%s)", uptime_secs, system_uptime) + + # get time now + now = time.time() + started_time = now - (uptime_secs - started_secs) + if DEBUG_BOOTTIME: + logg.debug(" BOOT 1. Proc has been running since: %s" % (datetime.datetime.fromtimestamp(started_time))) + + # Variant 2: + system_stat = _proc_sys_stat + system_btime = 0. + with open(system_stat, "rb") as f: + for line in f: + assert isinstance(line, bytes) + if line.startswith(b"btime"): + system_btime = float(line.decode().split()[1]) + f.closed + if DEBUG_BOOTTIME: + logg.debug(" BOOT 2. System btime secs: %.3f (%s)", system_btime, system_stat) + + started_btime = system_btime + started_secs + if DEBUG_BOOTTIME: + logg.debug(" BOOT 2. Proc has been running since: %s" % (datetime.datetime.fromtimestamp(started_btime))) + + # return started_time + return started_btime + + def get_filetime(self, filename): + return os.path.getmtime(filename) + def truncate_old(self, filename): + filetime = self.get_filetime(filename) + boottime = self.get_boottime() + if filetime >= boottime: + if DEBUG_BOOTTIME: + logg.debug(" file time: %s (%s)", datetime.datetime.fromtimestamp(filetime), o22(filename)) + logg.debug(" boot time: %s (%s)", datetime.datetime.fromtimestamp(boottime), "status modified later") + return False # OK + if DEBUG_BOOTTIME: + logg.info(" file time: %s (%s)", datetime.datetime.fromtimestamp(filetime), o22(filename)) + logg.info(" boot time: %s (%s)", datetime.datetime.fromtimestamp(boottime), "status TRUNCATED NOW") + try: + shutil_truncate(filename) + except Exception as e: + logg.warning("while truncating: %s", e) + return True # truncated + def getsize(self, filename): + if filename is None: # pragma: no cover (is never null) + return 0 + if not os.path.isfile(filename): + return 0 + if self.truncate_old(filename): + return 0 + try: + return os.path.getsize(filename) + except Exception as e: + logg.warning("while reading file size: %s\n of %s", e, filename) + return 0 + # + def read_env_file(self, env_file): # -> generate[ (name,value) ] + """ EnvironmentFile= is being scanned """ + mode, env_file = load_path(env_file) + real_file = os_path(self._root, env_file) + if not os.path.exists(real_file): + if mode.check: + logg.error("file does not exist: %s", real_file) + else: + logg.debug("file does not exist: %s", real_file) + return + try: + for real_line in open(os_path(self._root, env_file)): + line = real_line.strip() + if not line or line.startswith("#"): + continue + m = re.match(r"(?:export +)?([\w_]+)[=]'([^']*)'", line) + if m: + yield m.group(1), m.group(2) + continue + m = re.match(r'(?:export +)?([\w_]+)[=]"([^"]*)"', line) + if m: + yield m.group(1), m.group(2) + continue + m = re.match(r'(?:export +)?([\w_]+)[=](.*)', line) + if m: + yield m.group(1), m.group(2) + continue + except Exception as e: + logg.info("while reading %s: %s", env_file, e) + def read_env_part(self, env_part): # -> generate[ (name, value) ] + """ Environment== is being scanned """ + # systemd Environment= spec says it is a space-separated list of + # assignments. In order to use a space or an equals sign in a value + # one should enclose the whole assignment with double quotes: + # Environment="VAR1=word word" VAR2=word3 "VAR3=$word 5 6" + # and the $word is not expanded by other environment variables. + try: + for real_line in env_part.split("\n"): + line = real_line.strip() + for found in re.finditer(r'\s*("[\w_]+=[^"]*"|[\w_]+=\S*)', line): + part = found.group(1) + if part.startswith('"'): + part = part[1:-1] + name, value = part.split("=", 1) + yield name, value + except Exception as e: + logg.info("while reading %s: %s", env_part, e) + def command_of_unit(self, unit): + """ [UNIT]. -- show service settings (experimental) + or use -p VarName to show another property than 'ExecStart' """ + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s could not be found.", unit) + self.error |= NOT_FOUND + return None + if self._only_property: + found = [] + for prop in self._only_property: + found += conf.getlist(Service, prop) + return found + return conf.getlist(Service, "ExecStart") + def environment_of_unit(self, unit): + """ [UNIT]. -- show environment parts """ + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s could not be found.", unit) + self.error |= NOT_FOUND + return None + return self.get_env(conf) + def extra_vars(self): + return self._extra_vars # from command line + def get_env(self, conf): + env = os.environ.copy() + for env_part in conf.getlist(Service, "Environment", []): + for name, value in self.read_env_part(self.expand_special(env_part, conf)): + env[name] = value # a '$word' is not special here (lazy expansion) + for env_file in conf.getlist(Service, "EnvironmentFile", []): + for name, value in self.read_env_file(self.expand_special(env_file, conf)): + env[name] = self.expand_env(value, env) # but nonlazy expansion here + logg.debug("extra-vars %s", self.extra_vars()) + for extra in self.extra_vars(): + if extra.startswith("@"): + for name, value in self.read_env_file(extra[1:]): + logg.info("override %s=%s", name, value) + env[name] = self.expand_env(value, env) + else: + for name, value in self.read_env_part(extra): + logg.info("override %s=%s", name, value) + env[name] = value # a '$word' is not special here + return env + def expand_env(self, cmd, env): + def get_env1(m): + name = m.group(1) + if name in env: + return env[name] + namevar = "$%s" % name + logg.debug("can not expand %s", namevar) + return (EXPAND_KEEP_VARS and namevar or "") + def get_env2(m): + name = m.group(1) + if name in env: + return env[name] + namevar = "${%s}" % name + logg.debug("can not expand %s", namevar) + return (EXPAND_KEEP_VARS and namevar or "") + # + maxdepth = EXPAND_VARS_MAXDEPTH + expanded = re.sub(r"[$](\w+)", lambda m: get_env1(m), cmd.replace("\\\n", "")) + for depth in xrange(maxdepth): + new_text = re.sub(r"[$][{](\w+)[}]", lambda m: get_env2(m), expanded) + if new_text == expanded: + return expanded + expanded = new_text + logg.error("shell variable expansion exceeded maxdepth %s", maxdepth) + return expanded + def expand_special(self, cmd, conf): + """ expand %i %t and similar special vars. They are being expanded + before any other expand_env takes place which handles shell-style + $HOME references. """ + def xx(arg): return unit_name_unescape(arg) + def yy(arg): return arg + def get_confs(conf): + confs={"%": "%"} + if conf is None: # pragma: no cover (is never null) + return confs + unit = parse_unit(conf.name()) + # + root = conf.root_mode() + VARTMP = get_VARTMP(root) # $TMPDIR # "/var/tmp" + TMP = get_TMP(root) # $TMPDIR # "/tmp" + RUN = get_RUNTIME_DIR(root) # $XDG_RUNTIME_DIR # "/run" + ETC = get_CONFIG_HOME(root) # $XDG_CONFIG_HOME # "/etc" + DAT = get_VARLIB_HOME(root) # $XDG_CONFIG_HOME # "/var/lib" + LOG = get_LOG_DIR(root) # $XDG_CONFIG_HOME/log # "/var/log" + CACHE = get_CACHE_HOME(root) # $XDG_CACHE_HOME # "/var/cache" + HOME = get_HOME(root) # $HOME or ~ # "/root" + USER = get_USER(root) # geteuid().pw_name # "root" + USER_ID = get_USER_ID(root) # geteuid() # 0 + GROUP = get_GROUP(root) # getegid().gr_name # "root" + GROUP_ID = get_GROUP_ID(root) # getegid() # 0 + SHELL = get_SHELL(root) # $SHELL # "/bin/sh" + # confs["b"] = boot_ID + confs["C"] = os_path(self._root, CACHE) # Cache directory root + confs["E"] = os_path(self._root, ETC) # Configuration directory root + confs["F"] = strE(conf.filename()) # EXTRA + confs["f"] = "/%s" % xx(unit.instance or unit.prefix) + confs["h"] = HOME # User home directory + # confs["H"] = host_NAME + confs["i"] = yy(unit.instance) + confs["I"] = xx(unit.instance) # same as %i but escaping undone + confs["j"] = yy(unit.component) # final component of the prefix + confs["J"] = xx(unit.component) # unescaped final component + confs["L"] = os_path(self._root, LOG) + # confs["m"] = machine_ID + confs["n"] = yy(unit.fullname) # Full unit name + confs["N"] = yy(unit.name) # Same as "%n", but with the type suffix removed. + confs["p"] = yy(unit.prefix) # before the first "@" or same as %n + confs["P"] = xx(unit.prefix) # same as %p but escaping undone + confs["s"] = SHELL + confs["S"] = os_path(self._root, DAT) + confs["t"] = os_path(self._root, RUN) + confs["T"] = os_path(self._root, TMP) + confs["g"] = GROUP + confs["G"] = str(GROUP_ID) + confs["u"] = USER + confs["U"] = str(USER_ID) + confs["V"] = os_path(self._root, VARTMP) + return confs + def get_conf1(m): + confs = get_confs(conf) + if m.group(1) in confs: + return confs[m.group(1)] + logg.warning("can not expand %%%s", m.group(1)) + return "" + result = "" + if cmd: + result = re.sub("[%](.)", lambda m: get_conf1(m), cmd) + # ++# logg.info("expanded => %s", result) + return result + def exec_newcmd(self, cmd, env, conf): + mode, exe = exec_path(cmd) + if mode.noexpand: + newcmd = self.split_cmd(exe) + else: + newcmd = self.expand_cmd(exe, env, conf) + if mode.argv0: + if len(newcmd) > 1: + del newcmd[1] # TODO: keep but allow execve calls to pick it up + return mode, newcmd + def split_cmd(self, cmd): + cmd2 = cmd.replace("\\\n", "") + newcmd = [] + for part in shlex.split(cmd2): + newcmd += [part] + return newcmd + def expand_cmd(self, cmd, env, conf): + """ expand ExecCmd statements including %i and $MAINPID """ + cmd2 = cmd.replace("\\\n", "") + # according to documentation, when bar="one two" then the expansion + # of '$bar' is ["one","two"] and '${bar}' becomes ["one two"]. We + # tackle that by expand $bar before shlex, and the rest thereafter. + def get_env1(m): + name = m.group(1) + if name in env: + return env[name] + logg.debug("can not expand $%s", name) + return "" # empty string + def get_env2(m): + name = m.group(1) + if name in env: + return env[name] + logg.debug("can not expand $%s}}", name) + return "" # empty string + cmd3 = re.sub(r"[$](\w+)", lambda m: get_env1(m), cmd2) + newcmd = [] + for part in shlex.split(cmd3): + part2 = self.expand_special(part, conf) + newcmd += [re.sub(r"[$][{](\w+)[}]", lambda m: get_env2(m), part2)] # type: ignore[arg-type] + return newcmd + def remove_service_directories(self, conf, section = Service): + # | + ok = True + nameRuntimeDirectory = self.get_RuntimeDirectory(conf, section) + keepRuntimeDirectory = self.get_RuntimeDirectoryPreserve(conf, section) + if not keepRuntimeDirectory: + root = conf.root_mode() + for name in nameRuntimeDirectory.split(" "): + if not name.strip(): continue + RUN = get_RUNTIME_DIR(root) + path = os.path.join(RUN, name) + dirpath = os_path(self._root, path) + ok = self.do_rm_tree(dirpath) and ok + if RUN == "/run": + for var_run in ("/var/run", "/tmp/run"): + if os.path.isdir(var_run): + var_path = os.path.join(var_run, name) + var_dirpath = os_path(self._root, var_path) + self.do_rm_tree(var_dirpath) + if not ok: + logg.debug("could not fully remove service directory %s", path) + return ok + def do_rm_tree(self, path): + ok = True + if os.path.isdir(path): + for dirpath, dirnames, filenames in os.walk(path, topdown=False): + for item in filenames: + filepath = os.path.join(dirpath, item) + try: + os.remove(filepath) + except Exception as e: # pragma: no cover + logg.debug("not removed file: %s (%s)", filepath, e) + ok = False + for item in dirnames: + dir_path = os.path.join(dirpath, item) + try: + os.rmdir(dir_path) + except Exception as e: # pragma: no cover + logg.debug("not removed dir: %s (%s)", dir_path, e) + ok = False + try: + os.rmdir(path) + except Exception as e: + logg.debug("not removed top dir: %s (%s)", path, e) + ok = False # pragma: no cover + logg.debug("%s rm_tree %s", ok and "done" or "fail", path) + return ok + def get_RuntimeDirectoryPreserve(self, conf, section = Service): + return conf.getbool(section, "RuntimeDirectoryPreserve", "no") + def get_RuntimeDirectory(self, conf, section = Service): + return self.expand_special(conf.get(section, "RuntimeDirectory", ""), conf) + def get_StateDirectory(self, conf, section = Service): + return self.expand_special(conf.get(section, "StateDirectory", ""), conf) + def get_CacheDirectory(self, conf, section = Service): + return self.expand_special(conf.get(section, "CacheDirectory", ""), conf) + def get_LogsDirectory(self, conf, section = Service): + return self.expand_special(conf.get(section, "LogsDirectory", ""), conf) + def get_ConfigurationDirectory(self, conf, section = Service): + return self.expand_special(conf.get(section, "ConfigurationDirectory", ""), conf) + def get_RuntimeDirectoryMode(self, conf, section = Service): + return conf.get(section, "RuntimeDirectoryMode", "") + def get_StateDirectoryMode(self, conf, section = Service): + return conf.get(section, "StateDirectoryMode", "") + def get_CacheDirectoryMode(self, conf, section = Service): + return conf.get(section, "CacheDirectoryMode", "") + def get_LogsDirectoryMode(self, conf, section = Service): + return conf.get(section, "LogsDirectoryMode", "") + def get_ConfigurationDirectoryMode(self, conf, section = Service): + return conf.get(section, "ConfigurationDirectoryMode", "") + def clean_service_directories(self, conf, which = ""): + ok = True + section = self.get_unit_section_from(conf) + nameRuntimeDirectory = self.get_RuntimeDirectory(conf, section) + nameStateDirectory = self.get_StateDirectory(conf, section) + nameCacheDirectory = self.get_CacheDirectory(conf, section) + nameLogsDirectory = self.get_LogsDirectory(conf, section) + nameConfigurationDirectory = self.get_ConfigurationDirectory(conf, section) + root = conf.root_mode() + for name in nameRuntimeDirectory.split(" "): + if not name.strip(): continue + RUN = get_RUNTIME_DIR(root) + path = os.path.join(RUN, name) + if which in ["all", "runtime", ""]: + dirpath = os_path(self._root, path) + ok = self.do_rm_tree(dirpath) and ok + if RUN == "/run": + for var_run in ("/var/run", "/tmp/run"): + var_path = os.path.join(var_run, name) + var_dirpath = os_path(self._root, var_path) + self.do_rm_tree(var_dirpath) + for name in nameStateDirectory.split(" "): + if not name.strip(): continue + DAT = get_VARLIB_HOME(root) + path = os.path.join(DAT, name) + if which in ["all", "state"]: + dirpath = os_path(self._root, path) + ok = self.do_rm_tree(dirpath) and ok + for name in nameCacheDirectory.split(" "): + if not name.strip(): continue + CACHE = get_CACHE_HOME(root) + path = os.path.join(CACHE, name) + if which in ["all", "cache", ""]: + dirpath = os_path(self._root, path) + ok = self.do_rm_tree(dirpath) and ok + for name in nameLogsDirectory.split(" "): + if not name.strip(): continue + LOGS = get_LOG_DIR(root) + path = os.path.join(LOGS, name) + if which in ["all", "logs"]: + dirpath = os_path(self._root, path) + ok = self.do_rm_tree(dirpath) and ok + for name in nameConfigurationDirectory.split(" "): + if not name.strip(): continue + CONFIG = get_CONFIG_HOME(root) + path = os.path.join(CONFIG, name) + if which in ["all", "configuration", ""]: + dirpath = os_path(self._root, path) + ok = self.do_rm_tree(dirpath) and ok + return ok + def env_service_directories(self, conf): + envs = {} + section = self.get_unit_section_from(conf) + nameRuntimeDirectory = self.get_RuntimeDirectory(conf, section) + nameStateDirectory = self.get_StateDirectory(conf, section) + nameCacheDirectory = self.get_CacheDirectory(conf, section) + nameLogsDirectory = self.get_LogsDirectory(conf, section) + nameConfigurationDirectory = self.get_ConfigurationDirectory(conf, section) + root = conf.root_mode() + for name in nameRuntimeDirectory.split(" "): + if not name.strip(): continue + RUN = get_RUNTIME_DIR(root) + path = os.path.join(RUN, name) + envs["RUNTIME_DIRECTORY"] = path + for name in nameStateDirectory.split(" "): + if not name.strip(): continue + DAT = get_VARLIB_HOME(root) + path = os.path.join(DAT, name) + envs["STATE_DIRECTORY"] = path + for name in nameCacheDirectory.split(" "): + if not name.strip(): continue + CACHE = get_CACHE_HOME(root) + path = os.path.join(CACHE, name) + envs["CACHE_DIRECTORY"] = path + for name in nameLogsDirectory.split(" "): + if not name.strip(): continue + LOGS = get_LOG_DIR(root) + path = os.path.join(LOGS, name) + envs["LOGS_DIRECTORY"] = path + for name in nameConfigurationDirectory.split(" "): + if not name.strip(): continue + CONFIG = get_CONFIG_HOME(root) + path = os.path.join(CONFIG, name) + envs["CONFIGURATION_DIRECTORY"] = path + return envs + def create_service_directories(self, conf): + envs = {} + section = self.get_unit_section_from(conf) + nameRuntimeDirectory = self.get_RuntimeDirectory(conf, section) + modeRuntimeDirectory = self.get_RuntimeDirectoryMode(conf, section) + nameStateDirectory = self.get_StateDirectory(conf, section) + modeStateDirectory = self.get_StateDirectoryMode(conf, section) + nameCacheDirectory = self.get_CacheDirectory(conf, section) + modeCacheDirectory = self.get_CacheDirectoryMode(conf, section) + nameLogsDirectory = self.get_LogsDirectory(conf, section) + modeLogsDirectory = self.get_LogsDirectoryMode(conf, section) + nameConfigurationDirectory = self.get_ConfigurationDirectory(conf, section) + modeConfigurationDirectory = self.get_ConfigurationDirectoryMode(conf, section) + root = conf.root_mode() + user = self.get_User(conf) + group = self.get_Group(conf) + for name in nameRuntimeDirectory.split(" "): + if not name.strip(): continue + RUN = get_RUNTIME_DIR(root) + path = os.path.join(RUN, name) + logg.debug("RuntimeDirectory %s", path) + self.make_service_directory(path, modeRuntimeDirectory) + self.chown_service_directory(path, user, group) + envs["RUNTIME_DIRECTORY"] = path + if RUN == "/run": + for var_run in ("/var/run", "/tmp/run"): + if os.path.isdir(var_run): + var_path = os.path.join(var_run, name) + var_dirpath = os_path(self._root, var_path) + if os.path.isdir(var_dirpath): + if not os.path.islink(var_dirpath): + logg.debug("not a symlink: %s", var_dirpath) + continue + dirpath = os_path(self._root, path) + basepath = os.path.dirname(var_dirpath) + if not os.path.isdir(basepath): + os.makedirs(basepath) + try: + os.symlink(dirpath, var_dirpath) + except Exception as e: + logg.debug("var symlink %s\n\t%s", var_dirpath, e) + for name in nameStateDirectory.split(" "): + if not name.strip(): continue + DAT = get_VARLIB_HOME(root) + path = os.path.join(DAT, name) + logg.debug("StateDirectory %s", path) + self.make_service_directory(path, modeStateDirectory) + self.chown_service_directory(path, user, group) + envs["STATE_DIRECTORY"] = path + for name in nameCacheDirectory.split(" "): + if not name.strip(): continue + CACHE = get_CACHE_HOME(root) + path = os.path.join(CACHE, name) + logg.debug("CacheDirectory %s", path) + self.make_service_directory(path, modeCacheDirectory) + self.chown_service_directory(path, user, group) + envs["CACHE_DIRECTORY"] = path + for name in nameLogsDirectory.split(" "): + if not name.strip(): continue + LOGS = get_LOG_DIR(root) + path = os.path.join(LOGS, name) + logg.debug("LogsDirectory %s", path) + self.make_service_directory(path, modeLogsDirectory) + self.chown_service_directory(path, user, group) + envs["LOGS_DIRECTORY"] = path + for name in nameConfigurationDirectory.split(" "): + if not name.strip(): continue + CONFIG = get_CONFIG_HOME(root) + path = os.path.join(CONFIG, name) + logg.debug("ConfigurationDirectory %s", path) + self.make_service_directory(path, modeConfigurationDirectory) + # not done according the standard + # self.chown_service_directory(path, user, group) + envs["CONFIGURATION_DIRECTORY"] = path + return envs + def make_service_directory(self, path, mode): + ok = True + dirpath = os_path(self._root, path) + if not os.path.isdir(dirpath): + try: + os.makedirs(dirpath) + logg.info("created directory path: %s", dirpath) + except Exception as e: # pragma: no cover + logg.debug("errors directory path: %s\n\t%s", dirpath, e) + ok = False + filemode = int_mode(mode) + if filemode: + try: + os.chmod(dirpath, filemode) + except Exception as e: # pragma: no cover + logg.debug("errors directory path: %s\n\t%s", dirpath, e) + ok = False + else: + logg.debug("path did already exist: %s", dirpath) + if not ok: + logg.debug("could not fully create service directory %s", path) + return ok + def chown_service_directory(self, path, user, group): + # the standard defines an optimization so that if the parent + # directory does have the correct user and group then there + # is no other chown on files and subdirectories to be done. + dirpath = os_path(self._root, path) + if not os.path.isdir(dirpath): + logg.debug("chown did not find %s", dirpath) + return True + if user or group: + st = os.stat(dirpath) + st_user = pwd.getpwuid(st.st_uid).pw_name + st_group = grp.getgrgid(st.st_gid).gr_name + change = False + if user and (user.strip() != st_user and user.strip() != str(st.st_uid)): + change = True + if group and (group.strip() != st_group and group.strip() != str(st.st_gid)): + change = True + if change: + logg.debug("do chown %s", dirpath) + try: + ok = self.do_chown_tree(dirpath, user, group) + logg.info("changed %s:%s %s", user, group, ok) + return ok + except Exception as e: + logg.info("oops %s\n\t%s", dirpath, e) + else: + logg.debug("untouched %s", dirpath) + return True + def do_chown_tree(self, path, user, group): + ok = True + uid, gid = -1, -1 + if user: + uid = pwd.getpwnam(user).pw_uid + gid = pwd.getpwnam(user).pw_gid + if group: + gid = grp.getgrnam(group).gr_gid + for dirpath, dirnames, filenames in os.walk(path, topdown=False): + for item in filenames: + filepath = os.path.join(dirpath, item) + try: + os.chown(filepath, uid, gid) + except Exception as e: # pragma: no cover + logg.debug("could not set %s:%s on %s\n\t%s", user, group, filepath, e) + ok = False + for item in dirnames: + dir_path = os.path.join(dirpath, item) + try: + os.chown(dir_path, uid, gid) + except Exception as e: # pragma: no cover + logg.debug("could not set %s:%s on %s\n\t%s", user, group, dir_path, e) + ok = False + try: + os.chown(path, uid, gid) + except Exception as e: # pragma: no cover + logg.debug("could not set %s:%s on %s\n\t%s", user, group, path, e) + ok = False + if not ok: + logg.debug("could not chown %s:%s service directory %s", user, group, path) + return ok + def clean_modules(self, *modules): + """ [UNIT]... -- remove the state directories + /// it recognizes --what=all or any of configuration, state, cache, logs, runtime + while an empty value (the default) removes cache and runtime directories""" + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + lines = _log_lines + follow = _force + ok = self.clean_units(units) + return ok and found_all + def clean_units(self, units, what = ""): + if not what: + what = self._only_what[0] + ok = True + for unit in units: + ok = self.clean_unit(unit, what) and ok + return ok + def clean_unit(self, unit, what = ""): + conf = self.load_unit_conf(unit) + if not conf: return False + return self.clean_unit_from(conf, what) + def clean_unit_from(self, conf, what): + if self.is_active_from(conf): + logg.warning("can not clean active unit: %s", conf.name()) + return False + return self.clean_service_directories(conf, what) + def log_modules(self, *modules): + """ [UNIT]... -- start 'less' on the log files for the services + /// use '-f' to follow and '-n lines' to limit output using 'tail', + using '--no-pager' just does a full 'cat'""" + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + lines = _log_lines + follow = _force + result = self.log_units(units, lines, follow) + if result: + self.error = result + return False + return found_all + def log_units(self, units, lines = None, follow = False): + result = 0 + for unit in self.sortedAfter(units): + exitcode = self.log_unit(unit, lines, follow) + if exitcode < 0: + return exitcode + if exitcode > result: + result = exitcode + return result + def log_unit(self, unit, lines = None, follow = False): + conf = self.load_unit_conf(unit) + if not conf: return -1 + return self.log_unit_from(conf, lines, follow) + def log_unit_from(self, conf, lines = None, follow = False): + cmd_args = [] + log_path = self.get_journal_log_from(conf) + if follow: + tail_cmd = get_exist_path(TAIL_CMDS) + if tail_cmd is None: + print("tail command not found") + return 1 + cmd = [tail_cmd, "-n", str(lines or 10), "-F", log_path] + logg.debug("journalctl %s -> %s", conf.name(), cmd) + cmd_args = [arg for arg in cmd] # satisfy mypy + return os.execvp(cmd_args[0], cmd_args) + elif lines: + tail_cmd = get_exist_path(TAIL_CMDS) + if tail_cmd is None: + print("tail command not found") + return 1 + cmd = [tail_cmd, "-n", str(lines or 10), log_path] + logg.debug("journalctl %s -> %s", conf.name(), cmd) + cmd_args = [arg for arg in cmd] # satisfy mypy + return os.execvp(cmd_args[0], cmd_args) + elif _no_pager: + cat_cmd = get_exist_path(CAT_CMDS) + if cat_cmd is None: + print("cat command not found") + return 1 + cmd = [cat_cmd, log_path] + logg.debug("journalctl %s -> %s", conf.name(), cmd) + cmd_args = [arg for arg in cmd] # satisfy mypy + return os.execvp(cmd_args[0], cmd_args) + else: + less_cmd = get_exist_path(LESS_CMDS) + if less_cmd is None: + print("less command not found") + return 1 + cmd = [less_cmd, log_path] + logg.debug("journalctl %s -> %s", conf.name(), cmd) + cmd_args = [arg for arg in cmd] # satisfy mypy + return os.execvp(cmd_args[0], cmd_args) + def get_journal_log_from(self, conf): + return os_path(self._root, self.get_journal_log(conf)) + def get_journal_log(self, conf): + """ /var/log/zzz.service.log or /var/log/default.unit.log """ + filename = os.path.basename(strE(conf.filename())) + unitname = (conf.name() or "default")+".unit" + name = filename or unitname + log_folder = expand_path(self._journal_log_folder, conf.root_mode()) + log_file = name.replace(os.path.sep, ".") + ".log" + if log_file.startswith("."): + log_file = "dot."+log_file + return os.path.join(log_folder, log_file) + def open_journal_log(self, conf): + log_file = self.get_journal_log_from(conf) + log_folder = os.path.dirname(log_file) + if not os.path.isdir(log_folder): + os.makedirs(log_folder) + return open(os.path.join(log_file), "a") + def get_WorkingDirectory(self, conf): + return conf.get(Service, "WorkingDirectory", "") + def chdir_workingdir(self, conf): + """ if specified then change the working directory """ + # the original systemd will start in '/' even if User= is given + if self._root: + os.chdir(self._root) + workingdir = self.get_WorkingDirectory(conf) + mode, workingdir = load_path(workingdir) + if workingdir: + into = os_path(self._root, self.expand_special(workingdir, conf)) + try: + logg.debug("chdir workingdir '%s'", into) + os.chdir(into) + return False + except Exception as e: + if mode.check: + logg.error("chdir workingdir '%s': %s", into, e) + return into + else: + logg.debug("chdir workingdir '%s': %s", into, e) + return None + return None + NotifySocket = collections.namedtuple("NotifySocket", ["socket", "socketfile"]) + def get_notify_socket_from(self, conf, socketfile = None, debug = False): + """ creates a notify-socket for the (non-privileged) user """ + notify_socket_folder = expand_path(_notify_socket_folder, conf.root_mode()) + notify_folder = os_path(self._root, notify_socket_folder) + notify_name = "notify." + str(conf.name() or "systemctl") + notify_socket = os.path.join(notify_folder, notify_name) + socketfile = socketfile or notify_socket + if len(socketfile) > 100: + # occurs during testsuite.py for ~user/test.tmp/root path + if debug: + logg.debug("https://unix.stackexchange.com/questions/367008/%s", + "why-is-socket-path-length-limited-to-a-hundred-chars") + logg.debug("old notify socketfile (%s) = %s", len(socketfile), socketfile) + notify_name44 = o44(notify_name) + notify_name77 = o77(notify_name) + socketfile = os.path.join(notify_folder, notify_name77) + if len(socketfile) > 100: + socketfile = os.path.join(notify_folder, notify_name44) + pref = "zz.%i.%s" % (get_USER_ID(), o22(os.path.basename(notify_socket_folder))) + if len(socketfile) > 100: + socketfile = os.path.join(get_TMP(), pref, notify_name) + if len(socketfile) > 100: + socketfile = os.path.join(get_TMP(), pref, notify_name77) + if len(socketfile) > 100: # pragma: no cover + socketfile = os.path.join(get_TMP(), pref, notify_name44) + if len(socketfile) > 100: # pragma: no cover + socketfile = os.path.join(get_TMP(), notify_name44) + if debug: + logg.info("new notify socketfile (%s) = %s", len(socketfile), socketfile) + return socketfile + def notify_socket_from(self, conf, socketfile = None): + socketfile = self.get_notify_socket_from(conf, socketfile, debug=True) + try: + if not os.path.isdir(os.path.dirname(socketfile)): + os.makedirs(os.path.dirname(socketfile)) + if os.path.exists(socketfile): + os.unlink(socketfile) + except Exception as e: + logg.warning("error %s: %s", socketfile, e) + sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM) + sock.bind(socketfile) + os.chmod(socketfile, 0o777) # the service my run under some User=setting + return Systemctl.NotifySocket(sock, socketfile) + def read_notify_socket(self, notify, timeout): + notify.socket.settimeout(timeout or DefaultMaximumTimeout) + result = "" + try: + result, client_address = notify.socket.recvfrom(4096) + assert isinstance(result, bytes) + if result: + result = result.decode("utf-8") + result_txt = result.replace("\n", "|") + result_len = len(result) + logg.debug("read_notify_socket(%s):%s", result_len, result_txt) + except socket.timeout as e: + if timeout > 2: + logg.debug("socket.timeout %s", e) + return result + def wait_notify_socket(self, notify, timeout, pid = None, pid_file = None): + if not os.path.exists(notify.socketfile): + logg.info("no $NOTIFY_SOCKET exists") + return {} + # + lapseTimeout = max(3, int(timeout / 100)) + mainpidTimeout = lapseTimeout # Apache sends READY before MAINPID + status = "" + logg.info("wait $NOTIFY_SOCKET, timeout %s (lapse %s)", timeout, lapseTimeout) + waiting = " ---" + results = {} + for attempt in xrange(int(timeout)+1): + if pid and not self.is_active_pid(pid): + logg.info("seen dead PID %s", pid) + return results + if not attempt: # first one + time.sleep(1) # until TimeoutStartSec + continue + result = self.read_notify_socket(notify, 1) # sleep max 1 second + for line in result.splitlines(): + # for name, value in self.read_env_part(line) + if "=" not in line: + continue + name, value = line.split("=", 1) + results[name] = value + if name in ["STATUS", "ACTIVESTATE", "MAINPID", "READY"]: + hint="seen notify %s " % (waiting) + logg.debug("%s :%s=%s", hint, name, value) + if status != results.get("STATUS", ""): + mainpidTimeout = lapseTimeout + status = results.get("STATUS", "") + if "READY" not in results: + time.sleep(1) # until TimeoutStart + continue + if "MAINPID" not in results and not pid_file: + mainpidTimeout -= 1 + if mainpidTimeout > 0: + waiting = "%4i" % (-mainpidTimeout) + time.sleep(1) # until TimeoutStart + continue + break # READY and MAINPID + if "READY" not in results: + logg.info(".... timeout while waiting for 'READY=1' status on $NOTIFY_SOCKET") + elif "MAINPID" not in results: + logg.info(".... seen 'READY=1' but no MAINPID update status on $NOTIFY_SOCKET") + logg.debug("notify = %s", results) + try: + notify.socket.close() + except Exception as e: + logg.debug("socket.close %s", e) + return results + def start_modules(self, *modules): + """ [UNIT]... -- start these units + /// SPECIAL: with --now or --init it will + run the init-loop and stop the units afterwards """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + init = self._now or self._init + return self.start_units(units, init) and found_all + def start_units(self, units, init = None): + """ fails if any unit does not start + /// SPECIAL: may run the init-loop and + stop the named units afterwards """ + self.wait_system() + done = True + started_units = [] + for unit in self.sortedAfter(units): + started_units.append(unit) + if not self.start_unit(unit): + done = False + if init: + logg.info("init-loop start") + sig = self.init_loop_until_stop(started_units) + logg.info("init-loop %s", sig) + for unit in reversed(started_units): + self.stop_unit(unit) + return done + def start_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.debug("unit could not be loaded (%s)", unit) + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.start_unit_from(conf) + def get_TimeoutStartSec(self, conf): + timeout = conf.get(Service, "TimeoutSec", strE(DefaultTimeoutStartSec)) + timeout = conf.get(Service, "TimeoutStartSec", timeout) + return time_to_seconds(timeout, DefaultMaximumTimeout) + def get_SocketTimeoutSec(self, conf): + timeout = conf.get(Socket, "TimeoutSec", strE(DefaultTimeoutStartSec)) + return time_to_seconds(timeout, DefaultMaximumTimeout) + def get_RemainAfterExit(self, conf): + return conf.getbool(Service, "RemainAfterExit", "no") + def start_unit_from(self, conf): + if not conf: return False + if self.syntax_check(conf) > 100: return False + with waitlock(conf): + logg.debug(" start unit %s => %s", conf.name(), strQ(conf.filename())) + return self.do_start_unit_from(conf) + def do_start_unit_from(self, conf): + if conf.name().endswith(".service"): + return self.do_start_service_from(conf) + elif conf.name().endswith(".socket"): + return self.do_start_socket_from(conf) + elif conf.name().endswith(".target"): + return self.do_start_target_from(conf) + else: + logg.error("start not implemented for unit type: %s", conf.name()) + return False + def do_start_service_from(self, conf): + timeout = self.get_TimeoutStartSec(conf) + doRemainAfterExit = self.get_RemainAfterExit(conf) + runs = conf.get(Service, "Type", "simple").lower() + env = self.get_env(conf) + if not self._quiet: + okee = self.exec_check_unit(conf, env, Service, "Exec") # all... + if not okee and _no_reload: return False + service_directories = self.create_service_directories(conf) + env.update(service_directories) # atleast sshd did check for /run/sshd + # for StopPost on failure: + returncode = 0 + service_result = "success" + if True: + if runs in ["simple", "exec", "forking", "notify", "idle"]: + env["MAINPID"] = strE(self.read_mainpid_from(conf)) + for cmd in conf.getlist(Service, "ExecStartPre", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info(" pre-start %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + logg.debug(" pre-start done (%s) <-%s>", + run.returncode or "OK", run.signal or "") + if run.returncode and exe.check: + logg.error("the ExecStartPre control process exited with error code") + active = "failed" + self.write_status_from(conf, AS=active) + if self._only_what[0] not in ["none", "keep"]: + self.remove_service_directories(conf) # cleanup that /run/sshd + return False + if runs in ["oneshot"]: + status_file = self.get_status_file_from(conf) + if self.get_status_from(conf, "ActiveState", "unknown") == "active": + logg.warning("the service was already up once") + return True + for cmd in conf.getlist(Service, "ExecStart", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("%s start %s", runs, shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: # pragma: no cover + os.setsid() # detach child process from parent + self.execve_from(conf, newcmd, env) + run = subprocess_waitpid(forkpid) + if run.returncode and exe.check: + returncode = run.returncode + service_result = "failed" + logg.error("%s start %s (%s) <-%s>", runs, service_result, + run.returncode or "OK", run.signal or "") + break + logg.info("%s start done (%s) <-%s>", runs, + run.returncode or "OK", run.signal or "") + if True: + self.set_status_from(conf, "ExecMainCode", strE(returncode)) + active = returncode and "failed" or "active" + self.write_status_from(conf, AS=active) + elif runs in ["simple", "exec", "idle"]: + status_file = self.get_status_file_from(conf) + pid = self.read_mainpid_from(conf) + if self.is_active_pid(pid): + logg.warning("the service is already running on PID %s", pid) + return True + if doRemainAfterExit: + logg.debug("%s RemainAfterExit -> AS=active", runs) + self.write_status_from(conf, AS="active") + cmdlist = conf.getlist(Service, "ExecStart", []) + for idx, cmd in enumerate(cmdlist): + logg.debug("ExecStart[%s]: %s", idx, cmd) + for cmd in cmdlist: + pid = self.read_mainpid_from(conf) + env["MAINPID"] = strE(pid) + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("%s start %s", runs, shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: # pragma: no cover + os.setsid() # detach child process from parent + self.execve_from(conf, newcmd, env) + self.write_status_from(conf, MainPID=forkpid) + logg.info("%s started PID %s", runs, forkpid) + env["MAINPID"] = strE(forkpid) + time.sleep(MinimumYield) + run = subprocess_testpid(forkpid) + if run.returncode is not None: + logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, + run.returncode or "OK", run.signal or "") + if doRemainAfterExit: + self.set_status_from(conf, "ExecMainCode", strE(run.returncode)) + active = run.returncode and "failed" or "active" + self.write_status_from(conf, AS=active) + if run.returncode and exe.check: + service_result = "failed" + break + elif runs in ["notify"]: + # "notify" is the same as "simple" but we create a $NOTIFY_SOCKET + # and wait for startup completion by checking the socket messages + pid_file = self.pid_file_from(conf) + pid = self.read_mainpid_from(conf) + if self.is_active_pid(pid): + logg.error("the service is already running on PID %s", pid) + return False + notify = self.notify_socket_from(conf) + if notify: + env["NOTIFY_SOCKET"] = notify.socketfile + logg.debug("use NOTIFY_SOCKET=%s", notify.socketfile) + if doRemainAfterExit: + logg.debug("%s RemainAfterExit -> AS=active", runs) + self.write_status_from(conf, AS="active") + cmdlist = conf.getlist(Service, "ExecStart", []) + for idx, cmd in enumerate(cmdlist): + logg.debug("ExecStart[%s]: %s", idx, cmd) + mainpid = None + for cmd in cmdlist: + mainpid = self.read_mainpid_from(conf) + env["MAINPID"] = strE(mainpid) + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("%s start %s", runs, shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: # pragma: no cover + os.setsid() # detach child process from parent + self.execve_from(conf, newcmd, env) + # via NOTIFY # self.write_status_from(conf, MainPID=forkpid) + logg.info("%s started PID %s", runs, forkpid) + mainpid = forkpid + self.write_status_from(conf, MainPID=mainpid) + env["MAINPID"] = strE(mainpid) + time.sleep(MinimumYield) + run = subprocess_testpid(forkpid) + if run.returncode is not None: + logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, + run.returncode or "OK", run.signal or "") + if doRemainAfterExit: + self.set_status_from(conf, "ExecMainCode", strE(run.returncode)) + active = run.returncode and "failed" or "active" + self.write_status_from(conf, AS=active) + if run.returncode and exe.check: + service_result = "failed" + break + if service_result in ["success"] and mainpid: + logg.debug("okay, waiting on socket for %ss", timeout) + results = self.wait_notify_socket(notify, timeout, mainpid, pid_file) + if "MAINPID" in results: + new_pid = to_intN(results["MAINPID"]) + if new_pid and new_pid != mainpid: + logg.info("NEW PID %s from sd_notify (was PID %s)", new_pid, mainpid) + self.write_status_from(conf, MainPID=new_pid) + mainpid = new_pid + logg.info("%s start done %s", runs, mainpid) + pid = self.read_mainpid_from(conf) + if pid: + env["MAINPID"] = strE(pid) + else: + service_result = "timeout" # "could not start service" + elif runs in ["forking"]: + pid_file = self.pid_file_from(conf) + for cmd in conf.getlist(Service, "ExecStart", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + if not newcmd: continue + logg.info("%s start %s", runs, shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: # pragma: no cover + os.setsid() # detach child process from parent + self.execve_from(conf, newcmd, env) + logg.info("%s started PID %s", runs, forkpid) + run = subprocess_waitpid(forkpid) + if run.returncode and exe.check: + returncode = run.returncode + service_result = "failed" + logg.info("%s stopped PID %s (%s) <-%s>", runs, run.pid, + run.returncode or "OK", run.signal or "") + if pid_file and service_result in ["success"]: + pid = self.wait_pid_file(pid_file) # application PIDFile + logg.info("%s start done PID %s [%s]", runs, pid, pid_file) + if pid: + env["MAINPID"] = strE(pid) + if not pid_file: + time.sleep(MinimumTimeoutStartSec) + logg.warning("No PIDFile for forking %s", strQ(conf.filename())) + status_file = self.get_status_file_from(conf) + self.set_status_from(conf, "ExecMainCode", strE(returncode)) + active = returncode and "failed" or "active" + self.write_status_from(conf, AS=active) + else: + logg.error("unsupported run type '%s'", runs) + return False + # POST sequence + if not self.is_active_from(conf): + logg.warning("%s start not active", runs) + # according to the systemd documentation, a failed start-sequence + # should execute the ExecStopPost sequence allowing some cleanup. + env["SERVICE_RESULT"] = service_result + for cmd in conf.getlist(Service, "ExecStopPost", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("post-fail %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + logg.debug("post-fail done (%s) <-%s>", + run.returncode or "OK", run.signal or "") + if self._only_what[0] not in ["none", "keep"]: + self.remove_service_directories(conf) + return False + else: + for cmd in conf.getlist(Service, "ExecStartPost", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("post-start %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + logg.debug("post-start done (%s) <-%s>", + run.returncode or "OK", run.signal or "") + return True + def listen_modules(self, *modules): + """ [UNIT]... -- listen socket units""" + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.listen_units(units) and found_all + def listen_units(self, units): + """ fails if any socket does not start """ + self.wait_system() + done = True + started_units = [] + active_units = [] + for unit in self.sortedAfter(units): + started_units.append(unit) + if not self.listen_unit(unit): + done = False + else: + active_units.append(unit) + if active_units: + logg.info("init-loop start") + sig = self.init_loop_until_stop(started_units) + logg.info("init-loop %s", sig) + for unit in reversed(started_units): + pass # self.stop_unit(unit) + return done + def listen_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.debug("unit could not be loaded (%s)", unit) + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.listen_unit_from(conf) + def listen_unit_from(self, conf): + if not conf: return False + with waitlock(conf): + logg.debug(" listen unit %s => %s", conf.name(), strQ(conf.filename())) + return self.do_listen_unit_from(conf) + def do_listen_unit_from(self, conf): + if conf.name().endswith(".socket"): + return self.do_start_socket_from(conf) + else: + logg.error("listen not implemented for unit type: %s", conf.name()) + return False + def do_accept_socket_from(self, conf, sock): + logg.debug("%s: accepting %s", conf.name(), sock.fileno()) + service_unit = self.get_socket_service_from(conf) + service_conf = self.load_unit_conf(service_unit) + if service_conf is None or TestAccept: # pragma: no cover + if sock.type == socket.SOCK_STREAM: + conn, addr = sock.accept() + data = conn.recv(1024) + logg.debug("%s: '%s'", conf.name(), data) + conn.send(b"ERROR: "+data.upper()) + conn.close() + return False + if sock.type == socket.SOCK_DGRAM: + data, sender = sock.recvfrom(1024) + logg.debug("%s: '%s'", conf.name(), data) + sock.sendto(b"ERROR: "+data.upper(), sender) + return False + logg.error("can not accept socket type %s", strINET(sock.type)) + return False + return self.do_start_service_from(service_conf) + def get_socket_service_from(self, conf): + socket_unit = conf.name() + accept = conf.getbool(Socket, "Accept", "no") + service_type = accept and "@.service" or ".service" + service_name = path_replace_extension(socket_unit, ".socket", service_type) + service_unit = conf.get(Socket, Service, service_name) + logg.debug("socket %s -> service %s", socket_unit, service_unit) + return service_unit + def do_start_socket_from(self, conf): + runs = "socket" + timeout = self.get_SocketTimeoutSec(conf) + accept = conf.getbool(Socket, "Accept", "no") + stream = conf.get(Socket, "ListenStream", "") + service_unit = self.get_socket_service_from(conf) + service_conf = self.load_unit_conf(service_unit) + if service_conf is None: + logg.debug("unit could not be loaded (%s)", service_unit) + logg.error("Unit %s not found.", service_unit) + return False + env = self.get_env(conf) + if not self._quiet: + okee = self.exec_check_unit(conf, env, Socket, "Exec") # all... + if not okee and _no_reload: return False + if True: + for cmd in conf.getlist(Socket, "ExecStartPre", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info(" pre-start %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + logg.debug(" pre-start done (%s) <-%s>", + run.returncode or "OK", run.signal or "") + if run.returncode and exe.check: + logg.error("the ExecStartPre control process exited with error code") + active = "failed" + self.write_status_from(conf, AS=active) + return False + # service_directories = self.create_service_directories(conf) + # env.update(service_directories) + listening=False + if not accept: + sock = self.create_socket(conf) + if sock and TestListen: + listening=True + self._sockets[conf.name()] = SystemctlSocket(conf, sock) + service_result = "success" + state = sock and "active" or "failed" + self.write_status_from(conf, AS=state) + if not listening: + # we do not listen but have the service started right away + done = self.do_start_service_from(service_conf) + service_result = done and "success" or "failed" + if not self.is_active_from(service_conf): + service_result = "failed" + state = service_result + if service_result in ["success"]: + state = "active" + self.write_status_from(conf, AS=state) + # POST sequence + if service_result in ["failed"]: + # according to the systemd documentation, a failed start-sequence + # should execute the ExecStopPost sequence allowing some cleanup. + env["SERVICE_RESULT"] = service_result + for cmd in conf.getlist(Socket, "ExecStopPost", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("post-fail %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + logg.debug("post-fail done (%s) <-%s>", + run.returncode or "OK", run.signal or "") + return False + else: + for cmd in conf.getlist(Socket, "ExecStartPost", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("post-start %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + logg.debug("post-start done (%s) <-%s>", + run.returncode or "OK", run.signal or "") + return True + def create_socket(self, conf): + unsupported = ["ListenUSBFunction", "ListenMessageQueue", "ListenNetlink"] + unsupported += ["ListenSpecial", "ListenFIFO", "ListenSequentialPacket"] + for item in unsupported: + if conf.get(Socket, item, ""): + logg.warning("%s: %s sockets are not implemented", conf.name(), item) + self.error |= NOT_OK + return None + vListenDatagram = conf.get(Socket, "ListenDatagram", "") + vListenStream = conf.get(Socket, "ListenStream", "") + address = vListenStream or vListenDatagram + m = re.match(r"(/.*)", address) + if m: + path = m.group(1) + sock = self.create_unix_socket(conf, path, not vListenStream) + self.set_status_from(conf, "path", path) + return sock + m = re.match(r"(\d+[.]\d*[.]\d*[.]\d+):(\d+)", address) + if m: + addr, port = m.group(1), m.group(2) + sock = self.create_port_ipv4_socket(conf, addr, port, not vListenStream) + self.set_status_from(conf, "port", port) + self.set_status_from(conf, "addr", addr) + return sock + m = re.match(r"\[([0-9a-fA-F:]*)\]:(\d+)", address) + if m: + addr, port = m.group(1), m.group(2) + sock = self.create_port_ipv6_socket(conf, addr, port, not vListenStream) + self.set_status_from(conf, "port", port) + self.set_status_from(conf, "addr", addr) + return sock + m = re.match(r"(\d+)$", address) + if m: + port = m.group(1) + sock = self.create_port_socket(conf, port, not vListenStream) + self.set_status_from(conf, "port", port) + return sock + if re.match("@.*", address): + logg.warning("%s: abstract namespace socket not implemented (%s)", conf.name(), address) + return None + if re.match("vsock:.*", address): + logg.warning("%s: virtual machine socket not implemented (%s)", conf.name(), address) + return None + logg.error("%s: unknown socket address type (%s)", conf.name(), address) + return None + def create_unix_socket(self, conf, path, dgram): + sock_stream = dgram and socket.SOCK_DGRAM or socket.SOCK_STREAM + sock = socket.socket(socket.AF_UNIX, sock_stream) + try: + dirmode = conf.get(Socket, "DirectoryMode", "0755") + mode = conf.get(Socket, "SocketMode", "0666") + user = conf.get(Socket, "SocketUser", "") + group = conf.get(Socket, "SocketGroup", "") + symlinks = conf.getlist(Socket, "SymLinks", []) + dirpath = os.path.dirname(path) + if not os.path.isdir(dirpath): + os.makedirs(dirpath, int(dirmode, 8)) + if os.path.exists(path): + os.unlink(path) + sock.bind(path) + os.fchmod(sock.fileno(), int(mode, 8)) + shutil_fchown(sock.fileno(), user, group) + if symlinks: + logg.warning("%s: symlinks for socket not implemented (%s)", conf.name(), path) + except Exception as e: + logg.error("%s: create socket failed [%s]: %s", conf.name(), path, e) + sock.close() + return None + return sock + def create_port_socket(self, conf, port, dgram): + inet = dgram and socket.SOCK_DGRAM or socket.SOCK_STREAM + sock = socket.socket(socket.AF_INET, inet) + try: + sock.bind(('', int(port))) + logg.info("%s: bound socket at %s %s:%s", conf.name(), strINET(inet), "*", port) + except Exception as e: + logg.error("%s: create socket failed (%s:%s): %s", conf.name(), "*", port, e) + sock.close() + return None + return sock + def create_port_ipv4_socket(self, conf, addr, port, dgram): + inet = dgram and socket.SOCK_DGRAM or socket.SOCK_STREAM + sock = socket.socket(socket.AF_INET, inet) + try: + sock.bind((addr, int(port))) + logg.info("%s: bound socket at %s %s:%s", conf.name(), strINET(inet), addr, port) + except Exception as e: + logg.error("%s: create socket failed (%s:%s): %s", conf.name(), addr, port, e) + sock.close() + return None + return sock + def create_port_ipv6_socket(self, conf, addr, port, dgram): + inet = dgram and socket.SOCK_DGRAM or socket.SOCK_STREAM + sock = socket.socket(socket.AF_INET6, inet) + try: + sock.bind((addr, int(port))) + logg.info("%s: bound socket at %s [%s]:%s", conf.name(), strINET(inet), addr, port) + except Exception as e: + logg.error("%s: create socket failed ([%s]:%s): %s", conf.name(), addr, port, e) + sock.close() + return None + return sock + def extend_exec_env(self, env): + env = env.copy() + # implant DefaultPath into $PATH + path = env.get("PATH", DefaultPath) + parts = path.split(os.pathsep) + for part in DefaultPath.split(os.pathsep): + if part and part not in parts: + parts.append(part) + env["PATH"] = str(os.pathsep).join(parts) + # reset locale to system default + for name in ResetLocale: + if name in env: + del env[name] + locale = {} + path = env.get("LOCALE_CONF", LocaleConf) + parts = path.split(os.pathsep) + for part in parts: + if os.path.isfile(part): + for var, val in self.read_env_file("-"+part): + locale[var] = val + env[var] = val + if "LANG" not in locale: + env["LANG"] = locale.get("LANGUAGE", locale.get("LC_CTYPE", "C")) + return env + def expand_list(self, group_lines, conf): + result = [] + for line in group_lines: + for item in line.split(): + if item: + result.append(self.expand_special(item, conf)) + return result + def get_User(self, conf): + return self.expand_special(conf.get(Service, "User", ""), conf) + def get_Group(self, conf): + return self.expand_special(conf.get(Service, "Group", ""), conf) + def get_SupplementaryGroups(self, conf): + return self.expand_list(conf.getlist(Service, "SupplementaryGroups", []), conf) + def skip_journal_log(self, conf): + if self.get_unit_type(conf.name()) not in ["service"]: + return True + std_out = conf.get(Service, "StandardOutput", DefaultStandardOutput) + std_err = conf.get(Service, "StandardError", DefaultStandardError) + out, err = False, False + if std_out in ["null"]: out = True + if std_out.startswith("file:"): out = True + if std_err in ["inherit"]: std_err = std_out + if std_err in ["null"]: err = True + if std_err.startswith("file:"): err = True + if std_err.startswith("append:"): err = True + return out and err + def dup2_journal_log(self, conf): + msg = "" + std_inp = conf.get(Service, "StandardInput", DefaultStandardInput) + std_out = conf.get(Service, "StandardOutput", DefaultStandardOutput) + std_err = conf.get(Service, "StandardError", DefaultStandardError) + inp, out, err = None, None, None + if std_inp in ["null"]: + inp = open(_dev_null, "r") + elif std_inp.startswith("file:"): + fname = std_inp[len("file:"):] + if os.path.exists(fname): + inp = open(fname, "r") + else: + inp = open(_dev_zero, "r") + else: + inp = open(_dev_zero, "r") + assert inp is not None + try: + if std_out in ["null"]: + out = open(_dev_null, "w") + elif std_out.startswith("file:"): + fname = std_out[len("file:"):] + fdir = os.path.dirname(fname) + if not os.path.exists(fdir): + os.makedirs(fdir) + out = open(fname, "w") + elif std_out.startswith("append:"): + fname = std_out[len("append:"):] + fdir = os.path.dirname(fname) + if not os.path.exists(fdir): + os.makedirs(fdir) + out = open(fname, "a") + except Exception as e: + msg += "\n%s: %s" % (fname, e) + if out is None: + out = self.open_journal_log(conf) + err = out + assert out is not None + try: + if std_err in ["inherit"]: + err = out + elif std_err in ["null"]: + err = open(_dev_null, "w") + elif std_err.startswith("file:"): + fname = std_err[len("file:"):] + fdir = os.path.dirname(fname) + if not os.path.exists(fdir): + os.makedirs(fdir) + err = open(fname, "w") + elif std_err.startswith("append:"): + fname = std_err[len("append:"):] + fdir = os.path.dirname(fname) + if not os.path.exists(fdir): + os.makedirs(fdir) + err = open(fname, "a") + except Exception as e: + msg += "\n%s: %s" % (fname, e) + if err is None: + err = self.open_journal_log(conf) + assert err is not None + if msg: + err.write("ERROR:") + err.write(msg.strip()) + err.write("\n") + if EXEC_DUP2: + os.dup2(inp.fileno(), sys.stdin.fileno()) + os.dup2(out.fileno(), sys.stdout.fileno()) + os.dup2(err.fileno(), sys.stderr.fileno()) + def execve_from(self, conf, cmd, env): + """ this code is commonly run in a child process // returns exit-code""" + # | + runs = conf.get(Service, "Type", "simple").lower() + # logg.debug("%s process for %s => %s", runs, strE(conf.name()), strQ(conf.filename())) + self.dup2_journal_log(conf) + cmd_args = [] + # + runuser = self.get_User(conf) + rungroup = self.get_Group(conf) + xgroups = self.get_SupplementaryGroups(conf) + envs = shutil_setuid(runuser, rungroup, xgroups) + badpath = self.chdir_workingdir(conf) # some dirs need setuid before + if badpath: + logg.error("(%s): bad workingdir: '%s'", shell_cmd(cmd), badpath) + sys.exit(1) + env = self.extend_exec_env(env) + env.update(envs) # set $HOME to ~$USER + try: + if EXEC_SPAWN: + cmd_args = [arg for arg in cmd] # satisfy mypy + exitcode = os.spawnvpe(os.P_WAIT, cmd[0], cmd_args, env) + sys.exit(exitcode) + else: # pragma: no cover + os.execve(cmd[0], cmd, env) + sys.exit(11) # pragma: no cover (can not be reached / bug like mypy#8401) + except Exception as e: + logg.error("(%s): %s", shell_cmd(cmd), e) + sys.exit(1) + def test_start_unit(self, unit): + """ helper function to test the code that is normally forked off """ + conf = self.load_unit_conf(unit) + if not conf: return None + env = self.get_env(conf) + for cmd in conf.getlist(Service, "ExecStart", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + self.execve_from(conf, newcmd, env) + return None + def stop_modules(self, *modules): + """ [UNIT]... -- stop these units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.stop_units(units) and found_all + def stop_units(self, units): + """ fails if any unit fails to stop """ + self.wait_system() + done = True + for unit in self.sortedBefore(units): + if not self.stop_unit(unit): + done = False + return done + def stop_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.stop_unit_from(conf) + + def get_TimeoutStopSec(self, conf): + timeout = conf.get(Service, "TimeoutSec", strE(DefaultTimeoutStartSec)) + timeout = conf.get(Service, "TimeoutStopSec", timeout) + return time_to_seconds(timeout, DefaultMaximumTimeout) + def stop_unit_from(self, conf): + if not conf: return False + if self.syntax_check(conf) > 100: return False + with waitlock(conf): + logg.info(" stop unit %s => %s", conf.name(), strQ(conf.filename())) + return self.do_stop_unit_from(conf) + def do_stop_unit_from(self, conf): + if conf.name().endswith(".service"): + return self.do_stop_service_from(conf) + elif conf.name().endswith(".socket"): + return self.do_stop_socket_from(conf) + elif conf.name().endswith(".target"): + return self.do_stop_target_from(conf) + else: + logg.error("stop not implemented for unit type: %s", conf.name()) + return False + def do_stop_service_from(self, conf): + # | + timeout = self.get_TimeoutStopSec(conf) + runs = conf.get(Service, "Type", "simple").lower() + env = self.get_env(conf) + if not self._quiet: + okee = self.exec_check_unit(conf, env, Service, "ExecStop") + if not okee and _no_reload: return False + service_directories = self.env_service_directories(conf) + env.update(service_directories) + returncode = 0 + service_result = "success" + if runs in ["oneshot"]: + status_file = self.get_status_file_from(conf) + if self.get_status_from(conf, "ActiveState", "unknown") == "inactive": + logg.warning("the service is already down once") + return True + for cmd in conf.getlist(Service, "ExecStop", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("%s stop %s", runs, shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + if run.returncode and exe.check: + returncode = run.returncode + service_result = "failed" + break + if True: + if returncode: + self.set_status_from(conf, "ExecStopCode", strE(returncode)) + self.write_status_from(conf, AS="failed") + else: + self.clean_status_from(conf) # "inactive" + # fallback Stop => Kill for ["simple","notify","forking"] + elif not conf.getlist(Service, "ExecStop", []): + logg.info("no ExecStop => systemctl kill") + if True: + self.do_kill_unit_from(conf) + self.clean_pid_file_from(conf) + self.clean_status_from(conf) # "inactive" + elif runs in ["simple", "exec", "notify", "idle"]: + status_file = self.get_status_file_from(conf) + size = os.path.exists(status_file) and os.path.getsize(status_file) + logg.info("STATUS %s %s", status_file, size) + pid = 0 + for cmd in conf.getlist(Service, "ExecStop", []): + env["MAINPID"] = strE(self.read_mainpid_from(conf)) + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("%s stop %s", runs, shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + run = must_have_failed(run, newcmd) # TODO: a workaround + # self.write_status_from(conf, MainPID=run.pid) # no ExecStop + if run.returncode and exe.check: + returncode = run.returncode + service_result = "failed" + break + pid = to_intN(env.get("MAINPID")) + if pid: + if self.wait_vanished_pid(pid, timeout): + self.clean_pid_file_from(conf) + self.clean_status_from(conf) # "inactive" + else: + logg.info("%s sleep as no PID was found on Stop", runs) + time.sleep(MinimumTimeoutStopSec) + pid = self.read_mainpid_from(conf) + if not pid or not pid_exists(pid) or pid_zombie(pid): + self.clean_pid_file_from(conf) + self.clean_status_from(conf) # "inactive" + elif runs in ["forking"]: + status_file = self.get_status_file_from(conf) + pid_file = self.pid_file_from(conf) + for cmd in conf.getlist(Service, "ExecStop", []): + # active = self.is_active_from(conf) + if pid_file: + new_pid = self.read_mainpid_from(conf) + if new_pid: + env["MAINPID"] = strE(new_pid) + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("fork stop %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + if run.returncode and exe.check: + returncode = run.returncode + service_result = "failed" + break + pid = to_intN(env.get("MAINPID")) + if pid: + if self.wait_vanished_pid(pid, timeout): + self.clean_pid_file_from(conf) + else: + logg.info("%s sleep as no PID was found on Stop", runs) + time.sleep(MinimumTimeoutStopSec) + pid = self.read_mainpid_from(conf) + if not pid or not pid_exists(pid) or pid_zombie(pid): + self.clean_pid_file_from(conf) + if returncode: + if os.path.isfile(status_file): + self.set_status_from(conf, "ExecStopCode", strE(returncode)) + self.write_status_from(conf, AS="failed") + else: + self.clean_status_from(conf) # "inactive" + else: + logg.error("unsupported run type '%s'", runs) + return False + # POST sequence + if not self.is_active_from(conf): + env["SERVICE_RESULT"] = service_result + for cmd in conf.getlist(Service, "ExecStopPost", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("post-stop %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + logg.debug("post-stop done (%s) <-%s>", + run.returncode or "OK", run.signal or "") + if self._only_what[0] not in ["none", "keep"]: + self.remove_service_directories(conf) + return service_result == "success" + def do_stop_socket_from(self, conf): + runs = "socket" + timeout = self.get_SocketTimeoutSec(conf) + accept = conf.getbool(Socket, "Accept", "no") + service_unit = self.get_socket_service_from(conf) + service_conf = self.load_unit_conf(service_unit) + if service_conf is None: + logg.debug("unit could not be loaded (%s)", service_unit) + logg.error("Unit %s not found.", service_unit) + return False + env = self.get_env(conf) + if not self._quiet: + okee = self.exec_check_unit(conf, env, Socket, "ExecStop") + if not okee and _no_reload: return False + if not accept: + # we do not listen but have the service started right away + done = self.do_stop_service_from(service_conf) + service_result = done and "success" or "failed" + else: + done = self.do_stop_service_from(service_conf) + service_result = done and "success" or "failed" + # service_directories = self.env_service_directories(conf) + # env.update(service_directories) + # POST sequence + if not self.is_active_from(conf): + env["SERVICE_RESULT"] = service_result + for cmd in conf.getlist(Socket, "ExecStopPost", []): + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("post-stop %s", shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + logg.debug("post-stop done (%s) <-%s>", + run.returncode or "OK", run.signal or "") + return service_result == "success" + def wait_vanished_pid(self, pid, timeout): + if not pid: + return True + if not self.is_active_pid(pid): + return True + logg.info("wait for PID %s to vanish (%ss)", pid, timeout) + for x in xrange(int(timeout)): + time.sleep(1) # until TimeoutStopSec + if not self.is_active_pid(pid): + logg.info("wait for PID %s is done (%s.)", pid, x) + return True + logg.info("wait for PID %s failed (%s.)", pid, timeout) + return False + def reload_modules(self, *modules): + """ [UNIT]... -- reload these units """ + self.wait_system() + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.reload_units(units) and found_all + def reload_units(self, units): + """ fails if any unit fails to reload """ + self.wait_system() + done = True + for unit in self.sortedAfter(units): + if not self.reload_unit(unit): + done = False + return done + def reload_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.reload_unit_from(conf) + def reload_unit_from(self, conf): + if not conf: return False + if self.syntax_check(conf) > 100: return False + with waitlock(conf): + logg.info(" reload unit %s => %s", conf.name(), strQ(conf.filename())) + return self.do_reload_unit_from(conf) + def do_reload_unit_from(self, conf): + if conf.name().endswith(".service"): + return self.do_reload_service_from(conf) + elif conf.name().endswith(".socket"): + service_unit = self.get_socket_service_from(conf) + service_conf = self.load_unit_conf(service_unit) + if service_conf: + return self.do_reload_service_from(service_conf) + else: + logg.error("no %s found for unit type: %s", service_unit, conf.name()) + return False + elif conf.name().endswith(".target"): + return self.do_reload_target_from(conf) + else: + logg.error("reload not implemented for unit type: %s", conf.name()) + return False + def do_reload_service_from(self, conf): + runs = conf.get(Service, "Type", "simple").lower() + env = self.get_env(conf) + if not self._quiet: + okee = self.exec_check_unit(conf, env, Service, "ExecReload") + if not okee and _no_reload: return False + initscript = conf.filename() + if self.is_sysv_file(initscript): + status_file = self.get_status_file_from(conf) + if initscript: + newcmd = [initscript, "reload"] + env["SYSTEMCTL_SKIP_REDIRECT"] = "yes" + logg.info("%s reload %s", runs, shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: nocover + run = subprocess_waitpid(forkpid) + self.set_status_from(conf, "ExecReloadCode", run.returncode) + if run.returncode: + self.write_status_from(conf, AS="failed") + return False + else: + self.write_status_from(conf, AS="active") + return True + service_directories = self.env_service_directories(conf) + env.update(service_directories) + if runs in ["simple", "exec", "notify", "forking", "idle"]: + if not self.is_active_from(conf): + logg.info("no reload on inactive service %s", conf.name()) + return True + for cmd in conf.getlist(Service, "ExecReload", []): + env["MAINPID"] = strE(self.read_mainpid_from(conf)) + exe, newcmd = self.exec_newcmd(cmd, env, conf) + logg.info("%s reload %s", runs, shell_cmd(newcmd)) + forkpid = os.fork() + if not forkpid: + self.execve_from(conf, newcmd, env) # pragma: no cover + run = subprocess_waitpid(forkpid) + if run.returncode and exe.check: + logg.error("Job for %s failed because the control process exited with error code. (%s)", + conf.name(), run.returncode) + return False + time.sleep(MinimumYield) + return True + elif runs in ["oneshot"]: + logg.debug("ignored run type '%s' for reload", runs) + return True + else: + logg.error("unsupported run type '%s'", runs) + return False + def restart_modules(self, *modules): + """ [UNIT]... -- restart these units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.restart_units(units) and found_all + def restart_units(self, units): + """ fails if any unit fails to restart """ + self.wait_system() + done = True + for unit in self.sortedAfter(units): + if not self.restart_unit(unit): + done = False + return done + def restart_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.restart_unit_from(conf) + def restart_unit_from(self, conf): + if not conf: return False + if self.syntax_check(conf) > 100: return False + with waitlock(conf): + if conf.name().endswith(".service"): + logg.info(" restart service %s => %s", conf.name(), strQ(conf.filename())) + if not self.is_active_from(conf): + return self.do_start_unit_from(conf) + else: + return self.do_restart_unit_from(conf) + else: + return self.do_restart_unit_from(conf) + def do_restart_unit_from(self, conf): + logg.info("(restart) => stop/start %s", conf.name()) + self.do_stop_unit_from(conf) + return self.do_start_unit_from(conf) + def try_restart_modules(self, *modules): + """ [UNIT]... -- try-restart these units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.try_restart_units(units) and found_all + def try_restart_units(self, units): + """ fails if any module fails to try-restart """ + self.wait_system() + done = True + for unit in self.sortedAfter(units): + if not self.try_restart_unit(unit): + done = False + return done + def try_restart_unit(self, unit): + """ only do 'restart' if 'active' """ + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + with waitlock(conf): + logg.info(" try-restart unit %s => %s", conf.name(), strQ(conf.filename())) + if self.is_active_from(conf): + return self.do_restart_unit_from(conf) + return True + def reload_or_restart_modules(self, *modules): + """ [UNIT]... -- reload-or-restart these units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.reload_or_restart_units(units) and found_all + def reload_or_restart_units(self, units): + """ fails if any unit does not reload-or-restart """ + self.wait_system() + done = True + for unit in self.sortedAfter(units): + if not self.reload_or_restart_unit(unit): + done = False + return done + def reload_or_restart_unit(self, unit): + """ do 'reload' if specified, otherwise do 'restart' """ + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.reload_or_restart_unit_from(conf) + def reload_or_restart_unit_from(self, conf): + """ do 'reload' if specified, otherwise do 'restart' """ + if not conf: return False + with waitlock(conf): + logg.info(" reload-or-restart unit %s => %s", conf.name(), strQ(conf.filename())) + return self.do_reload_or_restart_unit_from(conf) + def do_reload_or_restart_unit_from(self, conf): + if not self.is_active_from(conf): + # try: self.stop_unit_from(conf) + # except Exception as e: pass + return self.do_start_unit_from(conf) + elif conf.getlist(Service, "ExecReload", []): + logg.info("found service to have ExecReload -> 'reload'") + return self.do_reload_unit_from(conf) + else: + logg.info("found service without ExecReload -> 'restart'") + return self.do_restart_unit_from(conf) + def reload_or_try_restart_modules(self, *modules): + """ [UNIT]... -- reload-or-try-restart these units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.reload_or_try_restart_units(units) and found_all + def reload_or_try_restart_units(self, units): + """ fails if any unit fails to reload-or-try-restart """ + self.wait_system() + done = True + for unit in self.sortedAfter(units): + if not self.reload_or_try_restart_unit(unit): + done = False + return done + def reload_or_try_restart_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.reload_or_try_restart_unit_from(conf) + def reload_or_try_restart_unit_from(self, conf): + with waitlock(conf): + logg.info(" reload-or-try-restart unit %s => %s", conf.name(), strQ(conf.filename())) + return self.do_reload_or_try_restart_unit_from(conf) + def do_reload_or_try_restart_unit_from(self, conf): + if conf.getlist(Service, "ExecReload", []): + return self.do_reload_unit_from(conf) + elif not self.is_active_from(conf): + return True + else: + return self.do_restart_unit_from(conf) + def kill_modules(self, *modules): + """ [UNIT]... -- kill these units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + # self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.kill_units(units) and found_all + def kill_units(self, units): + """ fails if any unit could not be killed """ + self.wait_system() + done = True + for unit in self.sortedBefore(units): + if not self.kill_unit(unit): + done = False + return done + def kill_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.kill_unit_from(conf) + def kill_unit_from(self, conf): + if not conf: return False + with waitlock(conf): + logg.info(" kill unit %s => %s", conf.name(), strQ(conf.filename())) + return self.do_kill_unit_from(conf) + def do_kill_unit_from(self, conf): + started = time.time() + doSendSIGKILL = self.get_SendSIGKILL(conf) + doSendSIGHUP = self.get_SendSIGHUP(conf) + useKillMode = self.get_KillMode(conf) + useKillSignal = self.get_KillSignal(conf) + kill_signal = getattr(signal, useKillSignal) + timeout = self.get_TimeoutStopSec(conf) + status_file = self.get_status_file_from(conf) + size = os.path.exists(status_file) and os.path.getsize(status_file) + logg.info("STATUS %s %s", status_file, size) + mainpid = self.read_mainpid_from(conf) + self.clean_status_from(conf) # clear RemainAfterExit and TimeoutStartSec + if not mainpid: + if useKillMode in ["control-group"]: + logg.warning("no main PID %s", strQ(conf.filename())) + logg.warning("and there is no control-group here") + else: + logg.info("no main PID %s", strQ(conf.filename())) + return False + if not pid_exists(mainpid) or pid_zombie(mainpid): + logg.debug("ignoring children when mainpid is already dead") + # because we list child processes, not processes in control-group + return True + pidlist = self.pidlist_of(mainpid) # here + if pid_exists(mainpid): + logg.info("stop kill PID %s", mainpid) + self._kill_pid(mainpid, kill_signal) + if useKillMode in ["control-group"]: + if len(pidlist) > 1: + logg.info("stop control-group PIDs %s", pidlist) + for pid in pidlist: + if pid != mainpid: + self._kill_pid(pid, kill_signal) + if doSendSIGHUP: + logg.info("stop SendSIGHUP to PIDs %s", pidlist) + for pid in pidlist: + self._kill_pid(pid, signal.SIGHUP) + # wait for the processes to have exited + while True: + dead = True + for pid in pidlist: + if pid_exists(pid) and not pid_zombie(pid): + dead = False + break + if dead: + break + if time.time() > started + timeout: + logg.info("service PIDs not stopped after %s", timeout) + break + time.sleep(1) # until TimeoutStopSec + if dead or not doSendSIGKILL: + logg.info("done kill PID %s %s", mainpid, dead and "OK") + return dead + if useKillMode in ["control-group", "mixed"]: + logg.info("hard kill PIDs %s", pidlist) + for pid in pidlist: + if pid != mainpid: + self._kill_pid(pid, signal.SIGKILL) + time.sleep(MinimumYield) + # useKillMode in [ "control-group", "mixed", "process" ] + if pid_exists(mainpid): + logg.info("hard kill PID %s", mainpid) + self._kill_pid(mainpid, signal.SIGKILL) + time.sleep(MinimumYield) + dead = not pid_exists(mainpid) or pid_zombie(mainpid) + logg.info("done hard kill PID %s %s", mainpid, dead and "OK") + return dead + def _kill_pid(self, pid, kill_signal = None): + try: + sig = kill_signal or signal.SIGTERM + os.kill(pid, sig) + except OSError as e: + if e.errno == errno.ESRCH or e.errno == errno.ENOENT: + logg.debug("kill PID %s => No such process", pid) + return True + else: + logg.error("kill PID %s => %s", pid, str(e)) + return False + return not pid_exists(pid) or pid_zombie(pid) + def is_active_modules(self, *modules): + """ [UNIT].. -- check if these units are in active state + implements True if all is-active = True """ + # systemctl returns multiple lines, one for each argument + # "active" when is_active + # "inactive" when not is_active + # "unknown" when not enabled + # The return code is set to + # 0 when "active" + # 1 when unit is not found + # 3 when any "inactive" or "unknown" + # However: # TODO! BUG in original systemctl! + # documentation says " exit code 0 if at least one is active" + # and "Unless --quiet is specified, print the unit state" + # | + units = [] + results = [] + for module in modules: + units = self.match_units(to_list(module)) + if not units: + logg.error("Unit %s not found.", unit_of(module)) + # self.error |= NOT_FOUND + self.error |= NOT_ACTIVE + results += ["inactive"] + continue + for unit in units: + active = self.get_active_unit(unit) + enabled = self.enabled_unit(unit) + if enabled != "enabled" and ACTIVE_IF_ENABLED: + active = "inactive" # "unknown" + results += [active] + break + # how it should work: + status = "active" in results + # how 'systemctl' works: + non_active = [result for result in results if result != "active"] + if non_active: + self.error |= NOT_ACTIVE + if non_active: + self.error |= NOT_OK # status + if _quiet: + return [] + return results + def is_active_from(self, conf): + """ used in try-restart/other commands to check if needed. """ + if not conf: return False + return self.get_active_from(conf) == "active" + def active_pid_from(self, conf): + if not conf: return False + pid = self.read_mainpid_from(conf) + return self.is_active_pid(pid) + def is_active_pid(self, pid): + """ returns pid if the pid is still an active process """ + if pid and pid_exists(pid) and not pid_zombie(pid): + return pid # usually a string (not null) + return None + def get_active_unit(self, unit): + """ returns 'active' 'inactive' 'failed' 'unknown' """ + conf = self.load_unit_conf(unit) + if not conf: + logg.warning("Unit %s not found.", unit) + return "unknown" + else: + return self.get_active_from(conf) + def get_active_from(self, conf): + if conf.name().endswith(".service"): + return self.get_active_service_from(conf) + elif conf.name().endswith(".socket"): + service_unit = self.get_socket_service_from(conf) + service_conf = self.load_unit_conf(service_unit) + return self.get_active_service_from(service_conf) + elif conf.name().endswith(".target"): + return self.get_active_target_from(conf) + else: + logg.debug("is-active not implemented for unit type: %s", conf.name()) + return "unknown" # TODO: "inactive" ? + def get_active_service_from(self, conf): + """ returns 'active' 'inactive' 'failed' 'unknown' """ + # used in try-restart/other commands to check if needed. + if not conf: return "unknown" + pid_file = self.pid_file_from(conf) + if pid_file: # application PIDFile + if not os.path.exists(pid_file): + return "inactive" + status_file = self.get_status_file_from(conf) + if self.getsize(status_file): + state = self.get_status_from(conf, "ActiveState", "") + if state: + if DEBUG_STATUS: + logg.info("get_status_from %s => %s", conf.name(), state) + return state + pid = self.read_mainpid_from(conf) + if DEBUG_STATUS: + logg.debug("pid_file '%s' => PID %s", pid_file or status_file, strE(pid)) + if pid: + if not pid_exists(pid) or pid_zombie(pid): + return "failed" + return "active" + else: + return "inactive" + def get_active_target_from(self, conf): + """ returns 'active' 'inactive' 'failed' 'unknown' """ + return self.get_active_target(conf.name()) + def get_active_target(self, target): + """ returns 'active' 'inactive' 'failed' 'unknown' """ + if target in self.get_active_target_list(): + status = self.is_system_running() + if status in ["running"]: + return "active" + return "inactive" + else: + services = self.target_default_services(target) + result = "active" + for service in services: + conf = self.load_unit_conf(service) + if conf: + state = self.get_active_from(conf) + if state in ["failed"]: + result = state + elif state not in ["active"]: + result = state + return result + def get_active_target_list(self): + current_target = self.get_default_target() + target_list = self.get_target_list(current_target) + target_list += [DefaultUnit] # upper end + target_list += [SysInitTarget] # lower end + return target_list + def get_substate_from(self, conf): + """ returns 'running' 'exited' 'dead' 'failed' 'plugged' 'mounted' """ + if not conf: return None + pid_file = self.pid_file_from(conf) + if pid_file: + if not os.path.exists(pid_file): + return "dead" + status_file = self.get_status_file_from(conf) + if self.getsize(status_file): + state = self.get_status_from(conf, "ActiveState", "") + if state: + if state in ["active"]: + return self.get_status_from(conf, "SubState", "running") + else: + return self.get_status_from(conf, "SubState", "dead") + pid = self.read_mainpid_from(conf) + if DEBUG_STATUS: + logg.debug("pid_file '%s' => PID %s", pid_file or status_file, strE(pid)) + if pid: + if not pid_exists(pid) or pid_zombie(pid): + return "failed" + return "running" + else: + return "dead" + def is_failed_modules(self, *modules): + """ [UNIT]... -- check if these units are in failes state + implements True if any is-active = True """ + units = [] + results = [] + for module in modules: + units = self.match_units(to_list(module)) + if not units: + logg.error("Unit %s not found.", unit_of(module)) + # self.error |= NOT_FOUND + results += ["inactive"] + continue + for unit in units: + active = self.get_active_unit(unit) + enabled = self.enabled_unit(unit) + if enabled != "enabled" and ACTIVE_IF_ENABLED: + active = "inactive" + results += [active] + break + if "failed" in results: + self.error = 0 + else: + self.error |= NOT_OK + if _quiet: + return [] + return results + def is_failed_from(self, conf): + if conf is None: return True + return self.get_active_from(conf) == "failed" + def reset_failed_modules(self, *modules): + """ [UNIT]... -- Reset failed state for all, one, or more units """ + units = [] + status = True + for module in modules: + units = self.match_units(to_list(module)) + if not units: + logg.error("Unit %s not found.", unit_of(module)) + # self.error |= NOT_FOUND + return False + for unit in units: + if not self.reset_failed_unit(unit): + logg.error("Unit %s could not be reset.", unit_of(module)) + status = False + break + return status + def reset_failed_unit(self, unit): + conf = self.load_unit_conf(unit) + if not conf: + logg.warning("Unit %s not found.", unit) + return False + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.reset_failed_from(conf) + def reset_failed_from(self, conf): + if conf is None: return True + if not self.is_failed_from(conf): return False + done = False + status_file = self.get_status_file_from(conf) + if status_file and os.path.exists(status_file): + try: + os.remove(status_file) + done = True + logg.debug("done rm %s", status_file) + except Exception as e: + logg.error("while rm %s: %s", status_file, e) + pid_file = self.pid_file_from(conf) + if pid_file and os.path.exists(pid_file): + try: + os.remove(pid_file) + done = True + logg.debug("done rm %s", pid_file) + except Exception as e: + logg.error("while rm %s: %s", pid_file, e) + return done + def status_modules(self, *modules): + """ [UNIT]... check the status of these units. + """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s could not be found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + result = self.status_units(units) + # if not found_all: + # self.error |= NOT_OK | NOT_ACTIVE # 3 + # # same as (dead) # original behaviour + return result + def status_units(self, units): + """ concatenates the status output of all units + and the last non-successful statuscode """ + status = 0 + result = "" + for unit in units: + status1, result1 = self.status_unit(unit) + if status1: status = status1 + if result: result += "\n\n" + result += result1 + if status: + self.error |= NOT_OK | NOT_ACTIVE # 3 + return result + def status_unit(self, unit): + conf = self.get_unit_conf(unit) + result = "%s - %s" % (unit, self.get_description_from(conf)) + loaded = conf.loaded() + if loaded: + filename = str(conf.filename()) + enabled = self.enabled_from(conf) + result += "\n Loaded: {loaded} ({filename}, {enabled})".format(**locals()) + for path in conf.overrides(): + result += "\n Drop-In: {path}".format(**locals()) + else: + result += "\n Loaded: failed" + return 3, result + active = self.get_active_from(conf) + substate = self.get_substate_from(conf) + result += "\n Active: {} ({})".format(active, substate) + if active == "active": + return 0, result + else: + return 3, result + def cat_modules(self, *modules): + """ [UNIT]... show the *.system file for these" + """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s could not be found.", unit_of(module)) + # self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + result = self.cat_units(units) + if not found_all: + self.error |= NOT_OK + return result + def cat_units(self, units): + done = True + result = "" + for unit in units: + text = self.cat_unit(unit) + if not text: + done = False + else: + if result: + result += "\n\n" + result += text + if not done: + self.error = NOT_OK + return result + def cat_unit(self, unit): + try: + unit_file = self.unit_file(unit) + if unit_file: + return open(unit_file).read() + logg.error("No files found for %s", unit) + except Exception as e: + print("Unit {} is not-loaded: {}".format(unit, e)) + self.error |= NOT_OK + return None + ## + ## + def load_preset_files(self, module = None): # -> [ preset-file-names,... ] + """ reads all preset files, returns the scanned files """ + if self._preset_file_list is None: + self._preset_file_list = {} + assert self._preset_file_list is not None + for folder in self.preset_folders(): + if not folder: + continue + if self._root: + folder = os_path(self._root, folder) + if not os.path.isdir(folder): + continue + for name in os.listdir(folder): + if not name.endswith(".preset"): + continue + if name not in self._preset_file_list: + path = os.path.join(folder, name) + if os.path.isdir(path): + continue + preset = PresetFile().read(path) + self._preset_file_list[name] = preset + logg.debug("found %s preset files", len(self._preset_file_list)) + return sorted(self._preset_file_list.keys()) + def get_preset_of_unit(self, unit): + """ [UNIT] check the *.preset of this unit + """ + self.load_preset_files() + assert self._preset_file_list is not None + for filename in sorted(self._preset_file_list.keys()): + preset = self._preset_file_list[filename] + status = preset.get_preset(unit) + if status: + return status + return None + def preset_modules(self, *modules): + """ [UNIT]... -- set 'enabled' when in *.preset + """ + if self.user_mode(): + logg.warning("preset makes no sense in --user mode") + return True + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s could not be found.", unit_of(module)) + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.preset_units(units) and found_all + def preset_units(self, units): + """ fails if any unit could not be changed """ + self.wait_system() + fails = 0 + found = 0 + for unit in units: + status = self.get_preset_of_unit(unit) + if not status: continue + found += 1 + if status.startswith("enable"): + if self._preset_mode == "disable": continue + logg.info("preset enable %s", unit) + if not self.enable_unit(unit): + logg.warning("failed to enable %s", unit) + fails += 1 + if status.startswith("disable"): + if self._preset_mode == "enable": continue + logg.info("preset disable %s", unit) + if not self.disable_unit(unit): + logg.warning("failed to disable %s", unit) + fails += 1 + return not fails and not not found + def preset_all_modules(self, *modules): + """ 'preset' all services + enable or disable services according to *.preset files + """ + if self.user_mode(): + logg.warning("preset-all makes no sense in --user mode") + return True + found_all = True + units = self.match_units() # TODO: how to handle module arguments + return self.preset_units(units) and found_all + def wanted_from(self, conf, default = None): + if not conf: return default + return conf.get(Install, "WantedBy", default, True) + def enablefolders(self, wanted): + if self.user_mode(): + for folder in self.user_folders(): + yield self.default_enablefolder(wanted, folder) + if True: + for folder in self.system_folders(): + yield self.default_enablefolder(wanted, folder) + def enablefolder(self, wanted): + if self.user_mode(): + user_folder = self.user_folder() + return self.default_enablefolder(wanted, user_folder) + else: + return self.default_enablefolder(wanted) + def default_enablefolder(self, wanted, basefolder = None): + basefolder = basefolder or self.system_folder() + if not wanted: + return wanted + if not wanted.endswith(".wants"): + wanted = wanted + ".wants" + return os.path.join(basefolder, wanted) + def enable_modules(self, *modules): + """ [UNIT]... -- enable these units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + # self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + logg.info("matched %s", unit) # ++ + if unit not in units: + units += [unit] + return self.enable_units(units) and found_all + def enable_units(self, units): + self.wait_system() + done = True + for unit in units: + if not self.enable_unit(unit): + done = False + elif self._now: + self.start_unit(unit) + return done + def enable_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + unit_file = conf.filename() + if unit_file is None: + logg.error("Unit file %s not found.", unit) + return False + if self.is_sysv_file(unit_file): + if self.user_mode(): + logg.error("Initscript %s not for --user mode", unit) + return False + return self.enable_unit_sysv(unit_file) + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.enable_unit_from(conf) + def enable_unit_from(self, conf): + wanted = self.wanted_from(conf) + if not wanted and not self._force: + logg.debug("%s has no target", conf.name()) + return False # "static" is-enabled + target = wanted or self.get_default_target() + folder = self.enablefolder(target) + if self._root: + folder = os_path(self._root, folder) + if not os.path.isdir(folder): + os.makedirs(folder) + source = conf.filename() + if not source: # pragma: no cover (was checked before) + logg.debug("%s has no real file", conf.name()) + return False + symlink = os.path.join(folder, conf.name()) + if True: + _f = self._force and "-f" or "" + logg.info("ln -s {_f} '{source}' '{symlink}'".format(**locals())) + if self._force and os.path.islink(symlink): + os.remove(target) + if not os.path.islink(symlink): + os.symlink(source, symlink) + return True + def rc3_root_folder(self): + old_folder = os_path(self._root, _rc3_boot_folder) + new_folder = os_path(self._root, _rc3_init_folder) + if os.path.isdir(old_folder): # pragma: no cover + return old_folder + return new_folder + def rc5_root_folder(self): + old_folder = os_path(self._root, _rc5_boot_folder) + new_folder = os_path(self._root, _rc5_init_folder) + if os.path.isdir(old_folder): # pragma: no cover + return old_folder + return new_folder + def enable_unit_sysv(self, unit_file): + # a "multi-user.target"/rc3 is also started in /rc5 + rc3 = self._enable_unit_sysv(unit_file, self.rc3_root_folder()) + rc5 = self._enable_unit_sysv(unit_file, self.rc5_root_folder()) + return rc3 and rc5 + def _enable_unit_sysv(self, unit_file, rc_folder): + name = os.path.basename(unit_file) + nameS = "S50"+name + nameK = "K50"+name + if not os.path.isdir(rc_folder): + os.makedirs(rc_folder) + # do not double existing entries + for found in os.listdir(rc_folder): + m = re.match(r"S\d\d(.*)", found) + if m and m.group(1) == name: + nameS = found + m = re.match(r"K\d\d(.*)", found) + if m and m.group(1) == name: + nameK = found + target = os.path.join(rc_folder, nameS) + if not os.path.exists(target): + os.symlink(unit_file, target) + target = os.path.join(rc_folder, nameK) + if not os.path.exists(target): + os.symlink(unit_file, target) + return True + def disable_modules(self, *modules): + """ [UNIT]... -- disable these units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + # self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.disable_units(units) and found_all + def disable_units(self, units): + self.wait_system() + done = True + for unit in units: + if not self.disable_unit(unit): + done = False + elif self._now: + self.stop_unit(unit) + return done + def disable_unit(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + unit_file = conf.filename() + if unit_file is None: + logg.error("Unit file %s not found.", unit) + return False + if self.is_sysv_file(unit_file): + if self.user_mode(): + logg.error("Initscript %s not for --user mode", unit) + return False + return self.disable_unit_sysv(unit_file) + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + return self.disable_unit_from(conf) + def disable_unit_from(self, conf): + wanted = self.wanted_from(conf) + if not wanted and not self._force: + logg.debug("%s has no target", conf.name()) + return False # "static" is-enabled + target = wanted or self.get_default_target() + for folder in self.enablefolders(target): + if self._root: + folder = os_path(self._root, folder) + symlink = os.path.join(folder, conf.name()) + if os.path.exists(symlink): + try: + _f = self._force and "-f" or "" + logg.info("rm {_f} '{symlink}'".format(**locals())) + if os.path.islink(symlink) or self._force: + os.remove(symlink) + except IOError as e: + logg.error("disable %s: %s", symlink, e) + except OSError as e: + logg.error("disable %s: %s", symlink, e) + return True + def disable_unit_sysv(self, unit_file): + rc3 = self._disable_unit_sysv(unit_file, self.rc3_root_folder()) + rc5 = self._disable_unit_sysv(unit_file, self.rc5_root_folder()) + return rc3 and rc5 + def _disable_unit_sysv(self, unit_file, rc_folder): + # a "multi-user.target"/rc3 is also started in /rc5 + name = os.path.basename(unit_file) + nameS = "S50"+name + nameK = "K50"+name + # do not forget the existing entries + for found in os.listdir(rc_folder): + m = re.match(r"S\d\d(.*)", found) + if m and m.group(1) == name: + nameS = found + m = re.match(r"K\d\d(.*)", found) + if m and m.group(1) == name: + nameK = found + target = os.path.join(rc_folder, nameS) + if os.path.exists(target): + os.unlink(target) + target = os.path.join(rc_folder, nameK) + if os.path.exists(target): + os.unlink(target) + return True + def is_enabled_sysv(self, unit_file): + name = os.path.basename(unit_file) + target = os.path.join(self.rc3_root_folder(), "S50%s" % name) + if os.path.exists(target): + return True + return False + def is_enabled_modules(self, *modules): + """ [UNIT]... -- check if these units are enabled + returns True if any of them is enabled.""" + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + # self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.is_enabled_units(units) # and found_all + def is_enabled_units(self, units): + """ true if any is enabled, and a list of infos """ + result = False + infos = [] + for unit in units: + infos += [self.enabled_unit(unit)] + if self.is_enabled(unit): + result = True + if not result: + self.error |= NOT_OK + return infos + def is_enabled(self, unit): + conf = self.load_unit_conf(unit) + if conf is None: + logg.error("Unit %s not found.", unit) + return False + unit_file = conf.filename() + if not unit_file: + logg.error("Unit %s not found.", unit) + return False + if self.is_sysv_file(unit_file): + return self.is_enabled_sysv(unit_file) + state = self.get_enabled_from(conf) + if state in ["enabled", "static"]: + return True + return False # ["disabled", "masked"] + def enabled_unit(self, unit): + conf = self.get_unit_conf(unit) + return self.enabled_from(conf) + def enabled_from(self, conf): + unit_file = strE(conf.filename()) + if self.is_sysv_file(unit_file): + state = self.is_enabled_sysv(unit_file) + if state: + return "enabled" + return "disabled" + return self.get_enabled_from(conf) + def get_enabled_from(self, conf): + if conf.masked: + return "masked" + wanted = self.wanted_from(conf) + target = wanted or self.get_default_target() + for folder in self.enablefolders(target): + if self._root: + folder = os_path(self._root, folder) + target = os.path.join(folder, conf.name()) + if os.path.isfile(target): + return "enabled" + if not wanted: + return "static" + return "disabled" + def mask_modules(self, *modules): + """ [UNIT]... -- mask non-startable units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.mask_units(units) and found_all + def mask_units(self, units): + self.wait_system() + done = True + for unit in units: + if not self.mask_unit(unit): + done = False + return done + def mask_unit(self, unit): + unit_file = self.unit_file(unit) + if not unit_file: + logg.error("Unit %s not found.", unit) + return False + if self.is_sysv_file(unit_file): + logg.error("Initscript %s can not be masked", unit) + return False + conf = self.get_unit_conf(unit) + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + folder = self.mask_folder() + if self._root: + folder = os_path(self._root, folder) + if not os.path.isdir(folder): + os.makedirs(folder) + target = os.path.join(folder, os.path.basename(unit_file)) + dev_null = _dev_null + if True: + _f = self._force and "-f" or "" + logg.debug("ln -s {_f} {dev_null} '{target}'".format(**locals())) + if self._force and os.path.islink(target): + os.remove(target) + if not os.path.exists(target): + os.symlink(dev_null, target) + logg.info("Created symlink {target} -> {dev_null}".format(**locals())) + return True + elif os.path.islink(target): + logg.debug("mask symlink does already exist: %s", target) + return True + else: + logg.error("mask target does already exist: %s", target) + return False + def mask_folder(self): + for folder in self.mask_folders(): + if folder: return folder + raise Exception("did not find any systemd/system folder") + def mask_folders(self): + if self.user_mode(): + for folder in self.user_folders(): + yield folder + if True: + for folder in self.system_folders(): + yield folder + def unmask_modules(self, *modules): + """ [UNIT]... -- unmask non-startable units """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s not found.", unit_of(module)) + self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.unmask_units(units) and found_all + def unmask_units(self, units): + self.wait_system() + done = True + for unit in units: + if not self.unmask_unit(unit): + done = False + return done + def unmask_unit(self, unit): + unit_file = self.unit_file(unit) + if not unit_file: + logg.error("Unit %s not found.", unit) + return False + if self.is_sysv_file(unit_file): + logg.error("Initscript %s can not be un/masked", unit) + return False + conf = self.get_unit_conf(unit) + if self.not_user_conf(conf): + logg.error("Unit %s not for --user mode", unit) + return False + folder = self.mask_folder() + if self._root: + folder = os_path(self._root, folder) + target = os.path.join(folder, os.path.basename(unit_file)) + if True: + _f = self._force and "-f" or "" + logg.info("rm {_f} '{target}'".format(**locals())) + if os.path.islink(target): + os.remove(target) + return True + elif not os.path.exists(target): + logg.debug("Symlink did not exist anymore: %s", target) + return True + else: + logg.warning("target is not a symlink: %s", target) + return True + def list_dependencies_modules(self, *modules): + """ [UNIT]... show the dependency tree" + """ + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s could not be found.", unit_of(module)) + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.list_dependencies_units(units) # and found_all + def list_dependencies_units(self, units): + result = [] + for unit in units: + if result: + result += ["", ""] + result += self.list_dependencies_unit(unit) + return result + def list_dependencies_unit(self, unit): + result = [] + for line in self.list_dependencies(unit, ""): + result += [line] + return result + def list_dependencies(self, unit, indent = None, mark = None, loop = []): + mapping = {} + mapping["Requires"] = "required to start" + mapping["Wants"] = "wanted to start" + mapping["Requisite"] = "required started" + mapping["Bindsto"] = "binds to start" + mapping["PartOf"] = "part of started" + mapping[".requires"] = ".required to start" + mapping[".wants"] = ".wanted to start" + mapping["PropagateReloadTo"] = "(to be reloaded as well)" + mapping["Conflicts"] = "(to be stopped on conflict)" + restrict = ["Requires", "Requisite", "ConsistsOf", "Wants", + "BindsTo", ".requires", ".wants"] + indent = indent or "" + mark = mark or "" + deps = self.get_dependencies_unit(unit) + conf = self.get_unit_conf(unit) + if not conf.loaded(): + if not self._show_all: + return + yield "%s(%s): %s" % (indent, unit, mark) + else: + yield "%s%s: %s" % (indent, unit, mark) + for stop_recursion in ["Conflict", "conflict", "reloaded", "Propagate"]: + if stop_recursion in mark: + return + for dep in deps: + if dep in loop: + logg.debug("detected loop at %s", dep) + continue + new_loop = loop + list(deps.keys()) + new_indent = indent + "| " + new_mark = deps[dep] + if not self._show_all: + if new_mark not in restrict: + continue + if new_mark in mapping: + new_mark = mapping[new_mark] + restrict = ["Requires", "Wants", "Requisite", "BindsTo", "PartOf", "ConsistsOf", + ".requires", ".wants"] + for line in self.list_dependencies(dep, new_indent, new_mark, new_loop): + yield line + def get_dependencies_unit(self, unit, styles = None): + styles = styles or ["Requires", "Wants", "Requisite", "BindsTo", "PartOf", "ConsistsOf", + ".requires", ".wants", "PropagateReloadTo", "Conflicts", ] + conf = self.get_unit_conf(unit) + deps = {} + for style in styles: + if style.startswith("."): + for folder in self.sysd_folders(): + if not folder: + continue + require_path = os.path.join(folder, unit + style) + if self._root: + require_path = os_path(self._root, require_path) + if os.path.isdir(require_path): + for required in os.listdir(require_path): + if required not in deps: + deps[required] = style + else: + for requirelist in conf.getlist(Unit, style, []): + for required in requirelist.strip().split(" "): + deps[required.strip()] = style + return deps + def get_required_dependencies(self, unit, styles = None): + styles = styles or ["Requires", "Wants", "Requisite", "BindsTo", + ".requires", ".wants"] + return self.get_dependencies_unit(unit, styles) + def get_start_dependencies(self, unit, styles = None): # pragma: no cover + """ the list of services to be started as well / TODO: unused """ + styles = styles or ["Requires", "Wants", "Requisite", "BindsTo", "PartOf", "ConsistsOf", + ".requires", ".wants"] + deps = {} + unit_deps = self.get_dependencies_unit(unit) + for dep_unit, dep_style in unit_deps.items(): + if dep_style in styles: + if dep_unit in deps: + if dep_style not in deps[dep_unit]: + deps[dep_unit].append(dep_style) + else: + deps[dep_unit] = [dep_style] + next_deps = self.get_start_dependencies(dep_unit) + for dep, styles in next_deps.items(): + for style in styles: + if dep in deps: + if style not in deps[dep]: + deps[dep].append(style) + else: + deps[dep] = [style] + return deps + def list_start_dependencies_modules(self, *modules): + """ [UNIT]... show the dependency tree (experimental)" + """ + return self.list_start_dependencies_units(list(modules)) + def list_start_dependencies_units(self, units): + unit_order = [] + deps = {} + for unit in units: + unit_order.append(unit) + # unit_deps = self.get_start_dependencies(unit) # TODO + unit_deps = self.get_dependencies_unit(unit) + for dep_unit, styles in unit_deps.items(): + dep_styles = to_list(styles) + for dep_style in dep_styles: + if dep_unit in deps: + if dep_style not in deps[dep_unit]: + deps[dep_unit].append(dep_style) + else: + deps[dep_unit] = [dep_style] + deps_conf = [] + for dep in deps: + if dep in unit_order: + continue + conf = self.get_unit_conf(dep) + if conf.loaded(): + deps_conf.append(conf) + for unit in unit_order: + deps[unit] = ["Requested"] + conf = self.get_unit_conf(unit) + if conf.loaded(): + deps_conf.append(conf) + result = [] + sortlist = conf_sortedAfter(deps_conf, cmp=compareAfter) + for item in sortlist: + line = (item.name(), "(%s)" % (" ".join(deps[item.name()]))) + result.append(line) + return result + def sortedAfter(self, unitlist): + """ get correct start order for the unit list (ignoring masked units) """ + conflist = [self.get_unit_conf(unit) for unit in unitlist] + if True: + conflist = [] + for unit in unitlist: + conf = self.get_unit_conf(unit) + if conf.masked: + logg.debug("ignoring masked unit %s", unit) + continue + conflist.append(conf) + sortlist = conf_sortedAfter(conflist) + return [item.name() for item in sortlist] + def sortedBefore(self, unitlist): + """ get correct start order for the unit list (ignoring masked units) """ + conflist = [self.get_unit_conf(unit) for unit in unitlist] + if True: + conflist = [] + for unit in unitlist: + conf = self.get_unit_conf(unit) + if conf.masked: + logg.debug("ignoring masked unit %s", unit) + continue + conflist.append(conf) + sortlist = conf_sortedAfter(reversed(conflist)) + return [item.name() for item in reversed(sortlist)] + def daemon_reload_target(self): + """ reload does will only check the service files here. + The returncode will tell the number of warnings, + and it is over 100 if it can not continue even + for the relaxed systemctl.py style of execution. """ + errors = 0 + for unit in self.match_units(): + try: + conf = self.get_unit_conf(unit) + except Exception as e: + logg.error("%s: can not read unit file %s\n\t%s", + unit, strQ(conf.filename()), e) + continue + errors += self.syntax_check(conf) + if errors: + logg.warning(" (%s) found %s problems", errors, errors % 100) + return True # errors + def syntax_check(self, conf): + filename = conf.filename() + if filename and filename.endswith(".service"): + return self.syntax_check_service(conf) + return 0 + def syntax_check_service(self, conf, section = Service): + unit = conf.name() + if not conf.data.has_section(Service): + logg.error(" %s: a .service file without [Service] section", unit) + return 101 + errors = 0 + haveType = conf.get(section, "Type", "simple") + haveExecStart = conf.getlist(section, "ExecStart", []) + haveExecStop = conf.getlist(section, "ExecStop", []) + haveExecReload = conf.getlist(section, "ExecReload", []) + usedExecStart = [] + usedExecStop = [] + usedExecReload = [] + if haveType not in ["simple", "exec", "forking", "notify", "oneshot", "dbus", "idle"]: + logg.error(" %s: Failed to parse service type, ignoring: %s", unit, haveType) + errors += 100 + for line in haveExecStart: + mode, exe = exec_path(line) + if not exe.startswith("/"): + if mode.check: + logg.error(" %s: %s Executable path is not absolute.", unit, section) + else: + logg.warning("%s: %s Executable path is not absolute.", unit, section) + logg.info("%s: %s exe = %s", unit, section, exe) + errors += 1 + usedExecStart.append(line) + for line in haveExecStop: + mode, exe = exec_path(line) + if not exe.startswith("/"): + if mode.check: + logg.error(" %s: %s Executable path is not absolute.", unit, section) + else: + logg.warning("%s: %s Executable path is not absolute.", unit, section) + logg.info("%s: %s exe = %s", unit, section, exe) + errors += 1 + usedExecStop.append(line) + for line in haveExecReload: + mode, exe = exec_path(line) + if not exe.startswith("/"): + if mode.check: + logg.error(" %s: %s Executable path is not absolute.", unit, section) + else: + logg.warning("%s: %s Executable path is not absolute.", unit, section) + logg.info("%s: %s exe = %s", unit, section, exe) + errors += 1 + usedExecReload.append(line) + if haveType in ["simple", "exec", "notify", "forking", "idle"]: + if not usedExecStart and not usedExecStop: + logg.error(" %s: %s lacks both ExecStart and ExecStop= setting. Refusing.", unit, section) + errors += 101 + elif not usedExecStart and haveType != "oneshot": + logg.error(" %s: %s has no ExecStart= setting, which is only allowed for Type=oneshot services. Refusing.", unit, section) + errors += 101 + if len(usedExecStart) > 1 and haveType != "oneshot": + logg.error(" %s: there may be only one %s ExecStart statement (unless for 'oneshot' services)." + + "\n\t\t\tYou can use ExecStartPre / ExecStartPost to add additional commands.", unit, section) + errors += 1 + if len(usedExecStop) > 1 and haveType != "oneshot": + logg.info(" %s: there should be only one %s ExecStop statement (unless for 'oneshot' services)." + + "\n\t\t\tYou can use ExecStopPost to add additional commands (also executed on failed Start)", unit, section) + if len(usedExecReload) > 1: + logg.info(" %s: there should be only one %s ExecReload statement." + + "\n\t\t\tUse ' ; ' for multiple commands (ExecReloadPost or ExedReloadPre do not exist)", unit, section) + if len(usedExecReload) > 0 and "/bin/kill " in usedExecReload[0]: + logg.warning(" %s: the use of /bin/kill is not recommended for %s ExecReload as it is asynchronous." + + "\n\t\t\tThat means all the dependencies will perform the reload simultaneously / out of order.", unit, section) + if conf.getlist(Service, "ExecRestart", []): # pragma: no cover + logg.error(" %s: there no such thing as an %s ExecRestart (ignored)", unit, section) + if conf.getlist(Service, "ExecRestartPre", []): # pragma: no cover + logg.error(" %s: there no such thing as an %s ExecRestartPre (ignored)", unit, section) + if conf.getlist(Service, "ExecRestartPost", []): # pragma: no cover + logg.error(" %s: there no such thing as an %s ExecRestartPost (ignored)", unit, section) + if conf.getlist(Service, "ExecReloadPre", []): # pragma: no cover + logg.error(" %s: there no such thing as an %s ExecReloadPre (ignored)", unit, section) + if conf.getlist(Service, "ExecReloadPost", []): # pragma: no cover + logg.error(" %s: there no such thing as an %s ExecReloadPost (ignored)", unit, section) + if conf.getlist(Service, "ExecStopPre", []): # pragma: no cover + logg.error(" %s: there no such thing as an %s ExecStopPre (ignored)", unit, section) + for env_file in conf.getlist(Service, "EnvironmentFile", []): + if env_file.startswith("-"): continue + if not os.path.isfile(os_path(self._root, self.expand_special(env_file, conf))): + logg.error(" %s: Failed to load environment files: %s", unit, env_file) + errors += 101 + return errors + def exec_check_unit(self, conf, env, section = Service, exectype = ""): + if conf is None: # pragma: no cover (is never null) + return True + if not conf.data.has_section(section): + return True # pragma: no cover + haveType = conf.get(section, "Type", "simple") + if self.is_sysv_file(conf.filename()): + return True # we don't care about that + unit = conf.name() + abspath = 0 + notexists = 0 + badusers = 0 + badgroups = 0 + for execs in ["ExecStartPre", "ExecStart", "ExecStartPost", "ExecStop", "ExecStopPost", "ExecReload"]: + if not execs.startswith(exectype): + continue + for cmd in conf.getlist(section, execs, []): + mode, newcmd = self.exec_newcmd(cmd, env, conf) + if not newcmd: + continue + exe = newcmd[0] + if not exe: + continue + if exe[0] != "/": + logg.error(" %s: Exec is not an absolute path: %s=%s", unit, execs, cmd) + abspath += 1 + if not os.path.isfile(exe): + logg.error(" %s: Exec command does not exist: (%s) %s", unit, execs, exe) + if mode.check: + notexists += 1 + newexe1 = os.path.join("/usr/bin", exe) + newexe2 = os.path.join("/bin", exe) + if os.path.exists(newexe1): + logg.error(" %s: but this does exist: %s %s", unit, " " * len(execs), newexe1) + elif os.path.exists(newexe2): + logg.error(" %s: but this does exist: %s %s", unit, " " * len(execs), newexe2) + users = [conf.get(section, "User", ""), conf.get(section, "SocketUser", "")] + groups = [conf.get(section, "Group", ""), conf.get(section, "SocketGroup", "")] + conf.getlist(section, "SupplementaryGroups") + for user in users: + if user: + try: pwd.getpwnam(self.expand_special(user, conf)) + except Exception as e: + logg.error(" %s: User does not exist: %s (%s)", unit, user, getattr(e, "__doc__", "")) + badusers += 1 + for group in groups: + if group: + try: grp.getgrnam(self.expand_special(group, conf)) + except Exception as e: + logg.error(" %s: Group does not exist: %s (%s)", unit, group, getattr(e, "__doc__", "")) + badgroups += 1 + tmpproblems = 0 + for setting in ("RootDirectory", "RootImage", "BindPaths", "BindReadOnlyPaths", + "ReadWritePaths", "ReadOnlyPaths", "TemporaryFileSystem"): + setting_value = conf.get(section, setting, "") + if setting_value: + logg.info("%s: %s private directory remounts ignored: %s=%s", unit, section, setting, setting_value) + tmpproblems += 1 + for setting in ("PrivateTmp", "PrivateDevices", "PrivateNetwork", "PrivateUsers", "DynamicUser", + "ProtectSystem", "ProjectHome", "ProtectHostname", "PrivateMounts", "MountAPIVFS"): + setting_yes = conf.getbool(section, setting, "no") + if setting_yes: + logg.info("%s: %s private directory option is ignored: %s=yes", unit, section, setting) + tmpproblems += 1 + if not abspath and not notexists and not badusers and not badgroups: + return True + if True: + filename = strE(conf.filename()) + if len(filename) > 44: filename = o44(filename) + logg.error(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + if abspath: + logg.error(" The SystemD ExecXY commands must always be absolute paths by definition.") + time.sleep(1) + if notexists: + logg.error(" Oops, %s executable paths were not found in the current environment. Refusing.", notexists) + time.sleep(1) + if badusers or badgroups: + logg.error(" Oops, %s user names and %s group names were not found. Refusing.", badusers, badgroups) + time.sleep(1) + if tmpproblems: + logg.info(" Note, %s private directory settings are ignored. The application should not depend on it.", tmpproblems) + time.sleep(1) + logg.error(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!") + return False + def show_modules(self, *modules): + """ [PATTERN]... -- Show properties of one or more units + Show properties of one or more units (or the manager itself). + If no argument is specified, properties of the manager will be + shown. If a unit name is specified, properties of the unit is + shown. By default, empty properties are suppressed. Use --all to + show those too. To select specific properties to show, use + --property=. This command is intended to be used whenever + computer-parsable output is required. Use status if you are looking + for formatted human-readable output. + / + NOTE: only a subset of properties is implemented """ + notfound = [] + units = [] + found_all = True + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s could not be found.", unit_of(module)) + units += [module] + # self.error |= NOT_FOUND + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + return self.show_units(units) + notfound # and found_all + def show_units(self, units): + logg.debug("show --property=%s", ",".join(self._only_property)) + result = [] + for unit in units: + if result: result += [""] + for var, value in self.show_unit_items(unit): + if self._only_property: + if var not in self._only_property: + continue + else: + if not value and not self._show_all: + continue + result += ["%s=%s" % (var, value)] + return result + def show_unit_items(self, unit): + """ [UNIT]... -- show properties of a unit. + """ + logg.info("try read unit %s", unit) + conf = self.get_unit_conf(unit) + for entry in self.each_unit_items(unit, conf): + yield entry + def each_unit_items(self, unit, conf): + loaded = conf.loaded() + if not loaded: + loaded = "not-loaded" + if "NOT-FOUND" in self.get_description_from(conf): + loaded = "not-found" + names = {unit: 1, conf.name(): 1} + yield "Id", conf.name() + yield "Names", " ".join(sorted(names.keys())) + yield "Description", self.get_description_from(conf) # conf.get(Unit, "Description") + yield "PIDFile", self.get_pid_file(conf) # not self.pid_file_from w/o default location + yield "PIDFilePath", self.pid_file_from(conf) + yield "MainPID", strE(self.active_pid_from(conf)) # status["MainPID"] or PIDFile-read + yield "SubState", self.get_substate_from(conf) or "unknown" # status["SubState"] or notify-result + yield "ActiveState", self.get_active_from(conf) or "unknown" # status["ActiveState"] + yield "LoadState", loaded + yield "UnitFileState", self.enabled_from(conf) + yield "StatusFile", self.get_StatusFile(conf) + yield "StatusFilePath", self.get_status_file_from(conf) + yield "JournalFile", self.get_journal_log(conf) + yield "JournalFilePath", self.get_journal_log_from(conf) + yield "NotifySocket", self.get_notify_socket_from(conf) + yield "User", self.get_User(conf) or "" + yield "Group", self.get_Group(conf) or "" + yield "SupplementaryGroups", " ".join(self.get_SupplementaryGroups(conf)) + yield "TimeoutStartUSec", seconds_to_time(self.get_TimeoutStartSec(conf)) + yield "TimeoutStopUSec", seconds_to_time(self.get_TimeoutStopSec(conf)) + yield "NeedDaemonReload", "no" + yield "SendSIGKILL", strYes(self.get_SendSIGKILL(conf)) + yield "SendSIGHUP", strYes(self.get_SendSIGHUP(conf)) + yield "KillMode", strE(self.get_KillMode(conf)) + yield "KillSignal", strE(self.get_KillSignal(conf)) + yield "StartLimitBurst", strE(self.get_StartLimitBurst(conf)) + yield "StartLimitIntervalSec", seconds_to_time(self.get_StartLimitIntervalSec(conf)) + yield "RestartSec", seconds_to_time(self.get_RestartSec(conf)) + yield "RemainAfterExit", strYes(self.get_RemainAfterExit(conf)) + yield "WorkingDirectory", strE(self.get_WorkingDirectory(conf)) + env_parts = [] + for env_part in conf.getlist(Service, "Environment", []): + env_parts.append(self.expand_special(env_part, conf)) + if env_parts: + yield "Environment", " ".join(env_parts) + env_files = [] + for env_file in conf.getlist(Service, "EnvironmentFile", []): + env_files.append(self.expand_special(env_file, conf)) + if env_files: + yield "EnvironmentFile", " ".join(env_files) + def get_SendSIGKILL(self, conf): + return conf.getbool(Service, "SendSIGKILL", "yes") + def get_SendSIGHUP(self, conf): + return conf.getbool(Service, "SendSIGHUP", "no") + def get_KillMode(self, conf): + return conf.get(Service, "KillMode", "control-group") + def get_KillSignal(self, conf): + return conf.get(Service, "KillSignal", "SIGTERM") + # + igno_centos = ["netconsole", "network"] + igno_opensuse = ["raw", "pppoe", "*.local", "boot.*", "rpmconf*", "postfix*"] + igno_ubuntu = ["mount*", "umount*", "ondemand", "*.local"] + igno_always = ["network*", "dbus*", "systemd-*", "kdump*", "kmod*"] + igno_always += ["purge-kernels.service", "after-local.service", "dm-event.*"] # as on opensuse + igno_targets = ["remote-fs.target"] + def _ignored_unit(self, unit, ignore_list): + for ignore in ignore_list: + if fnmatch.fnmatchcase(unit, ignore): + return True # ignore + if fnmatch.fnmatchcase(unit, ignore+".service"): + return True # ignore + return False + def default_services_modules(self, *modules): + """ show the default services + This is used internally to know the list of service to be started in the 'get-default' + target runlevel when the container is started through default initialisation. It will + ignore a number of services - use '--all' to show a longer list of services and + use '--all --force' if not even a minimal filter shall be used. + """ + results = [] + targets = modules or [self.get_default_target()] + for target in targets: + units = self.target_default_services(target) + logg.debug(" %s # %s", " ".join(units), target) + for unit in units: + if unit not in results: + results.append(unit) + return results + def target_default_services(self, target = None, sysv = "S"): + """ get the default services for a target - this will ignore a number of services, + use '--all' and --force' to get more services. + """ + igno = self.igno_centos + self.igno_opensuse + self.igno_ubuntu + self.igno_always + if self._show_all: + igno = self.igno_always + if self._force: + igno = [] + logg.debug("ignored services filter for default.target:\n\t%s", igno) + default_target = target or self.get_default_target() + return self.enabled_target_services(default_target, sysv, igno) + def enabled_target_services(self, target, sysv = "S", igno = []): + units = [] + if self.user_mode(): + targetlist = self.get_target_list(target) + logg.debug("check for %s user services : %s", target, targetlist) + for targets in targetlist: + for unit in self.enabled_target_user_local_units(targets, ".target", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.required_target_units(targets, ".socket", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.enabled_target_user_local_units(targets, ".socket", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.required_target_units(targets, ".service", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.enabled_target_user_local_units(targets, ".service", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.enabled_target_user_system_units(targets, ".service", igno): + if unit not in units: + units.append(unit) + else: + targetlist = self.get_target_list(target) + logg.debug("check for %s system services: %s", target, targetlist) + for targets in targetlist: + for unit in self.enabled_target_configured_system_units(targets, ".target", igno + self.igno_targets): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.required_target_units(targets, ".socket", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.enabled_target_installed_system_units(targets, ".socket", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.required_target_units(targets, ".service", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.enabled_target_installed_system_units(targets, ".service", igno): + if unit not in units: + units.append(unit) + for targets in targetlist: + for unit in self.enabled_target_sysv_units(targets, sysv, igno): + if unit not in units: + units.append(unit) + return units + def enabled_target_user_local_units(self, target, unit_kind = ".service", igno = []): + units = [] + for basefolder in self.user_folders(): + if not basefolder: + continue + folder = self.default_enablefolder(target, basefolder) + if self._root: + folder = os_path(self._root, folder) + if os.path.isdir(folder): + for unit in sorted(os.listdir(folder)): + path = os.path.join(folder, unit) + if os.path.isdir(path): continue + if self._ignored_unit(unit, igno): + continue # ignore + if unit.endswith(unit_kind): + units.append(unit) + return units + def enabled_target_user_system_units(self, target, unit_kind = ".service", igno = []): + units = [] + for basefolder in self.system_folders(): + if not basefolder: + continue + folder = self.default_enablefolder(target, basefolder) + if self._root: + folder = os_path(self._root, folder) + if os.path.isdir(folder): + for unit in sorted(os.listdir(folder)): + path = os.path.join(folder, unit) + if os.path.isdir(path): continue + if self._ignored_unit(unit, igno): + continue # ignore + if unit.endswith(unit_kind): + conf = self.load_unit_conf(unit) + if conf is None: + pass + elif self.not_user_conf(conf): + pass + else: + units.append(unit) + return units + def enabled_target_installed_system_units(self, target, unit_type = ".service", igno = []): + units = [] + for basefolder in self.system_folders(): + if not basefolder: + continue + folder = self.default_enablefolder(target, basefolder) + if self._root: + folder = os_path(self._root, folder) + if os.path.isdir(folder): + for unit in sorted(os.listdir(folder)): + path = os.path.join(folder, unit) + if os.path.isdir(path): continue + if self._ignored_unit(unit, igno): + continue # ignore + if unit.endswith(unit_type): + units.append(unit) + return units + def enabled_target_configured_system_units(self, target, unit_type = ".service", igno = []): + units = [] + if True: + folder = self.default_enablefolder(target) + if self._root: + folder = os_path(self._root, folder) + if os.path.isdir(folder): + for unit in sorted(os.listdir(folder)): + path = os.path.join(folder, unit) + if os.path.isdir(path): continue + if self._ignored_unit(unit, igno): + continue # ignore + if unit.endswith(unit_type): + units.append(unit) + return units + def enabled_target_sysv_units(self, target, sysv = "S", igno = []): + units = [] + folders = [] + if target in ["multi-user.target", DefaultUnit]: + folders += [self.rc3_root_folder()] + if target in ["graphical.target"]: + folders += [self.rc5_root_folder()] + for folder in folders: + if not os.path.isdir(folder): + logg.warning("non-existent %s", folder) + continue + for unit in sorted(os.listdir(folder)): + path = os.path.join(folder, unit) + if os.path.isdir(path): continue + m = re.match(sysv+r"\d\d(.*)", unit) + if m: + service = m.group(1) + unit = service + ".service" + if self._ignored_unit(unit, igno): + continue # ignore + units.append(unit) + return units + def required_target_units(self, target, unit_type, igno): + units = [] + deps = self.get_required_dependencies(target) + for unit in sorted(deps): + if self._ignored_unit(unit, igno): + continue # ignore + if unit.endswith(unit_type): + if unit not in units: + units.append(unit) + return units + def get_target_conf(self, module): # -> conf (conf | default-conf) + """ accept that a unit does not exist + and return a unit conf that says 'not-loaded' """ + conf = self.load_unit_conf(module) + if conf is not None: + return conf + target_conf = self.default_unit_conf(module) + if module in target_requires: + target_conf.set(Unit, "Requires", target_requires[module]) + return target_conf + def get_target_list(self, module): + """ the Requires= in target units are only accepted if known """ + target = module + if "." not in target: target += ".target" + targets = [target] + conf = self.get_target_conf(module) + requires = conf.get(Unit, "Requires", "") + while requires in target_requires: + targets = [requires] + targets + requires = target_requires[requires] + logg.debug("the %s requires %s", module, targets) + return targets + def default_system(self, arg = True): + """ start units for default system level + This will go through the enabled services in the default 'multi-user.target'. + However some services are ignored as being known to be installation garbage + from unintended services. Use '--all' so start all of the installed services + and with '--all --force' even those services that are otherwise wrong. + /// SPECIAL: with --now or --init the init-loop is run and afterwards + a system_halt is performed with the enabled services to be stopped.""" + self.sysinit_status(SubState = "initializing") + logg.info("system default requested - %s", arg) + init = self._now or self._init + return self.start_system_default(init = init) + def start_system_default(self, init = False): + """ detect the default.target services and start them. + When --init is given then the init-loop is run and + the services are stopped again by 'systemctl halt'.""" + target = self.get_default_target() + services = self.start_target_system(target, init) + logg.info("%s system is up", target) + if init: + logg.info("init-loop start") + sig = self.init_loop_until_stop(services) + logg.info("init-loop %s", sig) + self.stop_system_default() + return not not services + def start_target_system(self, target, init = False): + services = self.target_default_services(target, "S") + self.sysinit_status(SubState = "starting") + self.start_units(services) + return services + def do_start_target_from(self, conf): + target = conf.name() + # services = self.start_target_system(target) + services = self.target_default_services(target, "S") + units = [service for service in services if not self.is_running_unit(service)] + logg.debug("start %s is starting %s from %s", target, units, services) + return self.start_units(units) + def stop_system_default(self): + """ detect the default.target services and stop them. + This is commonly run through 'systemctl halt' or + at the end of a 'systemctl --init default' loop.""" + target = self.get_default_target() + services = self.stop_target_system(target) + logg.info("%s system is down", target) + return not not services + def stop_target_system(self, target): + services = self.target_default_services(target, "K") + self.sysinit_status(SubState = "stopping") + self.stop_units(services) + return services + def do_stop_target_from(self, conf): + target = conf.name() + # services = self.stop_target_system(target) + services = self.target_default_services(target, "K") + units = [service for service in services if self.is_running_unit(service)] + logg.debug("stop %s is stopping %s from %s", target, units, services) + return self.stop_units(units) + def do_reload_target_from(self, conf): + target = conf.name() + return self.reload_target_system(target) + def reload_target_system(self, target): + services = self.target_default_services(target, "S") + units = [service for service in services if self.is_running_unit(service)] + return self.reload_units(units) + def halt_target(self, arg = True): + """ stop units from default system level """ + logg.info("system halt requested - %s", arg) + done = self.stop_system_default() + try: + os.kill(1, signal.SIGQUIT) # exit init-loop on no_more_procs + except Exception as e: + logg.warning("SIGQUIT to init-loop on PID-1: %s", e) + return done + def system_get_default(self): + """ get current default run-level""" + return self.get_default_target() + def get_targets_folder(self): + return os_path(self._root, self.mask_folder()) + def get_default_target_file(self): + targets_folder = self.get_targets_folder() + return os.path.join(targets_folder, DefaultUnit) + def get_default_target(self, default_target = None): + """ get current default run-level""" + current = default_target or self._default_target + default_target_file = self.get_default_target_file() + if os.path.islink(default_target_file): + current = os.path.basename(os.readlink(default_target_file)) + return current + def set_default_modules(self, *modules): + """ set current default run-level""" + if not modules: + logg.debug(".. no runlevel given") + self.error |= NOT_OK + return "Too few arguments" + current = self.get_default_target() + default_target_file = self.get_default_target_file() + msg = "" + for module in modules: + if module == current: + continue + targetfile = None + for targetname, targetpath in self.each_target_file(): + if targetname == module: + targetfile = targetpath + if not targetfile: + self.error |= NOT_OK | NOT_ACTIVE # 3 + msg = "No such runlevel %s" % (module) + continue + # + if os.path.islink(default_target_file): + os.unlink(default_target_file) + if not os.path.isdir(os.path.dirname(default_target_file)): + os.makedirs(os.path.dirname(default_target_file)) + os.symlink(targetfile, default_target_file) + msg = "Created symlink from %s -> %s" % (default_target_file, targetfile) + logg.debug("%s", msg) + return msg + def init_modules(self, *modules): + """ [UNIT*] -- init loop: '--init default' or '--init start UNIT*' + The systemctl init service will start the enabled 'default' services, + and then wait for any zombies to be reaped. When a SIGINT is received + then a clean shutdown of the enabled services is ensured. A Control-C in + in interactive mode will also run 'stop' on all the enabled services. // + When a UNIT name is given then only that one is started instead of the + services in the 'default.target'. Using 'init UNIT' is better than + '--init start UNIT' because the UNIT is also stopped cleanly even when + it was never enabled in the system. + /// SPECIAL: when using --now then only the init-loop is started, + with the reap-zombies function and waiting for an interrupt. + (and no unit is started/stoppped wether given or not). + """ + if self._now: + result = self.init_loop_until_stop([]) + return not not result + if not modules: + # like 'systemctl --init default' + if self._now or self._show_all: + logg.debug("init default --now --all => no_more_procs") + self.doExitWhenNoMoreProcs = True + return self.start_system_default(init = True) + # + # otherwise quit when all the init-services have died + self.doExitWhenNoMoreServices = True + if self._now or self._show_all: + logg.debug("init services --now --all => no_more_procs") + self.doExitWhenNoMoreProcs = True + found_all = True + units = [] + for module in modules: + matched = self.match_units(to_list(module)) + if not matched: + logg.error("Unit %s could not be found.", unit_of(module)) + found_all = False + continue + for unit in matched: + if unit not in units: + units += [unit] + logg.info("init %s -> start %s", ",".join(modules), ",".join(units)) + done = self.start_units(units, init = True) + logg.info("-- init is done") + return done # and found_all + def start_log_files(self, units): + self._log_file = {} + self._log_hold = {} + for unit in units: + conf = self.load_unit_conf(unit) + if not conf: continue + if self.skip_journal_log(conf): continue + log_path = self.get_journal_log_from(conf) + try: + opened = os.open(log_path, os.O_RDONLY | os.O_NONBLOCK) + self._log_file[unit] = opened + self._log_hold[unit] = b"" + except Exception as e: + logg.error("can not open %s log: %s\n\t%s", unit, log_path, e) + def read_log_files(self, units): + self.print_log_files(units) + def print_log_files(self, units, stdout = 1): + BUFSIZE=8192 + printed = 0 + for unit in units: + if unit in self._log_file: + new_text = b"" + while True: + buf = os.read(self._log_file[unit], BUFSIZE) + if not buf: break + new_text += buf + continue + text = self._log_hold[unit] + new_text + if not text: continue + lines = text.split(b"\n") + if not text.endswith(b"\n"): + self._log_hold[unit] = lines[-1] + lines = lines[:-1] + for line in lines: + prefix = unit.encode("utf-8") + content = prefix+b": "+line+b"\n" + try: + os.write(stdout, content) + try: + os.fsync(stdout) + except Exception: + pass + printed += 1 + except BlockingIOError: + pass + return printed + def stop_log_files(self, units): + for unit in units: + try: + if unit in self._log_file: + if self._log_file[unit]: + os.close(self._log_file[unit]) + except Exception as e: + logg.error("can not close log: %s\n\t%s", unit, e) + self._log_file = {} + self._log_hold = {} + + def get_StartLimitBurst(self, conf): + defaults = DefaultStartLimitBurst + return to_int(conf.get(Service, "StartLimitBurst", strE(defaults)), defaults) # 5 + def get_StartLimitIntervalSec(self, conf, maximum = None): + maximum = maximum or 999 + defaults = DefaultStartLimitIntervalSec + interval = conf.get(Service, "StartLimitIntervalSec", strE(defaults)) # 10s + return time_to_seconds(interval, maximum) + def get_RestartSec(self, conf, maximum = None): + maximum = maximum or DefaultStartLimitIntervalSec + delay = conf.get(Service, "RestartSec", strE(DefaultRestartSec)) + return time_to_seconds(delay, maximum) + def restart_failed_units(self, units, maximum = None): + """ This function will restart failed units. + / + NOTE that with standard settings the LimitBurst implementation has no effect. If + the InitLoopSleep is ticking at the Default of 5sec and the LimitBurst Default + is 5x within a Default 10secs time frame then within those 10sec only 2 loop + rounds have come here checking for possible restarts. You can directly shorten + the interval ('-c InitLoopSleep=1') or have it indirectly shorter from the + service descriptor's RestartSec ("RestartSec=2s"). + """ + global InitLoopSleep + me = os.getpid() + maximum = maximum or DefaultStartLimitIntervalSec + restartDelay = MinimumYield + for unit in units: + now = time.time() + try: + conf = self.load_unit_conf(unit) + if not conf: continue + restartPolicy = conf.get(Service, "Restart", "no") + if restartPolicy in ["no", "on-success"]: + logg.debug("[%s] [%s] Current NoCheck (Restart=%s)", me, unit, restartPolicy) + continue + restartSec = self.get_RestartSec(conf) + if restartSec == 0: + if InitLoopSleep > 1: + logg.warning("[%s] set InitLoopSleep from %ss to 1 (caused by RestartSec=0!)", + unit, InitLoopSleep) + InitLoopSleep = 1 + elif restartSec > 0.9 and restartSec < InitLoopSleep: + restartSleep = int(restartSec + 0.2) + if restartSleep < InitLoopSleep: + logg.warning("[%s] set InitLoopSleep from %ss to %s (caused by RestartSec=%.3fs)", + unit, InitLoopSleep, restartSleep, restartSec) + InitLoopSleep = restartSleep + isUnitState = self.get_active_from(conf) + isUnitFailed = isUnitState in ["failed"] + logg.debug("[%s] [%s] Current Status: %s (%s)", me, unit, isUnitState, isUnitFailed) + if not isUnitFailed: + if unit in self._restart_failed_units: + del self._restart_failed_units[unit] + continue + limitBurst = self.get_StartLimitBurst(conf) + limitSecs = self.get_StartLimitIntervalSec(conf) + if limitBurst > 1 and limitSecs >= 1: + try: + if unit not in self._restarted_unit: + self._restarted_unit[unit] = [] + # we want to register restarts from now on + restarted = self._restarted_unit[unit] + logg.debug("[%s] [%s] Current limitSecs=%ss limitBurst=%sx (restarted %sx)", + me, unit, limitSecs, limitBurst, len(restarted)) + oldest = 0. + interval = 0. + if len(restarted) >= limitBurst: + logg.debug("[%s] [%s] restarted %s", + me, unit, ["%.3fs" % (t - now) for t in restarted]) + while len(restarted): + oldest = restarted[0] + interval = time.time() - oldest + if interval > limitSecs: + restarted = restarted[1:] + continue + break + self._restarted_unit[unit] = restarted + logg.debug("[%s] [%s] ratelimit %s", + me, unit, ["%.3fs" % (t - now) for t in restarted]) + # all values in restarted have a time below limitSecs + if len(restarted) >= limitBurst: + logg.info("[%s] [%s] Blocking Restart - oldest %s is %s ago (allowed %s)", + me, unit, oldest, interval, limitSecs) + self.write_status_from(conf, AS="error") + unit = "" # dropped out + continue + except Exception as e: + logg.error("[%s] burst exception %s", unit, e) + if unit: # not dropped out + if unit not in self._restart_failed_units: + self._restart_failed_units[unit] = now + restartSec + logg.debug("[%s] [%s] restart scheduled in %+.3fs", + me, unit, (self._restart_failed_units[unit] - now)) + except Exception as e: + logg.error("[%s] [%s] An error occurred while restart checking: %s", me, unit, e) + if not self._restart_failed_units: + self.error |= NOT_OK + return [] + # NOTE: this function is only called from InitLoop when "running" + # let's check if any of the restart_units has its restartSec expired + now = time.time() + restart_done = [] + logg.debug("[%s] Restart checking %s", + me, ["%+.3fs" % (t - now) for t in self._restart_failed_units.values()]) + for unit in sorted(self._restart_failed_units): + restartAt = self._restart_failed_units[unit] + if restartAt > now: + continue + restart_done.append(unit) + try: + conf = self.load_unit_conf(unit) + if not conf: continue + isUnitState = self.get_active_from(conf) + isUnitFailed = isUnitState in ["failed"] + logg.debug("[%s] [%s] Restart Status: %s (%s)", me, unit, isUnitState, isUnitFailed) + if isUnitFailed: + logg.debug("[%s] [%s] --- restarting failed unit...", me, unit) + self.restart_unit(unit) + logg.debug("[%s] [%s] --- has been restarted.", me, unit) + if unit in self._restarted_unit: + self._restarted_unit[unit].append(time.time()) + except Exception as e: + logg.error("[%s] [%s] An error occurred while restarting: %s", me, unit, e) + for unit in restart_done: + if unit in self._restart_failed_units: + del self._restart_failed_units[unit] + logg.debug("[%s] Restart remaining %s", + me, ["%+.3fs" % (t - now) for t in self._restart_failed_units.values()]) + return restart_done + + def init_loop_until_stop(self, units): + """ this is the init-loop - it checks for any zombies to be reaped and + waits for an interrupt. When a SIGTERM /SIGINT /Control-C signal + is received then the signal name is returned. Any other signal will + just raise an Exception like one would normally expect. As a special + the 'systemctl halt' emits SIGQUIT which puts it into no_more_procs mode.""" + signal.signal(signal.SIGQUIT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGQUIT")) + signal.signal(signal.SIGINT, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGINT")) + signal.signal(signal.SIGTERM, lambda signum, frame: ignore_signals_and_raise_keyboard_interrupt("SIGTERM")) + result = None + # + self.start_log_files(units) + logg.debug("start listen") + listen = SystemctlListenThread(self) + logg.debug("starts listen") + listen.start() + logg.debug("started listen") + self.sysinit_status(ActiveState = "active", SubState = "running") + timestamp = time.time() + while True: + try: + if DEBUG_INITLOOP: # pragma: no cover + logg.debug("DONE InitLoop (sleep %ss)", InitLoopSleep) + sleep_sec = InitLoopSleep - (time.time() - timestamp) + if sleep_sec < MinimumYield: + sleep_sec = MinimumYield + sleeping = sleep_sec + while sleeping > 2: + time.sleep(1) # accept signals atleast every second + sleeping = InitLoopSleep - (time.time() - timestamp) + if sleeping < MinimumYield: + sleeping = MinimumYield + break + time.sleep(sleeping) # remainder waits less that 2 seconds + timestamp = time.time() + self.loop.acquire() + if DEBUG_INITLOOP: # pragma: no cover + logg.debug("NEXT InitLoop (after %ss)", sleep_sec) + self.read_log_files(units) + if DEBUG_INITLOOP: # pragma: no cover + logg.debug("reap zombies - check current processes") + running = self.reap_zombies() + if DEBUG_INITLOOP: # pragma: no cover + logg.debug("reap zombies - init-loop found %s running procs", running) + if self.doExitWhenNoMoreServices: + active = False + for unit in units: + conf = self.load_unit_conf(unit) + if not conf: continue + if self.is_active_from(conf): + active = True + if not active: + logg.info("no more services - exit init-loop") + break + if self.doExitWhenNoMoreProcs: + if not running: + logg.info("no more procs - exit init-loop") + break + if RESTART_FAILED_UNITS: + self.restart_failed_units(units) + self.loop.release() + except KeyboardInterrupt as e: + if e.args and e.args[0] == "SIGQUIT": + # the original systemd puts a coredump on that signal. + logg.info("SIGQUIT - switch to no more procs check") + self.doExitWhenNoMoreProcs = True + continue + signal.signal(signal.SIGTERM, signal.SIG_DFL) + signal.signal(signal.SIGINT, signal.SIG_DFL) + logg.info("interrupted - exit init-loop") + result = str(e) or "STOPPED" + break + except Exception as e: + logg.info("interrupted - exception %s", e) + raise + self.sysinit_status(ActiveState = None, SubState = "degraded") + try: self.loop.release() + except: pass + listen.stop() + listen.join(2) + self.read_log_files(units) + self.read_log_files(units) + self.stop_log_files(units) + logg.debug("done - init loop") + return result + def reap_zombies_target(self): + """ -- check to reap children (internal) """ + running = self.reap_zombies() + return "remaining {running} process".format(**locals()) + def reap_zombies(self): + """ check to reap children """ + selfpid = os.getpid() + running = 0 + for pid_entry in os.listdir(_proc_pid_dir): + pid = to_intN(pid_entry) + if pid is None: + continue + if pid == selfpid: + continue + proc_status = _proc_pid_status.format(**locals()) + if os.path.isfile(proc_status): + zombie = False + ppid = -1 + try: + for line in open(proc_status): + m = re.match(r"State:\s*Z.*", line) + if m: zombie = True + m = re.match(r"PPid:\s*(\d+)", line) + if m: ppid = int(m.group(1)) + except IOError as e: + logg.warning("%s : %s", proc_status, e) + continue + if zombie and ppid == os.getpid(): + logg.info("reap zombie %s", pid) + try: os.waitpid(pid, os.WNOHANG) + except OSError as e: + logg.warning("reap zombie %s: %s", e.strerror) + if os.path.isfile(proc_status): + if pid > 1: + running += 1 + return running # except PID 0 and PID 1 + def sysinit_status(self, **status): + conf = self.sysinit_target() + self.write_status_from(conf, **status) + def sysinit_target(self): + if not self._sysinit_target: + self._sysinit_target = self.default_unit_conf(SysInitTarget, "System Initialization") + assert self._sysinit_target is not None + return self._sysinit_target + def is_system_running(self): + conf = self.sysinit_target() + if not self.is_running_unit_from(conf): + time.sleep(MinimumYield) + if not self.is_running_unit_from(conf): + return "offline" + status = self.read_status_from(conf) + return status.get("SubState", "unknown") + def is_system_running_info(self): + state = self.is_system_running() + if state not in ["running"]: + self.error |= NOT_OK # 1 + if self._quiet: + return None + return state + def wait_system(self, target = None): + target = target or SysInitTarget + for attempt in xrange(int(SysInitWait)): + state = self.is_system_running() + if "init" in state: + if target in [SysInitTarget, "basic.target"]: + logg.info("system not initialized - wait %s", target) + time.sleep(1) + continue + if "start" in state or "stop" in state: + if target in ["basic.target"]: + logg.info("system not running - wait %s", target) + time.sleep(1) + continue + if "running" not in state: + logg.info("system is %s", state) + break + def is_running_unit_from(self, conf): + status_file = self.get_status_file_from(conf) + pid_file = self.pid_file_from(conf) + return self.getsize(status_file) > 0 or self.getsize(pid_file) > 0 + def is_running_unit(self, unit): + conf = self.get_unit_conf(unit) + return self.is_running_unit_from(conf) + def pidlist_of(self, pid): + if not pid: + return [] + pidlist = [pid] + pids = [pid] + for depth in xrange(PROC_MAX_DEPTH): + for pid_entry in os.listdir(_proc_pid_dir): + pid = to_intN(pid_entry) + if pid is None: + continue + proc_status = _proc_pid_status.format(**locals()) + if os.path.isfile(proc_status): + try: + for line in open(proc_status): + if line.startswith("PPid:"): + ppid_text = line[len("PPid:"):].strip() + try: ppid = int(ppid_text) + except: continue + if ppid in pidlist and pid not in pids: + pids += [pid] + except IOError as e: + logg.warning("%s : %s", proc_status, e) + continue + if len(pids) != len(pidlist): + pidlist = pids[:] + continue + return pids + def echo(self, *targets): + line = " ".join(*targets) + logg.info(" == echo == %s", line) + return line + def killall(self, *targets): + mapping = {} + mapping[":3"] = signal.SIGQUIT + mapping[":QUIT"] = signal.SIGQUIT + mapping[":6"] = signal.SIGABRT + mapping[":ABRT"] = signal.SIGABRT + mapping[":9"] = signal.SIGKILL + mapping[":KILL"] = signal.SIGKILL + sig = signal.SIGTERM + for target in targets: + if target.startswith(":"): + if target in mapping: + sig = mapping[target] + else: # pragma: no cover + logg.error("unsupported %s", target) + continue + for pid_entry in os.listdir(_proc_pid_dir): + pid = to_intN(pid_entry) + if pid: + try: + cmdline = _proc_pid_cmdline.format(**locals()) + cmd = open(cmdline).read().split("\0") + if DEBUG_KILLALL: logg.debug("cmdline %s", cmd) + found = None + cmd_exe = os.path.basename(cmd[0]) + if DEBUG_KILLALL: logg.debug("cmd.exe '%s'", cmd_exe) + if fnmatch.fnmatchcase(cmd_exe, target): found = "exe" + if len(cmd) > 1 and cmd_exe.startswith("python"): + X = 1 + while cmd[X].startswith("-"): X += 1 # atleast '-u' unbuffered + cmd_arg = os.path.basename(cmd[X]) + if DEBUG_KILLALL: logg.debug("cmd.arg '%s'", cmd_arg) + if fnmatch.fnmatchcase(cmd_arg, target): found = "arg" + if cmd_exe.startswith("coverage") or cmd_arg.startswith("coverage"): + x = cmd.index("--") + if x > 0 and x+1 < len(cmd): + cmd_run = os.path.basename(cmd[x+1]) + if DEBUG_KILLALL: logg.debug("cmd.run '%s'", cmd_run) + if fnmatch.fnmatchcase(cmd_run, target): found = "run" + if found: + if DEBUG_KILLALL: logg.debug("%s found %s %s", found, pid, [c for c in cmd]) + if pid != os.getpid(): + logg.debug(" kill -%s %s # %s", sig, pid, target) + os.kill(pid, sig) + except Exception as e: + logg.error("kill -%s %s : %s", sig, pid, e) + return True + def force_ipv4(self, *args): + """ only ipv4 localhost in /etc/hosts """ + logg.debug("checking hosts sysconf for '::1 localhost'") + lines = [] + sysconf_hosts = os_path(self._root, _etc_hosts) + for line in open(sysconf_hosts): + if "::1" in line: + newline = re.sub("\\slocalhost\\s", " ", line) + if line != newline: + logg.info("%s: '%s' => '%s'", _etc_hosts, line.rstrip(), newline.rstrip()) + line = newline + lines.append(line) + f = open(sysconf_hosts, "w") + for line in lines: + f.write(line) + f.close() + def force_ipv6(self, *args): + """ only ipv4 localhost in /etc/hosts """ + logg.debug("checking hosts sysconf for '127.0.0.1 localhost'") + lines = [] + sysconf_hosts = os_path(self._root, _etc_hosts) + for line in open(sysconf_hosts): + if "127.0.0.1" in line: + newline = re.sub("\\slocalhost\\s", " ", line) + if line != newline: + logg.info("%s: '%s' => '%s'", _etc_hosts, line.rstrip(), newline.rstrip()) + line = newline + lines.append(line) + f = open(sysconf_hosts, "w") + for line in lines: + f.write(line) + f.close() + def help_modules(self, *args): + """[command] -- show this help + """ + lines = [] + okay = True + prog = os.path.basename(sys.argv[0]) + if not args: + argz = {} + for name in dir(self): + arg = None + if name.startswith("system_"): + arg = name[len("system_"):].replace("_", "-") + if name.startswith("show_"): + arg = name[len("show_"):].replace("_", "-") + if name.endswith("_of_unit"): + arg = name[:-len("_of_unit")].replace("_", "-") + if name.endswith("_modules"): + arg = name[:-len("_modules")].replace("_", "-") + if arg: + argz[arg] = name + lines.append("%s command [options]..." % prog) + lines.append("") + lines.append("Commands:") + for arg in sorted(argz): + name = argz[arg] + method = getattr(self, name) + doc = "..." + doctext = getattr(method, "__doc__") + if doctext: + doc = doctext + elif not self._show_all: + continue # pragma: no cover + firstline = doc.split("\n")[0] + doc_text = firstline.strip() + if "--" not in firstline: + doc_text = "-- " + doc_text + lines.append(" %s %s" % (arg, firstline.strip())) + return lines + for arg in args: + arg = arg.replace("-", "_") + func1 = getattr(self.__class__, arg+"_modules", None) + func2 = getattr(self.__class__, arg+"_of_unit", None) + func3 = getattr(self.__class__, "show_"+arg, None) + func4 = getattr(self.__class__, "system_"+arg, None) + func5 = None + if arg.startswith("__"): + func5 = getattr(self.__class__, arg[2:], None) + func = func1 or func2 or func3 or func4 or func5 + if func is None: + print("error: no such command '%s'" % arg) + okay = False + else: + doc_text = "..." + doc = getattr(func, "__doc__", "") + if doc: + doc_text = doc.replace("\n", "\n\n", 1).strip() + if "--" not in doc_text: + doc_text = "-- " + doc_text + else: + func_name = arg # FIXME + logg.debug("__doc__ of %s is none", func_name) + if not self._show_all: continue + lines.append("%s %s %s" % (prog, arg, doc_text)) + if not okay: + self.help_modules() + self.error |= NOT_OK + return [] + return lines + def systemd_version(self): + """ the version line for systemd compatibility """ + return "systemd %s\n - via systemctl.py %s" % (self._systemd_version, __version__) + def systemd_features(self): + """ the info line for systemd features """ + features1 = "-PAM -AUDIT -SELINUX -IMA -APPARMOR -SMACK" + features2 = " +SYSVINIT -UTMP -LIBCRYPTSETUP -GCRYPT -GNUTLS" + features3 = " -ACL -XZ -LZ4 -SECCOMP -BLKID -ELFUTILS -KMOD -IDN" + return features1+features2+features3 + def version_info(self): + return [self.systemd_version(), self.systemd_features()] + def test_float(self): + return 0. # "Unknown result type" + +def print_begin(argv, args): + script = os.path.realpath(argv[0]) + system = _user_mode and " --user" or " --system" + init = _init and " --init" or "" + logg.info("EXEC BEGIN %s %s%s%s", script, " ".join(args), system, init) + if _root and not is_good_root(_root): + root44 = path44(_root) + logg.warning("the --root=%s should have atleast three levels /tmp/test_123/root", root44) + +def print_begin2(args): + logg.debug("======= systemctl.py %s", " ".join(args)) + +def is_not_ok(result): + if DebugPrintResult: + logg.log(HINT, "EXEC END %s", result) + if result is False: + return NOT_OK + return 0 + +def print_str(result): + if result is None: + if DebugPrintResult: + logg.debug(" END %s", result) + return + print(result) + if DebugPrintResult: + result1 = result.split("\n")[0][:-20] + if result == result1: + logg.log(HINT, "EXEC END '%s'", result) + else: + logg.log(HINT, "EXEC END '%s...'", result1) + logg.debug(" END '%s'", result) +def print_str_list(result): + if result is None: + if DebugPrintResult: + logg.debug(" END %s", result) + return + shown = 0 + for element in result: + print(element) + shown += 1 + if DebugPrintResult: + logg.log(HINT, "EXEC END %i items", shown) + logg.debug(" END %s", result) +def print_str_list_list(result): + shown = 0 + for element in result: + print("\t".join([str(elem) for elem in element])) + shown += 1 + if DebugPrintResult: + logg.log(HINT, "EXEC END %i items", shown) + logg.debug(" END %s", result) +def print_str_dict(result): + if result is None: + if DebugPrintResult: + logg.debug(" END %s", result) + return + shown = 0 + for key in sorted(result.keys()): + element = result[key] + print("%s=%s" % (key, element)) + shown += 1 + if DebugPrintResult: + logg.log(HINT, "EXEC END %i items", shown) + logg.debug(" END %s", result) +def print_str_dict_dict(result): + if result is None: + if DebugPrintResult: + logg.debug(" END %s", result) + return + shown = 0 + for key in sorted(result): + element = result[key] + for name in sorted(element): + value = element[name] + print("%s [%s] %s" % (key, value, name)) + shown += 1 + if DebugPrintResult: + logg.log(HINT, "EXEC END %i items", shown) + logg.debug(" END %s", result) + +def run(command, *modules): + exitcode = 0 + if command in ["help"]: + print_str_list(systemctl.help_modules(*modules)) + elif command in ["cat"]: + print_str(systemctl.cat_modules(*modules)) + elif command in ["clean"]: + exitcode = is_not_ok(systemctl.clean_modules(*modules)) + elif command in ["command"]: + print_str_list(systemctl.command_of_unit(*modules)) + elif command in ["daemon-reload"]: + exitcode = is_not_ok(systemctl.daemon_reload_target()) + elif command in ["default"]: + exitcode = is_not_ok(systemctl.default_system()) + elif command in ["default-services"]: + print_str_list(systemctl.default_services_modules(*modules)) + elif command in ["disable"]: + exitcode = is_not_ok(systemctl.disable_modules(*modules)) + elif command in ["enable"]: + exitcode = is_not_ok(systemctl.enable_modules(*modules)) + elif command in ["environment"]: + print_str_dict(systemctl.environment_of_unit(*modules)) + elif command in ["get-default"]: + print_str(systemctl.get_default_target()) + elif command in ["get-preset"]: + print_str(systemctl.get_preset_of_unit(*modules)) + elif command in ["halt"]: + exitcode = is_not_ok(systemctl.halt_target()) + elif command in ["init"]: + exitcode = is_not_ok(systemctl.init_modules(*modules)) + elif command in ["is-active"]: + print_str_list(systemctl.is_active_modules(*modules)) + elif command in ["is-enabled"]: + print_str_list(systemctl.is_enabled_modules(*modules)) + elif command in ["is-failed"]: + print_str_list(systemctl.is_failed_modules(*modules)) + elif command in ["is-system-running"]: + print_str(systemctl.is_system_running_info()) + elif command in ["kill"]: + exitcode = is_not_ok(systemctl.kill_modules(*modules)) + elif command in ["list-start-dependencies"]: + print_str_list_list(systemctl.list_start_dependencies_modules(*modules)) + elif command in ["list-dependencies"]: + print_str_list(systemctl.list_dependencies_modules(*modules)) + elif command in ["list-unit-files"]: + print_str_list_list(systemctl.list_unit_files_modules(*modules)) + elif command in ["list-units"]: + print_str_list_list(systemctl.list_units_modules(*modules)) + elif command in ["listen"]: + exitcode = is_not_ok(systemctl.listen_modules(*modules)) + elif command in ["log", "logs"]: + exitcode = is_not_ok(systemctl.log_modules(*modules)) + elif command in ["mask"]: + exitcode = is_not_ok(systemctl.mask_modules(*modules)) + elif command in ["preset"]: + exitcode = is_not_ok(systemctl.preset_modules(*modules)) + elif command in ["preset-all"]: + exitcode = is_not_ok(systemctl.preset_all_modules()) + elif command in ["reap-zombies"]: + print_str(systemctl.reap_zombies_target()) + elif command in ["reload"]: + exitcode = is_not_ok(systemctl.reload_modules(*modules)) + elif command in ["reload-or-restart"]: + exitcode = is_not_ok(systemctl.reload_or_restart_modules(*modules)) + elif command in ["reload-or-try-restart"]: + exitcode = is_not_ok(systemctl.reload_or_try_restart_modules(*modules)) + elif command in ["reset-failed"]: + exitcode = is_not_ok(systemctl.reset_failed_modules(*modules)) + elif command in ["restart"]: + exitcode = is_not_ok(systemctl.restart_modules(*modules)) + elif command in ["set-default"]: + print_str(systemctl.set_default_modules(*modules)) + elif command in ["show"]: + print_str_list(systemctl.show_modules(*modules)) + elif command in ["start"]: + exitcode = is_not_ok(systemctl.start_modules(*modules)) + elif command in ["status"]: + print_str(systemctl.status_modules(*modules)) + elif command in ["stop"]: + exitcode = is_not_ok(systemctl.stop_modules(*modules)) + elif command in ["try-restart"]: + exitcode = is_not_ok(systemctl.try_restart_modules(*modules)) + elif command in ["unmask"]: + exitcode = is_not_ok(systemctl.unmask_modules(*modules)) + elif command in ["version"]: + print_str_list(systemctl.version_info()) + elif command in ["__cat_unit"]: + print_str(systemctl.cat_unit(*modules)) + elif command in ["__get_active_unit"]: + print_str(systemctl.get_active_unit(*modules)) + elif command in ["__get_description"]: + print_str(systemctl.get_description(*modules)) + elif command in ["__get_status_file"]: + print_str(systemctl.get_status_file(modules[0])) + elif command in ["__get_status_pid_file", "__get_pid_file"]: + print_str(systemctl.get_status_pid_file(modules[0])) + elif command in ["__disable_unit"]: + exitcode = is_not_ok(systemctl.disable_unit(*modules)) + elif command in ["__enable_unit"]: + exitcode = is_not_ok(systemctl.enable_unit(*modules)) + elif command in ["__is_enabled"]: + exitcode = is_not_ok(systemctl.is_enabled(*modules)) + elif command in ["__killall"]: + exitcode = is_not_ok(systemctl.killall(*modules)) + elif command in ["__kill_unit"]: + exitcode = is_not_ok(systemctl.kill_unit(*modules)) + elif command in ["__load_preset_files"]: + print_str_list(systemctl.load_preset_files(*modules)) + elif command in ["__mask_unit"]: + exitcode = is_not_ok(systemctl.mask_unit(*modules)) + elif command in ["__read_env_file"]: + print_str_list_list(list(systemctl.read_env_file(*modules))) + elif command in ["__reload_unit"]: + exitcode = is_not_ok(systemctl.reload_unit(*modules)) + elif command in ["__reload_or_restart_unit"]: + exitcode = is_not_ok(systemctl.reload_or_restart_unit(*modules)) + elif command in ["__reload_or_try_restart_unit"]: + exitcode = is_not_ok(systemctl.reload_or_try_restart_unit(*modules)) + elif command in ["__reset_failed_unit"]: + exitcode = is_not_ok(systemctl.reset_failed_unit(*modules)) + elif command in ["__restart_unit"]: + exitcode = is_not_ok(systemctl.restart_unit(*modules)) + elif command in ["__start_unit"]: + exitcode = is_not_ok(systemctl.start_unit(*modules)) + elif command in ["__stop_unit"]: + exitcode = is_not_ok(systemctl.stop_unit(*modules)) + elif command in ["__try_restart_unit"]: + exitcode = is_not_ok(systemctl.try_restart_unit(*modules)) + elif command in ["__test_start_unit"]: + systemctl.test_start_unit(*modules) + elif command in ["__unmask_unit"]: + exitcode = is_not_ok(systemctl.unmask_unit(*modules)) + elif command in ["__show_unit_items"]: + print_str_list_list(list(systemctl.show_unit_items(*modules))) + else: + logg.error("Unknown operation %s", command) + return EXIT_FAILURE + # + exitcode |= systemctl.error + return exitcode + +if __name__ == "__main__": + import optparse + _o = optparse.OptionParser("%prog [options] command [name...]", + epilog="use 'help' command for more information") + _o.add_option("--version", action="store_true", + help="Show package version") + _o.add_option("--system", action="store_true", default=False, + help="Connect to system manager (default)") # overrides --user + _o.add_option("--user", action="store_true", default=_user_mode, + help="Connect to user service manager") + # _o.add_option("-H", "--host", metavar="[USER@]HOST", + # help="Operate on remote host*") + # _o.add_option("-M", "--machine", metavar="CONTAINER", + # help="Operate on local container*") + _o.add_option("-t", "--type", metavar="TYPE", action="append", dest="only_type", default=_only_type, + help="List units of a particual type") + _o.add_option("--state", metavar="STATE", action="append", dest="only_state", default=_only_state, + help="List units with particular LOAD or SUB or ACTIVE state") + _o.add_option("-p", "--property", metavar="NAME", action="append", dest="only_property", default=_only_property, + help="Show only properties by this name") + _o.add_option("--what", metavar="TYPE", action="append", dest="only_what", default=_only_what, + help="Defines the service directories to be cleaned (configuration, state, cache, logs, runtime)") + _o.add_option("-a", "--all", action="store_true", dest="show_all", default=_show_all, + help="Show all loaded units/properties, including dead empty ones. To list all units installed on the system, use the 'list-unit-files' command instead") + _o.add_option("-l", "--full", action="store_true", default=_full, + help="Don't ellipsize unit names on output (never ellipsized)") + _o.add_option("--reverse", action="store_true", + help="Show reverse dependencies with 'list-dependencies' (ignored)") + _o.add_option("--job-mode", metavar="MODE", + help="Specify how to deal with already queued jobs, when queuing a new job (ignored)") + _o.add_option("--show-types", action="store_true", + help="When showing sockets, explicitly show their type (ignored)") + _o.add_option("-i", "--ignore-inhibitors", action="store_true", + help="When shutting down or sleeping, ignore inhibitors (ignored)") + _o.add_option("--kill-who", metavar="WHO", + help="Who to send signal to (ignored)") + _o.add_option("-s", "--signal", metavar="SIG", + help="Which signal to send (ignored)") + _o.add_option("--now", action="store_true", default=_now, + help="Start or stop unit in addition to enabling or disabling it") + _o.add_option("-q", "--quiet", action="store_true", default=_quiet, + help="Suppress output") + _o.add_option("--no-block", action="store_true", default=False, + help="Do not wait until operation finished (ignored)") + _o.add_option("--no-legend", action="store_true", default=_no_legend, + help="Do not print a legend (column headers and hints)") + _o.add_option("--no-wall", action="store_true", default=False, + help="Don't send wall message before halt/power-off/reboot (ignored)") + _o.add_option("--no-reload", action="store_true", default=_no_reload, + help="Don't reload daemon after en-/dis-abling unit files") + _o.add_option("--no-ask-password", action="store_true", default=_no_ask_password, + help="Do not ask for system passwords") + # _o.add_option("--global", action="store_true", dest="globally", default=_globally, + # help="Enable/disable unit files globally") # for all user logins + # _o.add_option("--runtime", action="store_true", + # help="Enable unit files only temporarily until next reboot") + _o.add_option("-f", "--force", action="store_true", default=_force, + help="When enabling unit files, override existing symblinks / When shutting down, execute action immediately") + _o.add_option("--preset-mode", metavar="TYPE", default=_preset_mode, + help="Apply only enable, only disable, or all presets [%default]") + _o.add_option("--root", metavar="PATH", default=_root, + help="Enable unit files in the specified root directory (used for alternative root prefix)") + _o.add_option("-n", "--lines", metavar="NUM", + help="Number of journal entries to show") + _o.add_option("-o", "--output", metavar="CAT", + help="change journal output mode [short, ..., cat] (ignored)") + _o.add_option("--plain", action="store_true", + help="Print unit dependencies as a list instead of a tree (ignored)") + _o.add_option("--no-pager", action="store_true", + help="Do not pipe output into pager (mostly ignored)") + _o.add_option("--no-warn", action="store_true", + help="Do not generate certain warnings (ignored)") + # + _o.add_option("-c", "--config", metavar="NAME=VAL", action="append", default=[], + help="..override internal variables (InitLoopSleep,SysInitTarget) {%default}") + _o.add_option("-e", "--extra-vars", "--environment", metavar="NAME=VAL", action="append", default=[], + help="..override settings in the syntax of 'Environment='") + _o.add_option("-v", "--verbose", action="count", default=0, + help="..increase debugging information level") + _o.add_option("-4", "--ipv4", action="store_true", default=False, + help="..only keep ipv4 localhost in /etc/hosts") + _o.add_option("-6", "--ipv6", action="store_true", default=False, + help="..only keep ipv6 localhost in /etc/hosts") + _o.add_option("-1", "--init", action="store_true", default=False, + help="..keep running as init-process (default if PID 1)") + opt, args = _o.parse_args() + logging.basicConfig(level = max(0, logging.FATAL - 10 * opt.verbose)) + logg.setLevel(max(0, logging.ERROR - 10 * opt.verbose)) + # + _extra_vars = opt.extra_vars + _force = opt.force + _full = opt.full + _log_lines = opt.lines + _no_pager = opt.no_pager + _no_reload = opt.no_reload + _no_legend = opt.no_legend + _no_ask_password = opt.no_ask_password + _now = opt.now + _preset_mode = opt.preset_mode + _quiet = opt.quiet + _root = opt.root + _show_all = opt.show_all + _only_state = opt.only_state + _only_type = opt.only_type + _only_property = opt.only_property + _only_what = opt.only_what + # being PID 1 (or 0) in a container will imply --init + _pid = os.getpid() + _init = opt.init or _pid in [1, 0] + _user_mode = opt.user + if os.geteuid() and _pid in [1, 0]: + _user_mode = True + if opt.system: + _user_mode = False # override --user + # + for setting in opt.config: + nam, val = setting, "1" + if "=" in setting: + nam, val = setting.split("=", 1) + elif nam.startswith("no-") or nam.startswith("NO-"): + nam, val = nam[3:], "0" + elif nam.startswith("No") or nam.startswith("NO"): + nam, val = nam[2:], "0" + if nam in globals(): + old = globals()[nam] + if old is False or old is True: + logg.debug("yes %s=%s", nam, val) + globals()[nam] = (val in ("true", "True", "TRUE", "yes", "y", "Y", "YES", "1")) + logg.debug("... _show_all=%s", _show_all) + elif isinstance(old, float): + logg.debug("num %s=%s", nam, val) + globals()[nam] = float(val) + logg.debug("... MinimumYield=%s", MinimumYield) + elif isinstance(old, int): + logg.debug("int %s=%s", nam, val) + globals()[nam] = int(val) + logg.debug("... InitLoopSleep=%s", InitLoopSleep) + elif isinstance(old, basestring): + logg.debug("str %s=%s", nam, val) + globals()[nam] = val.strip() + logg.debug("... SysInitTarget=%s", SysInitTarget) + elif isinstance(old, list): + logg.debug("str %s+=[%s]", nam, val) + globals()[nam] += val.strip().split(",") + logg.debug("... _extra_vars=%s", _extra_vars) + else: + logg.warning("(ignored) unknown target type -c '%s' : %s", nam, type(old)) + else: + logg.warning("(ignored) unknown target config -c '%s' : no such variable", nam) + # + systemctl_debug_log = os_path(_root, expand_path(SYSTEMCTL_DEBUG_LOG, not _user_mode)) + systemctl_extra_log = os_path(_root, expand_path(SYSTEMCTL_EXTRA_LOG, not _user_mode)) + if os.access(systemctl_extra_log, os.W_OK): + loggfile = logging.FileHandler(systemctl_extra_log) + loggfile.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) + logg.addHandler(loggfile) + logg.setLevel(max(0, logging.INFO - 10 * opt.verbose)) + if os.access(systemctl_debug_log, os.W_OK): + loggfile = logging.FileHandler(systemctl_debug_log) + loggfile.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) + logg.addHandler(loggfile) + logg.setLevel(logging.DEBUG) + # + print_begin(sys.argv, args) + # + systemctl = Systemctl() + if opt.version: + args = ["version"] + if not args: + if _init: + args = ["default"] + else: + args = ["list-units"] + print_begin2(args) + command = args[0] + modules = args[1:] + try: + modules.remove("service") + except ValueError: + pass + if opt.ipv4: + systemctl.force_ipv4() + elif opt.ipv6: + systemctl.force_ipv6() + sys.exit(run(command, *modules)) diff --git a/battybirdnet-pi/rootfs/helpers/timedatectl b/battybirdnet-pi/rootfs/helpers/timedatectl new file mode 100644 index 000000000..482f0d75b --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/timedatectl @@ -0,0 +1,72 @@ +#!/bin/bash + +# Function to show the current timezone, with two alternative methods +show_timezone() { + # Check if the /etc/timezone file exists + if [ -f /etc/timezone ]; then + timezone=$(cat /etc/timezone) + elif [ -f /etc/localtime ]; then + timezone=$(readlink /etc/localtime) + timezone=${timezone/\/usr\/share\/zoneinfo\//} + else + timezone="Cannot determine timezone." + fi + echo "$timezone" +} + +# Function to set the timezone +set_timezone() { + new_timezone="$1" + echo "$new_timezone" | sudo tee /etc/timezone >/dev/null + sudo ln -sf /usr/share/zoneinfo/"$new_timezone" /etc/localtime + if [ -f /etc/environment ]; then sudo sed -i "/TZ/c\TZ=$new_timezone" /etc/environment; fi + if [ -d /var/run/s6/container_environment ]; then echo "$new_timezone" | sudo tee /var/run/s6/container_environment/TZ > /dev/null; fi + echo "$new_timezone" +} + +# Main script +case "$1" in + "set-ntp") + case "$2" in + "false") + sudo systemctl stop systemd-timesyncd + sudo systemctl disable systemd-timesyncd + echo "NTP disabled" + ;; + "true") + sudo systemctl start systemd-timesyncd + sudo systemctl enable systemd-timesyncd + echo "NTP enabled" + ;; + *) + echo "Invalid argument for set-ntp. Use 'false' or 'true'." + ;; + esac + ;; + "show") + show_timezone + ;; + "set-timezone") + set_timezone "$2" + ;; + *) + # Get values + local_time="$(date)" + utc_time="$(date -u)" + time_zone="$(show_timezone)" + # Check if NTP is used + if sudo systemctl status systemd-timesyncd | grep -q " active"; then + ntp_status="yes" + ntp_service="active" + else + ntp_status="no" + ntp_service="inactive" + fi + # Print the information + echo "Local time: $local_time" + echo "Universal time: $utc_time" + echo "Time zone: $time_zone" + echo "Network time on: $ntp_status" + echo "NTP service: $ntp_service" + ;; +esac diff --git a/battybirdnet-pi/rootfs/helpers/views.add b/battybirdnet-pi/rootfs/helpers/views.add new file mode 100644 index 000000000..8514078a0 --- /dev/null +++ b/battybirdnet-pi/rootfs/helpers/views.add @@ -0,0 +1,27 @@ + if($_GET['view'] == "Converted"){ + ensure_authenticated(); + if(isset($_GET['species']) && isset($_GET['add'])){ + $file = './scripts/convert_species_list.txt'; + $str = file_get_contents("$file"); + $str = preg_replace("/(^[\r\n]*|[\r\n]+)[\s\t]*[\r\n]+/", "\n", $str); + file_put_contents("$file", "$str"); + // Write $_GET['species'] to the file + file_put_contents("./scripts/convert_species_list.txt", htmlspecialchars_decode($_GET['species'], ENT_QUOTES)."\n", FILE_APPEND); + } elseif (isset($_GET['species']) && isset($_GET['del'])){ + $file = './scripts/convert_species_list.txt'; + $str = file_get_contents("$file"); + $str = preg_replace('/^\h*\v+/m', '', $str); + file_put_contents("$file", "$str"); + foreach($_GET['species'] as $selectedOption) { + $content = file_get_contents("./scripts/convert_species_list.txt"); + $newcontent = str_replace($selectedOption, "", "$content"); + $newcontent = str_replace(htmlspecialchars_decode($selectedOption, ENT_QUOTES), "", "$content"); + file_put_contents("./scripts/convert_species_list.txt", "$newcontent"); + } + $file = './scripts/convert_species_list.txt'; + $str = file_get_contents("$file"); + $str = preg_replace('/^\h*\v+/m', '', $str); + file_put_contents("$file", "$str"); + } + include('./scripts/convert_list.php'); + } diff --git a/battybirdnet-pi/stats.png b/battybirdnet-pi/stats.png new file mode 100644 index 0000000000000000000000000000000000000000..41b39cf941c5aa1f0231ae53a2638c31c26fb779 GIT binary patch literal 1768 zcmXX`dpO(I8~>s!+7VQz5kcEk>93-rr34K{C~+C8WlB4j)@^C1TQbQMHPK=&eN&&(8Cn&wI{$-uL}{&iUi~?Cy$II-z+2001Q? zN4swUKt?QG7bwU{QRilczcg^(-Nnlu1VL$pN~In-CIFB?CIA8g0TTuE_5wDK=0T<| zKteG|;FUlU830fu03?A#aD;a0lC%^xHzcjo)YLRFF~MLkAt50|BC)Wru(7d`$z%=; z3@j}zNhw6e#@T}Gnu%qRw`dLo1vANDb61@}0FDDvHXulf#B=0x2(r(=domP?l}Z7C z!Dp<`z>!1{V6j2B_dEnZ653by9Sx{ z1Q109bvrsbfRgB_@6wMRM-l6IHyQwx|2Ud5`TtXf0l*jGPIfk4@s#!A@KvB z51wBSErr@Vj7*q7jb=@@zLlpPb2?cVEF*34-{}1_0X$~(^m(tTxOcKtAMU_3m+aE_ zo=k5ah7reSA#H#D=(mX$jW3_W^NHL3q2!mVIA5xH(X(@NHO=74b4Y{J4zcGwLG_v`rp7Af|=Y}6v z>2VC%|Kj|V%cx!31x?JT`htL9dNa{uqay6kc|jxBI=LRyw@#GT84sy?{k2uZYR=Wm zoPggJFuNn2Kls2nNc8v53#=6$ILD38AjkG^{_<6y`ju>$>~2ZPY319hXyJIV83&(| z$xZ2%qgP|=Gc2>q@H%SGWB7boqwcQDe+h~g2NYJSbJF0llfDs2%AEF#j|T0nEA}Bz zZouvcv&dH?TrA}1g0to(XB~f4VQWI^-<&;fcJ>o{zvh~oqfk}P!zCT}f@_Nl0j}oi zYe|oXs6E?1_J&zRC-A_b_JvqBfBQBAqD!D9IMjO8ToVS!t9G2@^ZHxF z-CJXhf1srNzYcF+^cbmZdxh!Pl<_;UTaq-za%Oo||Gp7+JdaUUWS*O!R&(!JuM$ z)vB~hiZ~uT=)#Isk{g5;s{f&1t?3TI+<*S($CQik#Bx!pZ1MYmsiG|8FfH&(wlzFU zxHEJO{b`;PL)_d9wJs0;S}ln?Yq+3b`yy6@&Hg^F592RR=<8UW_~3~dWfz8Ot82Ry zV0rY!B19qMR5v52HaAKSC%zZo@Ham7207N}qE1a~=hLmv9&m<69WwSUmN<>{)A+x| zZU|Nx0$E;lBbS%9oV?@HioH~|;^#w9=?FTFXyW?K&tJ%k`b32Hx5Q1Z9!Qj)BGewM zc3dX67!8p@*Mokj@nwF)zvC;R`lb$fr=I?pt;|;M1& literal 0 HcmV?d00001 diff --git a/battybirdnet-pi/updater.json b/battybirdnet-pi/updater.json new file mode 100644 index 000000000..b615690df --- /dev/null +++ b/battybirdnet-pi/updater.json @@ -0,0 +1,8 @@ +{ + "last_update": "22-06-2024", + "repository": "alexbelgium/hassio-addons", + "slug": "battybattybirdnet-pi", + "source": "github", + "upstream_repo": "rdz-oss/Battybattybirdnet-pi", + "upstream_version": "0.1" +} diff --git a/birdnet-pi/updater.json b/birdnet-pi/updater.json index 8b3a6507f..da5f4a6ce 100644 --- a/birdnet-pi/updater.json +++ b/birdnet-pi/updater.json @@ -1,7 +1,7 @@ { "last_update": "22-06-2024", "repository": "alexbelgium/hassio-addons", - "slug": "birdnet-go", + "slug": "birdnet-pi", "source": "github", "upstream_repo": "Nachtzuster/BirdNET-Pi", "upstream_version": "0.1"