Files
ascoetpi/dietpi-software
2026-01-04 13:22:08 +00:00

16078 lines
660 KiB
Bash

#!/bin/bash
{
#////////////////////////////////////
# DietPi Software
#
#////////////////////////////////////
# Created by Daniel Knight / daniel.knight@dietpi.com / dietpi.com
#
#////////////////////////////////////
#
# Info:
# - Location: /boot/dietpi/dietpi-software
# - Installs "ready to run" software with optimisations unique to the device.
# - Generates and uses /boot/dietpi/.installed listing installed software.
USAGE='
Usage: dietpi-software [<command> [<software_id>...]]
Available commands:
<empty> Interactive menu to install or uninstall software
install <software_id>... Install each software given by space-separated list of IDs
reinstall <software_id>... Reinstall each software given by space-separated list of IDs
uninstall <software_id>... Uninstall each software given by space-separated list of IDs
list [--machine-readable] Print a list with IDs and info for all available software titles
free Print an unused software ID, free for a new software implementation
' #////////////////////////////////////
# Import DietPi-Globals ---------------------------------------------------------------
. /boot/dietpi/func/dietpi-globals
readonly G_PROGRAM_NAME='DietPi-Software'
if [[ $1 != 'list' ]]
then
G_CHECK_ROOT_USER "$@"
G_CHECK_ROOTFS_RW
G_INIT
fi
# Import DietPi-Globals ---------------------------------------------------------------
[[ $1 == 'list' && $2 == '--machine-readable' ]] && MACHINE_READABLE=1 || MACHINE_READABLE=
#/////////////////////////////////////////////////////////////////////////////////////
# Install states file
#/////////////////////////////////////////////////////////////////////////////////////
Write_InstallFileList()
{
# Update webserver stack meta install states
aSOFTWARE_INSTALL_STATE[75]=0 aSOFTWARE_INSTALL_STATE[76]=0
aSOFTWARE_INSTALL_STATE[78]=0 aSOFTWARE_INSTALL_STATE[79]=0
aSOFTWARE_INSTALL_STATE[81]=0 aSOFTWARE_INSTALL_STATE[82]=0
if (( ${aSOFTWARE_INSTALL_STATE[89]} == 2 ))
then
# Apache
if (( ${aSOFTWARE_INSTALL_STATE[83]} == 2 ))
then
(( ${aSOFTWARE_INSTALL_STATE[87]} == 2 )) && aSOFTWARE_INSTALL_STATE[75]=2 # SQLite: LASP
(( ${aSOFTWARE_INSTALL_STATE[88]} == 2 )) && aSOFTWARE_INSTALL_STATE[76]=2 # MariaDB: LAMP
# Nginx
elif (( ${aSOFTWARE_INSTALL_STATE[85]} == 2 ))
then
(( ${aSOFTWARE_INSTALL_STATE[87]} == 2 )) && aSOFTWARE_INSTALL_STATE[78]=2 # SQLite: LESP
(( ${aSOFTWARE_INSTALL_STATE[88]} == 2 )) && aSOFTWARE_INSTALL_STATE[79]=2 # MariaDB: LEMP
# Lighttpd
elif (( ${aSOFTWARE_INSTALL_STATE[84]} == 2 ))
then
(( ${aSOFTWARE_INSTALL_STATE[87]} == 2 )) && aSOFTWARE_INSTALL_STATE[81]=2 # SQLite: LLSP
(( ${aSOFTWARE_INSTALL_STATE[88]} == 2 )) && aSOFTWARE_INSTALL_STATE[82]=2 # MariaDB: LLMP
fi
fi
# Save installed states
local i install_states
for i in "${!aSOFTWARE_NAME[@]}"
do
# Don't save pending and uninstalled states (-1/0/1)
if (( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 ))
then
install_states+="aSOFTWARE_INSTALL_STATE[$i]=2
"
# Store DietPi-RAMlog and Dropbear uninstalled state as well, as it is initialised as installed matching our image defaults
elif (( $i == 103 || $i == 104 ))
then
install_states+="aSOFTWARE_INSTALL_STATE[$i]=0
"
fi
done
# Save logging choice
install_states+="INDEX_LOGGING=$INDEX_LOGGING"
echo "$install_states" > /boot/dietpi/.installed
}
Read_InstallFileList()
{
if [[ -f '/boot/dietpi/.installed' ]]
then
# shellcheck disable=SC1091
if [[ $MACHINE_READABLE ]]
then
. /boot/dietpi/.installed
else
G_EXEC_DESC='Reading database' G_EXEC . /boot/dietpi/.installed
fi
else
# Assure that the file exists to allow choice/preference selections on first run: https://github.com/MichaIng/DietPi/issues/5080
>> /boot/dietpi/.installed
fi
}
Check_Net_and_Time_sync()
{
# Check network connectivity and sync system clock
G_CHECK_NET
/boot/dietpi/func/run_ntpd
}
#/////////////////////////////////////////////////////////////////////////////////////
# Installation system
#/////////////////////////////////////////////////////////////////////////////////////
# Flag to trigger Run_Installations()
GOSTARTINSTALL=0
# Flag to skip APT update in Run_Installations(), set by DietPi-Automation_Pre()
SKIP_APT_UPDATE=0
# Logging choice index
INDEX_LOGGING=-1
# Array to collect all installed services to be enabled after installs have finished
aENABLE_SERVICES=()
# Since no automated reboot is done anymore after installs, collect services to start manually, when not controlled by DietPi-Services
aSTART_SERVICES=()
# Global password for software installs
GLOBAL_PW=
Update_Global_Pw()
{
# Loop until password is valid or dietpi-set_software fails
while :
do
# Read encrypted password
if [[ -f '/var/lib/dietpi/dietpi-software/.GLOBAL_PW.bin' ]]
then
# In case of error, assure empty password to fallback to default
GLOBAL_PW=$(openssl enc -d -a -md sha256 -aes-256-cbc -iter 10000 -salt -pass pass:'DietPiRocks!' -in /var/lib/dietpi/dietpi-software/.GLOBAL_PW.bin) || GLOBAL_PW=
# Return on valid password
[[ $GLOBAL_PW && $GLOBAL_PW != 'dietpi' ]] && return 0
G_DIETPI-NOTIFY 1 'Global software password failed to be read, is empty, or still the default. Asking to set a new one ...'
fi
# Apply default password as fallback if empty
[[ $GLOBAL_PW ]] || GLOBAL_PW='dietpi'
# In case ask for and apply new valid password
/boot/dietpi/func/dietpi-set_software password software check || return 1
done
}
# Total physical system RAM: Used to calculate percentage based value for software cache limits, e.g.: OPcache/APCu
readonly RAM_PHYS=$(free -m | mawk '/^Mem:/{print $2;exit}')
# Total RAM + swap space: Used to estimate whether the swap file size needs to be increased.
readonly RAM_TOTAL=$(free -tm | mawk '/^Total:/{print $2;exit}')
# Whether to restart Deluge web UI once, required on fresh installs for auto-connection to work, more precisely a little delay between daemon and web UI is required
RESTART_DELUGE_WEB=0
# PHP version
case $G_DISTRO in
6) PHP_VERSION='7.4';;
7) PHP_VERSION='8.2';;
*) PHP_VERSION='8.4';;
esac
# Available for [$software_id,$G_*] 2D array
declare -A aSOFTWARE_AVAIL_G_HW_MODEL
declare -A aSOFTWARE_AVAIL_G_HW_ARCH
declare -A aSOFTWARE_AVAIL_G_DISTRO
# ToDo: On RPi 4, the 64-bit kernel is now used by default, without "arm_64bit=1" set: https://forums.raspberrypi.com/viewtopic.php?p=2088935#p2088935
# - We could set "arm_64bit=0", but for now lets assure that 32-bit software is installed and see how it goes. This enables general support for RPi with 64-bit kernel running 32-bit userland.
# - Also set a little flag here for the "dietpi-software list" command to correctly show that a software title is disabled because of the userland architecture, not because of the kernel architecture.
RPI_64KERNEL_32OS=
[[ $G_HW_MODEL == [2-9] && $G_HW_ARCH == 3 && $(dpkg --print-architecture) == 'armhf' ]] && G_HW_ARCH=2 G_HW_ARCH_NAME='armv7l' RPI_64KERNEL_32OS='32-bit image'
# Generate arrays for all available software titles
Software_Arrays_Init()
{
[[ $MACHINE_READABLE ]] || G_DIETPI-NOTIFY -2 'Initialising database'
#--------------------------------------------------------------------------------
# Software categories
# NB: Unique IDs, do not re-arrange or re-order!
#--------------------------------------------------------------------------------
readonly aSOFTWARE_CATEGORIES=(
# Use "-1" to hide software title from menu
'●─ Desktops ' #0
'●─ Remote Desktop ' #1
'●─ Media Systems ' #2
'●─ BitTorrent & Download ' #3
'●─ Cloud & Backup ' #4
'●─ Gaming & Emulation ' #5
'●─ Social & Search ' #6
'●─ Camera & Surveillance ' #7
'●─ System Stats & Management ' #8
'●─ Remote Access ' #9
'●─ Hardware Projects ' #10
'●─ System Security ' #11
'●─ Webserver Stacks ' #12
'●─ DNS Servers ' #13
'●─ File Servers ' #14
'●─ VPN Servers ' #15
'●─ Advanced Networking ' #16
'●─ Home Automation ' #17
'●─ Printing ' #18
'●─ Distributed Projects ' #19
'●─ SSH Clients ' #20
'●─ File Server Clients ' #21
'●─ System ' #22
'●─ Databases & Data Stores ' #23
'●─ Development & Programming ' #24
'●─ Desktop Utilities ' #25
)
#--------------------------------------------------------------------------------
# Software items
#--------------------------------------------------------------------------------
# Before adding, please check 'dietpi-software free' to list free IDs for use.
# Assign unique ID to each item
local software_id i
# Desktops
#--------------------------------------------------------------------------------
software_id=23
aSOFTWARE_NAME[$software_id]='LXDE'
aSOFTWARE_DESC[$software_id]='ultra lightweight desktop'
aSOFTWARE_CATX[$software_id]=0
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#lxde'
aSOFTWARE_DEPS[$software_id]='5 6 browser'
#------------------
software_id=24
aSOFTWARE_NAME[$software_id]='MATE'
aSOFTWARE_DESC[$software_id]='desktop enviroment'
aSOFTWARE_CATX[$software_id]=0
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#mate'
aSOFTWARE_DEPS[$software_id]='5 6 browser'
#------------------
software_id=25
aSOFTWARE_NAME[$software_id]='Xfce'
aSOFTWARE_DESC[$software_id]='lightweight desktop'
aSOFTWARE_CATX[$software_id]=0
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#xfce'
aSOFTWARE_DEPS[$software_id]='5 6 browser'
#------------------
software_id=26
aSOFTWARE_NAME[$software_id]='GNUstep'
aSOFTWARE_DESC[$software_id]='lightweight desktop based on OpenStep'
aSOFTWARE_CATX[$software_id]=0
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#gnustep'
aSOFTWARE_DEPS[$software_id]='5 6 browser'
#------------------
software_id=173
aSOFTWARE_NAME[$software_id]='LXQt'
aSOFTWARE_DESC[$software_id]='lightweight desktop'
aSOFTWARE_CATX[$software_id]=0
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#lxqt'
aSOFTWARE_DEPS[$software_id]='5 6 browser'
# Remote Desktop
#--------------------------------------------------------------------------------
software_id=28
aSOFTWARE_NAME[$software_id]='TigerVNC Server'
aSOFTWARE_DESC[$software_id]='desktop for remote connection'
aSOFTWARE_CATX[$software_id]=1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/remote_desktop/#tigervnc-server'
aSOFTWARE_DEPS[$software_id]='desktop'
aSOFTWARE_CONFLICTS[$software_id]='120'
#------------------
software_id=29
aSOFTWARE_NAME[$software_id]='XRDP'
aSOFTWARE_DESC[$software_id]='remote desktop protocol (rdp) server'
aSOFTWARE_CATX[$software_id]=1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/remote_desktop/#xrdp'
aSOFTWARE_DEPS[$software_id]='desktop'
#------------------
software_id=30
aSOFTWARE_NAME[$software_id]='NoMachine'
aSOFTWARE_DESC[$software_id]='multi-platform server and client access'
aSOFTWARE_CATX[$software_id]=1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/remote_desktop/#nomachine'
aSOFTWARE_DEPS[$software_id]='desktop'
# - RISC-V: https://downloads.nomachine.com/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=120
aSOFTWARE_NAME[$software_id]='RealVNC Server'
aSOFTWARE_DESC[$software_id]='desktop for remote connection'
aSOFTWARE_CATX[$software_id]=1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/remote_desktop/#realvnc-server'
aSOFTWARE_DEPS[$software_id]='desktop'
aSOFTWARE_CONFLICTS[$software_id]='28'
# RPi only (archive.raspberrypi.com repo, libraspberrypi0 dependency, license)
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=12
aSOFTWARE_NAME[$software_id]='RustDesk Server'
aSOFTWARE_DESC[$software_id]='open-source remote desktop server, written in Rust'
aSOFTWARE_CATX[$software_id]=1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/remote_desktop/#rustdesk-server'
# - RISC-V & ARMv6: https://github.com/rustdesk/rustdesk-server/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
# Media Systems
#--------------------------------------------------------------------------------
software_id=31
aSOFTWARE_NAME[$software_id]='Kodi'
aSOFTWARE_DESC[$software_id]='The media centre for Linux'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#kodi'
aSOFTWARE_DEPS[$software_id]='5 152'
#------------------
software_id=32
aSOFTWARE_NAME[$software_id]='ympd'
aSOFTWARE_DESC[$software_id]='lightweight web interface music player for mpd'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#ympd'
aSOFTWARE_DEPS[$software_id]='128'
#------------------
software_id=148
aSOFTWARE_NAME[$software_id]='myMPD'
aSOFTWARE_DESC[$software_id]='fork of ympd with improved features'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#mympd'
aSOFTWARE_DEPS[$software_id]='128'
# - RISC-V: https://download.opensuse.org/repositories/home:/jcorporation/Debian_Testing/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
# - ARMv6 Trixie: No Raspbian_13 suite available and Bookworm package depends on libflac12
(( $G_HW_ARCH == 1 && $G_DISTRO > 7 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=119
aSOFTWARE_NAME[$software_id]='CAVA'
aSOFTWARE_DESC[$software_id]='Console audio visualisation for MPD'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#cava'
aSOFTWARE_DEPS[$software_id]='128'
#------------------
software_id=33
aSOFTWARE_NAME[$software_id]='Airsonic-Advanced'
aSOFTWARE_DESC[$software_id]='Web interface media streaming server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#airsonic'
aSOFTWARE_DEPS[$software_id]='5 7 196'
#------------------
software_id=35
aSOFTWARE_NAME[$software_id]='Lyrion Music Server'
aSOFTWARE_DESC[$software_id]='formerly Logitech Media Server and Squeezebox Server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#lyrion-music-server'
# - RISC-V: https://lms-community.github.io/lms-server-repository/stable.xml
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=36
aSOFTWARE_NAME[$software_id]='Squeezelite'
aSOFTWARE_DESC[$software_id]='audio player for LMS & Squeezebox'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#squeezelite'
aSOFTWARE_DEPS[$software_id]='5'
#------------------
software_id=37
aSOFTWARE_NAME[$software_id]='Shairport Sync'
aSOFTWARE_DESC[$software_id]='AirPlay audio player with multiroom sync'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#shairport-sync'
aSOFTWARE_DEPS[$software_id]='5 152'
#------------------
software_id=39
aSOFTWARE_NAME[$software_id]='ReadyMedia'
aSOFTWARE_DESC[$software_id]='(MiniDLNA) media streaming server (DLNA, UPnP)'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#readymedia'
#------------------
software_id=40
aSOFTWARE_NAME[$software_id]='Ampache'
aSOFTWARE_DESC[$software_id]='web interface media streaming server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#ampache'
aSOFTWARE_DEPS[$software_id]='5 7 88 89 webserver'
#------------------
software_id=41
aSOFTWARE_NAME[$software_id]='Emby'
aSOFTWARE_DESC[$software_id]='web interface media streaming server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#emby'
# - ARMv6: https://github.com/MichaIng/DietPi/issues/534#issuecomment-416405968
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - RISC-V: https://github.com/MediaBrowser/Emby.Releases/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=42
aSOFTWARE_NAME[$software_id]='Plex Media Server'
aSOFTWARE_DESC[$software_id]='web interface media streaming server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#plex-media-server'
# - ARMv6: https://github.com/MichaIng/DietPi/issues/648
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - RISC-V: https://www.plex.tv/media-server-downloads/?cat=computer&plat=linux#plex-media-server
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=43
aSOFTWARE_NAME[$software_id]='Mumble Server'
aSOFTWARE_DESC[$software_id]='(Murmur) Low latency encrypted VoIP server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#murmur'
#------------------
software_id=118
aSOFTWARE_NAME[$software_id]='Mopidy'
aSOFTWARE_DESC[$software_id]='Web interface music & radio player'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#mopidy'
aSOFTWARE_DEPS[$software_id]='5 130'
#------------------
software_id=121
aSOFTWARE_NAME[$software_id]='Roon Bridge'
aSOFTWARE_DESC[$software_id]='Turns device into Roon capable audio player'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#roon-bridge'
aSOFTWARE_DEPS[$software_id]='5'
# - ARMv6
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - RISC-V: https://help.roonlabs.com/portal/en/kb/articles/linux-install
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=124
aSOFTWARE_NAME[$software_id]='NAA Daemon'
aSOFTWARE_DESC[$software_id]='Signalyst Network Audio Adaptor (NAA)'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#naa-daemon'
aSOFTWARE_DEPS[$software_id]='5'
# - RISC-V: https://signalyst.com/bins/naa/linux/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=128
aSOFTWARE_NAME[$software_id]='MPD'
aSOFTWARE_DESC[$software_id]='music player daemon'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DEPS[$software_id]='5 152'
#------------------
software_id=129
aSOFTWARE_NAME[$software_id]='O!MPD'
aSOFTWARE_DESC[$software_id]='Feature-rich, web interface audio player for MPD'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#ompd'
aSOFTWARE_DEPS[$software_id]='88 89 128 195 webserver'
#------------------
software_id=135
aSOFTWARE_NAME[$software_id]='Icecast'
aSOFTWARE_DESC[$software_id]='Shoutcast streaming server (+DarkIce)'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#icecast'
aSOFTWARE_DEPS[$software_id]='5'
#------------------
software_id=143
aSOFTWARE_NAME[$software_id]='Koel'
aSOFTWARE_DESC[$software_id]='web interface audio streamer'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#koel'
aSOFTWARE_DEPS[$software_id]='7 88 89'
#------------------
software_id=146
aSOFTWARE_NAME[$software_id]='Tautulli'
aSOFTWARE_DESC[$software_id]='monitoring and tracking tool for Plex'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#tautulli'
aSOFTWARE_DEPS[$software_id]='17'
#------------------
software_id=154
aSOFTWARE_NAME[$software_id]='Roon Server'
aSOFTWARE_DESC[$software_id]='Roon capable audio player and core'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#roon-server'
aSOFTWARE_DEPS[$software_id]='1 5 7'
# x86_64 only: https://help.roonlabs.com/portal/en/kb/articles/linux-install
(( $G_HW_ARCH == 10 )) || aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,$G_HW_ARCH]=0
#------------------
software_id=159
aSOFTWARE_NAME[$software_id]='Allo GUI full'
aSOFTWARE_DESC[$software_id]='Audiophile web interface with all dependencies'
aSOFTWARE_CATX[$software_id]=-1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/forum/t/dietpi-allo-com-web-gui-image/1523'
aSOFTWARE_DEPS[$software_id]='36 37 88 89 96 128 129 152 160 163 webserver'
# Roon Bridge is not supported on ARMv6 and RISC-V
(( $G_HW_ARCH == 1 || $G_HW_ARCH == 11 )) || aSOFTWARE_DEPS[$software_id]+=' 121'
# Netdata not available on Trixie
(( $G_DISTRO > 7 )) || aSOFTWARE_DEPS[$software_id]+=' 65'
# NAA Daemon not available for RISC-V
(( $G_HW_ARCH == 11 )) || aSOFTWARE_DEPS[$software_id]+=' 124'
#------------------
software_id=160
aSOFTWARE_NAME[$software_id]='Allo GUI'
aSOFTWARE_DESC[$software_id]='Audiophile web interface without dependencies'
aSOFTWARE_CATX[$software_id]=-1
#------------------
software_id=163
aSOFTWARE_NAME[$software_id]='GMediaRender'
aSOFTWARE_DESC[$software_id]='Resource efficient UPnP/DLNA renderer'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#gmediarender'
aSOFTWARE_DEPS[$software_id]='5 152'
#------------------
software_id=167
aSOFTWARE_NAME[$software_id]='Raspotify'
aSOFTWARE_DESC[$software_id]='A Spotify Connect client that mostly Just Works™'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#raspotify'
aSOFTWARE_DEPS[$software_id]='5 152'
# - Bookworm/Trixie on ARMv6 (older package): "/usr/bin/librespot: error while loading shared libraries: ld-linux.so.3: cannot open shared object file: No such file or directory"
(( $G_HW_ARCH == 1 && $G_DISTRO > 6 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=80
aSOFTWARE_NAME[$software_id]='Ubooquity'
aSOFTWARE_DESC[$software_id]='free home server for your comics and ebooks library'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#ubooquity'
aSOFTWARE_DEPS[$software_id]='196'
#------------------
software_id=179
aSOFTWARE_NAME[$software_id]='Komga'
aSOFTWARE_DESC[$software_id]='free and open source comics/mangas media server with web UI'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#komga'
aSOFTWARE_DEPS[$software_id]='196'
#------------------
software_id=86
aSOFTWARE_NAME[$software_id]='Roon Extension Manager'
aSOFTWARE_DESC[$software_id]='manage extensions from within Roon'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#roon-extension-manager'
aSOFTWARE_DEPS[$software_id]='162'
# - ARMv6
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
#------------------
software_id=178
aSOFTWARE_NAME[$software_id]='Jellyfin'
aSOFTWARE_DESC[$software_id]='FOSS web interface media streaming server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#jellyfin'
# - ARMv6: https://github.com/jellyfin/jellyfin/issues/5011
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - RISC-V: https://repo.jellyfin.org/?path=/server/debian/latest-unstable
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
# - ARMv7 Trixie: https://repo.jellyfin.org/?path=/server/debian/latest-stable/armhf
(( $G_HW_ARCH == 2 && $G_DISTRO > 7 )) && aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,2]=0
#------------------
software_id=190
aSOFTWARE_NAME[$software_id]='Beets'
aSOFTWARE_DESC[$software_id]='music organizer and manager'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#beets'
#------------------
software_id=191
aSOFTWARE_NAME[$software_id]='Snapcast Server'
aSOFTWARE_DESC[$software_id]='Multiroom audio server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#snapcast-server'
aSOFTWARE_DEPS[$software_id]='5'
#------------------
software_id=192
aSOFTWARE_NAME[$software_id]='Snapcast Client'
aSOFTWARE_DESC[$software_id]='Multiroom audio client'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#snapcast-client'
aSOFTWARE_DEPS[$software_id]='5'
#------------------
software_id=199
aSOFTWARE_NAME[$software_id]='Spotifyd'
aSOFTWARE_DESC[$software_id]='Open source Spotify client running as UNIX daemon'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#spotifyd'
aSOFTWARE_DEPS[$software_id]='5'
# - RISC-V: No archive: https://github.com/Spotifyd/spotifyd/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=204
aSOFTWARE_NAME[$software_id]='Navidrome'
aSOFTWARE_DESC[$software_id]='Web interface media streaming server'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#navidrome'
aSOFTWARE_DEPS[$software_id]='5 7'
# - RISC-V: https://github.com/navidrome/navidrome/releases/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=212
aSOFTWARE_NAME[$software_id]='Kavita'
aSOFTWARE_DESC[$software_id]='open source comics/mangas/ebooks media server with web reader'
aSOFTWARE_CATX[$software_id]=2
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/media/#kavita'
# - ARMv6: ARM binaries are ARMv7+ Only
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - No RISC-V support yet: https://github.com/Kareadita/Kavita/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
# BitTorrent & Download
#--------------------------------------------------------------------------------
software_id=44
aSOFTWARE_NAME[$software_id]='Transmission'
aSOFTWARE_DESC[$software_id]='BitTorrent server with web interface (C)'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#transmission'
#------------------
software_id=45
aSOFTWARE_NAME[$software_id]='Deluge'
aSOFTWARE_DESC[$software_id]='BitTorrent server with web interface (Python)'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#deluge'
#------------------
software_id=46
aSOFTWARE_NAME[$software_id]='qBittorrent'
aSOFTWARE_DESC[$software_id]='BitTorrent server with web interface (C++)'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#qbittorrent'
#------------------
software_id=107
aSOFTWARE_NAME[$software_id]='rTorrent'
aSOFTWARE_DESC[$software_id]='BitTorrent server (C++) with rutorrent web interface (PHP)'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#rtorrent'
aSOFTWARE_DEPS[$software_id]='89 170 webserver'
#------------------
software_id=116
aSOFTWARE_NAME[$software_id]='Medusa'
aSOFTWARE_DESC[$software_id]='Automatic video library manager for TV shows'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#medusa'
aSOFTWARE_DEPS[$software_id]='170'
#------------------
software_id=132
aSOFTWARE_NAME[$software_id]='Aria2'
aSOFTWARE_DESC[$software_id]='Download manager with web interface'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#arias'
aSOFTWARE_DEPS[$software_id]='87 89 webserver'
#------------------
software_id=139
aSOFTWARE_NAME[$software_id]='SABnzbd'
aSOFTWARE_DESC[$software_id]='NZB download manager'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#sabnzbd'
aSOFTWARE_DEPS[$software_id]='130 170'
#------------------
software_id=144
aSOFTWARE_NAME[$software_id]='Sonarr'
aSOFTWARE_DESC[$software_id]='Automatically download TV shows'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#sonarr'
(( $G_HW_ARCH == 1 )) && aSOFTWARE_DEPS[$software_id]='150'
# - RISC-V: https://github.com/Sonarr/Sonarr/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=145
aSOFTWARE_NAME[$software_id]='Radarr'
aSOFTWARE_DESC[$software_id]='Automatically download movies'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#radarr'
(( $G_HW_ARCH == 1 )) && aSOFTWARE_DEPS[$software_id]='150'
# - RISC-V: https://github.com/Radarr/Radarr/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=106
aSOFTWARE_NAME[$software_id]='Lidarr'
aSOFTWARE_DESC[$software_id]='Automatically download music'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#lidarr'
(( $G_HW_ARCH == 1 )) && aSOFTWARE_DEPS[$software_id]='150'
# - RISC-V: https://github.com/Lidarr/Lidarr/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=180
aSOFTWARE_NAME[$software_id]='Bazarr'
aSOFTWARE_DESC[$software_id]='Automatically download subtitles'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#bazarr'
aSOFTWARE_DEPS[$software_id]='130'
# FFmpeg x86_64 binaries are shipped with Bazarr: https://github.com/morpheus65535/bazarr-binaries/tree/master/bin/Linux/x86_64/ffmpeg
(( $G_HW_ARCH == 10 )) || aSOFTWARE_DEPS[$software_id]+=' 7'
# UnRAR x86_64 and ARMv8 binaries are shipped with Bazarr, ARMv6 needs to use "unar": https://github.com/morpheus65535/bazarr-binaries/tree/master/bin/Linux, https://github.com/morpheus65535/bazarr/issues/2172
(( $G_HW_ARCH == 2 || $G_HW_ARCH == 11 )) && aSOFTWARE_DEPS[$software_id]+=' 170'
#------------------
software_id=147
aSOFTWARE_NAME[$software_id]='Jackett'
aSOFTWARE_DESC[$software_id]='API support for your torrent trackers'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#jackett'
(( $G_HW_ARCH == 1 )) && aSOFTWARE_DEPS[$software_id]='150'
# - RISC-V: https://github.com/Jackett/Jackett/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=149
aSOFTWARE_NAME[$software_id]='NZBGet'
aSOFTWARE_DESC[$software_id]='NZB download manager'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#nzbget'
#------------------
software_id=151
aSOFTWARE_NAME[$software_id]='Prowlarr'
aSOFTWARE_DESC[$software_id]='Indexer manager & proxy for PVR'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#prowlarr'
# - ARMv6
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - RISC-V: https://github.com/Prowlarr/Prowlarr/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=203
aSOFTWARE_NAME[$software_id]='Readarr'
aSOFTWARE_DESC[$software_id]='Ebook and audiobook collection manager'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#readarr'
# - ARMv6
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - RISC-V: https://github.com/Readarr/Readarr/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=155
aSOFTWARE_NAME[$software_id]='HTPC Manager'
aSOFTWARE_DESC[$software_id]='Manage your HTPC from anywhere'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#htpc-manager'
aSOFTWARE_DEPS[$software_id]='17 130'
#------------------
software_id=195
aSOFTWARE_NAME[$software_id]='youtube-dl'
aSOFTWARE_DESC[$software_id]='Download videos from YouTube and other sites (using yt-dlp fork)'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#youtube-dl'
aSOFTWARE_DEPS[$software_id]='7'
#------------------
software_id=169
aSOFTWARE_NAME[$software_id]='LazyLibrarian'
aSOFTWARE_DESC[$software_id]='Ebook and audiobook collection manager'
aSOFTWARE_CATX[$software_id]=3
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/bittorrent/#lazylibrarian'
aSOFTWARE_DEPS[$software_id]='130'
# - ARMv6/7 Bullseye: https://github.com/piwheels/packages/issues/210#issuecomment-3401251677
(( $G_HW_ARCH < 3 && $G_DISTRO < 7 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
# Cloud & Backup
#--------------------------------------------------------------------------------
software_id=47
aSOFTWARE_NAME[$software_id]='ownCloud'
aSOFTWARE_DESC[$software_id]='File sync, sharing and collaboration platform'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#owncloud'
aSOFTWARE_DEPS[$software_id]='88 89 91 webserver'
# - Bookworm/Trixie: No PHP 8.x support yet
(( $G_DISTRO > 6 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=114
aSOFTWARE_NAME[$software_id]='Nextcloud'
aSOFTWARE_DESC[$software_id]='File sync, sharing and collaboration platform'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#nextcloud'
aSOFTWARE_DEPS[$software_id]='88 89 91 webserver'
#------------------
software_id=168
aSOFTWARE_NAME[$software_id]='Nextcloud Talk'
aSOFTWARE_DESC[$software_id]='Video calls with configured Coturn server'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#nextcloud-talk'
aSOFTWARE_DEPS[$software_id]='114'
#------------------
software_id=48
aSOFTWARE_NAME[$software_id]='Pydio'
aSOFTWARE_DESC[$software_id]='Feature-rich backup and sync server'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#pydio'
aSOFTWARE_DEPS[$software_id]='88 89 webserver'
# - Bookworm/Trixie: No support for PHP 8: https://github.com/MichaIng/DietPi/issues/3469
(( $G_DISTRO > 6 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=111
aSOFTWARE_NAME[$software_id]='UrBackup Server'
aSOFTWARE_DESC[$software_id]='Full system backup server'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#urbackup'
# - RISC-V: https://www.urbackup.org/download.html#server_debian
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
# - ARMv6 Trixie: 64-bit time_t transition: https://download.opensuse.org/repositories/home:/uroni/Raspbian_13/
(( $G_HW_ARCH == 1 && $G_DISTRO > 7 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=49
aSOFTWARE_NAME[$software_id]='Gogs'
aSOFTWARE_DESC[$software_id]='Personal Git server with web interface'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#gogs'
aSOFTWARE_DEPS[$software_id]='17 88 0'
aSOFTWARE_CONFLICTS[$software_id]='165 177'
#------------------
software_id=50
aSOFTWARE_NAME[$software_id]='Syncthing'
aSOFTWARE_DESC[$software_id]='Backup and sync server with web interface'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#syncthing'
#------------------
software_id=158
aSOFTWARE_NAME[$software_id]='MinIO'
aSOFTWARE_DESC[$software_id]='S3 compatible distributed object server'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#minio'
# - RISC-V: https://dl.minio.io/server/minio/release/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=161
aSOFTWARE_NAME[$software_id]='FuguHub'
aSOFTWARE_DESC[$software_id]='Lightweight WebDAV cloud with a CMS, album and blog integration'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#fuguhub'
# - ARMv8
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
# - RISC-V: https://fuguhub.com/download.lsp
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=165
aSOFTWARE_NAME[$software_id]='Gitea'
aSOFTWARE_DESC[$software_id]='Git with a cup of tea'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#gitea'
aSOFTWARE_DEPS[$software_id]='17 88 0'
aSOFTWARE_CONFLICTS[$software_id]='49 177'
#------------------
software_id=177
aSOFTWARE_NAME[$software_id]='Forgejo'
aSOFTWARE_DESC[$software_id]='Self-hosted lightweight software forge. Fork of Gitea.'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#forgejo'
aSOFTWARE_DEPS[$software_id]='17 88 0'
aSOFTWARE_CONFLICTS[$software_id]='49 165'
# - RISC-V: https://codeberg.org/forgejo/forgejo/releases
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=183
aSOFTWARE_NAME[$software_id]='vaultwarden'
aSOFTWARE_DESC[$software_id]='Unofficial Bitwarden password manager server written in Rust'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#vaultwarden'
aSOFTWARE_DEPS[$software_id]='87'
#------------------
software_id=198
aSOFTWARE_NAME[$software_id]='File Browser'
aSOFTWARE_DESC[$software_id]='web based file manager'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#filebrowser'
#------------------
software_id=202
aSOFTWARE_NAME[$software_id]='Rclone'
aSOFTWARE_DESC[$software_id]='Utility to sync your files to cloud storages'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#rclone'
#------------------
software_id=209
aSOFTWARE_NAME[$software_id]='Restic'
aSOFTWARE_DESC[$software_id]='Fast, efficient and secure command-line backup program'
aSOFTWARE_CATX[$software_id]=4
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#restic'
# Gaming & Emulation
#--------------------------------------------------------------------------------
software_id=108
aSOFTWARE_NAME[$software_id]='Amiberry'
aSOFTWARE_DESC[$software_id]='Optimised Amiga emulator for multiple platforms'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#amiberry'
aSOFTWARE_DEPS[$software_id]='5'
#------------------
software_id=10
aSOFTWARE_NAME[$software_id]='Amiberry-Lite'
aSOFTWARE_DESC[$software_id]='Optimised Amiga emulator recommended for smaller ARM/RISC-V SBCs'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#amiberry-lite'
aSOFTWARE_DEPS[$software_id]='5'
# - ARMv6: Compile error: "selected processor does not support `ubfx r5,r2,...' in ARM mode"
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
#------------------
software_id=51
aSOFTWARE_NAME[$software_id]='OpenTyrian'
aSOFTWARE_DESC[$software_id]='a classic retro game, addictive'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#opentyrian'
aSOFTWARE_DEPS[$software_id]='5 6'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
# - ARMv8
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
#------------------
software_id=112
aSOFTWARE_NAME[$software_id]='DXX-Rebirth'
aSOFTWARE_DESC[$software_id]='Descent 1/2'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#dxx-rebirth'
aSOFTWARE_DEPS[$software_id]='5'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
# - ARMv8
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
#------------------
software_id=52
aSOFTWARE_NAME[$software_id]='Cuberite'
aSOFTWARE_DESC[$software_id]='Minecraft server with web interface (C++)'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#cuberite'
# - RISC-V: https://cuberite.org/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=53
aSOFTWARE_NAME[$software_id]='MineOS'
aSOFTWARE_DESC[$software_id]='Minecraft servers with web interface (Java/Node.js)'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#mineos'
aSOFTWARE_DEPS[$software_id]='9 17 196'
#------------------
software_id=156
aSOFTWARE_NAME[$software_id]='Steam'
aSOFTWARE_DESC[$software_id]='Valve gaming platform client'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#steam'
aSOFTWARE_DEPS[$software_id]='5 6 desktop'
# Box86 required on ARM
(( $G_HW_ARCH == 2 )) && aSOFTWARE_DEPS[$software_id]+=' 62'
# x86_64 and ARMv7 only
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=164
aSOFTWARE_NAME[$software_id]='Nukkit'
aSOFTWARE_DESC[$software_id]='A nuclear-powered server for Minecraft Pocket Edition'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#nukkit'
aSOFTWARE_DEPS[$software_id]='196'
#------------------
software_id=181
aSOFTWARE_NAME[$software_id]='PaperMC'
aSOFTWARE_DESC[$software_id]='Highly optimised Minecraft server with plugins, written in Java'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#papermc'
aSOFTWARE_DEPS[$software_id]='196'
# Allow unattended installs if EULA has been accepted already
[[ -f '/mnt/dietpi_userdata/papermc/eula.txt' ]] || aSOFTWARE_INTERACTIVE[$software_id]=1
#------------------
software_id=62
aSOFTWARE_NAME[$software_id]='Box86'
aSOFTWARE_DESC[$software_id]='x86 userspace emulation'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#box86'
# ARMv7 only
(( $G_HW_ARCH == 2 )) || aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,$G_HW_ARCH]=0
#------------------
software_id=197
aSOFTWARE_NAME[$software_id]='Box64'
aSOFTWARE_DESC[$software_id]='x86_64 userspace emulation'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#box64'
# ARMv8 and RISC-V only
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,2]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,10]=0
#------------------
software_id=207
aSOFTWARE_NAME[$software_id]='Moonlight (CLI)'
aSOFTWARE_DESC[$software_id]='CLI game streaming client for Sunshine and NVIDIA GameStream'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#moonlight-cli'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
# - ARMv6: https://github.com/moonlight-stream/moonlight-embedded/issues/832
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - Trixie on ARMv7: There is no Trixie suite yet, and the Bookworm packages depend on libssl3. On Trixie, libssl3t64 is provided instead, but it "provides" libssl3 for 64-bit only. The 64-bit time_t transition to prevent year 2038 problems is relevant for 32-bit systems only. So for 64-bit systems, libssl3t64 equals libssl3, and the package name has been changed only to align with the 32-bit one.
(( $G_DISTRO > 7 && $G_HW_ARCH < 3 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=208
aSOFTWARE_NAME[$software_id]='Moonlight (GUI)'
aSOFTWARE_DESC[$software_id]='GUI game streaming client for Sunshine and NVIDIA GameStream'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#moonlight-gui'
# - ARMv6: https://github.com/moonlight-stream/moonlight-embedded/issues/832
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - x86_64
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,10]=0
#------------------
software_id=11
aSOFTWARE_NAME[$software_id]='GZDoom'
aSOFTWARE_DESC[$software_id]='Modder-friendly OpenGL and Vulkan source port based on the DOOM engine'
aSOFTWARE_CATX[$software_id]=5
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/gaming/#gzdoom'
aSOFTWARE_DEPS[$software_id]='5'
# - ARMv6/7: https://github.com/ZDoom/gzdoom/commit/1dedcee
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,2]=0
# Social & Search
#--------------------------------------------------------------------------------
software_id=54
aSOFTWARE_NAME[$software_id]='phpBB'
aSOFTWARE_DESC[$software_id]='bulletin board forum software'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#phpbb'
aSOFTWARE_DEPS[$software_id]='88 89 webserver'
#------------------
software_id=55
aSOFTWARE_NAME[$software_id]='WordPress'
aSOFTWARE_DESC[$software_id]='website blog and publishing platform'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#wordpress'
aSOFTWARE_DEPS[$software_id]='88 89 webserver'
#------------------
software_id=38
aSOFTWARE_NAME[$software_id]='FreshRSS'
aSOFTWARE_DESC[$software_id]='self-hosted RSS feed aggregator'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#freshrss'
aSOFTWARE_DEPS[$software_id]='88 89 webserver'
#------------------
software_id=56
aSOFTWARE_NAME[$software_id]='Single File PHP Gallery'
aSOFTWARE_DESC[$software_id]='Website to host and browse your images'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#single-file-php-gallery'
aSOFTWARE_DEPS[$software_id]='89 webserver'
#------------------
software_id=57
aSOFTWARE_NAME[$software_id]='Baïkal'
aSOFTWARE_DESC[$software_id]='lightweight caldav + carddav server'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#baikal'
aSOFTWARE_DEPS[$software_id]='88 89 webserver'
#------------------
software_id=125
aSOFTWARE_NAME[$software_id]='Synapse'
aSOFTWARE_DESC[$software_id]='Matrix homeserver implementation'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#synapse'
aSOFTWARE_DEPS[$software_id]='130 194'
#-----------------
software_id=16
aSOFTWARE_NAME[$software_id]='microblog.pub'
aSOFTWARE_DESC[$software_id]='A self-hosted, single-user, ActivityPub powered microblog.'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DEPS[$software_id]='17'
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#microblogpub'
#------------------
software_id=210
aSOFTWARE_NAME[$software_id]='MediaWiki'
aSOFTWARE_DESC[$software_id]='A collaboration and documentation platform'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#mediawiki'
aSOFTWARE_DEPS[$software_id]='88 89 webserver'
#------------------
software_id=213
aSOFTWARE_NAME[$software_id]='soju'
aSOFTWARE_DESC[$software_id]='A user-friendly IRC bouncer'
aSOFTWARE_CATX[$software_id]=6
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/social/#soju'
aSOFTWARE_DEPS[$software_id]='188'
# Camera & Surveillance
#--------------------------------------------------------------------------------
software_id=59
aSOFTWARE_NAME[$software_id]='RPi Cam Web Interface'
aSOFTWARE_DESC[$software_id]='Web interface & controls for your RPi camera module'
aSOFTWARE_CATX[$software_id]=7
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/camera/#rpi-cam-web-interface'
aSOFTWARE_DEPS[$software_id]='89 webserver'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
# - ARMv8: https://github.com/silvanmelchior/RPi_Cam_Web_Interface/tree/master/bin
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,3]=0
# - Bookworm: raspimjpeg depends on legacy MMAL, removed from RPi userland libs since Bookworm
(( $G_DISTRO > 6 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=136
aSOFTWARE_NAME[$software_id]='motionEye'
aSOFTWARE_DESC[$software_id]='Web interface & surveillance for your camera'
aSOFTWARE_CATX[$software_id]=7
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/camera/#motioneye'
aSOFTWARE_DEPS[$software_id]='7 130'
#------------------
software_id=137
aSOFTWARE_NAME[$software_id]='mjpg-streamer'
aSOFTWARE_DESC[$software_id]='Simple camera streaming tool with HTML plugin'
aSOFTWARE_CATX[$software_id]=7
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/camera/#mjpg-streamer'
#------------------
software_id=127
aSOFTWARE_NAME[$software_id]='BirdNET-Go'
aSOFTWARE_DESC[$software_id]='Continuous avian monitoring and identification'
aSOFTWARE_CATX[$software_id]=7
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/camera/#birdnet-go'
aSOFTWARE_DEPS[$software_id]='7 87'
# - ARMv6/7/RISC-V: https://github.com/tphakala/birdnet-go/releases
(( $G_HW_ARCH == 3 || $G_HW_ARCH == 10 )) || aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,$G_HW_ARCH]=0
# - Bullseye: Too old libc6/libstdc++6 and missing yq
aSOFTWARE_AVAIL_G_DISTRO[$software_id,6]=0
# System Stats & Management
#--------------------------------------------------------------------------------
software_id=3
aSOFTWARE_NAME[$software_id]='MC'
aSOFTWARE_DESC[$software_id]='Midnight Commander - a powerful file manager'
aSOFTWARE_CATX[$software_id]=8
#------------------
software_id=63
aSOFTWARE_NAME[$software_id]='LinuxDash'
aSOFTWARE_DESC[$software_id]='web interface system stats'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#linuxdash'
aSOFTWARE_DEPS[$software_id]='89 webserver'
#------------------
software_id=64
aSOFTWARE_NAME[$software_id]='phpSysInfo'
aSOFTWARE_DESC[$software_id]='web interface system stats'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#phpsysinfo'
aSOFTWARE_DEPS[$software_id]='89 webserver'
#------------------
software_id=65
aSOFTWARE_NAME[$software_id]='Netdata'
aSOFTWARE_DESC[$software_id]='real-time performance monitoring'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#netdata'
# - Trixie: https://bugs.debian.org/1107082
(( $G_DISTRO > 7 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=66
aSOFTWARE_NAME[$software_id]='RPi-Monitor'
aSOFTWARE_DESC[$software_id]='Web interface for Raspberry Pi real-time monitoring'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#rpi-monitor'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=115
aSOFTWARE_NAME[$software_id]='Webmin'
aSOFTWARE_DESC[$software_id]='web interface system management'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#webmin'
#------------------
software_id=162
aSOFTWARE_NAME[$software_id]='Docker'
aSOFTWARE_DESC[$software_id]='Build, ship, and run distributed applications'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#docker'
#------------------
software_id=185
aSOFTWARE_NAME[$software_id]='Portainer'
aSOFTWARE_DESC[$software_id]='Simplifies container management in Docker (standalone host)'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#portainer'
aSOFTWARE_DEPS[$software_id]='162'
# - ARMv6: https://dietpi.com/forum/t/16380/11
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
#------------------
software_id=134
aSOFTWARE_NAME[$software_id]='Docker Compose'
aSOFTWARE_DESC[$software_id]='Manage multi-container Docker applications'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#docker-compose'
aSOFTWARE_DEPS[$software_id]='162'
#------------------
software_id=193
aSOFTWARE_NAME[$software_id]='K3s'
aSOFTWARE_DESC[$software_id]='The certified Kubernetes distribution built for IoT & Edge computing'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#k3s'
# - RISC-V: Not yet supported: https://get.k3s.io/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=142
aSOFTWARE_NAME[$software_id]='MicroK8s'
aSOFTWARE_DESC[$software_id]='The simplest production-grade upstream K8s, light and focused'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/cloud/#microk8s'
# - ARMv6/7/RISC-V: https://snapcraft.io/microk8s
(( $G_HW_ARCH == 3 || $G_HW_ARCH == 10 )) || aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,$G_HW_ARCH]=0
#------------------
software_id=200
aSOFTWARE_NAME[$software_id]='DietPi-Dashboard'
aSOFTWARE_DESC[$software_id]='(rework beta!) Official lightweight DietPi web interface (Rust)'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#dietpi-dashboard'
#------------------
software_id=99
aSOFTWARE_NAME[$software_id]='Prometheus Node Exporter'
aSOFTWARE_DESC[$software_id]='Prometheus exporter for hardware and OS metrics'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#prometheus_node_exporter'
#------------------
software_id=205
aSOFTWARE_NAME[$software_id]='Homer'
aSOFTWARE_DESC[$software_id]='Simple HOMepage for your servER to keep your services on hand'
aSOFTWARE_CATX[$software_id]=8
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_stats/#homer'
aSOFTWARE_DEPS[$software_id]='webserver'
# Remote Access
#--------------------------------------------------------------------------------
software_id=68
aSOFTWARE_NAME[$software_id]='Remote.It'
aSOFTWARE_DESC[$software_id]='Provides secure connections to your networked devices'
aSOFTWARE_CATX[$software_id]=9
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/remote_desktop/#remot3it'
# - RISC-V: https://www.remote.it/download-list
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=138
aSOFTWARE_NAME[$software_id]='VirtualHere'
aSOFTWARE_DESC[$software_id]='server: share USB devices over the network'
aSOFTWARE_CATX[$software_id]=9
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/remote_desktop/#virtualhere'
aSOFTWARE_DEPS[$software_id]='152'
# - RISC-V: https://github.com/virtualhere/script/blob/main/install_server
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
# Hardware Projects
#--------------------------------------------------------------------------------
software_id=69
aSOFTWARE_NAME[$software_id]='Python 3 RPi.GPIO'
aSOFTWARE_DESC[$software_id]='Control Raspberry Pi GPIO channels in Python 3'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#rpigpio'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=70
aSOFTWARE_NAME[$software_id]='WiringPi'
aSOFTWARE_DESC[$software_id]='GPIO interface library (C)'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#wiringpi'
# RPi, Odroids and Orange Pi only
(( $G_HW_MODEL > 19 )) && [[ ! $G_HW_MODEL =~ ^(80|82|83|87|88|89|91|93|94|95|96|97|98)$ ]] && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=71
aSOFTWARE_NAME[$software_id]='WebIOPi'
aSOFTWARE_DESC[$software_id]='Web interface to control RPi GPIO channels'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#webiopi'
aSOFTWARE_DEPS[$software_id]='69'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=72
aSOFTWARE_NAME[$software_id]='I2C'
aSOFTWARE_DESC[$software_id]='enables support for I2C based hardware'
aSOFTWARE_CATX[$software_id]=10
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=100
aSOFTWARE_NAME[$software_id]='PiJuice'
aSOFTWARE_DESC[$software_id]='pisupply ups'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#pijuice'
aSOFTWARE_DEPS[$software_id]='72'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=122
aSOFTWARE_NAME[$software_id]='Node-RED'
aSOFTWARE_DESC[$software_id]='tool for wiring devices, APIs and online services'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#node-red'
aSOFTWARE_DEPS[$software_id]='9'
(( $G_HW_MODEL > 9 )) || aSOFTWARE_DEPS[$software_id]+=' 69'
#------------------
software_id=123
aSOFTWARE_NAME[$software_id]='Mosquitto'
aSOFTWARE_DESC[$software_id]='MQTT messaging broker'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#mosquitto'
#------------------
software_id=131
aSOFTWARE_NAME[$software_id]='Blynk Server'
aSOFTWARE_DESC[$software_id]='msg controller for blynk mobile app and sbcs'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#blynk-server'
aSOFTWARE_DEPS[$software_id]='196 9'
#------------------
software_id=166
aSOFTWARE_NAME[$software_id]='Audiophonics PI-SPC'
aSOFTWARE_DESC[$software_id]='Raspberry Pi power management module'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#audiophonics-pi-spc'
aSOFTWARE_DEPS[$software_id]='70'
# RPi only
(( $G_HW_MODEL > 9 )) && aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=77
aSOFTWARE_NAME[$software_id]='Grafana'
aSOFTWARE_DESC[$software_id]='platform for analytics and monitoring'
aSOFTWARE_CATX[$software_id]=10
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/hardware_projects/#grafana'
# - RISC-V: https://apt.grafana.com/dists/stable/main/binary-riscv64/Packages
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
# System Security
#--------------------------------------------------------------------------------
software_id=73
aSOFTWARE_NAME[$software_id]='Fail2Ban'
aSOFTWARE_DESC[$software_id]='prevents brute-force attacks with ip ban'
aSOFTWARE_CATX[$software_id]=11
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_security/#fail2ban'
#------------------
software_id=92
aSOFTWARE_NAME[$software_id]='Certbot'
aSOFTWARE_DESC[$software_id]="Obtain and renew Let's Encrypt SSL certs for HTTPS"
aSOFTWARE_CATX[$software_id]=11
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system_security/#lets-encrypt'
# Webserver Stacks
#--------------------------------------------------------------------------------
software_id=75
aSOFTWARE_NAME[$software_id]='LASP'
aSOFTWARE_DESC[$software_id]='Apache + SQLite + PHP'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lasp-web-stack'
aSOFTWARE_DEPS[$software_id]='83 87 89'
aSOFTWARE_CONFLICTS[$software_id]='78 79 81 82 84 85'
aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,70]=0
#------------------
software_id=76
aSOFTWARE_NAME[$software_id]='LAMP'
aSOFTWARE_DESC[$software_id]='Apache + MariaDB + PHP'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lamp-web-stack'
aSOFTWARE_DEPS[$software_id]='83 88 89'
aSOFTWARE_CONFLICTS[$software_id]='78 79 81 82 84 85'
aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,70]=0
#------------------
software_id=78
aSOFTWARE_NAME[$software_id]='LESP'
aSOFTWARE_DESC[$software_id]='Nginx + SQLite + PHP'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lesp-web-stack'
aSOFTWARE_DEPS[$software_id]='85 87 89'
aSOFTWARE_CONFLICTS[$software_id]='75 76 81 82 83 84'
#------------------
software_id=79
aSOFTWARE_NAME[$software_id]='LEMP'
aSOFTWARE_DESC[$software_id]='Nginx + MariaDB + PHP'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lemp-web-stack'
aSOFTWARE_DEPS[$software_id]='85 88 89'
aSOFTWARE_CONFLICTS[$software_id]='75 76 81 82 83 84'
#------------------
software_id=81
aSOFTWARE_NAME[$software_id]='LLSP'
aSOFTWARE_DESC[$software_id]='Lighttpd + SQLite + PHP'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#llsp-web-stack'
aSOFTWARE_DEPS[$software_id]='84 87 89'
aSOFTWARE_CONFLICTS[$software_id]='75 76 78 79 83 85'
#------------------
software_id=82
aSOFTWARE_NAME[$software_id]='LLMP'
aSOFTWARE_DESC[$software_id]='Lighttpd + MariaDB + PHP'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#llmp-web-stack'
aSOFTWARE_DEPS[$software_id]='84 88 89'
aSOFTWARE_CONFLICTS[$software_id]='75 76 78 79 83 85'
#------------------
software_id=83
aSOFTWARE_NAME[$software_id]='Apache'
aSOFTWARE_DESC[$software_id]='Popular webserver'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#apache2'
aSOFTWARE_CONFLICTS[$software_id]='78 79 81 82 84 85'
aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,70]=0
#------------------
software_id=84
aSOFTWARE_NAME[$software_id]='Lighttpd'
aSOFTWARE_DESC[$software_id]='Extremely lightweight webserver'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#lighttpd'
aSOFTWARE_CONFLICTS[$software_id]='75 76 78 79 83 85'
#------------------
software_id=85
aSOFTWARE_NAME[$software_id]='Nginx'
aSOFTWARE_DESC[$software_id]='Lightweight webserver'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#nginx'
aSOFTWARE_CONFLICTS[$software_id]='75 76 81 82 83 84'
#------------------
software_id=89
aSOFTWARE_NAME[$software_id]='PHP'
aSOFTWARE_DESC[$software_id]='Hypertext Preprocessor for dynamic web content'
aSOFTWARE_CATX[$software_id]=12
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/webserver_stack/#php'
# DNS Servers
#--------------------------------------------------------------------------------
software_id=93
aSOFTWARE_NAME[$software_id]='Pi-hole'
aSOFTWARE_DESC[$software_id]='block adverts for any device on your network'
aSOFTWARE_CATX[$software_id]=13
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/dns_servers/#pi-hole'
aSOFTWARE_DEPS[$software_id]='17'
aSOFTWARE_INTERACTIVE[$software_id]=1
aSOFTWARE_CONFLICTS[$software_id]='126'
#------------------
software_id=126
aSOFTWARE_NAME[$software_id]='AdGuard Home'
aSOFTWARE_DESC[$software_id]='powerful network-wide ads & trackers blocking DNS server'
aSOFTWARE_CATX[$software_id]=13
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/dns_servers/#adguard-home'
aSOFTWARE_CONFLICTS[$software_id]='93'
#------------------
software_id=182
aSOFTWARE_NAME[$software_id]='Unbound'
aSOFTWARE_DESC[$software_id]='validating, recursive, caching DNS resolver'
aSOFTWARE_CATX[$software_id]=13
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/dns_servers/#unbound'
# File Servers
#--------------------------------------------------------------------------------
software_id=94
aSOFTWARE_NAME[$software_id]='ProFTPD'
aSOFTWARE_DESC[$software_id]='Feature-rich FTP server with Apache-like config and FTPS+SFTP modules'
aSOFTWARE_CATX[$software_id]=14
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/file_servers/#proftpd'
aSOFTWARE_CONFLICTS[$software_id]='95'
#------------------
software_id=95
aSOFTWARE_NAME[$software_id]='vsftpd'
aSOFTWARE_DESC[$software_id]='Lightweight FTP/FTPS server'
aSOFTWARE_CATX[$software_id]=14
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/file_servers/#vsftpd'
aSOFTWARE_CONFLICTS[$software_id]='94'
#------------------
software_id=96
aSOFTWARE_NAME[$software_id]='Samba Server'
aSOFTWARE_DESC[$software_id]='Feature-rich SMB/CIFS server'
aSOFTWARE_CATX[$software_id]=14
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/file_servers/#samba'
#------------------
software_id=109
aSOFTWARE_NAME[$software_id]='NFS Server'
aSOFTWARE_DESC[$software_id]='Network File System server'
aSOFTWARE_CATX[$software_id]=14
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/file_servers/#nfs'
# VPN Servers
#--------------------------------------------------------------------------------
software_id=97
aSOFTWARE_NAME[$software_id]='OpenVPN'
aSOFTWARE_DESC[$software_id]='vpn server'
aSOFTWARE_CATX[$software_id]=15
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/vpn/#openvpn'
#------------------
software_id=172
aSOFTWARE_NAME[$software_id]='WireGuard'
aSOFTWARE_DESC[$software_id]='an extremely simple yet fast and modern VPN'
aSOFTWARE_CATX[$software_id]=15
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/vpn/#wireguard'
# Requires the WireGuard kernel module: We do not support DKMS builds anymore, which were required only for legacy kernel versions, not supported by WireGuard anymore: https://dietpi.com/forum/t/15173
(( $G_HW_MODEL == 75 )) || modprobe -nq wireguard || aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]=0
#------------------
software_id=117
aSOFTWARE_NAME[$software_id]='PiVPN'
aSOFTWARE_DESC[$software_id]='openvpn/wireguard server install & management tool'
aSOFTWARE_CATX[$software_id]=15
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/vpn/#pivpn'
aSOFTWARE_DEPS[$software_id]='17'
# Require interactive install if no config file containing options for unattended install is present
[[ -f '/boot/unattended_pivpn.conf' ]] || aSOFTWARE_INTERACTIVE[$software_id]=1
#------------------
software_id=58
aSOFTWARE_NAME[$software_id]='Tailscale'
aSOFTWARE_DESC[$software_id]='Zero config VPN'
aSOFTWARE_CATX[$software_id]=15
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/vpn/#tailscale'
#-----------------
software_id=201
aSOFTWARE_NAME[$software_id]='ZeroTier'
aSOFTWARE_DESC[$software_id]='Free easy to deploy cloud-hosted VPN service'
aSOFTWARE_CATX[$software_id]=15
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/vpn/#zerotier'
# Advanced Networking
#--------------------------------------------------------------------------------
software_id=60
aSOFTWARE_NAME[$software_id]='WiFi Hotspot'
aSOFTWARE_DESC[$software_id]='turn your device into a WiFi hotspot'
aSOFTWARE_CATX[$software_id]=16
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/advanced_networking/#wifi-hotspot'
#------------------
software_id=61
aSOFTWARE_NAME[$software_id]='Tor Hotspot'
aSOFTWARE_DESC[$software_id]='optional: route hotspot traffic through tor'
aSOFTWARE_CATX[$software_id]=16
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/advanced_networking/#tor-hotspot'
aSOFTWARE_DEPS[$software_id]='60'
aSOFTWARE_CONFLICTS[$software_id]='184'
#------------------
software_id=98
aSOFTWARE_NAME[$software_id]='HAProxy'
aSOFTWARE_DESC[$software_id]='high performance TCP/HTTP load balancer'
aSOFTWARE_CATX[$software_id]=16
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/advanced_networking/#haproxy'
#------------------
software_id=152
aSOFTWARE_NAME[$software_id]='Avahi-Daemon'
aSOFTWARE_DESC[$software_id]='Hostname broadcast via mDNS (Zeroconf, Bonjour)'
aSOFTWARE_CATX[$software_id]=16
#------------------
software_id=171
aSOFTWARE_NAME[$software_id]='frp'
aSOFTWARE_DESC[$software_id]='reverse proxy'
aSOFTWARE_CATX[$software_id]=16
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/advanced_networking/#frp'
# Home Automation
#--------------------------------------------------------------------------------
software_id=157
aSOFTWARE_NAME[$software_id]='Home Assistant'
aSOFTWARE_DESC[$software_id]='Open source home automation platform'
aSOFTWARE_CATX[$software_id]=17
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/home_automation/#home-assistant'
#------------------
software_id=140
aSOFTWARE_NAME[$software_id]='Domoticz'
aSOFTWARE_DESC[$software_id]='Open source home automation platform'
aSOFTWARE_CATX[$software_id]=17
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/home_automation/#domoticz'
#------------------
software_id=27
aSOFTWARE_NAME[$software_id]='TasmoAdmin'
aSOFTWARE_DESC[$software_id]='Website to manage ESP8266 devices flashed with Tasmota'
aSOFTWARE_CATX[$software_id]=17
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/home_automation/#tasmoadmin'
aSOFTWARE_DEPS[$software_id]='89 webserver'
#------------------
software_id=206
aSOFTWARE_NAME[$software_id]='openHAB'
aSOFTWARE_DESC[$software_id]='Vendor and technology agnostic FLOSS home automation software'
aSOFTWARE_CATX[$software_id]=17
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/home_automation/#openhab'
aSOFTWARE_DEPS[$software_id]='196'
#------------------
software_id=211
aSOFTWARE_NAME[$software_id]='Homebridge'
aSOFTWARE_DESC[$software_id]='Bringing HomeKit support where there is none'
aSOFTWARE_CATX[$software_id]=17
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/home_automation/#homebridge'
# - RISC-V: https://repo.homebridge.io/dists/stable/Release
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
# Printing
#--------------------------------------------------------------------------------
software_id=153
aSOFTWARE_NAME[$software_id]='OctoPrint'
aSOFTWARE_DESC[$software_id]='web interface for controlling 3d printers'
aSOFTWARE_CATX[$software_id]=18
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/printing/#octoprint'
aSOFTWARE_DEPS[$software_id]='130'
#------------------
software_id=187
aSOFTWARE_NAME[$software_id]='CUPS'
aSOFTWARE_DESC[$software_id]='common UNIX printing system'
aSOFTWARE_CATX[$software_id]=18
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/printing/#cups'
aSOFTWARE_DEPS[$software_id]='152'
# Distributed Projects
#--------------------------------------------------------------------------------
software_id=2
aSOFTWARE_NAME[$software_id]='Folding@Home'
aSOFTWARE_DESC[$software_id]='Help disease research with your computing power!'
aSOFTWARE_CATX[$software_id]=19
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/distributed_projects/#foldinghome'
# - ARMv6/7/RISC-V: No package: https://download.foldingathome.org/releases/public/release/fahclient/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,2]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=133
aSOFTWARE_NAME[$software_id]='YaCy'
aSOFTWARE_DESC[$software_id]='decentralised open source search engine'
aSOFTWARE_CATX[$software_id]=19
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/distributed_projects/#yacy'
aSOFTWARE_DEPS[$software_id]='196'
#------------------
software_id=141
aSOFTWARE_NAME[$software_id]='ADS-B Feeder'
aSOFTWARE_DESC[$software_id]='track airplanes using SDRs and feed the data to ADS-B aggregators'
aSOFTWARE_CATX[$software_id]=19
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/distributed_projects/#adsb-feeder'
aSOFTWARE_DEPS[$software_id]='17 130 134 162' # Git, Python, Docker & Docker Compose
#------------------
software_id=184
aSOFTWARE_NAME[$software_id]='Tor Relay'
aSOFTWARE_DESC[$software_id]='add a node to the Tor network'
aSOFTWARE_CATX[$software_id]=19
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/distributed_projects/#tor-relay'
aSOFTWARE_CONFLICTS[$software_id]='61'
#------------------
software_id=186
aSOFTWARE_NAME[$software_id]='Kubo'
aSOFTWARE_DESC[$software_id]='(IPFS Node) contribute to a decentralized Internet'
aSOFTWARE_CATX[$software_id]=19
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/distributed_projects/#ipfs-node'
# - RISC-V: https://dist.ipfs.io/kubo/
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
# SSH Clients
#--------------------------------------------------------------------------------
software_id=0
aSOFTWARE_NAME[$software_id]='OpenSSH Client'
aSOFTWARE_DESC[$software_id]='Feature-rich SSH, SFTP and SCP client'
aSOFTWARE_CATX[$software_id]=20
# File Server Clients
#--------------------------------------------------------------------------------
software_id=1
aSOFTWARE_NAME[$software_id]='Samba Client'
aSOFTWARE_DESC[$software_id]='access SMB/CIFS/Samba network shares'
aSOFTWARE_CATX[$software_id]=21
aSOFTWARE_DOCS[$software_id]=' dietpi-drive_manager > Add network drive'
#------------------
software_id=110
aSOFTWARE_NAME[$software_id]='NFS Client'
aSOFTWARE_DESC[$software_id]='network file system client'
aSOFTWARE_CATX[$software_id]=21
aSOFTWARE_DOCS[$software_id]=' dietpi-drive_manager > Add network drive'
# System
#--------------------------------------------------------------------------------
software_id=5
aSOFTWARE_NAME[$software_id]='ALSA'
aSOFTWARE_DESC[$software_id]='Advanced Linux Sound Architecture'
aSOFTWARE_CATX[$software_id]=22
#------------------
software_id=7
aSOFTWARE_NAME[$software_id]='FFmpeg'
aSOFTWARE_DESC[$software_id]='Audio & video codec library and programs'
aSOFTWARE_CATX[$software_id]=22
#------------------
software_id=6
aSOFTWARE_NAME[$software_id]='X11'
aSOFTWARE_DESC[$software_id]='X.Org X Window System implementation'
aSOFTWARE_CATX[$software_id]=22
#------------------
software_id=170
aSOFTWARE_NAME[$software_id]='UnRAR'
aSOFTWARE_DESC[$software_id]='unarchiver for .rar files'
aSOFTWARE_CATX[$software_id]=22
#------------------
software_id=4
aSOFTWARE_NAME[$software_id]='fish'
aSOFTWARE_DESC[$software_id]='friendly interactive shell'
aSOFTWARE_CATX[$software_id]=22
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/system/#fish'
# Databases & Data Stores
#--------------------------------------------------------------------------------
software_id=87
aSOFTWARE_NAME[$software_id]='SQLite'
aSOFTWARE_DESC[$software_id]='Persistent single-file database system'
aSOFTWARE_CATX[$software_id]=23
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/databases/#sqlite'
#------------------
software_id=88
aSOFTWARE_NAME[$software_id]='MariaDB'
aSOFTWARE_DESC[$software_id]='Persistent cached file-per-table database server'
aSOFTWARE_CATX[$software_id]=23
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/databases/#mariadb'
#------------------
software_id=90
aSOFTWARE_NAME[$software_id]='phpMyAdmin'
aSOFTWARE_DESC[$software_id]='Optional MariaDB web interface admin tools'
aSOFTWARE_CATX[$software_id]=23
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/databases/#phpmyadmin'
aSOFTWARE_DEPS[$software_id]='88 89 webserver'
#------------------
software_id=91
aSOFTWARE_NAME[$software_id]='Redis'
aSOFTWARE_DESC[$software_id]='Volatile in-memory non-SQL database server'
aSOFTWARE_CATX[$software_id]=23
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/databases/#redis'
#------------------
software_id=74
aSOFTWARE_NAME[$software_id]='InfluxDB'
aSOFTWARE_DESC[$software_id]='Persistent time-series database server'
aSOFTWARE_CATX[$software_id]=23
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/databases/#influxdb'
#------------------
software_id=194
aSOFTWARE_NAME[$software_id]='PostgreSQL'
aSOFTWARE_DESC[$software_id]='Persistent advanced object-relational database server'
aSOFTWARE_CATX[$software_id]=23
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/databases/#postgresql'
# Development & Programming
#--------------------------------------------------------------------------------
software_id=17
aSOFTWARE_NAME[$software_id]='Git'
aSOFTWARE_DESC[$software_id]='Clone and manage Git repositories locally'
aSOFTWARE_CATX[$software_id]=24
#------------------
software_id=130
aSOFTWARE_NAME[$software_id]='Python 3'
aSOFTWARE_DESC[$software_id]='Runtime system, pip package installer and development headers'
aSOFTWARE_CATX[$software_id]=24
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/programming/#python-3'
#------------------
software_id=189
aSOFTWARE_NAME[$software_id]='VSCodium'
aSOFTWARE_DESC[$software_id]='FLOSS version of MS VSCode'
aSOFTWARE_CATX[$software_id]=24
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/programming/#vscodium'
aSOFTWARE_DEPS[$software_id]='5 6 17'
# - ARMv6/7/RISC-V: https://download.vscodium.com/debs/dists/vscodium/Release
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,2]=0
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=188
aSOFTWARE_NAME[$software_id]='Go'
aSOFTWARE_DESC[$software_id]='Runtime environment and package installer'
aSOFTWARE_CATX[$software_id]=24
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/programming/#go'
aSOFTWARE_DEPS[$software_id]='17'
#------------------
software_id=8
aSOFTWARE_NAME[$software_id]='Java JDK'
aSOFTWARE_DESC[$software_id]='OpenJDK Development Kit'
aSOFTWARE_CATX[$software_id]=24
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/programming/#java'
aSOFTWARE_DEPS[$software_id]='196'
#------------------
software_id=196
aSOFTWARE_NAME[$software_id]='Java JRE'
aSOFTWARE_DESC[$software_id]='OpenJDK Runtime Environment'
aSOFTWARE_CATX[$software_id]=24
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/programming/#java'
#------------------
software_id=9
aSOFTWARE_NAME[$software_id]='Node.js'
aSOFTWARE_DESC[$software_id]='JavaScript runtime environment'
aSOFTWARE_CATX[$software_id]=24
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/programming/#nodejs'
#------------------
software_id=150
aSOFTWARE_NAME[$software_id]='Mono'
aSOFTWARE_DESC[$software_id]='Runtime libraries and repository'
aSOFTWARE_CATX[$software_id]=24
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/programming/#mono'
# - RISC-V: https://download.mono-project.com/repo/debian/dists/buster/main/, https://packages.debian.org/trixie/mono-runtime
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=34
aSOFTWARE_NAME[$software_id]='PHP Composer'
aSOFTWARE_DESC[$software_id]='Package manager for PHP'
aSOFTWARE_CATX[$software_id]=24
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/programming/#php-composer'
aSOFTWARE_DEPS[$software_id]='89'
# Desktop Utilities
#--------------------------------------------------------------------------------
software_id=22
aSOFTWARE_NAME[$software_id]='QuiteRSS'
aSOFTWARE_DESC[$software_id]='cross-platform, free rss reader'
aSOFTWARE_CATX[$software_id]=25
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#quiterss'
aSOFTWARE_DEPS[$software_id]='6'
# - Trixie: https://bugs.debian.org/1093725
(( $G_DISTRO > 7 )) && aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]=0
#------------------
software_id=113
aSOFTWARE_NAME[$software_id]='Chromium'
aSOFTWARE_DESC[$software_id]='web browser for desktop or autostart'
aSOFTWARE_CATX[$software_id]=25
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#chromium'
aSOFTWARE_DEPS[$software_id]='5 6'
# - ARMv6: https://github.com/RPi-Distro/chromium-browser/issues/21
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
# - RISC-V: https://packages.debian.org/trixie/chromium
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,11]=0
#------------------
software_id=67
aSOFTWARE_NAME[$software_id]='Firefox'
aSOFTWARE_DESC[$software_id]='web browser for desktop'
aSOFTWARE_CATX[$software_id]=25
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#firefox'
aSOFTWARE_DEPS[$software_id]='5 6'
# - ARMv6: https://github.com/RPi-Distro/chromium-browser/issues/21#issuecomment-997044303
aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,1]=0
#------------------
software_id=174
aSOFTWARE_NAME[$software_id]='GIMP'
aSOFTWARE_DESC[$software_id]='mspaint on steroids'
aSOFTWARE_CATX[$software_id]=25
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#gimp'
aSOFTWARE_DEPS[$software_id]='6'
#------------------
software_id=175
aSOFTWARE_NAME[$software_id]='Xfce Power Manager'
aSOFTWARE_DESC[$software_id]='with brightness control, recommended for LXDE/LXQt'
aSOFTWARE_CATX[$software_id]=25
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/desktop/#xfce-power-manager'
aSOFTWARE_DEPS[$software_id]='6'
#--------------------------------------------------------------------------------
# Logging Systems (hidden)
#--------------------------------------------------------------------------------
software_id=101
aSOFTWARE_NAME[$software_id]='Logrotate'
aSOFTWARE_DESC[$software_id]='Rotates and compresses old log files in /var/log'
aSOFTWARE_CATX[$software_id]=-1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/log_system/#full-logging'
#------------------
software_id=102
aSOFTWARE_NAME[$software_id]='Rsyslog'
aSOFTWARE_DESC[$software_id]='Writes system logs (journalctl) as plain text files to /var/log'
aSOFTWARE_CATX[$software_id]=-1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/log_system/#full-logging'
#------------------
software_id=103
aSOFTWARE_NAME[$software_id]='DietPi-RAMlog'
aSOFTWARE_DESC[$software_id]='Makes /var/log a RAM disk, preserves file structure on reboot'
aSOFTWARE_CATX[$software_id]=-1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/log_system/#dietpi-ramlog'
#--------------------------------------------------------------------------------
# SSH Servers (hidden)
#--------------------------------------------------------------------------------
software_id=104
aSOFTWARE_NAME[$software_id]='Dropbear'
aSOFTWARE_DESC[$software_id]='Lightweight SSH server'
aSOFTWARE_CATX[$software_id]=-1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/ssh/#dropbear'
#------------------
software_id=105
aSOFTWARE_NAME[$software_id]='OpenSSH Server'
aSOFTWARE_DESC[$software_id]='Feature-rich SSH server with SFTP and SCP support'
aSOFTWARE_CATX[$software_id]=-1
aSOFTWARE_DOCS[$software_id]='https://dietpi.com/docs/software/ssh/#openssh'
aSOFTWARE_DEPS[$software_id]='0'
#--------------------------------------------------------------------------------
# Init install state for defined software
for i in "${!aSOFTWARE_NAME[@]}"
do
aSOFTWARE_INSTALL_STATE[$i]=0
done
# - Pre-installed on DietPi images:
aSOFTWARE_INSTALL_STATE[103]=2 # DietPi-RAMlog
aSOFTWARE_INSTALL_STATE[104]=2 # Dropbear
#--------------------------------------------------------------------------------
[[ $MACHINE_READABLE ]] || G_DIETPI-NOTIFY 0 'Initialised database'
}
# Unmark software installs for automated installs if user input is required
Unmark_Unattended()
{
(( $G_INTERACTIVE )) && return
for i in "${!aSOFTWARE_NAME[@]}"
do
if (( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 && ${aSOFTWARE_INTERACTIVE[$i]:-0} == 1 ))
then
aSOFTWARE_INSTALL_STATE[$i]=0
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$i]}: Install requires user input and cannot be automated."
G_DIETPI-NOTIFY 1 "${aSOFTWARE_NAME[$i]}: Please run 'dietpi-software' to install manually."
fi
done
}
# Unmark all dependants of a software title
# $1: software ID
Unmark_Dependants()
{
aSOFTWARE_INSTALL_STATE[$1]=0
local i
for i in "${!aSOFTWARE_NAME[@]}"
do
# NB: This does not work for dependencies given as "webserver", "desktop" or "browser". However, there are no conflicts among desktops and browser, and webservers only conflict with other webservers, hence obviously one is installed already which satisfies the dependency.
[[ ${aSOFTWARE_INSTALL_STATE[$i]} == 1 && ${aSOFTWARE_DEPS[$i]} =~ (^|[[:blank:]])$1([[:blank:]]|$) ]] || continue
aSOFTWARE_INSTALL_STATE[$i]=0
[[ $dependants_text ]] || dependants_text='\n\nThe following dependants will be unmarked as well:'
dependants_text+="\n\n - ${aSOFTWARE_NAME[$i]}: It depends on ${aSOFTWARE_NAME[$1]}."
# Recursive call to unmark all dependants of this dependant
Unmark_Dependants "$i"
done
}
# Unmark conflicting software titles
CONFLICTS_RESOLVED=0
Unmark_Conflicts()
{
# Loop through marked software
local i j unmarked_text dependants_text
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) || continue
# Loop through installed or marked conflicts
for j in ${aSOFTWARE_CONFLICTS[$i]}
do
(( ${aSOFTWARE_INSTALL_STATE[$j]} > 0 )) || continue
# At least one conflict is installed or marked: Unmark software
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && { unmarked_text+="\n\n${aSOFTWARE_NAME[$i]} won't be installed, as it conflicts with:"; Unmark_Dependants "$i"; }
# Unmark all marked conflicts
if (( ${aSOFTWARE_INSTALL_STATE[$j]} == 1 ))
then
unmarked_text+="\n\n - ${aSOFTWARE_NAME[$j]}: It won't be installed either."
Unmark_Dependants "$j"
else
unmarked_text+="\n\n - ${aSOFTWARE_NAME[$j]}: It is installed already."
fi
done
done
CONFLICTS_RESOLVED=1
[[ $unmarked_text ]] || return 0
# Conflicts have been unmarked: Inform user!
G_WHIP_MSG "[WARNING] Conflicting installs have been detected!$unmarked_text$dependants_text"
}
Select_Webserver_Dependency()
{
# Check for existing webserver (Apache, Nginx, Lighttpd) installation
# - Do no reinstalls, as those are currently too intrusive, overriding custom configs
(( ${aSOFTWARE_INSTALL_STATE[83]} < 1 && ${aSOFTWARE_INSTALL_STATE[84]} < 1 && ${aSOFTWARE_INSTALL_STATE[85]} < 1 )) || return 1
# Auto-select webserver if manually installed
if dpkg-query -s 'apache2' &> /dev/null
then
SELECTED_WEBSERVER=83; return 0
elif dpkg-query -s 'nginx-common' &> /dev/null
then
SELECTED_WEBSERVER=85; return 0
elif dpkg-query -s 'lighttpd' &> /dev/null
then
SELECTED_WEBSERVER=84; return 0
fi
local dependant=$1 preference_index=$(sed -n '/^[[:blank:]]*AUTO_SETUP_WEB_SERVER_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt) software_id
# Preference index to software ID
case $preference_index in
-2) software_id=84;; # Lighttpd
-1) software_id=85;; # Nginx
*) software_id=83;; # Apache (default)
esac
G_WHIP_MENU_ARRAY=(
"${aSOFTWARE_NAME[83]}" ": ${aSOFTWARE_DESC[83]}"
"${aSOFTWARE_NAME[85]}" ": ${aSOFTWARE_DESC[85]}"
"${aSOFTWARE_NAME[84]}" ": ${aSOFTWARE_DESC[84]}"
)
G_WHIP_DEFAULT_ITEM=${aSOFTWARE_NAME[$software_id]}
G_WHIP_BUTTON_OK_TEXT='Confirm' G_WHIP_NOCANCEL=1
G_WHIP_MENU "${aSOFTWARE_NAME[$dependant]} requires a webserver. Which one shall be installed?
\n- Apache: Feature-rich and popular. Recommended for beginners and users who are looking to follow Apache based guides.
\n- Nginx: Lightweight alternative to Apache. Nginx claims faster webserver performance compared to Apache.
\n- Lighttpd: Extremely lightweight and is generally considered to offer the \"best\" webserver performance for SBCs. Recommended for users who expect low webserver traffic.
\n- More info: https://dietpi.com/docs/software/webserver_stack/" || G_WHIP_RETURNED_VALUE=${aSOFTWARE_NAME[$software_id]}
# Software name to ID and preference index
if [[ $G_WHIP_RETURNED_VALUE == "${aSOFTWARE_NAME[83]}" ]]
then
SELECTED_WEBSERVER=83 preference_index=0
elif [[ $G_WHIP_RETURNED_VALUE == "${aSOFTWARE_NAME[85]}" ]]
then
SELECTED_WEBSERVER=85 preference_index=-1
elif [[ $G_WHIP_RETURNED_VALUE == "${aSOFTWARE_NAME[84]}" ]]
then
SELECTED_WEBSERVER=84 preference_index=-2
fi
G_CONFIG_INJECT 'AUTO_SETUP_WEB_SERVER_INDEX=' "AUTO_SETUP_WEB_SERVER_INDEX=$preference_index" /boot/dietpi.txt
return 0
}
Select_Desktop_Dependency()
{
# Check for existing desktop (LXDE, MATE, Xfce, GNUstep, LXQt) installation
# - Do no reinstalls, as those are loose dependencies
(( ${aSOFTWARE_INSTALL_STATE[23]} < 1 && ${aSOFTWARE_INSTALL_STATE[24]} < 1 && ${aSOFTWARE_INSTALL_STATE[25]} < 1 && ${aSOFTWARE_INSTALL_STATE[26]} < 1 && ${aSOFTWARE_INSTALL_STATE[173]} < 1 )) || return 1
# Auto-select desktop if manually installed
if dpkg-query -s 'lxde' &> /dev/null
then
SELECTED_DESKTOP=23; return 0
elif dpkg-query -s 'xfce4' &> /dev/null
then
SELECTED_DESKTOP=25; return 0
elif dpkg-query -s 'mate-desktop-environment-core' &> /dev/null
then
SELECTED_DESKTOP=24; return 0
elif dpkg-query -s 'lxqt' &> /dev/null
then
SELECTED_DESKTOP=173; return 0
elif dpkg-query -s 'gnustep' &> /dev/null
then
SELECTED_DESKTOP=26; return 0
fi
local dependant=$1 preference_index=$(sed -n '/^[[:blank:]]*AUTO_SETUP_DESKTOP_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt) software_id
# Preference index to software ID
case $preference_index in
-1) software_id=25;; # Xfce
-2) software_id=24;; # MATE
-3) software_id=173;; # LXQt
-4) software_id=26;; # GNUstep
*) software_id=23;; # LXDE (default)
esac
G_WHIP_MENU_ARRAY=(
"${aSOFTWARE_NAME[23]}" ": ${aSOFTWARE_DESC[23]}"
"${aSOFTWARE_NAME[25]}" ": ${aSOFTWARE_DESC[25]}"
"${aSOFTWARE_NAME[24]}" ": ${aSOFTWARE_DESC[24]}"
"${aSOFTWARE_NAME[173]}" ": ${aSOFTWARE_DESC[173]}"
"${aSOFTWARE_NAME[26]}" ": ${aSOFTWARE_DESC[26]}"
)
G_WHIP_DEFAULT_ITEM=${aSOFTWARE_NAME[$software_id]}
G_WHIP_BUTTON_OK_TEXT='Confirm' G_WHIP_NOCANCEL=1
G_WHIP_MENU "${aSOFTWARE_NAME[$dependant]} requires a desktop. Which one shall be installed?" || G_WHIP_RETURNED_VALUE=${aSOFTWARE_NAME[$software_id]}
# Software name to ID and preference index
case $G_WHIP_RETURNED_VALUE in
"${aSOFTWARE_NAME[25]}") SELECTED_DESKTOP=25 preference_index=-1;;
"${aSOFTWARE_NAME[24]}") SELECTED_DESKTOP=24 preference_index=-2;;
"${aSOFTWARE_NAME[173]}") SELECTED_DESKTOP=173 preference_index=-3;;
"${aSOFTWARE_NAME[26]}") SELECTED_DESKTOP=26 preference_index=-4;;
*) SELECTED_DESKTOP=23 preference_index=0;;
esac
G_CONFIG_INJECT 'AUTO_SETUP_DESKTOP_INDEX=' "AUTO_SETUP_DESKTOP_INDEX=$preference_index" /boot/dietpi.txt
return 0
}
Select_Browser_Dependency()
{
# Check for existing browser (Firefox, Chromium) installation
# - Do no reinstalls, as those are loose dependencies
(( ${aSOFTWARE_INSTALL_STATE[67]} < 1 && ${aSOFTWARE_INSTALL_STATE[113]} < 1 )) || return 1
# Disable browser preference on ARMv6 and RISC-V systems: https://github.com/RPi-Distro/chromium-browser/issues/21
(( $G_HW_ARCH == 1 || $G_HW_ARCH == 11 )) && return 1
# Auto-select browser if manually installed
if dpkg-query -s 'firefox-esr' &> /dev/null
then
SELECTED_BROWSER=67; return 0
elif dpkg-query -s 'chromium' &> /dev/null || dpkg-query -s 'chromium-browser' &> /dev/null
then
SELECTED_BROWSER=113; return 0
fi
local dependant=$1 preference_index=$(sed -n '/^[[:blank:]]*AUTO_SETUP_BROWSER_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt) software_id
# Preference index to software ID
case $preference_index in
0) return 1;; # None: Skip menu if "None" was explicitly selected or set via dietpi.txt before
-2) software_id=113;; # Chromium
*) software_id=67;; # Firefox (default)
esac
G_WHIP_MENU_ARRAY=(
'None' ': If you do not require a web browser'
"${aSOFTWARE_NAME[67]}" ": ${aSOFTWARE_DESC[67]}"
"${aSOFTWARE_NAME[113]}" ": ${aSOFTWARE_DESC[113]}"
)
G_WHIP_DEFAULT_ITEM=${aSOFTWARE_NAME[$software_id]}
G_WHIP_BUTTON_OK_TEXT='Confirm' G_WHIP_NOCANCEL=1
G_WHIP_MENU "Which web browser shall be installed with the ${aSOFTWARE_NAME[$dependant]} desktop environment?" || G_WHIP_RETURNED_VALUE=${aSOFTWARE_NAME[$software_id]}
# Software name to ID and preference index
case $G_WHIP_RETURNED_VALUE in
'None') preference_index=0;;
"${aSOFTWARE_NAME[113]}") SELECTED_BROWSER=113 preference_index=-2;;
*) SELECTED_BROWSER=67 preference_index=-1;;
esac
G_CONFIG_INJECT 'AUTO_SETUP_BROWSER_INDEX=' "AUTO_SETUP_BROWSER_INDEX=$preference_index" /boot/dietpi.txt
[[ $G_WHIP_RETURNED_VALUE == 'None' ]] && return 1 || return 0
}
# $1: software ID
Resolve_Dependencies()
{
# Loop through dependencies
local i
for i in ${aSOFTWARE_DEPS[$1]}
do
# Resolve webserver dependency based on install state and user preference
if [[ $i == 'webserver' ]]
then
Select_Webserver_Dependency "$1" || continue
i=$SELECTED_WEBSERVER
# Resolve desktop dependency based on install state
elif [[ $i == 'desktop' ]]
then
Select_Desktop_Dependency "$1" || continue
i=$SELECTED_DESKTOP
# Resolve browser dependency based on install state
elif [[ $i == 'browser' ]]
then
Select_Browser_Dependency "$1" || continue
i=$SELECTED_BROWSER
fi
# Skip if dependency is marked for install already
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && continue
# Is it reinstalled or freshly installed?
local re_installed='installed'
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 )) && re_installed='reinstalled'
aSOFTWARE_INSTALL_STATE[$i]=1
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$i]} will be $re_installed"
# Recursive call to resolve dependencies of this dependency
Resolve_Dependencies "$i"
done
}
# Work out which additional software we need to install
# - We do reinstall most =2 marked software as well, just to be sure.
Mark_Dependencies()
{
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Checking for prerequisite software'
# If OctoPrint and mjpg-streamer both are installed, OctoPrint is automatically configured to use mjpg-streamer. For the integrated time-lapse feature, FFmpeg is required.
if (( ${aSOFTWARE_INSTALL_STATE[137]} > 0 && ${aSOFTWARE_INSTALL_STATE[153]} > 0 && ${aSOFTWARE_INSTALL_STATE[137]} + ${aSOFTWARE_INSTALL_STATE[153]} < 4 ))
then
aSOFTWARE_INSTALL_STATE[7]=1
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[7]} will be installed"
fi
# Loop through marked software to resolve dependencies each
local i
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) || continue
Resolve_Dependencies "$i"
done
}
Create_Desktop_Shared_Items()
{
# Pre-create dirs
G_EXEC mkdir -p /usr/share/applications /var/lib/dietpi/dietpi-software/installed/desktop/{icons,wallpapers}
# Icons
G_THREAD_START curl -sSfL 'https://raw.githubusercontent.com/MichaIng/DietPi-Website/master/images/dietpi-logo_128x128.png' -o /var/lib/dietpi/dietpi-software/installed/desktop/icons/dietpi-icon.png
G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/icons/grey_16x16.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/icons/grey_16x16.png
G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/icons/justboom.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/icons/justboom.png
# Wallpapers
G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/wallpapers/dietpi-logo_inverted_1080p.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/wallpapers/dietpi-logo_inverted_1080p.png
G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/wallpapers/dietpi-logo_1080p.png" -o /var/lib/dietpi/dietpi-software/installed/desktop/wallpapers/dietpi-logo_1080p.png
# File manager bookmarks
G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/gtk/.gtk-bookmarks" -o /var/lib/dietpi/dietpi-software/installed/desktop/.gtk-bookmarks
# Desktop applications
local adesktop_items=(
'dietpi-software'
'dietpi-drive_manager'
'dietpi-update'
'dietpi-config'
'dietpi-backup'
'dietpi-sync'
'dietpi-services'
'dietpi-cleaner'
'dietpi-cron'
'dietpi-launcher'
'dietpi-justboom'
)
local i
for i in "${adesktop_items[@]}"
do
G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/apps/$i.desktop" -o "/usr/share/applications/$i.desktop"
done
# Create autostart script to add default desktop icons and configs to users that login the first time into a desktop
cat << '_EOF_' > /var/lib/dietpi/dietpi-software/installed/desktop/dietpi-desktop_setup.sh
#!/bin/dash
# Create desktop shortcuts
set 'opentyrian' 'kodi' 'steam' 'dxx-rebirth' 'chromium' 'chromium-browser' 'htop' 'codium'
[ $USER = 'root' ] && set "$@" 'dietpi-launcher' 'dietpi-software' 'dietpi-config'
[ -d ~/Desktop ] || mkdir -p ~/Desktop
for i in "$@"
do
[ -f /usr/share/applications/$i.desktop ] && ! [ -f ~/Desktop/$i.desktop ] && ln -sf /usr/share/applications/$i.desktop ~/Desktop/$i.desktop
done
# Create file manager bookmarks
if ! [ -f ~/.gtk-bookmarks ]
then
cp /var/lib/dietpi/dietpi-software/installed/desktop/.gtk-bookmarks ~/.gtk-bookmarks
sed --follow-symlinks -i "s|/root|$HOME|g" ~/.gtk-bookmarks
fi
# Disable this autostart entry
[ -d ~/.config/autostart ] || mkdir -p ~/.config/autostart
echo '[Desktop Entry]\nHidden=true' > ~/.config/autostart/dietpi-desktop_setup.desktop
_EOF_
G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/desktop/dietpi-desktop_setup.sh
cat << '_EOF_' > /etc/xdg/autostart/dietpi-desktop_setup.desktop
[Desktop Entry]
Version=1.0
Name=DietPi-Desktop_setup
Type=Application
Comment=Adds default desktop entries and configs to the users desktop
NoDisplay=true
Exec=/var/lib/dietpi/dietpi-software/installed/desktop/dietpi-desktop_setup.sh
Icon=/var/lib/dietpi/dietpi-software/installed/desktop/icons/dietpi-icon.png
_EOF_
G_THREAD_WAIT
# https://gitlab.xfce.org/xfce/thunar/-/merge_requests/317 > https://packages.debian.org/bookworm/thunar
if (( $G_DISTRO < 7 && ${aSOFTWARE_INSTALL_STATE[25]} == 1 ))
then
G_DIETPI-NOTIFY 2 'Adding execute bit to our default desktop launchers/icons'
adesktop_items=()
for i in 'opentyrian' 'kodi' 'steam' 'dxx-rebirth' 'chromium' 'chromium-browser' 'htop' 'codium' 'dietpi-launcher' 'dietpi-software' 'dietpi-config'
do
[[ -f /usr/share/applications/$i.desktop ]] && adesktop_items+=("/usr/share/applications/$i.desktop")
done
G_EXEC chmod +x "${adesktop_items[@]}"
fi
}
# Add desktop entry for users which logged into a desktop already and hence have DietPi-Desktop_setup disabled.
# $1 = Application name
Create_Desktop_Shortcut()
{
local uid gid app=$1
for i in /root /home/*
do
[[ -f $i/.config/autostart/dietpi-desktop_setup.desktop && ! -f $i/Desktop/$app.desktop ]] || continue
if [[ ! -d $i/Desktop ]]
then
# shellcheck disable=SC2012
read -r uid gid < <(ls -dn "$i" | mawk '{print $3,$4}')
G_EXEC mkdir -p "$i/Desktop"
G_EXEC chown "$uid:$gid" "$i/Desktop"
fi
G_EXEC ln -sf "/usr/share/applications/$app.desktop" "$i/Desktop/$app.desktop"
# https://gitlab.xfce.org/xfce/thunar/-/issues/50
(( ${aSOFTWARE_INSTALL_STATE[25]} > 0 )) && G_EXEC chmod +x "/usr/share/applications/$app.desktop"
done
}
Create_Required_Dirs()
{
G_EXEC mkdir -p /mnt/dietpi_userdata/{Music,Pictures,Video,downloads} /var/www /opt /usr/local/bin
getent passwd dietpi > /dev/null && G_EXEC chown dietpi:dietpi /mnt/dietpi_userdata/{Music,Pictures,Video,downloads}
G_EXEC chmod 0775 /mnt/dietpi_userdata/{Music,Pictures,Video,downloads}
}
Download_Test_Media()
{
[[ -f '/mnt/dietpi_userdata/Music/fourdee_tech.ogg' ]] && return
G_EXEC curl -sSf 'https://dietpi.com/downloads/audio/fourdee_tech.ogg' -o /mnt/dietpi_userdata/Music/fourdee_tech.ogg
getent passwd dietpi > /dev/null && G_EXEC chown dietpi:dietpi /mnt/dietpi_userdata/Music/fourdee_tech.ogg
G_EXEC chmod 0664 /mnt/dietpi_userdata/Music/fourdee_tech.ogg
}
# Return optimisation values for BitTorrent servers based on device and hardware capabilities.
Optimise_BitTorrent()
{
local gigabit_ethernet=1
(( $G_HW_MODEL < 4 || $G_HW_MODEL == 40 || $G_HW_MODEL == 60 || $G_HW_MODEL == 70 )) && gigabit_ethernet=0
local output
case $1 in
0) output=$(( $RAM_PHYS / 10 ));; # Cache size (MiB) 10% of RAM size
1)
# Max active downloads
output=2
# Bump up for x86_64
(( $G_HW_ARCH == 10 )) && output=3
;;
2)
# Max global connections
output=20
# Bump up for Gbit Ethernet
(( $gigabit_ethernet )) && output=30
# Bump up for x86_64
(( $G_HW_ARCH == 10 )) && output=40
# Reduce for RPi 1-3 due to the USB bus Ethernet in the ARM SoC, which cripples network throughput/performance/latency.
case $G_HW_MODEL in
3) output=15;;
2) output=13;;
1) output=7;;
*) :;;
esac
;;
3)
# Max upload slots
output=3
# Bump up for Gbit Ethernet
(( $gigabit_ethernet )) && output=4
# Bump up for x86_64
(( $G_HW_ARCH == 10 )) && output=5
# Reduce for RPi 1-3 due to the USB bus Ethernet in the ARM SoC, which cripples network throughput/performance/latency.
(( $G_HW_MODEL < 4 )) && output=2
;;
*) G_DIETPI-NOTIFY 1 "Invalid argument \$1=\"$1\" for function Optimise_BitTorrent()"; return 1;;
esac
echo "$output"
}
# Usage:
# Download_Install 'https://file.com/file' [/path/to/target]
# dps_index=$software_id Download_Install 'conf_0' /etc/conf.conf
# Optional input variables:
# fallback_url='http...' = URL to use if e.g. grabbing URL from api.github.com fails
# dps_index=$software_id = Download from DietPi GitHub repo based on software ID/index
# aDEPS=('pkg1' 'pkg2' ...) = Install APT dependency packages
# NB: This does not support installs that require user input (e.g.: a whiptail prompt for deb installs)
Download_Install()
{
# Verify input URL
if [[ $1 ]]
then
local url=$1
elif [[ $fallback_url ]]
then
G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} download URL detection failed."
G_DIETPI-NOTIFY 1 "\"$fallback_url\" will be used as fallback, but a newer version might be available."
G_DIETPI-NOTIFY 1 'Please report this at: https://github.com/MichaIng/DietPi/issues'
local url=$fallback_url
else
G_DIETPI-NOTIFY 1 "An empty download URL was passed during ${aSOFTWARE_NAME[$software_id]} install. Please report this at: https://github.com/MichaIng/DietPi/issues"
return 1
fi
local target=$2 # Target path
local file=${url##*/} # Grab file name from URL
# DietPi-Software conf/service mode
# shellcheck disable=SC2154
[[ $dps_index && ${aSOFTWARE_NAME[$dps_index]} ]] && url="https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/dps_$dps_index/$url"
G_EXEC cd "$G_WORKING_DIR" # Failsafe
# Add decompressor to deps list if missing
case $file in
*'.xz'|*'.txz') command -v xz > /dev/null || aDEPS+=('xz-utils');;
*'.bz2'|*'.tbz2') command -v bzip2 > /dev/null || aDEPS+=('bzip2');;
*'.zip') command -v unzip > /dev/null || aDEPS+=('unzip');;
*'.7z') command -v 7zr > /dev/null || { (( $G_DISTRO > 7 )) && aDEPS+=('7zip') || aDEPS+=('p7zip'); };;
*) :;;
esac
# Download file
if [[ ${aDEPS[0]} ]]
then
# Check URL before starting background download, as a failure does not terminate the install
# shellcheck disable=SC2154
G_CHECK_URL "$url"
# Download as background thread if dependencies are to be installed
G_THREAD_START curl -sSfL "$url" -o "$file"
# shellcheck disable=SC2086
G_AGI "${aDEPS[@]}"
aDEPS=()
G_THREAD_WAIT
else
G_EXEC curl -sSfL "$url" -o "$file"
fi
unset -v fallback_url dps_index
# Process downloaded file
case $file in
*'.gz') [[ $file == *'.tar.gz' ]] || { G_EXEC gzip -df "$file" && file=${file%.gz}; };;
*'.xz') [[ $file == *'.tar.xz' ]] || { G_EXEC xz -df "$file" && file=${file%.xz}; };;
*'.bz2') [[ $file == *'.tar.bz2' ]] || { G_EXEC bzip2 -df "$file" && file=${file%.bz2}; };;
*) :;;
esac
if [[ $file == *'.deb' ]]
then
G_AGI "./$file"
elif [[ $file == *'.zip' ]]
then
G_EXEC unzip -o "$file" ${target:+"-d$target"}
elif [[ $file =~ \.(t?gz|t?xz|t?bz2|tar)$ ]]
then
G_EXEC tar xf "$file" ${target:+"--one-top-level=$target"}
elif [[ $file == *'.7z' ]]
then
[[ $target && ! -d $target ]] && G_EXEC mkdir -p "$target" # Workaround for 7zr ignoring umask: https://github.com/MichaIng/DietPi/issues/4251
G_EXEC 7zr x -y "$file" ${target:+"-o$target"}
elif [[ $target && $target != "$file" ]]
then
# Pre-create target dir if given: $target can be a file name only, so assure it is a path containing a dir
[[ $target == *'/'* && ! -d ${target%/*} ]] && G_EXEC mkdir -p "${target%/*}"
G_EXEC mv "$file" "$target"
else
return 0
fi
[[ -f $file ]] && G_EXEC rm "$file"
}
Create_User()
{
# Parse input and mark as read-only to throw an error if given doubled
local group_primary groups_supplementary home shell password user
while (( $# ))
do
case $1 in
'-g') shift; readonly group_primary=$1;; # Primary group is always pre-created and defaults to same name as user for new users
'-G') shift; readonly groups_supplementary=$1;; # User is only added to supplementary groups that do exist
'-d') shift; readonly home=$1;; # Home is never pre-created and defaults to "/nonexistent" which prevents login
'-s') shift; readonly shell=$1;; # Shell defaults to "nologin" which prevents shell access on login even if home exists
'-p') shift; readonly password=$1;; # Password is only set for new users while existing users' passwords stay untouched
*) readonly user=$1;;
esac
shift
done
# Pre-create given primary group, as useradd and usermod fail if it does not exist
[[ $group_primary ]] && ! getent group "$group_primary" > /dev/null && G_EXEC groupadd -r "$group_primary"
# Only add to supplementary groups that do exist
local group groups
for group in ${groups_supplementary//,/ }
do
getent group "$group" > /dev/null && groups+=",$group"
done
groups=${groups#,}
# Create user if missing, else modify it according to input
if getent passwd "$user" > /dev/null
then
G_EXEC usermod ${group_primary:+-g "$group_primary"} ${groups:+-aG "$groups"} -d "${home:-/nonexistent}" -s "${shell:-$(command -v nologin)}" "$user"
else
local options='-rMU'
[[ $group_primary ]] && options='-rMN'
G_EXEC useradd "$options" ${group_primary:+-g "$group_primary"} ${groups:+-G "$groups"} -d "${home:-/nonexistent}" -s "${shell:-$(command -v nologin)}" "$user"
[[ $password ]] && G_EXEC_DESC="Applying user password: \e[33m${password//?/*}\e[0m" G_EXEC eval "chpasswd <<< '$user:$password'"
fi
}
# Start a service and wait for a default config file to be created.
Create_Config()
{
local file=$1 service=$2 # Config file path and service name
local timeout=${3:-25} # Optional [s]
local output=$4 pid # Optional, if timeout is set, hence for startups which may take long so that showing some process becomes reasonable
# shellcheck disable=SC2154
local content=$CREATE_CONFIG_CONTENT stop=$CC_STOP # Optional required config file content and whether to stop service afterwards (!= 0)
unset -v CREATE_CONFIG_CONTENT CC_STOP
# If file exists already and contains required content, continue install
[[ -f $file ]] && { [[ ! $content ]] || grep -q "$content" "$file"; } && return 0
# Reload services
G_EXEC systemctl daemon-reload
# File does not exist or does not contain required content: Start service
G_EXEC_DESC="Starting ${aSOFTWARE_NAME[$software_id]} to pre-create config file in max $timeout seconds" G_EXEC systemctl start "$service"
# Print and follow output of the service startup
[[ $output ]] && { journalctl -fn 0 -u "$service" & pid=$!; }
# Wait for max $timeout seconds until config file has been created and required content added if given
local i=0
until [[ -f $file ]] && { [[ ! $content ]] || grep -q "$content" "$file"; } || (( $i >= $timeout )) || ! systemctl -q is-active "$service"
do
((i++))
[[ $output ]] || G_DIETPI-NOTIFY -2 "Waiting for ${aSOFTWARE_NAME[$software_id]} config file to be created ($i/$timeout)"
G_SLEEP 1
done
# Stop journal prints
[[ $output ]] && { kill "$pid"; wait "$pid"; } 2> /dev/null
# Stop service
if [[ $stop != 0 ]]
then
G_SLEEP 1
G_EXEC_NOHALT=1 G_EXEC systemctl stop "$service"
G_SLEEP 1
fi
# If file exists already and contains required content, continue install
if [[ -f $file ]] && { [[ ! $content ]] || grep -q "$content" "$file"; }
then
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} config file got created after $i seconds"
return 0
fi
G_DIETPI-NOTIFY 1 "Waiting for ${aSOFTWARE_NAME[$software_id]} config file failed, skipping pre-configuration"
return 1
}
# Remove obsolete SysV service
# $1: Service name
# $2: If set, remove defaults file as well (optional)
Remove_SysV()
{
G_DIETPI-NOTIFY 2 "Removing obsolete $1 SysV service"
[[ -f '/etc/init.d/'$1 ]] && G_EXEC rm "/etc/init.d/$1"
G_EXEC update-rc.d "$1" remove
[[ $2 && -f '/etc/default/'$1 ]] && G_EXEC rm "/etc/default/$1"
}
# Run marked software installs
Install_Software()
{
local software_id aDEPS=()
# Configure iptables to use nf_tables or legacy API, depending on which is supported by kernel
Configure_iptables()
{
local alt='nft'
iptables-nft -L &> /dev/null || alt='legacy'
G_EXEC update-alternatives --set iptables "/usr/sbin/iptables-$alt"
G_EXEC update-alternatives --set ip6tables "/usr/sbin/ip6tables-$alt"
}
Enable_memory_cgroup()
{
if (( $G_HW_MODEL < 10 ))
then
grep -Eq '(^|[[:blank:]])cgroup_enable=memory([[:blank:]]|$)' /boot/cmdline.txt || G_EXEC sed --follow-symlinks -i '/root=/s/[[:blank:]]*$/ cgroup_enable=memory/' /boot/cmdline.txt
elif [[ -f '/boot/boot.scr' ]] && grep -q 'docker_optimizations' /boot/boot.scr
then
# DietPi mainline U-Boot
[[ -f '/boot/dietpiEnv.txt' ]] && G_CONFIG_INJECT 'docker_optimizations=' 'docker_optimizations=on' /boot/dietpiEnv.txt
# Armbian
[[ -f '/boot/armbianEnv.txt' ]] && G_CONFIG_INJECT 'docker_optimizations=' 'docker_optimizations=on' /boot/armbianEnv.txt
# Radxa Zero
[[ -f '/boot/uEnv.txt' ]] && G_CONFIG_INJECT 'docker_optimizations=' 'docker_optimizations=on' /boot/uEnv.txt
elif [[ -f '/boot/extlinux/extlinux.conf' ]]
then
grep -Eq '^[[:blank:]]*append[[:blank:]].*cgroup_enable=memory([[:blank:]]|$)' /boot/extlinux/extlinux.conf || G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*append[[:blank:]]/s/[[:blank:]]*$/ cgroup_enable=memory/' /boot/extlinux/extlinux.conf
fi
}
# $1: Software name, so config can be created and removed per-software
Enable_IP_forwarding()
{
if (( $G_HW_MODEL == 75 ))
then
[[ $(sysctl -n net.ipv4.ip_forward net.ipv6.conf.default.accept_ra net.ipv6.conf.all.accept_ra net.ipv6.conf.default.forwarding net.ipv6.conf.all.forwarding) == $'1\n2\n2\n1\n1' ]] || G_WHIP_MSG "[WARNING] IP forwarding needs to be enabled on container host
\nIP forwarding is not enabled for all interfaces, hence ${aSOFTWARE_NAME[$software_id]} clients cannot access the LAN or Internet behind this DietPi system vice versa.
\nIP forwarding can only be enabled on the host of this container. E.g. add the following lines to a new sysctl config file like /etc/sysctl.d/dietpi-$1.conf:
net.ipv4.ip_forward=1
net.ipv6.conf.default.accept_ra=2
net.ipv6.conf.all.accept_ra=2
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1
\nApply them without reboot like this:
sudo sysctl -p /etc/sysctl.d/dietpi-$1.conf"
else
G_DIETPI-NOTIFY 2 'Enabling IP forwarding to allow access across network interfaces'
G_EXEC eval "echo -e 'net.ipv4.ip_forward=1\nnet.ipv6.conf.default.accept_ra=2\nnet.ipv6.conf.all.accept_ra=2\nnet.ipv6.conf.default.forwarding=1\nnet.ipv6.conf.all.forwarding=1' > '/etc/sysctl.d/dietpi-$1.conf'"
local dryrun=()
[[ ! -w '/proc/sys/net/ipv4/ip_forward' ]] && systemd-detect-virt -cq && dryrun=('--dry-run')
G_EXEC sysctl -p "${dryrun[@]}" "/etc/sysctl.d/dietpi-$1.conf"
fi
}
# Store APT dependencies for Python modules in aDEPS array, or optionally install them directly
# Arguments:
# --install, -i Install APT packages directly
# --user, -u User to install Rust with
# --rust, -r Install Rust in any case
# --no-piwheels, -P Declare that piwheels is not used
# pyenv Add APT dependencies needed to compile Python for pyenv
# <module> Add APT dependencies needed to compile and in case run the named module
# Environment variables:
# PYTHON_VERSION Python version in case e.g. pyenv is used
Python_Deps()
{
local install=0 user rust=0 piwheels=0
if [[ $PYTHON_VERSION ]]
then
until [[ $PYTHON_VERSION =~ ^3\.[0-9]+$ ]]; do PYTHON_VERSION=${PYTHON_VERSION%\.*}; done
else
case $G_DISTRO in
6) PYTHON_VERSION='3.9';;
7) PYTHON_VERSION='3.11';;
*) PYTHON_VERSION='3.13';;
esac
fi
PYTHON_VERSION=${PYTHON_VERSION/.} # so we can check in bash arithmetic
# Can piwheels be used? The Python version needs to match the Debian version, else dynamically linked shared libraries may not match!
if (( $G_HW_ARCH < 3 ))
then
if (( ( $G_DISTRO == 6 && $PYTHON_VERSION == 39 ) ||
( $G_DISTRO == 7 && $PYTHON_VERSION == 311 ) ||
( $G_DISTRO > 7 && $PYTHON_VERSION == 313 ) ))
then
piwheels=1
fi
fi
# Process arguments
while (( $# ))
do
case $1 in
'--install'|'-i') install=1;;
'--user'|'-u') shift; user=$1;;
'--rust'|'-r') rust=1;;
'--no-piwheels'|'-P') piwheels=0;;
'av') (( $G_HW_ARCH == 11 || ( $G_HW_ARCH < 3 && $PYTHON_VERSION != 313 ) )) && aDEPS+=('libavdevice-dev');;
'bcrypt') (( $G_HW_ARCH == 11 || ( $G_HW_ARCH == 1 && ! $piwheels ) )) && rust=1;;
'brotli'|'sabctools'|'ujson') (( $G_HW_ARCH == 11 || ( $G_HW_ARCH < 3 && ! $piwheels ) )) && aDEPS+=('g++');;
'cffi') (( $G_HW_ARCH == 11 || ( $G_HW_ARCH < 3 && $PYTHON_VERSION != 311 ) )) && aDEPS+=('gcc' 'libffi-dev');;
'cryptography')
if (( $G_HW_ARCH == 11 || ( $G_HW_ARCH == 1 && ! $piwheels ) ))
then
aDEPS+=('pkg-config' 'libssl-dev')
rust=1
fi
set -- "$@" cffi
;;
'greenlet113') (( $PYTHON_VERSION > 311 || $G_HW_ARCH == 11 || ( $G_HW_ARCH < 3 && ! $piwheels ) )) && aDEPS+=('g++');;
'libsass') (( $G_HW_ARCH == 10 || $piwheels )) || aDEPS+=('g++');;
'lxml') (( $G_HW_ARCH == 1 && ( ! $piwheels || $PYTHON_VERSION == 313 ) )) && aDEPS+=('libxslt1-dev');;
'matrix-synapse') (( $PYTHON_VERSION > 311 || $G_HW_ARCH == 11 || ( $G_HW_ARCH < 3 && ! $piwheels ) )) && rust=1;;
'netifaces') (( $piwheels || ( $G_HW_ARCH == 10 && $PYTHON_VERSION <= 39 ) )) || aDEPS+=('gcc');;
'numpy')
if (( $G_HW_ARCH == 11 || ( $G_HW_ARCH == 1 && ! $piwheels ) ))
then
aDEPS+=('g++' 'pkg-config' 'libopenblas-pthread-dev')
# cmake + make + automake > patchelf > meson-python > numpy
# cmake > ninja > meson-python > numpy
(( $G_HW_ARCH == 1 )) && aDEPS+=('cmake' 'make' 'automake')
fi
if (( $piwheels ))
then
aDEPS+=('libgfortran5' 'libopenblas0-pthread')
fi
;;
'pillow')
(( $G_HW_ARCH == 11 || ( $G_HW_ARCH < 3 && ( ! $piwheels || $PYTHON_VERSION == 39 ) ) )) && aDEPS+=('gcc' 'libjpeg62-turbo-dev')
if (( $piwheels && $PYTHON_VERSION != 39 ))
then
aDEPS+=('libopenjp2-7' 'libxcb1')
(( $G_DISTRO > 6 )) && aDEPS+=('libtiff6') || aDEPS+=('libtiff5')
fi
;;
'poetry')
# cryptography > SecretStorage > keyring > poetry
set -- "$@" cryptography
;;
'psutil') (( $G_HW_ARCH < 3 || $G_HW_ARCH == 11 )) && aDEPS+=('gcc');;
'psycopg2') (( $piwheels && $PYTHON_VERSION != 39 )) || aDEPS+=('gcc' 'libpq-dev');;
'pycurl') (( $G_HW_ARCH == 11 || ( $G_HW_ARCH < 3 && ! $piwheels ) )) && aDEPS+=('gcc' 'libcurl4-openssl-dev' 'libssl-dev');;
'pydantic')
# pydantic_core > pydantic
(( $G_HW_ARCH == 1 || $G_HW_ARCH == 11 )) && rust=1
;;
'pyenv')
# Required: gcc libc6-dev make libssl-dev zlib1g-dev
# Optional to suppress warnings: libbz2-dev libreadline-dev libsqlite3-dev liblzma-dev
# libffi-dev for the optional _ctypes module, needed at least for Home Assistant => include it in all pyenv builds to avoid surprises
aDEPS+=('gcc' 'libc6-dev' 'make' 'libssl-dev' 'zlib1g-dev' 'libbz2-dev' 'libreadline-dev' 'libsqlite3-dev' 'liblzma-dev' 'libffi-dev')
;;
'pymicro-vad') aDEPS+=('g++');;
'pynacl')
(( $G_HW_ARCH == 11 || ( $G_HW_ARCH < 3 && ! $piwheels ) )) && aDEPS+=('make')
set -- "$@" cffi
;;
*) G_DIETPI-NOTIFY 1 "Unknown argument \"$1\" for Python_Deps(). This should never happen unless you are developing a software implementation. Otherwise, please report this at https://github.com/MichaIng/DietPi/issues. Aborting ..."; exit 1;;
esac
shift
done
unset -v PYTHON_VERSION
# Install Rust if needed
if (( $rust ))
then
G_EXEC curl -sSfo rustup-init.sh 'https://sh.rustup.rs'
G_EXEC chmod +x rustup-init.sh
# ARMv6 Bullseye: rustup-init depends on libatomic1, not assured to be pre-installed on Bullseye
(( $G_HW_ARCH == 1 && $G_DISTRO == 6 )) && G_AGI libatomic1 "${aDEPS[@]}" && aDEPS=() install=0
G_EXEC_OUTPUT=1 G_EXEC ${user:+runuser -u "$user" --} ./rustup-init.sh -y --profile minimal
G_EXEC rm rustup-init.sh
[[ $user ]] || export PATH="/root/.cargo/bin:$PATH"
fi
[[ ${aDEPS[*]} ]] || return 0
# Deduplicate
declare -A dedupe
for i in "${aDEPS[@]}"; do dedupe["$i"]=; done
aDEPS=("${!dedupe[@]}")
# Return or install packages
(( $install )) || return 0
G_AGI "${aDEPS[@]}"
aDEPS=()
}
To_Install()
{
(( ${aSOFTWARE_INSTALL_STATE[$1]} == 1 )) || return 1
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" "Installing ${aSOFTWARE_NAME[$1]}: ${aSOFTWARE_DESC[$1]}"
aDEPS=() # failsafe
software_id=$1
shift
[[ $1 ]] || return 0
[[ $(readlink -m "/etc/systemd/system/$1.service") == '/dev/null' ]] && G_EXEC systemctl --no-reload unmask "$1"
aENABLE_SERVICES+=("$@")
}
if To_Install 4 # fish
then
G_AGI fish
# Create DietPi source script
G_EXEC mkdir -p /etc/fish/conf.d
# - Run only if interactive
G_EXEC eval 'echo '\''if status is-interactive'\'' > /etc/fish/conf.d/dietpi.fish'
# - Take aliases from /etc/bashrc.d/dietpi.bash
mawk '/# - DietPi programs/ {flag=1; next}
/# - Optional DietPi software aliases/ {flag=0}
flag {print}' /etc/bashrc.d/dietpi.bash >> /etc/fish/conf.d/dietpi.fish
# - Launch dietpi-login script for first run setup and autostart features
cat << '_EOF_' >> /etc/fish/conf.d/dietpi.fish
function fish_greeting
/boot/dietpi/dietpi-login
end
end
_EOF_
G_EXEC chsh root -s /usr/bin/fish
getent passwd dietpi &> /dev/null && G_EXEC chsh dietpi -s /usr/bin/fish
fi
if To_Install 5 # ALSA
then
# Get chosen sound card, use default if unset or "none"
local soundcard=$(sed -n '/^[[:blank:]]*CONFIG_SOUNDCARD=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ ! $soundcard || $soundcard == 'none' ]] && soundcard='default'
# Apply: Installs "alsa-utils"
/boot/dietpi/func/dietpi-set_hardware soundcard "$soundcard"
fi
if To_Install 6 # X11
then
# Generic X server and utilities
aDEPS=('xserver-xorg-core' 'xserver-xorg-input-libinput' 'xinit' 'dbus-user-session' 'xfonts-base' 'x11-xserver-utils' 'x11-utils')
# Pre-create config dir: https://dietpi.com/forum/t/19963
G_EXEC mkdir -p /etc/X11/xorg.conf.d
# RPi
if (( $G_HW_MODEL < 10 ))
then
# Add fbdev display driver for legacy framebuffer graphics support, as modesetting requires KMS driver overlay for /dev/dri/card0 to exists.
aDEPS+=('xserver-xorg-video-fbdev')
# RPi 5: Add config to force X11 using the correct DRM device: https://forums.raspberrypi.com/viewtopic.php?t=358853
(( $G_HW_MODEL == 5 )) && cat << '_EOF_' > /etc/X11/xorg.conf.d/02-dietpi-rpi5.conf
Section "OutputClass"
Identifier "vc4"
MatchDriver "vc4"
Driver "modesetting"
Option "PrimaryGPU" "true"
EndSection
_EOF_
# x86_64 VM
elif (( $G_HW_ARCH == 10 && $G_HW_MODEL == 20 ))
then
# If KMS/DRM is supported, add VMware DDX, which offers slightly better performance compared to modesetting. VirtualBox can emulate it as well, which is even the nowadays recommended default.
# Else (e.g. Hyper-V) add classic framebuffer DDX
[[ -d '/dev/dri' ]] && aDEPS+=('xserver-xorg-video-vmware') || aDEPS+=('xserver-xorg-video-fbdev')
# Amlogic S905
elif [[ $G_HW_CPUID == 7 && -d '/dev/dri' ]]
then
cat << '_EOF_' > /etc/X11/xorg.conf.d/02-dietpi-aml-s905.conf
Section "OutputClass"
Identifier "Amlogic"
MatchDriver "meson"
Driver "modesetting"
Option "PrimaryGPU" "true"
EndSection
Section "Screen"
Identifier "Default Screen"
Device "Meson"
Monitor "foo"
DefaultDepth 24
SubSection "Display"
Depth 24
Modes "1920x1080" "1440x900" "1280x720" "1280x1024" "1280x960" "1024x768" "800x600" "640x480" "720x400"
EndSubSection
EndSection
_EOF_
fi
# Disable DPMS and screen blanking
cat << '_EOF_' > /etc/X11/xorg.conf.d/98-dietpi-disable_dpms.conf
Section "Extensions"
Option "DPMS" "Disable"
EndSection
Section "ServerFlags"
Option "BlankTime" "0"
EndSection
_EOF_
# Install packages
G_AGI "${aDEPS[@]}"
aDEPS=()
fi
if To_Install 191 snapserver # Snapcast Server
then
# RISC-V: Install from Debian repo: https://github.com/snapcast/snapcast/releases
if (( $G_HW_ARCH == 11 ))
then
G_AGI snapserver
# Install and enable snapweb web UI, not included in Debian's snapserver package: https://github.com/MichaIng/DietPi/issues/7073
local fallback_url='https://github.com/snapcast/snapweb/releases/download/v0.9.2/snapweb_0.9.2-1_all.deb'
Download_Install "$(curl -sSfL 'https://api.github.com/repos/snapcast/snapweb/releases/latest' | mawk -F\" '/^ *"browser_download_url": ".*\/snapweb_[^"\/]*_all.deb"$/{print $4}')"
G_CONFIG_INJECT 'doc_root[[:blank:]=]' 'doc_root = /usr/share/snapweb' /etc/snapserver.conf '\[http\]'
# Fix /var/lib/snapserver permissions, just in case snapcast's package was previously used, which uses the "snapserver" user, instead of "_snapserver"
[[ -d '/var/lib/snapserver' ]] && G_EXEC chown -R '_snapserver:_snapserver' /var/lib/snapserver
getent passwd snapserver > /dev/null && G_EXEC userdel snapserver
else
local arch=$(dpkg --print-architecture)
local dist=${G_DISTRO_NAME/forky/trixie}
local fallback_url="https://github.com/snapcast/snapcast/releases/download/v0.34.0/snapserver_0.34.0-1_${arch}_$dist.deb"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/snapcast/snapcast/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/snapserver_[^\"\/]*_${arch}_$dist.deb\"$/{print \$4}")"
# Fix /var/lib/snapserver permissions, just in case Debian's package was previously used, which uses the "_snapserver" user, instead of "snapserver"
[[ -d '/var/lib/snapserver' ]] && G_EXEC chown -R 'snapserver:snapserver' /var/lib/snapserver
getent passwd _snapserver > /dev/null && G_EXEC userdel _snapserver
fi
G_EXEC systemctl stop snapserver
# Disable JSON RPC by default if setting was never touched yet
G_EXEC sed --follow-symlinks -i '/^\[tcp\]/,/^\[/s/^#enabled = true$/enabled = false/' /etc/snapserver.conf
fi
if To_Install 192 snapclient # Snapcast Client
then
# RISC-V: Install from Debian repo: https://github.com/snapcast/snapcast/releases
if (( $G_HW_ARCH == 11 ))
then
G_AGI snapclient
else
local arch=$(dpkg --print-architecture)
local dist=${G_DISTRO_NAME/forky/trixie}
local fallback_url="https://github.com/snapcast/snapcast/releases/download/v0.34.0/snapclient_0.34.0-1_${arch}_$dist.deb"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/snapcast/snapcast/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/snapclient_[^\"\/]*_${arch}_$dist.deb\"$/{print \$4}")"
fi
G_EXEC systemctl stop snapclient
# Config: Skip it host has been applied before. ToDo: v0.32.0 deprecated "--host"/"--port" in favour of "host:port" URI argument. But v0.31.0 from Debian does not support this yet.
if ! grep -q '^[[:blank:]]*SNAPCLIENT_OPTS=.*["[:blank:]]-h[[:blank:]]' /etc/default/snapclient
then
# Ask for server, use local one by default if installed
dpkg-query -s 'snapserver' &> /dev/null && G_WHIP_DEFAULT_ITEM='127.0.0.1'
G_WHIP_NOCANCEL=1
G_WHIP_INPUTBOX 'Please enter the IP address or hostname of your Snapcast server to connect this client to:'
if [[ $G_WHIP_RETURNED_VALUE ]]
then
local snapcast_server_host=$G_WHIP_RETURNED_VALUE
G_WHIP_DEFAULT_ITEM=1704 G_WHIP_NOCANCEL=1 G_WHIP_INPUTBOX_REGEX='^[1-9][0-9]*$' G_WHIP_INPUTBOX_REGEX_TEXT='a valid port number'
G_WHIP_INPUTBOX 'Please enter the network port of your Snapcast server (default: 1704):'
local snapcast_server_port=$G_WHIP_RETURNED_VALUE
G_CONFIG_INJECT 'SNAPCLIENT_OPTS=' "SNAPCLIENT_OPTS=\"-h $snapcast_server_host -p $snapcast_server_port\"" /etc/default/snapclient
fi
fi
# If no Snapcast server was defined explicitly, e.g. on unattended install without having the server installed first, server and client use Avahi to find each other.
# The client throws endless errors if Avahi is not running, and endless messages until the server announced itself: https://github.com/snapcast/snapcast/issues/647
# Hence Avahi-Daemon is added as fixed dependency for the server, and added for the client hereby if no server was explicitly defined. But is this as fallback only for unattended installs.
if ! grep -q '^[[:blank:]]*SNAPCLIENT_OPTS=.*["[:blank:]]-h[[:blank:]]' /etc/default/snapclient
then
G_DIETPI-NOTIFY 2 'Adding Avahi-Daemon as dependency for the Snapcast client to be able to auto-detect a server'
aSOFTWARE_INSTALL_STATE[152]=1
fi
fi
if To_Install 152 avahi-daemon # Avahi-Daemon
then
G_AGI avahi-daemon
G_EXEC systemctl stop avahi-daemon
fi
if To_Install 17 # Git
then
G_AGI git
fi
if To_Install 3 # Midnight Commander
then
G_AGI mc
fi
if To_Install 0 # OpenSSH Client
then
G_AGI openssh-client
fi
if To_Install 1 # Samba Client
then
G_AGI cifs-utils smbclient
fi
if To_Install 110 # NFS Client
then
# "netbase" is required for mounting NFSv3 and showmount to solve "clnt_create: RPC: Unknown host": https://github.com/MichaIng/DietPi/issues/1898#issuecomment-406247814
G_AGI nfs-common netbase
fi
if To_Install 104 dropbear # Dropbear
then
# Stop OpenSSH service to unbind port 22
systemctl -q is-active ssh && G_EXEC systemctl stop ssh
G_AGI dropbear
# Enable Dropbear daemon, obsolete since Bookworm
(( $G_DISTRO > 6 )) || G_CONFIG_INJECT 'NO_START=' 'NO_START=0' /etc/default/dropbear
# Apply password login setting from dietpi.txt or default (implies a restart)
# - Assure service is active
G_EXEC systemctl unmask dropbear
G_EXEC systemctl start dropbear
/boot/dietpi/func/dietpi-set_software disable_ssh_password_logins
# Mark OpenSSH for uninstall
dpkg-query -s 'openssh-server' &> /dev/null && aSOFTWARE_INSTALL_STATE[105]=-1
fi
if To_Install 105 ssh # OpenSSH Server
then
# Stop Dropbear service to unbind port 22
systemctl -q is-active dropbear && G_EXEC systemctl stop dropbear
G_AGI openssh-server
# Apply password login setting from dietpi.txt or default (implies a restart)
# - Assure service is active
G_EXEC systemctl unmask ssh
G_EXEC systemctl start ssh
/boot/dietpi/func/dietpi-set_software disable_ssh_password_logins
# Mark Dropbear for uninstall
dpkg-query -s 'dropbear' &> /dev/null || dpkg-query -s 'dropbear-run' &> /dev/null && aSOFTWARE_INSTALL_STATE[104]=-1
fi
if To_Install 194 postgresql # PostgreSQL
then
G_DIETPI-NOTIFY 2 'Preparing database directory at: /mnt/dietpi_userdata/postgresql'
if [[ -d '/mnt/dietpi_userdata/postgresql' ]]; then
G_DIETPI-NOTIFY 2 '/mnt/dietpi_userdata/postgresql exists, will migrate containing databases'
else
# Otherwise use possibly existent /var/lib/postgresql
# - Remove possible dead symlinks/files:
G_EXEC rm -f /mnt/dietpi_userdata/postgresql
if [[ -d '/var/lib/postgresql' ]]; then
G_DIETPI-NOTIFY 2 '/var/lib/postgresql exists, will migrate containing databases'
# Failsafe: Move symlink target in case, otherwise readlink will resolve to dir
G_EXEC mv "$(readlink -f '/var/lib/postgresql')" /mnt/dietpi_userdata/postgresql
else
G_EXEC mkdir /mnt/dietpi_userdata/postgresql
fi
fi
G_EXEC rm -Rf /var/lib/postgresql
G_EXEC ln -s /mnt/dietpi_userdata/postgresql /var/lib/postgresql
G_AGI postgresql
G_EXEC systemctl stop postgresql
# Disable TCP/IP listener and assure that UNIX domain socket is enabled at expected path
for i in /etc/postgresql/*/main/conf.d
do
[[ -d $i ]] || continue
echo "# Disable TCP listener and assure that UNIX socket is enabled at expected path
# NB: Do not edit this file, instead override settings via e.g.: conf.d/99local.conf
listen_addresses = ''
unix_socket_directories = '/run/postgresql'" > "$i/00dietpi.conf"
done
fi
if To_Install 103 dietpi-ramlog # DietPi-RAMlog
then
# Install persistent tmpfs
local var_log_size=$(sed -n '/^[[:blank:]]*AUTO_SETUP_RAMLOG_MAXSIZE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
sed --follow-symlinks -i '/[[:blank:]]\/var\/log[[:blank:]]/d' /etc/fstab
echo "tmpfs /var/log tmpfs size=${var_log_size:-50}M,noatime,lazytime,nodev,nosuid" >> /etc/fstab
# Apply logging choice index
[[ $INDEX_LOGGING == -[12] ]] || INDEX_LOGGING=-1
# Sync logs to disk once
local acommand=('/boot/dietpi/func/dietpi-ramlog' '1')
systemctl -q is-active dietpi-ramlog && acommand=('systemctl' 'stop' 'dietpi-ramlog')
G_EXEC_DESC='Storing /var/log metadata to disk' G_EXEC "${acommand[@]}"
unset -v acommand
# Cleanup mount point
findmnt /var/log > /dev/null && G_EXEC_DESC='Unmounting /var/log' G_EXEC_NOHALT=1 G_EXEC umount -Rfl /var/log
G_EXEC_DESC='Cleaning /var/log mount point' G_EXEC rm -Rf /var/log/{,.??,.[^.]}*
# Start DietPi-RAMlog
G_EXEC_DESC='Mounting tmpfs to /var/log' G_EXEC mount /var/log
G_EXEC_DESC='Restoring metadata to /var/log tmpfs' G_EXEC systemctl start dietpi-ramlog
fi
if To_Install 101 # Logrotate
then
G_AGI logrotate
fi
if To_Install 190 # Beets
then
# Config: Preserve existing on reinstall
G_EXEC mkdir -p /mnt/dietpi_userdata/beets
[[ -f '/mnt/dietpi_userdata/beets/config.yaml' ]] || G_EXEC eval 'echo -e '\''directory: /mnt/dietpi_userdata/Music\nlibrary: /mnt/dietpi_userdata/beets/library.db'\'' > /mnt/dietpi_userdata/beets/config.yaml'
# Allow dietpi user and audio group members to manage library
[[ -f '/mnt/dietpi_userdata/beets/library.db' ]] || > /mnt/dietpi_userdata/beets/library.db
[[ -f '/mnt/dietpi_userdata/beets/state.pickle' ]] || > /mnt/dietpi_userdata/beets/state.pickle
# shellcheck disable=SC2015
getent passwd dietpi > /dev/null && G_EXEC chown -R dietpi:audio /mnt/dietpi_userdata/beets || G_EXEC chgrp -R audio /mnt/dietpi_userdata/beets
G_EXEC chmod -R g+w /mnt/dietpi_userdata/beets
# Load global beets config in all interactive bash shells
echo 'export BEETSDIR=/mnt/dietpi_userdata/beets' > /etc/bashrc.d/dietpi-beets.sh
# shellcheck disable=SC1091
. /etc/bashrc.d/dietpi-beets.sh
# Install
G_AGI beets
fi
if To_Install 102 rsyslog # Rsyslog
then
# Workaround for dpkg failure on 1st install if service is already running but APT not installed: https://github.com/MichaIng/DietPi/pull/2277/#issuecomment-441460925
systemctl -q is-active rsyslog && G_EXEC systemctl stop rsyslog
G_AGI rsyslog
G_EXEC systemctl start rsyslog
# Apply logging choice index
grep -q '[[:blank:]]/var/log[[:blank:]]' /etc/fstab || findmnt -t tmpfs -M /var/log > /dev/null || INDEX_LOGGING=-3
fi
if To_Install 7 # FFmpeg
then
# RPi: Enable hardware codecs
(( $G_HW_MODEL > 9 )) || /boot/dietpi/func/dietpi-set_hardware rpi-codec 1
G_AGI ffmpeg
fi
if To_Install 196 # Java JRE
then
local JAVA_VERSION=21
# ARMv6/7: Install Debian's OpenJDK packages since Adoptium does not support it
if (( $G_HW_ARCH < 3 ))
then
# ARMv6/Bullseye/Bookworm: Java 17 is the latest available one
(( $G_DISTRO < 8 || $G_HW_ARCH == 1 )) && JAVA_VERSION=17
G_AGI "openjdk-$JAVA_VERSION-jre-headless"
JAVA_PATH="/usr/lib/jvm/java-$JAVA_VERSION-openjdk-$(dpkg --print-architecture)/bin/java"
else
# APT key: https://adoptium.net/installation/linux
G_EXEC mkdir -p /etc/apt/keyrings
G_EXEC curl -sSfLo /etc/apt/keyrings/dietpi-adoptium.asc 'https://packages.adoptium.net/artifactory/api/gpg/key/public'
# APT source
G_EXEC eval "cat << '_EOF_' > /etc/apt/sources.list.d/dietpi-adoptium.sources
Types: deb
URIs: https://packages.adoptium.net/artifactory/deb
Suites: ${G_DISTRO_NAME/forky/trixie}
Components: main
Signed-By: /etc/apt/keyrings/dietpi-adoptium.asc"
G_AGUP
# Allow Debian's OpenJDK to be autopurged
G_EXEC apt-mark auto 'openjdk-*-jre-headless'
G_AGI "temurin-$JAVA_VERSION-jre"
JAVA_PATH="/usr/lib/jvm/temurin-$JAVA_VERSION-jre-$(dpkg --print-architecture)/bin/java"
fi
# Define some default max heap size here
local JAVA_MAX_HEAP_SIZE=$(( $RAM_PHYS / 5 ))
(( $JAVA_MAX_HEAP_SIZE < 200 )) && JAVA_MAX_HEAP_SIZE=200
# - On 32-bit systems the head size cannot be above 3 GiB
(( $G_HW_ARCH < 3 && $JAVA_MAX_HEAP_SIZE > 3072 )) && JAVA_MAX_HEAP_SIZE=3072
fi
if To_Install 8 # Java JDK
then
if (( $G_HW_ARCH < 3 ))
then
G_AGI "openjdk-$JAVA_VERSION-jdk-headless"
else
# Allow OpenJDK to be autopurged
G_EXEC apt-mark auto 'openjdk-*-jdk-headless'
G_AGI "temurin-$JAVA_VERSION-jdk"
fi
fi
if To_Install 9 # Node.js
then
# Deps (verified 2025-10-22): https://github.com/MichaIng/DietPi/issues/3614, https://dietpi.com/forum/t/24574
(( $G_HW_ARCH == 1 && $G_DISTRO > 6 )) || aDEPS=('libatomic1')
# Download installer
Download_Install 'https://raw.githubusercontent.com/MichaIng/nodejs-linux-installer/master/node-install.sh'
G_EXEC chmod +x node-install.sh
# ARMv6/RISC-V: Use unofficial builds to get the latest version: https://github.com/MichaIng/nodejs-linux-installer/pull/2, https://github.com/MichaIng/nodejs-linux-installer/commit/cd952fe
local options=()
(( $G_HW_ARCH == 1 || $G_HW_ARCH == 11 )) && options=('-u')
# ARMv6/7 Bullseye: node: /usr/lib/arm-linux-gnueabihf/libstdc++.so.6: version `GLIBCXX_3.4.30'/`GLIBCXX_3.4.29' not found (required by node)
if (( $G_HW_ARCH < 3 && $G_DISTRO < 7 ))
then
if (( $G_HW_ARCH == 1 ))
then
version='v20.19.4' # newest ARMv6 builds which does not depend on GLIBCXX_3.4.30
else
local version=22 fallback='v22.21.0'
version=$(./node-install.sh -l | mawk "/^v$version/{print \$1;exit}")
[[ $version ]] || version=$fallback
fi
options+=('-v' "$version")
fi
G_EXEC_OUTPUT=1 G_EXEC ./node-install.sh "${options[@]}"
G_EXEC rm node-install.sh
fi
if To_Install 130 # Python 3
then
# Allow pip to install modules on system level, and mute the warning when doing so as root (which is needed, of course)
>> /etc/pip.conf
G_CONFIG_INJECT '\[global\]' '[global]' /etc/pip.conf
G_CONFIG_INJECT 'break-system-packages[[:blank:]]*=' 'break-system-packages=true' /etc/pip.conf '\[global\]'
G_CONFIG_INJECT 'root-user-action[[:blank:]]*=' 'root-user-action=ignore' /etc/pip.conf '\[global\]'
# Disable cache: It is rarely ever needed to install the very same module and version twice, hence this usually only causes unnecessary disk writes and usage.
G_CONFIG_INJECT 'no-cache-dir[[:blank:]]*=' 'no-cache-dir=true' /etc/pip.conf '\[global\]'
# ARMv6/7: Add piwheels
(( $G_HW_ARCH < 3 )) && G_CONFIG_INJECT 'extra-index-url[[:blank:]]*=' 'extra-index-url=https://www.piwheels.org/simple/' /etc/pip.conf '\[global\]'
# Perform pip3 install (which includes setuptools and wheel modules)
aDEPS=('python3-dev')
Download_Install 'https://bootstrap.pypa.io/get-pip.py'
G_EXEC_OUTPUT=1 G_EXEC python3 get-pip.py
G_EXEC rm get-pip.py
fi
if To_Install 150 # Mono: https://www.mono-project.com/download/stable/#download-lin-debian
then
# Trixie: Install from Debian repo instead: "mono-devel : Depends: libglib2.0-0 (>= 2.58.3) but it is not installable"
if (( $G_DISTRO < 8 ))
then
# APT key
G_EXEC curl -sSfL 'https://download.mono-project.com/repo/xamarin_ring.gpg' -o /etc/apt/trusted.gpg.d/dietpi-mono.gpg
# APT list
# - Only Buster suites are available: https://download.mono-project.com/repo/debian/dists/
# - On Raspbian use separate suite: https://github.com/MichaIng/DietPi/issues/1023
local suite='buster'
(( $G_RASPBIAN )) && suite='raspbianbuster'
G_EXEC eval "echo 'deb https://download.mono-project.com/repo/debian $suite main' > /etc/apt/sources.list.d/dietpi-mono.list"
G_AGUP
fi
# APT package
G_AGI mono-devel
# Cleanup: https://github.com/MichaIng/DietPi/issues/1877#issuecomment-403856446
G_EXEC rm -f /tmp/mono*
fi
if To_Install 188 # Go
then
# https://go.dev/doc/install#install
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='amd64';;
11) local arch='riscv64';;
*) local arch='armv6l';;
esac
# Get latest version
local file=$(curl -sSfL 'https://go.dev/dl/?mode=json' | grep -o "go[0-9.]*\.linux-$arch\.tar\.gz" | head -1)
[[ $file ]] || { file="go1.25.5.linux-$arch.tar.gz"; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. \"$file\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
# Reinstall: Remove previous instance
[[ -d '/usr/local/go' ]] && G_EXEC rm -R /usr/local/go
Download_Install "https://dl.google.com/go/$file" /usr/local
# Add to PATH, but do not overwrite existing script since we did set GOPATH until v8.9 and do not want to cause a breaking change for existing installs
# shellcheck disable=SC2016
[[ -f '/etc/bashrc.d/go.sh' ]] || G_EXEC eval 'echo '\''export PATH="$PATH:/usr/local/go/bin"'\'' > /etc/bashrc.d/go.sh'
# shellcheck disable=SC1091
. /etc/bashrc.d/go.sh
# RISC-V pre-v8.24 and general failsafe action: Purge Go APT Package
dpkg-query -s 'golang-go' &> /dev/null && G_AGP golang-go
fi
if To_Install 170 # UnRAR
then
# On Raspbian, only "unrar-free" is available which does not support all RAR formats, thus we use "unrar" [non-free] from Debian on ARMv7+ models: http://raspbian.raspberrypi.com/raspbian/pool/non-free/u/unrar-nonfree/
if (( $G_HW_ARCH == 1 ))
then
G_AGI unrar-free
elif (( $G_RASPBIAN ))
then
Download_Install "https://dietpi.com/downloads/binaries/rpi/unrar-armhf-$G_DISTRO_NAME.deb"
else
G_AGI unrar
fi
fi
if To_Install 23 # LXDE
then
# LXDE pulls in LightDM as dependency, which sets up itself as display manager so that the system boots automatically into it. So check whether a default display manager is present first, and if not, remove it afterwards.
local display_manager=0
[[ -f '/etc/systemd/system/display-manager.service' ]] && display_manager=1
# librsvg2-common: https://github.com/MichaIng/DietPi/issues/4687
G_AGI lxde lxhotkey-plugin-openbox upower librsvg2-common
[[ $display_manager == 0 && -f '/etc/systemd/system/display-manager.service' ]] && G_EXEC rm /etc/systemd/system/display-manager.service
# lxpanel
G_EXEC curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/lxde/panel" -o /etc/xdg/lxpanel/LXDE/panels/panel
# - Adjust Firefox app icon if it is not installed
if (( ${aSOFTWARE_INSTALL_STATE[67]} < 1 ))
then
# Chromium
if (( ${aSOFTWARE_INSTALL_STATE[113]} > 0 ))
then
local chromium='chromium'
(( $G_DISTRO < 7 && $G_HW_MODEL < 10 )) && chromium='chromium-browser'
G_EXEC sed --follow-symlinks -i "s/firefox-esr/$chromium/" /etc/xdg/lxpanel/LXDE/panels/panel
else
G_EXEC sed --follow-symlinks -i 's/firefox-esr/mousepad/' /etc/xdg/lxpanel/LXDE/panels/panel
fi
fi
# Openbox: Enable only one desktop and disable icon animations
G_CONFIG_INJECT '<number>[0-9]*</number>' '<number>1</number>' /etc/xdg/openbox/LXDE/rc.xml '<desktops>'
G_CONFIG_INJECT '<animateIconify>.*</animateIconify>' '<animateIconify>no</animateIconify>' /etc/xdg/openbox/LXDE/rc.xml '<theme>'
# PCmanFM: Set wallpaper and desktop icon text shadow color
G_CONFIG_INJECT '\[desktop\]' '[desktop]' /etc/xdg/pcmanfm/LXDE/pcmanfm.conf
G_CONFIG_INJECT 'wallpaper_mode=' 'wallpaper_mode=fit' /etc/xdg/pcmanfm/LXDE/pcmanfm.conf '\[desktop\]'
G_CONFIG_INJECT 'wallpaper=' 'wallpaper=/var/lib/dietpi/dietpi-software/installed/desktop/wallpapers/dietpi-logo_inverted_1080p.png' /etc/xdg/pcmanfm/LXDE/pcmanfm.conf '\[desktop\]'
G_CONFIG_INJECT 'desktop_shadow=' 'desktop_shadow=#333333' /etc/xdg/pcmanfm/LXDE/pcmanfm.conf '\[desktop\]'
# Apply better default icon theme: nuoveXT2 has more individual icons than Adwaita, but lower resolution and overrides the Firefox application icon with an old low res version.
G_CONFIG_INJECT 'sNet/IconThemeName=' 'sNet/IconThemeName=Adwaita' /etc/xdg/lxsession/LXDE/desktop.conf '\[GTK\]'
# Workaround for "No session for pid" error: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=864402
G_CONFIG_INJECT 'polkit/command=' 'polkit/command=' /etc/xdg/lxsession/LXDE/desktop.conf '\[Session\]'
# Disable trash
G_CONFIG_INJECT 'use_trash=' 'use_trash=0' /etc/xdg/libfm/libfm.conf '\[config\]'
# Execute desktop icons and executable files directly without asking what to do
G_CONFIG_INJECT 'quick_exec=' 'quick_exec=1' /etc/xdg/libfm/libfm.conf '\[config\]'
Create_Desktop_Shared_Items
fi
if To_Install 173 # LXQt
then
# SpeedCrunch has been removed from Debian since Trixie: https://tracker.debian.org/pkg/speedcrunch
local speedcrunch=('speedcrunch')
(( $G_DISTRO > 7 )) && speedcrunch=()
# lxqt-archiver is the default dependency from Trixie on, but our config uses Archiver=xarchiver.
G_AGI lxqt xarchiver xscreensaver "${speedcrunch[@]}"
# Configs
Download_Install "https://github.com/$G_GITOWNER/DietPi/raw/$G_GITBRANCH/.conf/desktop/lxqt/lxqt-$G_DISTRO_NAME.7z" /etc/xdg/
Create_Desktop_Shared_Items
# Composition manager: LXQt has one integrated with xfwm4, which is blocked by xcompmgr, breaking settings and leading to ugly results: https://github.com/MichaIng/DietPi/issues/3665
[[ -f '/etc/xdg/autostart/xcompmgr.desktop' ]] && G_EXEC rm /etc/xdg/autostart/xcompmgr.desktop
fi
if To_Install 24 # MATE
then
# Add xterm, as the mate-terminal is not compatible with desktop console shortcuts: https://github.com/MichaIng/DietPi/issues/3160#issuecomment-828305136
G_AGI mate-desktop-environment-core mate-media upower xterm
Create_Desktop_Shared_Items
fi
if To_Install 26 # GNUstep
then
G_AGI wmaker gnustep gnustep-devel gnustep-games xterm
Create_Desktop_Shared_Items
fi
if To_Install 25 # Xfce
then
# Elevated permissions for non-root users to perform e.g. reboot and shutdown from logout menu
local polkit='polkitd'
(( $G_DISTRO < 7 )) && polkit='policykit-1'
G_AGI xfce4 xfce4-terminal gnome-icon-theme tango-icon-theme upower "$polkit"
Create_Desktop_Shared_Items
# Composition manager: Xfce has one integrated with xfwm4, which is blocked by xcompmgr, breaking settings and leading to ugly results: https://github.com/MichaIng/DietPi/issues/3665
[[ -f '/etc/xdg/autostart/xcompmgr.desktop' ]] && G_EXEC rm /etc/xdg/autostart/xcompmgr.desktop
fi
if To_Install 175 # Xfce Power Manager
then
G_AGI xfce4-power-manager
fi
if To_Install 22 # QuiteRSS
then
G_AGI quiterss
fi
if To_Install 67 # Firefox
then
# libpci3: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=993308
G_AGI firefox-esr libpci3
# Config
#G_CONFIG_INJECT 'pref\("extensions.update.enabled"' 'pref("extensions.update.enabled", false);' /etc/firefox-esr/firefox-esr.js
#G_CONFIG_INJECT 'pref\("datareporting.healthreport.uploadEnabled"' 'pref("datareporting.healthreport.uploadEnabled", false);' /etc/firefox-esr/firefox-esr.js
#G_CONFIG_INJECT 'pref\("browser.cache.disk.parent_directory"' 'pref("browser.cache.disk.parent_directory", "/tmp/firefox_cache");' /etc/firefox-esr/firefox-esr.js
#G_CONFIG_INJECT 'pref\("general.smoothScroll"' 'pref("general.smoothScroll", false);' /etc/firefox-esr/firefox-esr.js
fi
if To_Install 174 # GIMP
then
G_AGI gimp
fi
if To_Install 29 # XRDP
then
G_AGI xrdp xorgxrdp
aSTART_SERVICES+=('xrdp')
# Workaround for failing mouse and keyboard input: https://github.com/neutrinolabs/xorgxrdp/issues/164
# - Solved on Bookworm, but G_CONFIG_INJECT detects and skips it then, and keeping it covers dist-upgraded systems.
GGI_PRESERVE=1 G_CONFIG_INJECT 'Option[[:blank:]]+"DefaultServerLayout"' ' Option "DefaultServerLayout" "X11 Server"' /etc/X11/xrdp/xorg.conf 'Section[[:blank:]]+"ServerFlags"'
# Enable TLS with snakeoil certificate: https://github.com/MichaIng/DietPi/issues/5976
G_EXEC usermod -aG ssl-cert xrdp
fi
if To_Install 30 # NoMachine
then
case $G_HW_ARCH in
1) local url='raspberry/v6/deb/nomachine_latest_armv6hf';;
2) local url='arm/v7/deb/nomachine_latest_armhf';;
3) local url='arm/v8/deb/nomachine_latest_arm64';;
*) local url='linux/64/deb/nomachine_latest_amd64';;
esac
Download_Install "https://www.nomachine.com/free/$url.deb"
aSTART_SERVICES+=('nxserver')
fi
if To_Install 200 # DietPi-Dashboard
then
G_WHIP_MSG '[ WARN ] Reworked DietPi-Dashboard beta version
\nYou are about to install a reworked beta version of the DietPi-Dashboard which has not undergone much testing yet. Please assure to close its network ports (default: TCP 5252 and 5253) on your NAT/router or via firewall, to protect them from public access.
\nIf you need to access the DietPi-Dashboard remotely, please use a VPN server on this system, like WireGuard.
\nIf the old version is currently installed, it will remain on the system but its service is disabled. So you can switch between the versions for now, if needed. They use different config files and executables.'
# Stable release or nightly?
local version='Nightly' # Enforce nightly until 0.7.0 is released
#local version=$(sed -n '/^[[:blank:]]*SOFTWARE_DIETPI_DASHBOARD_VERSION=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
#[[ $version == 'Nightly' ]] || version='Stable'
#G_WHIP_MENU_ARRAY=(
# 'Stable' ': Use a release version of DietPi-Dashboard.'
# 'Nightly' ': Use an unstable version of DietPi-Dashboard.'
#)
#G_WHIP_DEFAULT_ITEM=$version
#G_WHIP_BUTTON_CANCEL_TEXT=$version
#G_WHIP_MENU 'Please choose which version of DietPi-Dashboard should be installed.' && version=$G_WHIP_RETURNED_VALUE
# Determine which components to install
local components=('backend')
local backend=$(sed -n '/^[[:blank:]]*SOFTWARE_DIETPI_DASHBOARD_BACKEND=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $backend == 1 ]] && G_WHIP_DEFAULT_ITEM='Backend' || G_WHIP_DEFAULT_ITEM='Frontend'
G_WHIP_MENU_ARRAY=(
'Backend' ': Install the DietPi-Dashboard backend only.'
'Frontend' ': Install both the DietPi-Dashboard backend and webserver.'
)
G_WHIP_MENU 'DietPi-Dashboard has two components, a frontend and a backend.
\nThe frontend is required to be installed on at least one node to view the webpage. The backend must be installed on all nodes.'
[[ $G_WHIP_RETURNED_VALUE == 'Backend' ]] || components+=('frontend')
# Download binary/binaries
if [[ $version == 'Nightly' ]]
then
command -v unzip > /dev/null || G_AGI unzip # G_CHECK_URL => HEAD request on nightly.link always returns 404, hence prevent Download_Install() from doing concurrent unzip install, which forks the download into background and runs G_CHECK_URL first.
for component in "${components[@]}"; do
Download_Install "https://nightly.link/nonnorm/DietPi-Dashboard/workflows/push-build/main/dietpi-dashboard-$G_HW_ARCH_NAME-$component.zip" /opt/dietpi-dashboard
done
else
# NB: Will not work until 0.7.0 is released
local manifest
# shellcheck disable=SC2016
G_EXEC eval 'manifest=$(curl -sSfL '\''https://api.github.com/repos/nonnorm/dietpi-dashboard/releases/latest'\'')'
for component in "${components[@]}"; do
Download_Install "$(echo "$manifest" | mawk -F\" "/^ *\"browser_download_url\": \".*dietpi-dashboard-$G_HW_ARCH_NAME-$component\"$/{print \$4}")" /opt/dietpi-dashboard
done
fi
# Users
Create_User dietpi-dashboard-frontend
# Services
G_EXEC eval 'cat << '\''_EOF_'\'' > /etc/systemd/system/dietpi-dashboard-backend.service
[Unit]
Description=Dashboard Backend (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/opt/dietpi-dashboard/backend
[Install]
WantedBy=multi-user.target
_EOF_'
[[ ${components[1]} ]] && G_EXEC eval 'cat << '\''_EOF_'\'' > /etc/systemd/system/dietpi-dashboard-frontend.service
[Unit]
Description=Dashboard Web Server (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
User=dietpi-dashboard-frontend
ExecStart=/opt/dietpi-dashboard/frontend
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
NoNewPrivileges=true
ReadWritePaths=-/opt/dietpi-dashboard
[Install]
WantedBy=multi-user.target
_EOF_'
# Derive based on existing backend config (since the backend is always installed) whether this is a new instance or a reinstall, so we know whether to pre-configure it
local new_instance=0
[[ -f '/opt/dietpi-dashboard/config-backend.toml' ]] || new_instance=1
# Pre-create empty frontend config with needed owner
[[ ${components[1]} && $new_instance == 1 ]] && G_EXEC install -m 0640 -o dietpi-dashboard-frontend /dev/null /opt/dietpi-dashboard/config-frontend.toml
for component in "${components[@]}"; do
G_EXEC chmod +x "/opt/dietpi-dashboard/$component"
CREATE_CONFIG_CONTENT='.' Create_Config "/opt/dietpi-dashboard/config-$component.toml" "dietpi-dashboard-$component"
aSTART_SERVICES+=("dietpi-dashboard-$component")
done
# Config: Touch for new instances only
if (( $new_instance ))
then
# If frontend is installed
if [[ ${components[1]} ]]
then
# Enable TLS OOTB with self-signed certificate
G_DIETPI-NOTIFY 2 'Generating a self-signed HTTPS certificate for the DietPi-Dashboard ...'
openssl req -reqexts SAN -subj '/CN=DietPi-Dashboard' -config <(cat /etc/ssl/openssl.cnf <(echo -ne "[SAN]\nsubjectAltName=DNS:$(</etc/hostname),IP:$(G_GET_NET ip)\nbasicConstraints=CA:TRUE,pathlen:0"))\
-x509 -days 7200 -sha256 -extensions SAN -out /opt/dietpi-dashboard/cert.pem\
-newkey ec:<(openssl ecparam -name prime256v1) -nodes -keyout /opt/dietpi-dashboard/privkey.pem || exit 1 # Replace deprecated -nodes with -noenc when dropping Bullseye support
G_EXEC chown dietpi-dashboard-frontend /opt/dietpi-dashboard/{cert,privkey}.pem
G_CONFIG_INJECT 'cert_path[[:blank:]]' 'cert_path = "/opt/dietpi-dashboard/cert.pem"' /opt/dietpi-dashboard/config-frontend.toml
G_CONFIG_INJECT 'key_path[[:blank:]]' 'key_path = "/opt/dietpi-dashboard/privkey.pem"' /opt/dietpi-dashboard/config-frontend.toml
G_CONFIG_INJECT 'enable_tls[[:blank:]]' 'enable_tls = true' /opt/dietpi-dashboard/config-frontend.toml
# Enable password authentication OOTB with default software password
GCI_PASSWORD=1 G_CONFIG_INJECT 'hash[[:blank:]]' "hash = \"$(echo -n "$GLOBAL_PW" | sha512sum | mawk '{print $1}')\"" /opt/dietpi-dashboard/config-frontend.toml
G_CONFIG_INJECT 'enable_login[[:blank:]]' 'enable_login = true' /opt/dietpi-dashboard/config-frontend.toml
# Create and assign a new random secret to match frontend and backend
local secret=$(openssl rand -hex 32)
GCI_PASSWORD=1 G_CONFIG_INJECT 'secret[[:blank:]]' "secret = \"$secret\"" /opt/dietpi-dashboard/config-frontend.toml
GCI_PASSWORD=1 G_CONFIG_INJECT 'secret[[:blank:]]' "secret = \"$secret\"" /opt/dietpi-dashboard/config-backend.toml
unset -v secret
else
# Ask for frontend address to connect
G_WHIP_DEFAULT_ITEM="$(G_GET_NET gateway):5253"
G_WHIP_INPUTBOX_REGEX='^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9]):[1-9][0-9]+$'
G_WHIP_INPUTBOX_REGEX_TEXT="match the format \"<ip/hostname>:<port>\", e.g. \"$G_WHIP_DEFAULT_ITEM\""
G_WHIP_BUTTON_CANCEL_TEXT='Skip'
G_WHIP_INPUTBOX "Please enter the IP address or hostname and network port of the frontend this backend shall connect to, in the format \"<ip/hostname>:<port>\", e.g. \"$G_WHIP_DEFAULT_ITEM\".
\nYou can find the port in /opt/dietpi-dashboard/config-frontend.toml on the frontend host system, or the \"Config\" page of its web UI.
\nThis can be changed any time later in /opt/dietpi-dashboard/config-backend.toml." &&
G_CONFIG_INJECT 'frontend_addr[[:blank:]]' "frontend_addr = \"$G_WHIP_RETURNED_VALUE\"" /opt/dietpi-dashboard/config-backend.toml
# Ask for frontend secret
G_WHIP_BUTTON_CANCEL_TEXT='Skip'
G_WHIP_PASSWORD 'Please enter the secret of the frontend this backend shall connect to.
\nYou can find the secret in /opt/dietpi-dashboard/config-frontend.toml on the frontend host system, or the "Config" page of its web UI.
\nThis can be changed any time later in /opt/dietpi-dashboard/config-backend.toml.' &&
GCI_PASSWORD=1 G_CONFIG_INJECT 'secret[[:blank:]]' "secret = \"${result//\"/\\\"}\"" /opt/dietpi-dashboard/config-backend.toml
unset -v result
fi
# If /mnt/dietpi_userdata points to a mount point, add that one to disks shown on system page
local userdata=$(findmnt -no TARGET -T "$(realpath /mnt/dietpi_userdata)")
[[ $userdata != '/' ]] && G_CONFIG_INJECT 'disks[[:blank:]]' "disks = [\"/\", \"$userdata\"]" /opt/dietpi-dashboard/config-backend.toml
fi
# Pre-v9.18: Disable old version but leave installed for now
if systemctl -q is-enabled dietpi-dashboard
then
G_EXEC systemctl disable dietpi-dashboard
G_WHIP_MSG '[ INFO ] Reboot required for new dashboard to load
\nThe old dashboard has been disabled, but not stopped, so this install does not kill itself in case it was triggered from the dashboard. This prevents the startup of the new service, unless network ports have been changed.
\nHence a reboot may be required afterwards for the new dashboard to replace the old one at port 5252.'
fi
fi
if To_Install 99 node_exporter # Prometheus Node Exporter
then
case $G_HW_ARCH in
1) local arch='armv6';;
2) local arch='armv7';;
3) local arch='arm64';;
11) local arch='riscv64';;
*) local arch='amd64';;
esac
# Download binary
local fallback_url="https://github.com/prometheus/node_exporter/releases/download/v1.10.2/node_exporter-1.10.2.linux-$arch.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/prometheus/node_exporter/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/node_exporter-.*\.linux-$arch\.tar\.gz\"$/{print \$4}")"
G_EXEC mkdir -p /opt/node_exporter
G_EXEC cp --preserve=mode node_exporter*/node_exporter /opt/node_exporter/
G_EXEC rm -R node_exporter*
# User
Create_User node_exporter
# Services
cat << '_EOF_' > /etc/systemd/system/node_exporter.service
[Unit]
Description=Prometheus Node Exporter (DietPi)
Documentation=https://prometheus.io/docs/guides/node-exporter/
[Service]
User=node_exporter
RuntimeDirectory=node_exporter
ExecStart=/opt/node_exporter/node_exporter --log.level=warn --collector.textfile.directory=/run/node_exporter
[Install]
WantedBy=multi-user.target
_EOF_
# RPi: Install raspberrypi_exporter
if (( $G_HW_MODEL < 10 ))
then
Download_Install 'https://github.com/fahlke/raspberrypi_exporter/archive/master.tar.gz'
G_CONFIG_INJECT 'ConditionPathExists=' 'ConditionPathExists=/run/node_exporter' raspberrypi_exporter-master/raspberrypi_exporter.service '\[Unit\]'
G_CONFIG_INJECT 'ExecStart=' 'ExecStart=/opt/node_exporter/raspberrypi_exporter' raspberrypi_exporter-master/raspberrypi_exporter.service
G_CONFIG_INJECT 'METRICS_FILE=' 'METRICS_FILE='\''/run/node_exporter/raspberrypi-metrics.prom'\' raspberrypi_exporter-master/raspberrypi_exporter
G_EXEC mv {raspberrypi_exporter-master,/opt/node_exporter}/raspberrypi_exporter
G_EXEC mv {raspberrypi_exporter-master,/etc/systemd/system}/raspberrypi_exporter.service
G_EXEC mv {raspberrypi_exporter-master,/etc/systemd/system}/raspberrypi_exporter.timer
G_EXEC rm -R raspberrypi_exporter-master
aENABLE_SERVICES+=('raspberrypi_exporter')
fi
fi
if To_Install 44 transmission-daemon # Transmission
then
G_AGI transmission-daemon
G_EXEC systemctl stop transmission-daemon
# Remove obsolete service and environment files
Remove_SysV transmission-daemon 1
G_EXEC rm -f /etc/init/transmission-daemon.conf
# Apply optimised settings
G_CONFIG_INJECT '"download-queue-size"' " \"download-queue-size\": $(Optimise_BitTorrent 1)," /etc/transmission-daemon/settings.json '^\{$'
G_CONFIG_INJECT '"peer-limit-global"' " \"peer-limit-global\": $(Optimise_BitTorrent 2)," /etc/transmission-daemon/settings.json '^\{$'
G_CONFIG_INJECT '"upload-slots-per-torrent"' " \"upload-slots-per-torrent\": $(Optimise_BitTorrent 3)," /etc/transmission-daemon/settings.json '^\{$'
G_CONFIG_INJECT '"encryption"' ' "encryption": 2,' /etc/transmission-daemon/settings.json '^\{$'
G_CONFIG_INJECT '"trash-original-torrent-files"' ' "trash-original-torrent-files": true,' /etc/transmission-daemon/settings.json '^\{$'
G_CONFIG_INJECT '"download-dir"' ' "download-dir": "/mnt/dietpi_userdata/downloads",' /etc/transmission-daemon/settings.json '^\{$'
G_CONFIG_INJECT '"rpc-username"' ' "rpc-username": "root",' /etc/transmission-daemon/settings.json '^\{$'
G_CONFIG_INJECT '"rpc-whitelist-enabled"' ' "rpc-whitelist-enabled": false,' /etc/transmission-daemon/settings.json '^\{$'
# - Generate hash from global software password: https://github.com/transmission/transmission/blob/7367d46/libtransmission/crypto-utils.cc#L58
local salt=$(tr -dc '[:alnum:]./' < /dev/random | head -c8)
local hash=$(echo -n "$GLOBAL_PW$salt" | sha1sum | mawk '{print $1}')
GCI_PASSWORD=1 G_CONFIG_INJECT '"rpc-password"' " \"rpc-password\": \"{$hash$salt\"," /etc/transmission-daemon/settings.json '^\{$'
unset -v hash salt
# Grant R/W access to and from download managers, media and file servers: Make "dietpi" the primary group, "debian-transmission" a supplementary group and create downloads with group write mode
G_EXEC usermod -g dietpi -aG debian-transmission debian-transmission
G_CONFIG_INJECT '"umask"' ' "umask": 7,' /etc/transmission-daemon/settings.json '^\{$'
fi
if To_Install 94 proftpd # ProFTPD
then
G_EXEC eval "debconf-set-selections <<< 'proftpd-basic shared/proftpd/inetd_or_standalone select standalone'"
G_AGI proftpd-basic
G_EXEC systemctl stop proftpd
# Disable conflicting activation socket: It would require "ServerType inetd" and replaces proftpd.service with ondemand proftpd@.service starts for each client connection.
G_EXEC systemctl --no-reload disable --now proftpd.socket
# Config
G_BACKUP_FP /etc/proftpd/proftpd.conf
dps_index=$software_id Download_Install 'conf' /etc/proftpd/proftpd.conf
fi
if To_Install 96 nmbd smbd # Samba Server
then
# Link disk cache to RAM: https://github.com/MichaIng/DietPi/issues/2396
# - Remove previous disk cache dir or symlink
G_EXEC rm -Rf /var/cache/samba
# - Pre-create RAM cache dir
G_EXEC mkdir -p /run/samba-cache
# - Link disk cache to RAM
G_EXEC ln -s /run/samba-cache /var/cache/samba
# - Create RAM cache dir automatically on boot
echo 'd /run/samba-cache' > /etc/tmpfiles.d/dietpi-samba_cache.conf
# Config: Preserve on reinstall
local reinstall=1
if [[ ! -f '/etc/samba/smb.conf' ]] || diff /usr/share/samba/smb.conf /etc/samba/smb.conf &> /dev/null
then
reinstall=0
dps_index=$software_id Download_Install 'conf' /etc/samba/smb.conf
G_CONFIG_INJECT 'max connections =' " max connections = $(( $G_HW_CPU_CORES * 2 ))" /etc/samba/smb.conf
Create_User -g dietpi -d /mnt/dietpi_userdata samba
G_CONFIG_INJECT 'valid users =' ' valid users = samba' /etc/samba/smb.conf
fi
G_AGI samba
G_EXEC systemctl stop nmbd smbd
(( $reinstall )) || echo -e "$GLOBAL_PW\n$GLOBAL_PW" | smbpasswd -s -a samba
fi
if To_Install 95 vsftpd # vsftpd
then
# Config: Preserve on reinstall
[[ -f '/etc/vsftpd.conf' ]] || dps_index=$software_id Download_Install 'conf' /etc/vsftpd.conf
G_AGI vsftpd
G_EXEC systemctl stop vsftpd
# Do not allow root access via FTP
G_EXEC sed --follow-symlinks -i 's/^[[:blank:]]*root/#root/' /etc/ftpusers
fi
if To_Install 109 nfs-server # NFS Server
then
G_AGI nfs-kernel-server
G_EXEC systemctl stop nfs-server
G_EXEC mkdir -p /etc/exports.d
[[ -f '/etc/exports.d/dietpi.exports' ]] || G_EXEC eval 'echo '\''/mnt/dietpi_userdata *(rw,async,no_root_squash,crossmnt,no_subtree_check)'\'' > /etc/exports.d/dietpi.exports'
fi
if To_Install 83 apache2 # Apache
then
# Pre-create a dummy port 80 vhost if it does not exist yet, so we can avoid overwriting it on reinstalls.
if [[ ! -f '/etc/apache2/sites-available/000-default.conf' ]]
then
G_EXEC mkdir -p /etc/apache2/sites-available
cat << _EOF_ > /etc/apache2/sites-available/000-default.conf
# /etc/apache2/sites-available/000-default.conf
<VirtualHost *:80>
ServerName $(G_GET_NET ip)
</VirtualHost>
_EOF_
# Otherwise assure that the webroot is changed, as all our install options depend on it.
else
G_EXEC sed --follow-symlinks -i 's|/var/www/html|/var/www|g' /etc/apache2/sites-available/000-default.conf
fi
local apackages=('apache2')
# Install Certbot module if Certbot was already installed
(( ${aSOFTWARE_INSTALL_STATE[92]} == 2 )) && apackages+=('python3-certbot-apache')
G_AGI "${apackages[@]}"
G_EXEC systemctl stop apache2
apachectl -M | grep -q 'cache_disk' || G_EXEC systemctl --no-reload disable --now apache-htcacheclean
# Enable event MPM and headers module
G_EXEC a2dismod -fq mpm_prefork
G_EXEC a2enmod -q mpm_event headers
# Disable obsolete default configs
for i in 'charset' 'localized-error-pages' 'other-vhosts-access-log' 'security' 'serve-cgi-bin'
do
[[ -L /etc/apache2/conf-enabled/$i.conf ]] && G_EXEC a2disconf "$i"
done
# Config
cat << _EOF_ > /etc/apache2/conf-available/dietpi.conf
# /etc/apache2/conf-available/dietpi.conf
# Default server name and webroot
ServerName $(G_GET_NET ip)
DocumentRoot /var/www
# Logging to: journalctl -u apache2
ErrorLog syslog
# Allow unlimited Keep-Alive requests
MaxKeepAliveRequests 0
# MPM event configuration
# - Run a single process which does not expire
# - Limit request handler threads to 64
StartServers 1
ServerLimit 1
MaxConnectionsPerChild 0
ThreadsPerChild 64
ThreadLimit 64
MinSpareThreads 1
MaxSpareThreads 64
MaxRequestWorkers 64
# Minimize public info
ServerTokens Prod
ServerSignature Off
TraceEnable Off
# Security headers
Header set X-Content-Type-Options "nosniff"
Header set X-Frame-Options "sameorigin"
Header set X-XSS-Protection "0"
Header set X-Robots-Tag "noindex, nofollow"
Header set X-Permitted-Cross-Domain-Policies "none"
Header set Referrer-Policy "no-referrer"
_EOF_
G_EXEC a2enconf dietpi
# Force service to start after PHP-FPM
G_EXEC mkdir -p /etc/systemd/system/apache2.service.d
G_EXEC eval "echo -e '[Unit]\nAfter=php$PHP_VERSION-fpm.service' > /etc/systemd/system/apache2.service.d/dietpi.conf"
# Webroot
if [[ -f '/var/www/html/index.html' ]]
then
# shellcheck disable=SC2015
[[ -f '/var/www/index.html' ]] && G_EXEC rm /var/www/html/index.html || G_EXEC mv /var/www/html/index.html /var/www/index.html
fi
[[ -d '/var/www/html' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /var/www/html
fi
if To_Install 85 nginx # Nginx
then
local apackages=('nginx-light')
# From Bookworm on, the "nginx" package does not include any extra modules while "nginx-light" pulls in "nginx" and additionally the "echo" module.
(( $G_DISTRO > 6 )) && apackages=('nginx')
# Install Certbot module if Certbot was already installed
(( ${aSOFTWARE_INSTALL_STATE[92]} == 2 )) && apackages+=('python3-certbot-nginx')
G_AGI "${apackages[@]}"
G_EXEC systemctl stop nginx
# Custom configs included by sites-enabled/default within server directive, while nginx/(conf.d|sites-enabled) is included by nginx.conf outside server directive
G_EXEC mkdir -p /etc/nginx/sites-dietpi
G_BACKUP_FP /etc/nginx/nginx.conf
dps_index=$software_id Download_Install 'nginx.conf' /etc/nginx/nginx.conf
# CPU core count
G_EXEC sed --follow-symlinks -i "/worker_processes/c\worker_processes $G_HW_CPU_CORES;" /etc/nginx/nginx.conf
# Default site
dps_index=$software_id Download_Install 'nginx.default' /etc/nginx/sites-available/default
# Force service to start after PHP-FPM
G_EXEC mkdir -p /etc/systemd/system/nginx.service.d
G_EXEC eval "echo -e '[Unit]\nAfter=php$PHP_VERSION-fpm.service' > /etc/systemd/system/nginx.service.d/dietpi.conf"
# Webroot
if [[ -f '/var/www/html/index.nginx-debian.html' ]]
then
# shellcheck disable=SC2015
[[ -f '/var/www/index.html' ]] && G_EXEC rm /var/www/html/index.nginx-debian.html || G_EXEC mv /var/www/html/index.nginx-debian.html /var/www/index.html
fi
[[ -d '/var/www/html' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /var/www/html
fi
if To_Install 84 lighttpd # Lighttpd
then
G_AGI lighttpd
G_EXEC systemctl stop lighttpd
Remove_SysV lighttpd
# Change webroot from /var/www/html to /var/www
G_CONFIG_INJECT 'server.document-root' 'server.document-root = "/var/www"' /etc/lighttpd/lighttpd.conf
if [[ -f '/var/www/html/index.lighttpd.html' ]]
then
# shellcheck disable=SC2015
[[ -f '/var/www/index.html' ]] && G_EXEC rm /var/www/html/index.lighttpd.html || G_EXEC mv /var/www/html/index.lighttpd.html /var/www/index.html
fi
[[ -d '/var/www/html' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /var/www/html
[[ -f '/etc/lighttpd/conf-enabled/99-unconfigured.conf' ]] && G_EXEC lighty-disable-mod unconfigured
# Disable conflicting fastcgi-php module
[[ -f '/etc/lighttpd/conf-enabled/15-fastcgi-php.conf' ]] && G_EXEC lighty-disable-mod fastcgi-php
[[ -f '/etc/lighttpd/conf-enabled/15-fastcgi-php-fpm.conf' ]] || G_EXEC lighty-enable-mod fastcgi-php-fpm
# Force service to start after PHP-FPM
G_EXEC mkdir -p /etc/systemd/system/lighttpd.service.d
G_EXEC eval "echo -e '[Unit]\nAfter=php$PHP_VERSION-fpm.service' > /etc/systemd/system/lighttpd.service.d/dietpi.conf"
fi
if To_Install 88 mariadb # MariaDB
then
G_DIETPI-NOTIFY 2 'Preparing database directory at: /mnt/dietpi_userdata/mysql'
if [[ -d '/mnt/dietpi_userdata/mysql' ]]
then
G_DIETPI-NOTIFY 2 '/mnt/dietpi_userdata/mysql exists, will migrate containing databases'
else
# Otherwise use possibly existent /var/lib/mysql
# - Remove possible dead symlinks/files:
G_EXEC rm -f /mnt/dietpi_userdata/mysql
if [[ -d '/var/lib/mysql' ]]
then
G_DIETPI-NOTIFY 2 '/var/lib/mysql exists, will migrate containing databases'
# Failsafe: Move symlink target in case, otherwise readlink will resolve to dir
G_EXEC mv "$(readlink -f '/var/lib/mysql')" /mnt/dietpi_userdata/mysql
else
G_EXEC mkdir /mnt/dietpi_userdata/mysql
fi
fi
G_EXEC rm -Rf /var/lib/mysql
G_EXEC ln -s /mnt/dietpi_userdata/mysql /var/lib/mysql
local apackages=('mariadb-server')
# Install PHP module if PHP was already installed
(( ${aSOFTWARE_INSTALL_STATE[89]} == 2 )) && apackages+=("php$PHP_VERSION-mysql")
G_AGI "${apackages[@]}"
G_EXEC systemctl stop mariadb
Remove_SysV mysql 1
# Assure correct owner in case the database dir got migrated from a different system: https://github.com/MichaIng/DietPi/issues/4721#issuecomment-917051930
# - The group is correctly recursively fixed by the preinst script already.
[[ -d '/mnt/dietpi_userdata/mysql/mysql' && $(stat -c '%U' /mnt/dietpi_userdata/mysql/mysql) != 'mysql' ]] && find /mnt/dietpi_userdata/mysql ! -user root ! -user mysql -exec chown mysql {} +
# Force service to start before cron
G_EXEC mkdir -p /etc/systemd/system/mariadb.service.d
G_EXEC eval 'echo -e '\''[Unit]\nBefore=cron.service'\'' > /etc/systemd/system/mariadb.service.d/dietpi.conf'
fi
if To_Install 87 # SQLite
then
local apackages=('sqlite3')
# Install PHP module if PHP was already installed
(( ${aSOFTWARE_INSTALL_STATE[89]} == 2 )) && apackages+=("php$PHP_VERSION-sqlite3")
G_AGI "${apackages[@]}"
fi
if To_Install 91 redis-server # Redis
then
local apackages=('redis-server')
# Install PHP module if PHP was already installed
(( ${aSOFTWARE_INSTALL_STATE[89]} == 2 )) && apackages+=("php$PHP_VERSION-redis")
G_AGI "${apackages[@]}"
G_EXEC systemctl stop redis-server
# Enable Redis php module if installed
command -v phpenmod > /dev/null && G_EXEC phpenmod redis
# Disable file logging and enable syslog instead, which resolves reported startup issues in cases as well: https://github.com/MichaIng/DietPi/issues/3291
G_CONFIG_INJECT 'loglevel[[:blank:]]' 'loglevel warning' /etc/redis/redis.conf
G_CONFIG_INJECT 'logfile[[:blank:]]' 'logfile ""' /etc/redis/redis.conf
G_CONFIG_INJECT 'syslog-enabled[[:blank:]]' 'syslog-enabled yes' /etc/redis/redis.conf
G_CONFIG_INJECT 'always-show-logo[[:blank:]]' 'always-show-logo no' /etc/redis/redis.conf
# Force service to start before cron
G_EXEC mkdir -p /etc/systemd/system/redis-server.service.d
G_EXEC eval 'echo -e '\''[Unit]\nBefore=cron.service'\'' > /etc/systemd/system/redis-server.service.d/dietpi.conf'
# Enable memory overcomit: https://github.com/jemalloc/jemalloc/issues/1328
if (( $G_HW_MODEL == 75 ))
then
[[ $(sysctl -n vm.overcommit_memory) == 1 ]] || G_WHIP_MSG '[WARNING] Memory overcomit is disabled
\nFor Redis to run reliably, memory overcomit should be enabled. This can only be done on the host system of this container. E.g. run the following two commands on the host:
echo vm.overcommit_memory=1 | sudo tee /etc/sysctl.d/98-dietpi-redis.conf
sudo sysctl -p /etc/sysctl.d/98-dietpi-redis.conf'
else
G_EXEC eval 'echo '\''vm.overcommit_memory=1'\'' > /etc/sysctl.d/98-dietpi-redis.conf'
local dryrun=()
[[ ! -w '/proc/sys/vm/overcommit_memory' ]] && systemd-detect-virt -cq && dryrun=('--dry-run')
G_EXEC sysctl -p "${dryrun[@]}" /etc/sysctl.d/98-dietpi-redis.conf
fi
fi
if To_Install 89 # PHP
then
# Base PHP modules
# - Webserver: PHP-FPM
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 || ${aSOFTWARE_INSTALL_STATE[84]} > 0 || ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
local apackages=("php$PHP_VERSION-fpm")
aENABLE_SERVICES+=("php$PHP_VERSION-fpm")
# - No webserver: CLI usage only (php binary)
else
local apackages=("php$PHP_VERSION-cli")
fi
# Additional PHP modules, commonly used by most web applications
apackages+=("php$PHP_VERSION-apcu" "php$PHP_VERSION-curl" "php$PHP_VERSION-gd" "php$PHP_VERSION-mbstring" "php$PHP_VERSION-xml" "php$PHP_VERSION-zip")
# MySQL/MariaDB PHP module
(( ${aSOFTWARE_INSTALL_STATE[88]} > 0 )) && apackages+=("php$PHP_VERSION-mysql")
# SQLite PHP module
(( ${aSOFTWARE_INSTALL_STATE[87]} > 0 )) && apackages+=("php$PHP_VERSION-sqlite3")
# Redis PHP module
(( ${aSOFTWARE_INSTALL_STATE[91]} > 0 )) && apackages+=("php$PHP_VERSION-redis")
G_AGI "${apackages[@]}"
systemctl -q is-active "php$PHP_VERSION-fpm" && G_EXEC systemctl stop "php$PHP_VERSION-fpm"
# Assure that mod_php is purged in favour of PHP-FPM
G_AGP 'libapache2-mod-php*'
G_EXEC rm -Rf /{etc/php,var/lib/php/modules}/*/apache2
# PHP-FPM
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 || ${aSOFTWARE_INSTALL_STATE[84]} > 0 || ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
# Optimisations based on total cores
G_CONFIG_INJECT 'pm.max_children[[:blank:]=]' "pm.max_children = $(( $G_HW_CPU_CORES * 3 ))" "/etc/php/$PHP_VERSION/fpm/pool.d/www.conf"
G_CONFIG_INJECT 'pm.start_servers[[:blank:]=]' "pm.start_servers = $G_HW_CPU_CORES" "/etc/php/$PHP_VERSION/fpm/pool.d/www.conf"
G_CONFIG_INJECT 'pm.min_spare_servers[[:blank:]=]' "pm.min_spare_servers = $G_HW_CPU_CORES" "/etc/php/$PHP_VERSION/fpm/pool.d/www.conf"
G_CONFIG_INJECT 'pm.max_spare_servers[[:blank:]=]' "pm.max_spare_servers = $G_HW_CPU_CORES" "/etc/php/$PHP_VERSION/fpm/pool.d/www.conf"
# Set static PATH, not passed by Lighttpd and Nginx by default but required by some web applications: https://github.com/MichaIng/DietPi/issues/5161#issuecomment-1013381362
G_CONFIG_INJECT 'env\[PATH\][[:blank:]=]' 'env[PATH] = /usr/local/bin:/usr/bin:/bin' "/etc/php/$PHP_VERSION/fpm/pool.d/www.conf"
# Force service to start after database servers
G_EXEC mkdir -p "/etc/systemd/system/php$PHP_VERSION-fpm.service.d"
G_EXEC eval "echo -e '[Unit]\nAfter=redis-server.service mariadb.service postgresql.service' > '/etc/systemd/system/php$PHP_VERSION-fpm.service.d/dietpi.conf'"
fi
# We create our own PHP mod to add DietPi specific configs.
target_php_ini="/etc/php/$PHP_VERSION/mods-available/dietpi.ini"
echo -e '; DietPi PHP settings\n; priority=97' > "$target_php_ini"
# Session files need to be outside of /tmp and /var/tmp due to PrivateTmp=true, else phpsessionclean.service cannot clean sessions
G_EXEC mkdir -p /run/php_sessions
G_EXEC chmod 1733 /run/php_sessions
echo -e '# Pre-create PHP sessions dir\nd /run/php_sessions 1733' > /etc/tmpfiles.d/dietpi-php_sessions.conf
G_CONFIG_INJECT 'session.save_path[[:blank:]=]' 'session.save_path="/run/php_sessions"' "$target_php_ini"
# File uploads: https://github.com/MichaIng/DietPi/issues/546
# - This is especially relevant for cloud software like ownCloud/Nextcloud.
# - Generally we want to hold tmp upload files in RAM to reduce disk (especially SD card) writes for performance and disk wear reasons.
# - By default only max 2 MiB file uploads are allowed, hold in /tmp tmpfs, which is safe but not usable for usual cloud usage.
# - ownCloud/Nextcloud do/did override this limit to 512 MiB, a reasonable limit which can usually still be hold in RAM without issues.
# - Low RAM devices (RPi1 256 MiB model) require a swap file for this, however, it is still better to cause disk writes through swap file during large file uploads only, then doing this for each and every uploaded file.
# - When larger file uploads are required, it depends on the system total RAM, rootfs disk and available external drives if/where to move tmp file uploads, resize or move swap file. This should be then left to user.
G_CONFIG_INJECT 'upload_tmp_dir[[:blank:]=]' 'upload_tmp_dir="/tmp"' "$target_php_ini"
G_CONFIG_INJECT 'upload_max_filesize[[:blank:]=]' 'upload_max_filesize=512M' "$target_php_ini"
G_CONFIG_INJECT 'post_max_size[[:blank:]=]' 'post_max_size=512M' "$target_php_ini"
# - Nginx: https://github.com/MichaIng/DietPi/issues/546 => https://github.com/MichaIng/DietPi/blob/dev/.conf/dps_85/nginx.conf
# Cache settings
local cache_size=$(( $RAM_PHYS / 30 ))
(( $cache_size < 16 )) && cache_size=16
# - OPcache
G_CONFIG_INJECT 'opcache.memory_consumption[[:blank:]=]' "opcache.memory_consumption=$cache_size" "$target_php_ini"
G_CONFIG_INJECT 'opcache.revalidate_freq[[:blank:]=]' 'opcache.revalidate_freq=60' "$target_php_ini" # 1 minute
# - JIT is available since PHP 8.0 for x86_64 and since PHP 8.2 for ARMv8. Enable it with 2 MiB size, since we never saw usage far above 1 MiB for whatever reason.
# - Skip on ARMv8 for now, since tracing JIT produces segmentation faults there, and function JIT requires a lot more memory: https://github.com/php/php-src/issues/7817
# - On dietpi.com x86_64 server, we suffered from segmentation faults as well for a while, but there it seems to have been solved.
if (( $G_DISTRO > 6 && $G_HW_ARCH == 10 ))
then
G_CONFIG_INJECT 'opcache.jit[[:blank:]=]' 'opcache.jit=1' "$target_php_ini" # 1/on = "tracing", follow default for now: https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.jit
G_CONFIG_INJECT 'opcache.jit_buffer_size[[:blank:]=]' 'opcache.jit_buffer_size=2M' "$target_php_ini"
fi
# - APCu
G_CONFIG_INJECT 'apc.shm_size[[:blank:]=]' "apc.shm_size=$(( $cache_size / 2 ))M" "$target_php_ini"
G_CONFIG_INJECT 'apc.ttl[[:blank:]=]' 'apc.ttl=259200' "$target_php_ini" # 3 days
# - Enable
G_EXEC phpenmod dietpi
# Apache: Enable PHP-FPM
command -v a2enconf > /dev/null && { G_EXEC a2enmod proxy_fcgi setenvif; G_EXEC a2enconf "php$PHP_VERSION-fpm"; }
fi
if To_Install 34 # PHP Composer
then
G_EXEC curl -sSfL 'https://getcomposer.org/composer-stable.phar' -o /usr/local/bin/composer
G_EXEC chmod +x /usr/local/bin/composer
fi
if To_Install 90 # phpMyAdmin
then
# Install required PHP modules: https://docs.phpmyadmin.net/en/latest/require.html#php
# - Add JSON module for PHP7, as it does not exist (embedded in core package) on PHP8
local json=()
[[ $PHP_VERSION == 8* ]] || json=("php$PHP_VERSION-json")
G_AGI "php$PHP_VERSION"-{curl,gd,mbstring,xml,zip} "${json[@]}"
# Quick install: https://docs.phpmyadmin.net/en/latest/setup.html#quick-install
# - Get latest version name
local version=$(curl -sSfL 'https://api.github.com/repos/phpmyadmin/phpmyadmin/releases' | mawk -F\" '/^ *"name": "/ && $4!~/rc/ {print $4}' | sort -rV | head -1)
[[ $version ]] || { version='5.2.3'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
Download_Install "https://files.phpmyadmin.net/phpMyAdmin/$version/phpMyAdmin-$version-english.tar.xz"
# - Reinstall: Clean install but preserve existing config file
[[ -f '/var/www/phpmyadmin/config.inc.php' ]] && G_EXEC mv /var/www/phpmyadmin/config.inc.php "phpMyAdmin-$version-english/"
G_EXEC rm -Rf /var/www/phpmyadmin # Include pre-v6.27 symlink: https://github.com/MichaIng/DietPi/issues/3304
# - Remove GUI setup: https://docs.phpmyadmin.net/en/latest/setup.html#securing-your-phpmyadmin-installation
G_EXEC rm -R "phpMyAdmin-$version-english/setup"
# - Move new instance in place
G_EXEC mv "phpMyAdmin-$version-english" /var/www/phpmyadmin
# Enable required PHP modules: https://docs.phpmyadmin.net/en/latest/require.html#php
G_EXEC phpenmod ctype curl gd mbstring xml zip "${json[@]##*-}"
# Install and enable webserver config
# - Apache
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
dps_index=$software_id Download_Install 'apache.phpmyadmin.conf' /etc/apache2/sites-available/dietpi-phpmyadmin.conf
G_EXEC a2ensite dietpi-phpmyadmin
# - Lighttpd
elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
dps_index=$software_id Download_Install 'lighttpd.phpmyadmin.conf' /etc/lighttpd/conf-available/98-dietpi-phpmyadmin.conf
G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
G_EXEC lighty-enable-mod dietpi-phpmyadmin
# - Nginx
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
dps_index=$software_id Download_Install 'nginx.phpmyadmin.conf' /etc/apache2/sites-dietpi/dietpi-phpmyadmin.conf
fi
# Copy default config in place and adjust if not already existent
if [[ ! -f '/var/www/phpmyadmin/config.inc.php' ]]
then
G_EXEC cp -a /var/www/phpmyadmin/config.sample.inc.php /var/www/phpmyadmin/config.inc.php
GCI_PASSWORD=1 G_CONFIG_INJECT "\\\$cfg\\['blowfish_secret'][[:blank:]]*=" "\$cfg['blowfish_secret'] = '$(openssl rand -base64 32)';" /var/www/phpmyadmin/config.inc.php
fi
# Create MariaDB database and user
if [[ -d '/mnt/dietpi_userdata/mysql/phpmyadmin' ]]
then
G_DIETPI-NOTIFY 2 'phpMyAdmin MariaDB database found, will NOT overwrite.'
else
/boot/dietpi/func/create_mysql_db phpmyadmin phpmyadmin "$GLOBAL_PW"
mysql phpmyadmin < /var/www/phpmyadmin/sql/create_tables.sql
# Since "root" user cannot be used for login (unix_socket authentication), grant full admin privileges to "phpmyadmin"
mysql -e 'grant all privileges on *.* to phpmyadmin@localhost with grant option;'
fi
# Pre-create TempDir: https://docs.phpmyadmin.net/en/latest/config.html#cfg_TempDir
G_EXEC mkdir -p /var/www/phpmyadmin/tmp
G_EXEC chown www-data:root /var/www/phpmyadmin/tmp
G_EXEC chmod 700 /var/www/phpmyadmin/tmp
fi
if To_Install 125 synapse # Synapse: https://element-hq.github.io/synapse/latest/setup/installation.html#installing-as-a-python-module-from-pypi
then
# Dependencies
Python_Deps -i bcrypt cryptography pillow psycopg2 pynacl matrix-synapse
# Install
G_EXEC_OUTPUT=1 G_EXEC pip3 install -U matrix-synapse psycopg2
# User
Create_User -d /mnt/dietpi_userdata/synapse synapse
# Service
cat << '_EOF_' > /etc/systemd/system/synapse.service
[Unit]
Description=Synapse (DietPi)
Wants=network-online.target
After=network-online.target postgresql.service
[Service]
Type=notify
SyslogIdentifier=Synapse
User=synapse
WorkingDirectory=/mnt/dietpi_userdata/synapse
ExecStart=/usr/bin/python3 -m synapse.app.homeserver -c homeserver.yaml -c homeserver.yaml.d
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
_EOF_
# Database
G_EXEC systemctl start postgresql
local synapse_pass=$(tr -dc '[:alnum:]' < /dev/random | head -c30)
if [[ $(runuser -u postgres -- psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='synapse'") != 1 ]]
then
G_EXEC runuser -u postgres -- psql -c "CREATE ROLE synapse WITH LOGIN PASSWORD '$synapse_pass';"
else
G_EXEC runuser -u postgres -- psql -c "ALTER ROLE synapse WITH PASSWORD '$synapse_pass';"
fi
if [[ $(runuser -u postgres -- psql -tAc "SELECT 1 FROM pg_database WHERE datname='synapse'") != 1 ]]
then
G_EXEC runuser -u postgres -- createdb --encoding=UTF8 --locale=C --template=template0 --owner=synapse synapse
fi
# Pre-create drop-in config for database access details
G_EXEC mkdir -p /mnt/dietpi_userdata/synapse/homeserver.yaml.d
cat << _EOF_ > /mnt/dietpi_userdata/synapse/homeserver.yaml.d/00-dietpi.yaml
# PostgreSQL access details
# NB: Do not edit this file, instead override settings via e.g.: homeserver.yaml.d/99-local.conf
database:
name: psycopg2
args:
host: /run/postgresql
port: 5432
user: synapse
password: $synapse_pass
database: synapse
cp_min: 5
cp_max: 10
_EOF_
# Config
if [[ ! -f '/mnt/dietpi_userdata/synapse/homeserver.yaml' ]]
then
G_WHIP_DEFAULT_ITEM=$(sed -n '/^[[:blank:]]*SOFTWARE_PUBLIC_DOMAIN_NAME=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $G_WHIP_DEFAULT_ITEM ]] || G_WHIP_DEFAULT_ITEM=$(find /etc/letsencrypt/live -mindepth 1 -maxdepth 1 -type d -print -quit 2> /dev/null)
[[ $G_WHIP_DEFAULT_ITEM ]] || G_WHIP_DEFAULT_ITEM=$(hostname -f)
G_WHIP_NOCANCEL=1
G_WHIP_INPUTBOX 'Please enter the name for this homeserver.\nIt is recommended that this be the public domain name that this server will be hosted on.'
local servername=${G_WHIP_RETURNED_VALUE#http*://}
GCI_PRESERVE=1 G_CONFIG_INJECT 'SOFTWARE_PUBLIC_DOMAIN_NAME=' "SOFTWARE_PUBLIC_DOMAIN_NAME=$servername" /boot/dietpi.txt
G_WHIP_DEFAULT_ITEM=$(sed -n '/^[[:blank:]]*SOFTWARE_SYNAPSE_USERNAME=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $G_WHIP_DEFAULT_ITEM ]] || G_WHIP_DEFAULT_ITEM='dietpi'
G_WHIP_NOCANCEL=1
G_WHIP_INPUTBOX 'Please enter the admin username for this homeserver. It will use the default software password.'
local username=$G_WHIP_RETURNED_VALUE
G_EXEC cd /mnt/dietpi_userdata/synapse
# Create initial config
G_EXEC python3 -m synapse.app.homeserver -H "$servername" -c homeserver.yaml --generate-config --report-stats 'no'
# Enable registrations via random shared secret
GCI_PASSWORD=1 G_CONFIG_INJECT 'registration_shared_secret:[[:blank:]]' "registration_shared_secret: $(openssl rand -hex 16)" homeserver.yaml
# Allow remote access
G_CONFIG_INJECT 'bind_addresses:[[:blank:]]' " bind_addresses: ['::']" homeserver.yaml
# Log to journal with log level WARNING
G_EXEC sed --follow-symlinks -i 's/ handlers: \[.*\]$/ handlers: [console]/' "$servername.log.config"
G_EXEC sed --follow-symlinks -i 's/ level: .*/ level: WARNING/' "$servername.log.config"
# Permissions
G_EXEC chown -R synapse:synapse .
G_EXEC chmod 0660 homeserver.yaml{,.d/*.yaml}
G_EXEC chmod 0770 homeserver.yaml.d
# Register admin user
G_EXEC systemctl daemon-reload
G_EXEC systemctl start synapse
G_EXEC_PRE_FUNC(){ acommand[4]="$GLOBAL_PW"; } # Mask password in console output
G_EXEC_OUTPUT=1 G_EXEC register_new_matrix_user -u "$username" -p "${GLOBAL_PW//?/X}" -a -c homeserver.yaml 'http://127.0.0.1:8008'
G_EXEC cd "$G_WORKING_DIR"
unset -v servername username
else
# Permissions
G_EXEC chown -R synapse:synapse /mnt/dietpi_userdata/synapse
G_EXEC chmod 0660 /mnt/dietpi_userdata/synapse/homeserver.yaml{,.d/*.yaml}
G_EXEC chmod 0770 /mnt/dietpi_userdata/synapse/homeserver.yaml.d
fi
fi
if To_Install 128 mpd # MPD
then
G_AGI mpd
G_EXEC systemctl stop mpd
# Service
# - Run systemd unit as "mpd" user instead of using config file based user change, to preserve supplementary group permissions (i.e. "dietpi")
# - Skip environment file so that usage of our config file is assured
G_EXEC mkdir -p /etc/systemd/system/mpd.service.d
cat << '_EOF_' > /etc/systemd/system/mpd.service.d/dietpi.conf
[Service]
User=mpd
RuntimeDirectory=mpd
EnvironmentFile=
ExecStart=
ExecStart=/usr/bin/mpd --systemd
_EOF_
(( $G_DISTRO < 7 )) && G_EXEC sed --follow-symlinks -i 's/systemd/no-daemon/' /etc/systemd/system/mpd.service.d/dietpi.conf
Remove_SysV mpd 1
# Config
G_CONFIG_INJECT 'music_directory[[:blank:]]' 'music_directory "/mnt/dietpi_userdata/Music"' /etc/mpd.conf
G_CONFIG_INJECT 'playlist_directory[[:blank:]]' 'playlist_directory "/mnt/dietpi_userdata/Music"' /etc/mpd.conf
G_CONFIG_INJECT 'db_file[[:blank:]]' 'db_file "/mnt/dietpi_userdata/.mpd_cache/db_file"' /etc/mpd.conf
# - Log to journal
G_EXEC sed --follow-symlinks -Ei 's/^[[:blank:]]*log_file[[:blank:]]+/#log_file /' /etc/mpd.conf
# - PID file is not required for systemd
G_EXEC sed --follow-symlinks -Ei 's/^[[:blank:]]*pid_file[[:blank:]]+/#pid_file /' /etc/mpd.conf
G_CONFIG_INJECT 'state_file[[:blank:]]' 'state_file "/mnt/dietpi_userdata/.mpd_cache/state"' /etc/mpd.conf
G_CONFIG_INJECT 'sticker_file[[:blank:]]' 'sticker_file "/mnt/dietpi_userdata/.mpd_cache/sticker.sql"' /etc/mpd.conf
# - Our service starts as "mpd" user already
G_EXEC sed --follow-symlinks -Ei 's/^[[:blank:]]*user[[:blank:]]+/#user /' /etc/mpd.conf
G_EXEC sed --follow-symlinks -Ei 's/^[[:blank:]]*group[[:blank:]]+/#group /' /etc/mpd.conf
# - Enable UNIX socket
G_CONFIG_INJECT 'bind_to_address[[:blank:]]+"/run/mpd/socket"' 'bind_to_address "/run/mpd/socket"' /etc/mpd.conf
# - Add simple ALSA output if none present yet
grep -q '^[[:blank:]]*audio_output[[:blank:]]' /etc/mpd.conf || cat << '_EOF_' >> /etc/mpd.conf
# Simple ALSA output
audio_output {
type "alsa"
name "DietPi ALSA"
}
_EOF_
# RPi JustBoom can handle SOXR VH @ 192khz 32bit: https://github.com/MichaIng/DietPi/issues/581#issuecomment-256643079
if grep -q '^[[:blank:]]*CONFIG_SOUNDCARD=justboom' /boot/dietpi.txt
then
# Force 192khz 32bit for all outputs: Better would be to apply it to the single JustBoom output device, but we'll leave this for now for simplicity.
G_CONFIG_INJECT 'audio_output_format[[:blank:]]' 'audio_output_format "32:192000:2"' /etc/mpd.conf
# Use highest SOXR quality: "samplerate_converter" is actually deprecated with v0.20.0 and replaced by a "resampler" block, but we'll leave this for now for simplicity.
G_CONFIG_INJECT 'samplerate_converter[[:blank:]]' 'samplerate_converter "soxr very high"' /etc/mpd.conf
fi
# User
Create_User -g audio -G dietpi -d /mnt/dietpi_userdata/.mpd_cache mpd
# Dirs
# - Move possibly existing audio and playlist files to userdata
[[ ! -e '/var/lib/mpd/music' || -L '/var/lib/mpd/music' || $(find /var/lib/mpd/music -maxdepth 0 -empty) ]] || G_EXEC mv -n /var/lib/mpd/music/* /mnt/dietpi_userdata/Music/
[[ ! -e '/var/lib/mpd/playlists' || -L '/var/lib/mpd/playlists' || $(find /var/lib/mpd/playlists -maxdepth 0 -empty) ]] || G_EXEC mv -n /var/lib/mpd/playlists/* /mnt/dietpi_userdata/Music/
# - Purge obsolete data and log dirs and clear cache
G_EXEC rm -Rf /var/lib/mpd /var/log/mpd /mnt/dietpi_userdata/.mpd_cache
# - Recreate cache dir
G_EXEC mkdir /mnt/dietpi_userdata/.mpd_cache
Download_Test_Media
# Permissions
G_EXEC chown -R mpd:root /mnt/dietpi_userdata/.mpd_cache /etc/mpd.conf
fi
if To_Install 54 # phpBB
then
# Pre-v6.33: Support old and new location
PHPBB_LOC='phpbb'
[[ -d '/var/www/phpBB3' ]] && PHPBB_LOC='phpBB3'
# Reinstall: Skip download and install, advice to use internal updater from web UI
if [[ -d /var/www/$PHPBB_LOC ]]
then
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/var/www/$PHPBB_LOC\" already exists. Download and install steps will be skipped.
- If you want to update ${aSOFTWARE_NAME[$software_id]}, please follow the instructions from web UI ACP.
- If you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\"."
else
local fallback_url='https://download.phpbb.com/pub/release/3.3/3.3.15/phpBB-3.3.15.tar.bz2'
Download_Install "$(curl -sSf 'https://version.phpbb.com/phpbb/versions.json' | sed -En '/"stable":/,/"unstable":/s/.*"current": "(.+)",.*/\1/p' | sort -Vr | head -1)"
# Files are shipped with UID:GID 1000:1000 while for security reasons it should be root:root.
G_EXEC chown -R root:root phpBB3
G_EXEC mv phpBB3 /var/www/phpbb
fi
# Preserve old database name
[[ -d '/mnt/dietpi_userdata/mysql/phpbb3' ]] || /boot/dietpi/func/create_mysql_db phpbb phpbb "$GLOBAL_PW"
# Permissions: The web-based installer writes setup and database access information to this file. Upload, cache and store directories are shipped with 777 modes already.
G_EXEC chown www-data "/var/www/$PHPBB_LOC/config.php"
fi
if To_Install 133 yacy # YaCy
then
# Get latest download
local file=$(curl -sSfL 'https://download.yacy.net/?C=N;O=D' | grep -o 'yacy_v[0-9._a-f]*\.tar\.gz' | head -1)
[[ $file ]] || { file='yacy_v1.940_202405270005_70454654f.tar.gz'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. \"$file\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
Download_Install "https://download.yacy.net/$file" /etc
# Service
cat << '_EOF_' > /etc/systemd/system/yacy.service
[Unit]
Description=YaCy (DietPi)
Documentation=https://wiki.yacy.net/index.php/En:Start
Wants=network-online.target
After=network-online.target
[Service]
Type=forking
ExecStart=/etc/yacy/startYACY.sh
ExecStop=/etc/yacy/stopYACY.sh
[Install]
WantedBy=multi-user.target
_EOF_
# Set admin interface passwd:
if [[ ! -f '/etc/yacy/DATA/SETTINGS/yacy.conf' ]]
then
Create_Config /etc/yacy/DATA/SETTINGS/yacy.conf yacy
/etc/yacy/bin/passwd.sh "$GLOBAL_PW"
fi
fi
if To_Install 186 ipfs # Kubo
then
local version=
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='amd64';;
*) local arch='arm' version='v0.33.2';; # latest version with 32-bit builds
esac
if [[ $version ]]
then
G_DIETPI-NOTIFY 2 "For your architecture, the latest available ${aSOFTWARE_NAME[$software_id]} builds are $version."
else
# Find latest version
version=$(curl -sSfL 'https://dist.ipfs.tech/kubo/versions' | sed '/-rc[0-9]*$/d' | tail -1)
[[ $version ]] || { version='v0.39.0'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
fi
Download_Install "https://dist.ipfs.tech/kubo/$version/kubo_${version}_linux-$arch.tar.gz"
# Install
G_EXEC_OUTPUT=1 G_EXEC kubo/install.sh
# Data and config directory
G_EXEC mkdir -p /mnt/dietpi_userdata/ipfs
# User
Create_User -d /mnt/dietpi_userdata/ipfs ipfs
# Service
cat << '_EOF_' > /etc/systemd/system/ipfs.service
[Unit]
Description=Kubo IPFS Node (DietPi)
Documentation=https://docs.ipfs.io/reference/go/api/
Wants=network-online.target
After=network-online.target
[Service]
User=ipfs
Environment=IPFS_PATH=/mnt/dietpi_userdata/ipfs
ExecStart=/usr/local/bin/ipfs daemon
[Install]
WantedBy=multi-user.target
_EOF_
# Config: Preserve existing on reinstall
if [[ ! -f '/mnt/dietpi_userdata/ipfs/config' ]]
then
IPFS_PATH='/mnt/dietpi_userdata/ipfs' G_EXEC_OUTPUT=1 G_EXEC ipfs init
IPFS_PATH='/mnt/dietpi_userdata/ipfs' G_EXEC ipfs config --json Addresses.Gateway '"/ip4/0.0.0.0/tcp/8087"'
IPFS_PATH='/mnt/dietpi_userdata/ipfs' G_EXEC ipfs config --json Addresses.API '"/ip4/0.0.0.0/tcp/5003"'
IPFS_PATH='/mnt/dietpi_userdata/ipfs' G_EXEC ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["*"]'
fi
# Permissions
G_EXEC chown -R 'ipfs:ipfs' /mnt/dietpi_userdata/ipfs
# CLI alias
echo 'alias ipfs='\''sudo -u ipfs IPFS_PATH=/mnt/dietpi_userdata/ipfs ipfs'\' > /etc/bashrc.d/dietpi-ipfs.sh
# Raise UDP buffer sizes: https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes
if (( $G_HW_MODEL == 75 ))
then
(( $(sysctl -n net.core.rmem_max) >= 7500000 && $(sysctl -n net.core.wmem_max) >= 7500000 )) || G_WHIP_MSG '[WARNING] UDP buffer size limits might be too small
\nFor QUIC, used by the Kubo, the bandwidth can be limited by the UDP buffer sizes. Hence it is recommended to raise their limit. This can only be done on the host of this container. E.g. add the following lines to a new sysctl config file like /etc/sysctl.d/dietpi-ipfs.conf:
net.core.rmem_max=7500000
net.core.wmem_max=7500000
\nApply them without reboot like this:
sudo sysctl -p /etc/sysctl.d/dietpi-ipfs.conf
\nMore info: https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes'
else
G_EXEC eval 'echo -e '\''net.core.rmem_max=7500000\nnet.core.wmem_max=7500000'\'' > /etc/sysctl.d/dietpi-ipfs.conf'
local dryrun=()
[[ ! -w '/proc/sys/net/core/rmem_max' ]] && systemd-detect-virt -cq && dryrun=('--dry-run')
G_EXEC sysctl -p "${dryrun[@]}" /etc/sysctl.d/dietpi-ipfs.conf
fi
fi
if To_Install 16 # microblog.pub
then
local micro_name='microblog-pub'
# Obtain latest Python 3.11.y version supported by pyenv
local micro_python_version=$(curl -sSfL 'https://api.github.com/repos/pyenv/pyenv/contents/plugins/python-build/share/python-build?ref=master' | mawk -F\" '/^ *"name": "3\.11\.[0-9]*",$/{print $4}' | sort -Vr | head -1)
[[ $micro_python_version ]] || { micro_python_version='3.11.14'; G_DIETPI-NOTIFY 1 "Automatic latest Python version detection failed. Version \"$micro_python_version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
local micro_pyenv_dir="/opt/$micro_name"
local micro_pyenv_activate_file="$micro_pyenv_dir/pyenv-activate.sh"
local micro_virtualenv="$micro_pyenv_dir/.virtual_path"
local micro_data_dir="/mnt/dietpi_userdata/$micro_name"
local micro_systemd="/etc/systemd/system/$micro_name.service"
local micro_functions="/etc/bashrc.d/$micro_name.sh"
# Clone the repo into data dir
if [[ ! -d $micro_data_dir ]]
then
G_EXEC_OUTPUT=1 G_EXEC git clone 'https://git.sr.ht/~tsileo/microblog.pub' "$micro_data_dir"
# Enable remote access on port 8007
G_EXEC sed --follow-symlinks -i 's/uvicorn app/uvicorn --host 0.0.0.0 --port 8007 app/' "$micro_data_dir/misc/supervisord.conf"
G_EXEC sed --follow-symlinks -i 's/8000/8007/' "$micro_data_dir/data/tests.toml"
fi
# Configure pip
# - Disable cache
G_EXEC mkdir -p "$micro_data_dir/.pip"
G_EXEC eval "echo -e '[global]\nno-cache-dir=true' > '$micro_data_dir/.pip/pip.conf'"
# Configure Poetry
# - > The "poetry.dev-dependencies" section is deprecated and will be removed in a future version. Use "poetry.group.dev.dependencies" instead.
G_EXEC sed --follow-symlinks -i '/^\[poetry.dev-dependencies\]$/c\[poetry.group.dev.dependencies]' "$micro_data_dir/pyproject.toml"
# - Disable package mode to prevent: "Error: The current project could not be installed: No file/folder found for package microblogpub"
G_CONFIG_INJECT 'package-mode[[:blank:]]' 'package-mode = false' "$micro_data_dir/pyproject.toml" '\[tool.poetry\]'
# User
Create_User -d "$micro_data_dir" "$micro_name"
G_EXEC chown -R "$micro_name:$micro_name" "$micro_data_dir"
# Dependencies: https://git.sr.ht/~tsileo/microblog.pub/tree/v2/item/pyproject.toml
PYTHON_VERSION=$micro_python_version Python_Deps -u "$micro_name" -P pyenv poetry bcrypt brotli greenlet113 libsass lxml pillow
# Create a Python environment via pyenv
# - https://python-poetry.org/docs/managing-environments/
Download_Install 'https://github.com/pyenv/pyenv/archive/master.tar.gz'
G_EXEC chown -R "$micro_name:$micro_name" pyenv-master
# - Start with fresh instance to allow clean pyenv and Python updates and fix broken instances.
[[ -d $micro_pyenv_dir ]] && G_EXEC rm -R "$micro_pyenv_dir"
G_EXEC mv pyenv-master "$micro_pyenv_dir"
# Generate script to activate pyenv
echo "#!/bin/dash
cd $micro_pyenv_dir || return 1
export PYENV_ROOT='$micro_pyenv_dir' || return 1
export PATH=\"\$PYENV_ROOT/bin:\$PATH\" || return 1
eval \"\$(pyenv init --path)\" || return 1
eval \"\$(pyenv init -)\" || return 1
cd $micro_data_dir
[ -f '.cargo/env' ] && . .cargo/env
PS1=\"(microblog.pub) \$PS1\"" > "$micro_pyenv_activate_file"
G_EXEC_DESC="Compiling and installing Python $micro_python_version into pyenv, which will take a while ..."
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$micro_name" -- dash -c ". $micro_pyenv_activate_file; exec pyenv install $micro_python_version"
G_EXEC_DESC="Setting Python $micro_python_version as global version for this pyenv instance"
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$micro_name" -- dash -c ". $micro_pyenv_activate_file; exec pyenv global $micro_python_version"
G_EXEC_DESC='Upgrading base modules: pip setuptools wheel'
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$micro_name" -- dash -c ". $micro_pyenv_activate_file; exec pip3 install -U pip setuptools wheel"
G_EXEC_DESC='Installing Poetry'
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$micro_name" -- dash -c ". $micro_pyenv_activate_file; exec pip3 install poetry"
G_EXEC_DESC='Updating dependency locks'
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$micro_name" -- dash -c ". $micro_pyenv_activate_file; exec poetry update --lock"
G_EXEC_DESC='Adding setuptools dependency for Poetry needed for config wizard' # <81: /mnt/dietpi_userdata/microblog-pub/.cache/pypoetry/virtualenvs/microblogpub-qTlMHCqJ-py3.11/lib/python3.11/site-packages/boussole/__init__.py:7: UserWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html. The pkg_resources package is slated for removal as early as 2025-11-30. Refrain from using this package or pin to Setuptools<81.
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$micro_name" -- dash -c ". $micro_pyenv_activate_file; exec poetry add --lock 'setuptools<81'"
G_EXEC_DESC='Installing microblog.pub'
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$micro_name" -- dash -c ". $micro_pyenv_activate_file; exec poetry install"
G_EXEC_DESC='Storing Poetry virtualenv path'
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$micro_name" -- dash -c ". $micro_pyenv_activate_file; poetry env info | grep -Po '(?<=^Path:\s).*/virtualenvs/.*' > '$micro_virtualenv'"
read -r VENV_DIR < "$micro_virtualenv"
# shellcheck disable=SC2016
G_EXEC_DESC='Checking Poetry virtualenv path' G_EXEC eval '[[ $VENV_DIR ]]'
G_EXEC rm "$micro_virtualenv"
# Generate functions for later configuration and install to profile
cat << _EOF_ > "$micro_functions"
#!/bin/dash
microblog-pub()
{
case \$1 in
c*) sudo -u '$micro_name' dash -c '. $micro_pyenv_activate_file; poetry run inv configuration-wizard && poetry run inv migrate-db' && systemctl enable '$micro_name';;
u*) sudo -u '$micro_name' dash -c '. $micro_pyenv_activate_file; git pull && poetry run inv update';;
v*) sudo -u '$micro_name' bash --init-file '$micro_pyenv_activate_file';;
*) echo 'microblog.pub helper script
usage: microblog-pub <configure|update|venv>
where:
configure runs the configuration wizard and updates the microblog environment.
update pulls the latest changes from the microblog repository and reconfigures.
venv starts a virtual environment where the microblog can be configured.' && return 1;;
esac
sudo systemctl restart '$micro_name'
}
_EOF_
# Generate systemd service
cat << _EOF_ > "$micro_systemd"
[Unit]
Description=microblog.pub (DietPi)
Wants=network-online.target
After=network-online.target
ConditionPathExists=$micro_data_dir/data/profile.toml
[Service]
User=$micro_name
SyslogIdentifier=microblog.pub
WorkingDirectory=$micro_data_dir
Environment="VENV_DIR=$VENV_DIR"
ExecStart=/bin/dash -c '. $micro_pyenv_activate_file; exec poetry run supervisord -c misc/supervisord.conf -n'
RestartForceExitStatus=100
[Install]
WantedBy=multi-user.target
_EOF_
G_WHIP_MSG '[ INFO ] microblog.pub installation finished\n\nIn order to complete the setup, please run "microblog-pub configure" in a new bash session.\n\nNB: Append the port ":8007" to the domain when being asked for it if you do not use a reverse proxy.'
G_DIETPI-NOTIFY 0 'microblog.pub installation finished. In order to complete the setup, please run "microblog-pub configure" in a new bash session. Append the port ":8007" to the domain when being asked for it if you do not use a reverse proxy.'
fi
if To_Install 2 fahclient # Folding@Home
then
G_DIETPI-NOTIFY 2 'Pre-configuring FAHClient DEB package'
debconf-set-selections <<< 'fahclient fahclient/autostart boolean false' # Do not start SysV service after package install
debconf-set-selections <<< 'fahclient fahclient/power select light'
debconf-set-selections <<< 'fahclient fahclient/team string 234437' # Team "DietPi"
debconf-set-selections <<< 'fahclient fahclient/user string DietPi' # User "DietPi"
debconf-set-selections <<< 'fahclient fahclient/passkey string 06c869246e88c00cb05cc4d1758a97f9' # Passkey for user "DietPi"
local arch1='64bit' arch2='amd64'
(( $G_HW_ARCH == 3 )) && arch1='arm64' arch2='arm64'
Download_Install "https://download.foldingathome.org/releases/v7/public/fahclient/debian-stable-$arch1/release/fahclient_7.6.21_$arch2.deb"
# Remove obsolete config + data directories and SysV service + config
[[ -d '/var/lib/fahclient' ]] && G_EXEC rm -R /var/lib/fahclient
[[ -d '/etc/fahclient' ]] && G_EXEC rm -R /etc/fahclient
[[ -f '/etc/default/fahclient' ]] && G_EXEC rm /etc/default/fahclient
Remove_SysV FAHClient
# Prepare our new config + data directory if not yet present
if [[ ! -f '/mnt/dietpi_userdata/fahclient/config.xml' ]]
then
G_EXEC mkdir -p /mnt/dietpi_userdata/fahclient
dps_index=$software_id Download_Install 'config.xml' /mnt/dietpi_userdata/fahclient/config.xml
fi
# Service
cat << _EOF_ > /etc/systemd/system/fahclient.service
[Unit]
Description=Folding@Home (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
User=fahclient
WorkingDirectory=/mnt/dietpi_userdata/fahclient
ExecStart=$(command -v FAHClient)
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chown -R fahclient /mnt/dietpi_userdata/fahclient
fi
if To_Install 47 # ownCloud
then
aDEPS=("php$PHP_VERSION-intl") # https://doc.owncloud.com/server/next/admin_manual/installation/manual_installation/manual_installation_prerequisites.html#php-extensions
if [[ -f '/var/www/owncloud/occ' ]]
then
G_DIETPI-NOTIFY 2 'Existing ownCloud installation found, will NOT overwrite...'
else
local datadir=$(sed -n '/^[[:blank:]]*SOFTWARE_OWNCLOUD_DATADIR=/{s/^[^=]*=//;s|/$||;p;q}' /boot/dietpi.txt)
[[ $datadir ]] || datadir='/mnt/dietpi_userdata/owncloud_data'
if [[ -f $datadir/dietpi-owncloud-installation-backup/occ ]]
then
G_DIETPI-NOTIFY 2 'ownCloud installation backup found, starting recovery...'
G_EXEC cp -a "$datadir/dietpi-owncloud-installation-backup/." /var/www/owncloud/
# Correct config.php data directory entry, in case it changed due to server migration:
G_CONFIG_INJECT "'datadirectory'" "'datadirectory' => '$datadir'," /var/www/owncloud/config/config.php "'dbtype'"
else
Download_Install 'https://download.owncloud.com/server/stable/owncloud-latest.tar.bz2' /var/www
fi
fi
[[ ${aDEPS[0]} ]] && { G_DIETPI-NOTIFY 2 'Installing required PHP modules'; G_AGI "${aDEPS[@]}"; aDEPS=(); }
G_DIETPI-NOTIFY 2 'Enabling required PHP modules'
# - Add JSON module for PHP7, as it does not exist (embedded in core package) on PHP8
local json=()
[[ $PHP_VERSION == 8* ]] || json=('json')
G_EXEC phpenmod ctype curl dom gd iconv intl mbstring pdo_mysql posix simplexml xmlreader xmlwriter zip fileinfo opcache apcu redis exif "${json[@]}"
G_DIETPI-NOTIFY 2 'Enabling APCu memory cache for PHP command line usage (CLI) as well, including ownCloud occ command and cron jobs.'
echo -e '; ownCloud PHP settings\n; priority=98\napc.enable_cli=1' > "/etc/php/$PHP_VERSION/mods-available/dietpi-owncloud.ini"
G_EXEC phpenmod dietpi-owncloud
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Apache webserver found, enabling ownCloud specific configuration.' # https://doc.owncloud.com/server/next/admin_manual/installation/manual_installation/manual_installation_apache.html
a2enmod rewrite headers env dir mime 1> /dev/null
local owncloud_conf='/etc/apache2/sites-available/dietpi-owncloud.conf'
[[ -f $owncloud_conf ]] && G_EXEC mv "$owncloud_conf" "$owncloud_conf.dietpi-old"
dps_index=$software_id Download_Install 'apache.owncloud.conf' "$owncloud_conf"
a2ensite dietpi-owncloud 1> /dev/null
# Cal/CardDAV redirects to ownCloud DAV endpoint
echo '# Redirect Cal/CardDAV requests to ownCloud endpoint:
Redirect 301 /.well-known/carddav /owncloud/remote.php/dav
Redirect 301 /.well-known/caldav /owncloud/remote.php/dav' > /etc/apache2/conf-available/dietpi-dav_redirect.conf
a2enconf dietpi-dav_redirect
elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Lighttpd webserver found, enabling ownCloud specific configuration.'
# Enable required modules
G_CONFIG_INJECT '"mod_access",' ' "mod_access",' /etc/lighttpd/lighttpd.conf '"mod_.+",'
[[ -f '/etc/lighttpd/conf-enabled/05-setenv.conf' ]] || G_EXEC lighty-enable-mod setenv
# Move ownCloud configuration file in place and activate it
owncloud_conf='/etc/lighttpd/conf-available/99-dietpi-owncloud.conf'
[[ -f $owncloud_conf ]] && G_EXEC mv "$owncloud_conf" "$owncloud_conf.dietpi-old"
dps_index=$software_id Download_Install 'lighttpd.owncloud.conf' "$owncloud_conf"
G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
G_EXEC lighty-enable-mod rewrite dietpi-owncloud
# Cal/CardDAV redirects to ownCloud DAV endpoint
echo '# Redirect Cal/CardDAV requests to ownCloud endpoint:
url.redirect += (
"^/.well-known/caldav" => "/owncloud/remote.php/dav",
"^/.well-known/carddav" => "/owncloud/remote.php/dav"
)' > /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
[[ -f '/etc/lighttpd/conf-enabled/99-dietpi-dav_redirect.conf' ]] || G_EXEC lighty-enable-mod dietpi-dav_redirect
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Nginx webserver found, enabling ownCloud specific configuration.' # https://github.com/owncloud/docs/blob/deda107/modules/admin_manual/examples/installation/nginx/subdirectory-configuration.conf
local owncloud_conf='/etc/nginx/sites-dietpi/dietpi-owncloud.conf'
[[ -f $owncloud_conf ]] && G_EXEC mv "$owncloud_conf" "$owncloud_conf.dietpi-old"
dps_index=$software_id Download_Install 'nginx.owncloud.conf' "$owncloud_conf"
# Cal/CardDAV redirects to ownCloud DAV endpoint
echo '# Redirect Cal/CardDAV requests to ownCloud endpoint:
location = /.well-known/carddav { return 301 /owncloud/remote.php/dav/; }
location = /.well-known/caldav { return 301 /owncloud/remote.php/dav/; }' > /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf
fi
# Start MariaDB and Redis (for reinstalls) for database creation and occ command
G_EXEC systemctl restart mariadb
G_EXEC systemctl restart redis-server
# Initially add occ command shortcut, will be added as alias by /etc/bashrc.d/dietpi.bash if occ file exist:
occ(){ runuser -u www-data -- php /var/www/owncloud/occ "$@"; }
# Adjusting config file:
local config_php='/var/www/owncloud/config/config.php'
local datadir=$(sed -n '/^[[:blank:]]*SOFTWARE_OWNCLOUD_DATADIR=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $datadir ]] || datadir='/mnt/dietpi_userdata/owncloud_data'
G_EXEC mkdir -p "$datadir"
G_EXEC chown -R www-data:www-data /var/www/owncloud "$datadir"
if [[ -d '/mnt/dietpi_userdata/mysql/owncloud' ]]; then
G_DIETPI-NOTIFY 2 'ownCloud database found, will NOT overwrite.'
if [[ ! -f $config_php ]]; then
G_WHIP_MSG '[WARNING] Existing ownCloud database was found, but no related install directory\n
A remaining MariaDB "owncloud" database from an earlier installed instance was found. But the related install directory "/var/www/owncloud/config/config.php" does not exist.
Since running a fresh install with an existing database can produce data corruption if the versions do not exactly match, you either need to remove the database or find and place the related install directory.\n
We cannot predict your aim and do not want to mess or break your data, so please do this manually.\n
To remove the existing database (including e.g. contacts, calendar, file tags etc.):
# mysqladmin drop owncloud
Otherwise to copy an existing instance in place:
# rm -R /var/www/owncloud
# mkdir /var/www/owncloud
# cp -a /path/to/existing/owncloud/. /var/www/owncloud/
The install script will now exit. After applying one of the the above, rerun dietpi-software, e.g.:
# dietpi-software install 47'
/boot/dietpi/dietpi-services start
exit 1
fi
elif [[ -f $datadir/dietpi-owncloud-database-backup.sql ]]; then
G_DIETPI-NOTIFY 2 'ownCloud database backup found, starting recovery...'
local dbuser=$(grep -m1 "^[[:blank:]]*'dbuser'" "$config_php" | mawk -F\' '{print $4}')
local dbpass=$(grep -m1 "^[[:blank:]]*'dbpassword'" "$config_php" | mawk -F\' '{print $4}')
/boot/dietpi/func/create_mysql_db owncloud "$dbuser" "$dbpass"
mysql owncloud < "$datadir/dietpi-owncloud-database-backup.sql"
# Adjust database data directory entry, in case it changed due to server migration
local datadir_old=$(grep -m1 "^[[:blank:]]*'datadirectory'" "$config_php" | mawk -F\' '{print $4}')
G_EXEC mysql -e "update owncloud.oc_storages set id='local::$datadir/' where id rlike 'local::$datadir_old';"
elif ! grep -q "'installed' => true," "$config_php" 2>/dev/null; then
local username=$(sed -n '/^[[:blank:]]*SOFTWARE_OWNCLOUD_NEXTCLOUD_USERNAME=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $username ]] || username='admin'
# For MariaDB, temporary database admin user needs to be created, as 'root' uses unix_socket login, which cannot be accessed by sudo -u www-data.
# - Create random temporary alphanumeric 30 characters password
local oc_password=$(tr -dc '[:alnum:]' < /dev/random | head -c30)
G_EXEC mysql -e "grant all privileges on *.* to tmp_root@localhost identified by '$oc_password' with grant option;"
G_EXEC_DESC='ownCloud occ install'
# - Replace password strings internally to avoid printing it to console
G_EXEC_PRE_FUNC(){ acommand[6]="--database-pass=$oc_password" acommand[8]="--admin-pass=$GLOBAL_PW"; }
# - Checking output for stack trace to handle internal errors that do not lead to php error exit code
G_EXEC_POST_FUNC(){ grep -qi 'Stack trace' "$fp_log" && exit_code=255; }
G_EXEC occ maintenance:install --no-interaction --database='mysql' --database-name='owncloud' --database-user='tmp_root' --database-pass="${oc_password//?/X}" --admin-user="$username" --admin-pass="${GLOBAL_PW//?/X}" --data-dir="$datadir"
G_EXEC mysql -e 'drop user tmp_root@localhost;'
unset -v oc_password
# Remove obsolete default data dir
[[ $(readlink -f "$datadir") != $(readlink -f /var/www/owncloud/data) ]] && G_EXEC rm -R /var/www/owncloud/data
fi
# Enable ownCloud to use 4-byte database
G_CONFIG_INJECT "'mysql.utf8mb4'" "'mysql.utf8mb4' => true," "$config_php" "'dbpassword'"
# Add local IP and hostname to trusted domains.
# If "1 => '" does not exist, the config.php is not copied e.g. from older instance, so we add entries.
if ! grep -q "1 => '" "$config_php"; then
sed --follow-symlinks -i "/0 => 'localhost'/a 1 => '$(G_GET_NET ip)'," "$config_php"
sed --follow-symlinks -i "/1 => '/a 2 => '$HOSTNAME'," "$config_php"
fi
# Set CLI URL to ownCloud sub directory:
G_EXEC sed --follow-symlinks -i "s|'http://localhost'|'http://localhost/owncloud'|" "$config_php"
# Set pretty URLs (without /index.php/) on Apache:
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then
GCI_PRESERVE=1 G_CONFIG_INJECT "'htaccess.RewriteBase'" "'htaccess.RewriteBase' => '/owncloud'," "$config_php" "'overwrite.cli.url'"
occ maintenance:update:htaccess
fi
# APCu Memcache
GCI_PRESERVE=1 G_CONFIG_INJECT "'memcache.local'" "'memcache.local' => '\\\\OC\\\\Memcache\\\\APCu'," "$config_php" "'version'"
# Redis for transactional file locking:
G_DIETPI-NOTIFY 2 'Enabling Redis for transactional file locking.' # https://doc.owncloud.org/server/administration_manual/configuration/server/caching_configuration.html#configuring-transactional-file-locking
local redis_conf='/etc/redis/redis.conf'
# - Enable Redis socket and grant www-data access to it
GCI_PRESERVE=1 G_CONFIG_INJECT 'unixsocket[[:blank:]]' 'unixsocket /run/redis/redis-server.sock' "$redis_conf"
G_CONFIG_INJECT 'unixsocketperm[[:blank:]]' 'unixsocketperm 770' "$redis_conf"
G_EXEC usermod -aG redis www-data
G_EXEC systemctl restart redis-server
# - Enable ownCloud to use Redis socket for transactional file locking:
G_CONFIG_INJECT "'filelocking.enabled'" "'filelocking.enabled' => true," "$config_php" "'memcache.local'"
local redis_sock=$(grep -m1 '^[[:blank:]]*unixsocket[[:blank:]]' "$redis_conf" | mawk '{print $2}') # Re-estimate in case of existing custom path
GCI_PRESERVE=1 GCI_NEWLINE=1 G_CONFIG_INJECT "'memcache.locking'" "'memcache.locking' => '\\\\OC\\\\Memcache\\\\Redis',\n'redis' => array ('host' => '$redis_sock', 'port' => 0,)," "$config_php" "'filelocking.enabled'"
# Enable ownCloud background cron job:
crontab -u www-data -l | grep -q '/var/www/owncloud/.*cron' || { crontab -u www-data -l; echo '*/15 * * * * php /var/www/owncloud/occ system:cron'; } | crontab -u www-data -
occ background:cron
# On <1 GiB devices assure at least 512 MiB swap space are available to stand 512 MiB file uploads + increased PHP cache and session file usage: https://github.com/MichaIng/DietPi/issues/2293
(( $RAM_PHYS < 924 && $(free -m | mawk '/^Swap:/{print $2;exit}') < 512 )) && /boot/dietpi/func/dietpi-set_swapfile 512
fi
if To_Install 114 # Nextcloud
then
aDEPS=("php$PHP_VERSION-intl") # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#prerequisites-for-manual-installation
if [[ -f '/var/www/nextcloud/occ' ]]
then
G_DIETPI-NOTIFY 2 'Existing Nextcloud installation found, will NOT overwrite...'
else
local datadir=$(sed -n '/^[[:blank:]]*SOFTWARE_NEXTCLOUD_DATADIR=/{s/^[^=]*=//;s|/$||;p;q}' /boot/dietpi.txt)
[[ $datadir ]] || datadir='/mnt/dietpi_userdata/nextcloud_data'
if [[ -f $datadir/dietpi-nextcloud-installation-backup/occ ]]
then
G_DIETPI-NOTIFY 2 'Nextcloud installation backup found, starting recovery...'
G_EXEC cp -a "$datadir/dietpi-nextcloud-installation-backup/." /var/www/nextcloud/
# Correct config.php data directory entry, in case it changed due to server migration:
G_CONFIG_INJECT "'datadirectory'" "'datadirectory' => '$datadir'," /var/www/nextcloud/config/config.php "'dbtype'"
else
# Nextcloud 26 doesn't support PHP7.4 anymore: https://github.com/nextcloud/server/pull/34997
local version='latest'
if (( $G_DISTRO < 7 ))
then
G_DIETPI-NOTIFY 2 'Downloading latest Nextcloud 25, since Nextcloud 26 does not support PHP7.4 anymore'
version='latest-25'
fi
Download_Install "https://download.nextcloud.com/server/releases/$version.tar.bz2" /var/www
fi
fi
# Bookworm: Patch for PHP 8.2 support, which works quite well: https://github.com/nextcloud/server/issues/32595#issuecomment-1387559520
if (( $G_DISTRO > 6 )) && grep -q '>= 80200' /var/www/nextcloud/lib/versioncheck.php
then
G_WHIP_MSG '[WARNING] Patching Nextcloud to support PHP 8.2 for Bookworm
\nNextcloud 25 does not support PHP 8.2, but it does work quite well:
- https://github.com/nextcloud/server/issues/32595#issuecomment-1387559520
\nWe are patching the PHP version check, but this has two implications:
- You will see an integrity check error on Nextcloud admin panel.
- You will need to redo the patch after Nextcloud updates to future 25.x versions:
# sed --follow-symlinks -i '\''s/>= 80200/>= 80300/'\'' /var/www/nextcloud/lib/versioncheck.php
\nWe recommend to update to Nextcloud 26 as fast as possible to get official PHP 8.2 support.'
G_EXEC sed --follow-symlinks -i 's/>= 80200/>= 80300/' /var/www/nextcloud/lib/versioncheck.php
fi
[[ ${aDEPS[0]} ]] && { G_DIETPI-NOTIFY 2 'Installing required PHP modules'; G_AGI "${aDEPS[@]}"; aDEPS=(); }
G_DIETPI-NOTIFY 2 'Enabling required PHP modules'
# - Add JSON module for PHP7, as it does not exist (embedded in core package) on PHP8
local json=()
[[ $PHP_VERSION == 8* ]] || json=('json')
G_EXEC phpenmod ctype curl dom gd intl mbstring pdo_mysql posix simplexml xmlreader xmlwriter zip fileinfo opcache apcu redis exif "${json[@]}"
G_DIETPI-NOTIFY 2 'Apply PHP override settings for Nextcloud.' # https://docs.nextcloud.com/server/stable/admin_manual/installation/server_tuning.html#enable-php-opcache
local memory_consumption=$(sed -n '/^[[:blank:]]*opcache.memory_consumption=/{s/^[^=]*=//p;q}' "/etc/php/$PHP_VERSION/mods-available/dietpi.ini")
(( $memory_consumption < 64 )) && memory_consumption='\nopcache.memory_consumption=64' || memory_consumption=
echo -e "; Nextcloud PHP settings\n; priority=98\nmemory_limit=512M$memory_consumption\nopcache.revalidate_freq=5" > "/etc/php/$PHP_VERSION/mods-available/dietpi-nextcloud.ini"
# Enable APCu for CLI only for Nextcloud 30 and lower: https://github.com/nextcloud/server/pull/46151
(( $(mawk -F[\'.] '/^\$OC_VersionString/{print $2}' /var/www/nextcloud/version.php) < 31 )) && G_EXEC eval "echo 'apc.enable_cli=1' >> '/etc/php/$PHP_VERSION/mods-available/dietpi-nextcloud.ini'"
G_EXEC phpenmod dietpi-nextcloud
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Apache webserver found, enabling Nextcloud specific configuration.' # https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html#apache-web-server-configuration
a2enmod rewrite headers env dir mime 1> /dev/null
local nextcloud_conf='/etc/apache2/sites-available/dietpi-nextcloud.conf'
[[ -f $nextcloud_conf ]] && G_EXEC mv "$nextcloud_conf" "$nextcloud_conf.dietpi-old"
dps_index=$software_id Download_Install 'apache.nextcloud.conf' "$nextcloud_conf"
a2ensite dietpi-nextcloud 1> /dev/null
# Cal/CardDAV redirects to Nextcloud DAV endpoint
echo '# Redirect Cal/CardDAV requests to Nextcloud endpoint:
Redirect 301 /.well-known/carddav /nextcloud/remote.php/dav
Redirect 301 /.well-known/caldav /nextcloud/remote.php/dav' > /etc/apache2/conf-available/dietpi-dav_redirect.conf
a2enconf dietpi-dav_redirect
elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Lighttpd webserver found, enabling Nextcloud specific configuration.'
# Enable required modules
G_CONFIG_INJECT '"mod_access",' ' "mod_access",' /etc/lighttpd/lighttpd.conf '"mod_.+",'
[[ -f '/etc/lighttpd/conf-enabled/05-setenv.conf' ]] || G_EXEC lighty-enable-mod setenv
# Move Nextcloud configuration file in place and activate it
nextcloud_conf='/etc/lighttpd/conf-available/99-dietpi-nextcloud.conf'
[[ -f $nextcloud_conf ]] && G_EXEC mv "$nextcloud_conf" "$nextcloud_conf.dietpi-old"
dps_index=$software_id Download_Install 'lighttpd.nextcloud.conf' "$nextcloud_conf"
G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
G_EXEC lighty-enable-mod rewrite dietpi-nextcloud
# Cal/CardDAV redirects to Nextcloud DAV endpoint
echo '# Redirect Cal/CardDAV requests to Nextcloud endpoint:
url.redirect += (
"^/.well-known/caldav" => "/nextcloud/remote.php/dav",
"^/.well-known/carddav" => "/nextcloud/remote.php/dav"
)' > /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
[[ -f '/etc/lighttpd/conf-enabled/99-dietpi-dav_redirect.conf' ]] || G_EXEC lighty-enable-mod dietpi-dav_redirect
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Nginx webserver found, enabling Nextcloud specific configuration.' # https://docs.nextcloud.com/server/stable/admin_manual/installation/nginx.html
# shellcheck disable=SC2016
echo '# Set the "immutable" cache control option for assets with a cache busting "v=" argument
map $arg_v $asset_immutable {
"" "";
default ", immutable";
}' > /etc/nginx/conf.d/dietpi-nextcloud.conf
local nextcloud_conf='/etc/nginx/sites-dietpi/dietpi-nextcloud.conf'
[[ -f $nextcloud_conf ]] && G_EXEC mv "$nextcloud_conf" "$nextcloud_conf.dietpi-old"
dps_index=$software_id Download_Install 'nginx.nextcloud.conf' "$nextcloud_conf"
# Cal/CardDAV redirects to Nextcloud DAV endpoint
echo '# Redirect Cal/CardDAV requests to Nextcloud endpoint:
location = /.well-known/carddav { return 301 /nextcloud/remote.php/dav/; }
location = /.well-known/caldav { return 301 /nextcloud/remote.php/dav/; }' > /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf
fi
# Start MariaDB and Redis (for reinstalls) for database creation and ncc command
G_EXEC systemctl restart mariadb
G_EXEC systemctl restart redis-server
# Initially add occ command shortcut, will be added as alias by /etc/bashrc.d/dietpi.bash if occ file exist:
ncc(){ runuser -u www-data -- php /var/www/nextcloud/occ "$@"; }
# Adjusting config file:
local config_php='/var/www/nextcloud/config/config.php'
local datadir=$(sed -n '/^[[:blank:]]*SOFTWARE_NEXTCLOUD_DATADIR=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $datadir ]] || datadir='/mnt/dietpi_userdata/nextcloud_data'
G_EXEC mkdir -p "$datadir"
G_EXEC chown -R www-data:www-data /var/www/nextcloud "$datadir"
if [[ -d '/mnt/dietpi_userdata/mysql/nextcloud' ]]; then
G_DIETPI-NOTIFY 2 'Nextcloud database found, will NOT overwrite.'
if [[ ! -f $config_php ]]; then
G_WHIP_MSG '[WARNING] Existing Nextcloud database was found, but no related install directory\n
A remaining MariaDB "nextcloud" database from an earlier installed instance was found. But the related install directory "/var/www/nextcloud/config/config.php" does not exist.
Since running a fresh install with an existing database can produce data corruption if the versions do not exactly match, you either need to remove the database or find and place the related install directory.\n
We cannot predict your aim and do not want to mess or break your data, so please do this manually.\n
To remove the existing database (including e.g. contacts, calendar, file tags etc.):
# mysqladmin drop nextcloud
Otherwise to copy an existing instance in place:
# rm -R /var/www/nextcloud
# mkdir /var/www/nextcloud
# cp -a /path/to/existing/nextcloud/. /var/www/nextcloud/
The install script will now exit. After applying one of the the above, rerun dietpi-software, e.g.:
# dietpi-software install 114'
/boot/dietpi/dietpi-services start
exit 1
fi
elif [[ -f $datadir/dietpi-nextcloud-database-backup.sql ]]; then
G_DIETPI-NOTIFY 2 'Nextcloud database backup found, starting recovery...'
local dbuser=$(grep -m1 "^[[:blank:]]*'dbuser'" "$config_php" | mawk -F\' '{print $4}')
local dbpass=$(grep -m1 "^[[:blank:]]*'dbpassword'" "$config_php" | mawk -F\' '{print $4}')
/boot/dietpi/func/create_mysql_db nextcloud "$dbuser" "$dbpass"
mysql nextcloud < "$datadir/dietpi-nextcloud-database-backup.sql"
# Adjust database data directory entry, in case it changed due to server migration
local datadir_old=$(grep -m1 "^[[:blank:]]*'datadirectory'" "$config_php" | mawk -F\' '{print $4}')
G_EXEC mysql -e "update nextcloud.oc_storages set id='local::$datadir/' where id rlike 'local::$datadir_old';"
elif ! grep -q "'installed' => true," "$config_php" 2>/dev/null; then
local username=$(sed -n '/^[[:blank:]]*SOFTWARE_OWNCLOUD_NEXTCLOUD_USERNAME=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $username ]] || username='admin'
# For MariaDB, temporary database admin user needs to be created, as 'root' uses unix_socket login, which cannot be accessed by sudo -u www-data.
# - Create random temporary alphanumeric 30 characters password
local nc_password=$(tr -dc '[:alnum:]' < /dev/random | head -c30)
G_EXEC mysql -e "grant all privileges on *.* to tmp_root@localhost identified by '$nc_password' with grant option;"
G_EXEC_DESC='Nextcloud ncc install'
# - Replace password strings internally to avoid printing it to console
G_EXEC_PRE_FUNC(){ acommand[6]="--database-pass=$nc_password" acommand[8]="--admin-pass=$GLOBAL_PW"; }
# - Checking output for stack trace to handle internal errors that do not lead to php error exit code
# - Workaround Nextcloud 14.0.3 throwing an error, when data dir path contains a symlink: https://github.com/nextcloud/server/issues/12247
G_EXEC_POST_FUNC(){
if (( $exit_code )); then
grep -q 'Following symlinks is not allowed' "$fp_log" && { cp -a /var/www/nextcloud/core/skeleton/. "$datadir/$username/files/"; exit_code=0; }
else
grep -qi 'Stack trace' "$fp_log" && exit_code=255
fi
}
G_EXEC ncc maintenance:install --no-interaction --database='mysql' --database-name='nextcloud' --database-user='tmp_root' --database-pass="${nc_password//?/X}" --admin-user="$username" --admin-pass="${GLOBAL_PW//?/X}" --data-dir="$datadir"
G_EXEC mysql -e 'drop user tmp_root@localhost;'
unset -v nc_password
# Remove obsolete default data dir
[[ $(readlink -f "$datadir") != $(readlink -f /var/www/nextcloud/data) ]] && G_EXEC rm -R /var/www/nextcloud/data
fi
# Enable Nextcloud to use 4-byte database
G_CONFIG_INJECT "'mysql.utf8mb4'" "'mysql.utf8mb4' => true," "$config_php" "'dbpassword'"
# Disable trusted_domains.
grep -q "1 => '*'" "$config_php" || sed --follow-symlinks -i "/0 => 'localhost'/a 1 => '*'," "$config_php"
# Set CLI URL to Nextcloud sub directory:
G_EXEC sed --follow-symlinks -i "s|'http://localhost'|'http://localhost/nextcloud'|" "$config_php"
# Set pretty URLs (without /index.php/) on Apache:
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 )); then
GCI_PRESERVE=1 G_CONFIG_INJECT "'htaccess.RewriteBase'" "'htaccess.RewriteBase' => '/nextcloud'," "$config_php" "'overwrite.cli.url'"
ncc maintenance:update:htaccess
fi
# APCu Memcache
GCI_PRESERVE=1 G_CONFIG_INJECT "'memcache.local'" "'memcache.local' => '\\\\OC\\\\Memcache\\\\APCu'," "$config_php" "'version'"
# Redis for transactional file locking:
G_DIETPI-NOTIFY 2 'Enabling Redis for transactional file locking.' # https://docs.nextcloud.com/server/stable/admin_manual/configuration_files/files_locking_transactional.html
local redis_conf='/etc/redis/redis.conf'
# - Enable Redis socket and grant www-data access to it
GCI_PRESERVE=1 G_CONFIG_INJECT 'unixsocket[[:blank:]]' 'unixsocket /run/redis/redis-server.sock' "$redis_conf"
G_CONFIG_INJECT 'unixsocketperm[[:blank:]]' 'unixsocketperm 770' "$redis_conf"
G_EXEC usermod -aG redis www-data
G_EXEC systemctl restart redis-server
# - Enable Nextcloud to use Redis socket:
G_CONFIG_INJECT "'filelocking.enabled'" "'filelocking.enabled' => true," "$config_php" "'memcache.local'"
local redis_sock=$(grep -m1 '^[[:blank:]]*unixsocket[[:blank:]]' "$redis_conf" | mawk '{print $2}') # Re-estimate in case of existing custom path
GCI_PRESERVE=1 GCI_NEWLINE=1 G_CONFIG_INJECT "'memcache.locking'" "'memcache.locking' => '\\\\OC\\\\Memcache\\\\Redis',\n'redis' => array ('host' => '$redis_sock', 'port' => 0,)," "$config_php" "'filelocking.enabled'"
# Tweak Argon2 hashing
# - Use all available CPU threads
GCI_PRESERVE=1 G_CONFIG_INJECT "'hashingThreads'" "'hashingThreads' => ${G_HW_CPU_CORES}," "$config_php" "'version'"
# - ToDo: Configure the other settings after getting some clarification: https://github.com/nextcloud/server/pull/19023#issuecomment-660071524
#GCI_PRESERVE=1 G_CONFIG_INJECT "'hashingMemoryCost'" "'hashingMemoryCost' => 65536," $config_php "'hashingThreads'"
#GCI_PRESERVE=1 G_CONFIG_INJECT "'hashingTimeCost'" "'hashingTimeCost' => 4," $config_php "'hashingMemoryCost'"
# Enable Nextcloud background cron job: https://docs.nextcloud.com/server/17/admin_manual/configuration_server/background_jobs_configuration.html#cron
crontab -u www-data -l | grep -q '/var/www/nextcloud/cron.php' || { crontab -u www-data -l; echo '*/5 * * * * php /var/www/nextcloud/cron.php'; } | crontab -u www-data -
ncc background:cron
# Convert filecache table to bigint, which is not done automatically by Nextcloud since v15
ncc db:convert-filecache-bigint -n
# Add missing database columns and indices, which is not done automatically by Nextcloud
ncc db:add-missing-columns
ncc db:add-missing-indices
ncc db:add-missing-primary-keys
# On <1 GiB devices assure at least 512 MiB swap space are available to stand 512 MiB file uploads + increased PHP cache and session file usage: https://github.com/MichaIng/DietPi/issues/2293
(( $RAM_PHYS < 924 && $(free -m | mawk '/^Swap:/{print $2;exit}') < 512 )) && /boot/dietpi/func/dietpi-set_swapfile 512
fi
if To_Install 168 coturn # Nextcloud Talk
then
G_DIETPI-NOTIFY 2 'Installing Coturn TURN server'
# Install Coturn server only, install Nextcloud Talk app after Nextcloud has been fully configured
G_AGI coturn
G_EXEC systemctl stop coturn
Remove_SysV coturn 1
# Ask user for server domain and desired TURN server port
G_WHIP_DEFAULT_ITEM=$(sed -n '/^[[:blank:]]*SOFTWARE_PUBLIC_DOMAIN_NAME=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $G_WHIP_DEFAULT_ITEM ]] || G_WHIP_DEFAULT_ITEM=$(find /etc/letsencrypt/live -mindepth 1 -maxdepth 1 -type d -print -quit 2> /dev/null)
[[ $G_WHIP_DEFAULT_ITEM ]] || G_WHIP_DEFAULT_ITEM=$(hostname -f)
G_WHIP_NOCANCEL=1
G_WHIP_INPUTBOX 'Please enter your server'\''s public domain name to allow Nextcloud Talk access your TURN server:'
local domain=${G_WHIP_RETURNED_VALUE#http*://}
GCI_PRESERVE=1 G_CONFIG_INJECT 'SOFTWARE_PUBLIC_DOMAIN_NAME=' "SOFTWARE_PUBLIC_DOMAIN_NAME=$domain" /boot/dietpi.txt
G_WHIP_DEFAULT_ITEM=3478 G_WHIP_NOCANCEL=1 G_WHIP_INPUTBOX_REGEX='^[1-9][0-9]*$' G_WHIP_INPUTBOX_REGEX_TEXT='a valid port number'
G_WHIP_INPUTBOX 'Please enter the network port, that should be used for your TURN server:
\nNB: This port (UDP + TCP) needs to be forwarded by your router and/or opened in your firewall settings. Default value is: 3478'
local port=$G_WHIP_RETURNED_VALUE
# Adjust Coturn settings
# - If /etc/turnserver.conf is not present, use default or create empty file
if [[ ! -f '/etc/turnserver.conf' ]]
then
# shellcheck disable=SC2015
[[ -f '/usr/share/doc/coturn/examples/etc/turnserver.conf.gz' ]] && gzip -cd /usr/share/doc/coturn/examples/etc/turnserver.conf.gz > /etc/turnserver.conf || > /etc/turnserver.conf
fi
# https://help.nextcloud.com/t/howto-setup-nextcloud-talk-with-turn-server/30794
G_CONFIG_INJECT 'listening-port=' "listening-port=$port" /etc/turnserver.conf
G_CONFIG_INJECT 'fingerprint' 'fingerprint' /etc/turnserver.conf
G_CONFIG_INJECT 'use-auth-secret' 'use-auth-secret' /etc/turnserver.conf
G_CONFIG_INJECT 'realm=' "realm=$domain" /etc/turnserver.conf
GCI_PRESERVE=1 G_CONFIG_INJECT 'total-quota=' 'total-quota=100' /etc/turnserver.conf
GCI_PRESERVE=1 G_CONFIG_INJECT 'bps-capacity=' 'bps-capacity=0' /etc/turnserver.conf
G_CONFIG_INJECT 'stale-nonce' 'stale-nonce' /etc/turnserver.conf
G_EXEC sed --follow-symlinks -i 's/^[[:blank:]]*allow-loopback-peers/#allow-loopback-peers/' /etc/turnserver.conf
G_CONFIG_INJECT 'no-multicast-peers' 'no-multicast-peers' /etc/turnserver.conf
# Install Nextcloud Talk app
G_EXEC systemctl start mariadb
G_EXEC systemctl start redis-server
G_EXEC ncc maintenance:mode --off
if [[ ! -d '/var/www/nextcloud/apps/spreed' ]]
then
# Succeed if app is already installed and on "Cannot declare class" bug: https://github.com/MichaIng/DietPi/issues/3499#issuecomment-622955490
G_EXEC_POST_FUNC(){ [[ $exit_code != 0 && $(<"$fp_log") =~ (' already installed'$|' Cannot declare class ') ]] && exit_code=0; }
G_EXEC ncc app:install spreed
fi
ncc app:enable spreed
# Adjust Nextcloud Talk settings to use Coturn
ncc talk:stun:add "$domain:$port"
# - Generate random secret to secure TURN server access
local secret=$(openssl rand -hex 32)
GCI_PASSWORD=1 GCI_PRESERVE=1 G_CONFIG_INJECT 'static-auth-secret=' "static-auth-secret=$secret" /etc/turnserver.conf
# - Scrape existing secret, in case user manually chose/edited it
secret=$(sed -n '/^[[:blank:]]*static-auth-secret=/{s/^[^=]*=//p;q}' /etc/turnserver.conf)
ncc talk:turn:add --secret "$secret" -- 'turn' "$domain:$port" 'udp,tcp'
unset -v secret domain port
fi
if To_Install 32 ympd # ympd
then
G_AGI ympd
fi
if To_Install 148 mympd # myMPD
then
# Distro: https://download.opensuse.org/repositories/home:/jcorporation/
local distro='Debian_'
(( $G_HW_ARCH == 1 )) && distro='Raspbian_'
case $G_DISTRO in
6) distro+='11';;
7) distro+='12';;
8) (( $G_HW_ARCH == 1 )) && distro+='12' || distro+='13';;
*) (( $G_HW_ARCH == 1 )) && distro+='12' || distro+='Testing';;
esac
# APT key
G_EXEC curl -sSfLo '/etc/apt/trusted.gpg.d/dietpi-mympd.asc' "https://download.opensuse.org/repositories/home:/jcorporation/$distro/Release.key"
# APT list
G_EXEC eval "echo 'deb https://download.opensuse.org/repositories/home:/jcorporation/$distro/ /' > /etc/apt/sources.list.d/dietpi-mympd.list"
G_AGUP
# APT package
G_AGI mympd
G_EXEC systemctl stop mympd
# Config: Create on fresh install
# - On reinstall /var/lib/mympd exists, so use its existence as reinstall flag
if [[ ! -d '/var/lib/mympd' ]]
then
G_EXEC_OUTPUT=1 G_EXEC systemd-run -p DynamicUser=yes -p StateDirectory=mympd -p CacheDirectory=mympd -p Environment='MYMPD_LOGLEVEL=4 MYMPD_MPD_HOST=/run/mpd/socket MYMPD_HTTP=false MYMPD_SSL_PORT=1333' mympd -c
# Add myMPD CA to trusted store: https://github.com/jcorporation/myMPD/issues/914
G_EXEC mkdir -p /usr/local/share/ca-certificates
G_EXEC ln -sf /var/lib/mympd/ssl/ca.pem /usr/local/share/ca-certificates/DietPi_myMPD_CA.crt
G_EXEC update-ca-certificates
fi
# myMPD pre-v8.0.0 cleanup
[[ -f '/etc/mympd.conf' ]] && G_EXEC rm /etc/mympd.conf
[[ -f '/etc/mympd.conf.dist' ]] && G_EXEC rm /etc/mympd.conf.dist
command -v mympd-config > /dev/null && G_EXEC rm "$(command -v mympd-config)"
fi
if To_Install 121 roonbridge # Roon Bridge
then
case $G_HW_ARCH in
2) local arch='armv7hf';;
3) local arch='armv8';;
*) local arch='x64';;
esac
Download_Install "https://download.roonlabs.net/builds/RoonBridge_linux$arch.tar.bz2"
# Always perform a clean install
[[ -d '/opt/roonbridge' ]] && G_EXEC rm -R /opt/roonbridge
[[ -d '/etc/roonbridge' ]] && G_EXEC rm -R /etc/roonbridge # Pre-v8.2
G_EXEC mv RoonBridge /opt/roonbridge
# Pre-v8.2 migration
[[ -d '/mnt/dietpi_userdata/roon' && ! -d '/mnt/dietpi_userdata/roonbridge' ]] && G_EXEC mv /mnt/dietpi_userdata/roon{,bridge}
# Log to /var/log/roonbridge
G_EXEC mkdir -p /mnt/dietpi_userdata/roonbridge/{RoonBridge,RAATServer}
G_EXEC rm -Rf /mnt/dietpi_userdata/roonbridge/{RoonBridge,RAATServer}/Logs /var/log/roon # /var/log/roon: Pre-v8.2
G_EXEC ln -s /var/log/roonbridge /mnt/dietpi_userdata/roonbridge/RoonBridge/Logs
G_EXEC ln -s /var/log/roonbridge /mnt/dietpi_userdata/roonbridge/RAATServer/Logs
# User
Create_User -G audio -d /mnt/dietpi_userdata/roonbridge roonbridge
# Permissions
G_EXEC chown -R roonbridge:root /{mnt/dietpi_userdata,opt}/roonbridge
# Service
cat << '_EOF_' > /etc/systemd/system/roonbridge.service
[Unit]
Description=Roon Bridge (DietPi)
Wants=network-online.target
After=network-online.target sound.target
[Service]
SyslogIdentifier=Roon Bridge
User=roonbridge
AmbientCapabilities=CAP_SYS_NICE
LogsDirectory=roonbridge
Environment=ROON_DATAROOT=/mnt/dietpi_userdata/roonbridge
Environment=ROON_ID_DIR=/mnt/dietpi_userdata/roonbridge
ExecStart=/opt/roonbridge/start.sh
Restart=on-abort
# Hardening
PrivateTmp=true
[Install]
WantedBy=multi-user.target
_EOF_
# Sparky SBC: Workaround for failing service start: https://dietpi.com/forum/t/allogui-not-working-apache-service-doesnt-start/15708
(( $G_HW_MODEL == 70 )) && G_EXEC sed --follow-symlinks -i '/^AmbientCapabilities=/d' /etc/systemd/system/roonbridge.service
fi
if To_Install 119 # CAVA
then
G_AGI cava
# Config: Preserve existing
if [[ ! -f '/root/.config/cava/config' ]]
then
G_EXEC mkdir -p /root/.config/cava
G_EXEC cp /usr/share/cava/example_files/config /root/.config/cava/
G_CONFIG_INJECT 'method[[:blank:]]+=' 'method = fifo' /root/.config/cava/config '\[input\]'
G_CONFIG_INJECT 'foreground[[:blank:]]+=' 'foreground = cyan' /root/.config/cava/config '\[color\]'
fi
# FIFO stream for MPD
grep -q '/tmp/mpd.fifo' /etc/mpd.conf || cat << '_EOF_' >> /etc/mpd.conf
# CAVA FIFO stream
audio_output {
type "fifo"
name "CAVA"
path "/tmp/mpd.fifo"
format "44100:16:2"
}
_EOF_
fi
if To_Install 118 mopidy # Mopidy: https://docs.mopidy.com/stable/installation/debian/
then
# Install our config file only if not yet existent, to preserve manual user config.
# - This needs to be done prior to APT install, since this would otherwise install a default config file as well.
[[ -f '/etc/mopidy/mopidy.conf' ]] || dps_index=$software_id Download_Install 'mopidy.conf' /etc/mopidy/mopidy.conf
# APT key
local url='https://apt.mopidy.com/mopidy.gpg'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-mopidy.gpg --yes"
# APT list: https://apt.mopidy.com/dists/
G_EXEC eval "echo 'deb https://apt.mopidy.com ${G_DISTRO_NAME/forky/trixie} main' > /etc/apt/sources.list.d/dietpi-mopidy.list"
G_AGUP
# APT package
G_AGI mopidy gstreamer1.0-alsa mopidy-local
G_EXEC systemctl stop mopidy
G_EXEC_OUTPUT=1 G_EXEC pip3 install -U Mopidy-MusicBox-Webclient
# Assure user home, data and cache dir as well on custom configs
G_CONFIG_INJECT 'data_dir[[:blank:]]*=' 'data_dir = /mnt/dietpi_userdata/mopidy/data' /etc/mopidy/mopidy.conf '\[core\]'
G_CONFIG_INJECT 'cache_dir[[:blank:]]*=' 'cache_dir = /mnt/dietpi_userdata/mopidy/cache' /etc/mopidy/mopidy.conf '\[core\]'
# Move existing home+data to dietpi_userdata if not yet existent
if [[ -d '/var/lib/mopidy' && ! -d '/mnt/dietpi_userdata/mopidy' ]]
then
G_EXEC mv /var/lib/mopidy /mnt/dietpi_userdata/mopidy
# Workaround error about moving data dir into itself
G_EXEC mkdir -p /mnt/dietpi_userdata/mopidy/.hidden_data
# Non-hidden files/dirs are data
G_EXEC mv /mnt/dietpi_userdata/mopidy/* /mnt/dietpi_userdata/mopidy/.hidden_data
G_EXEC mv /mnt/dietpi_userdata/mopidy/.hidden_data /mnt/dietpi_userdata/mopidy/data
else
G_EXEC mkdir -p /mnt/dietpi_userdata/mopidy/data
[[ -d '/var/lib/mopidy' ]] && G_EXEC rm -R /var/lib/mopidy
fi
# Move existing cache to dietpi_userdata if not yet existent
if [[ -d '/var/cache/mopidy' && ! -d '/mnt/dietpi_userdata/mopidy/cache' ]]
then
G_EXEC mv /var/cache/mopidy /mnt/dietpi_userdata/mopidy/cache
else
G_EXEC mkdir -p /mnt/dietpi_userdata/mopidy/cache
[[ -d '/var/cache/mopidy' ]] && G_EXEC rm -R /var/cache/mopidy
fi
# Permissions
G_EXEC chown -R mopidy:root /mnt/dietpi_userdata/mopidy
# User
G_EXEC usermod -g dietpi -aG audio -d /mnt/dietpi_userdata/mopidy mopidy
# Do not pre-create obsolete cache dir
G_EXEC mkdir -p /etc/systemd/system/mopidy.service.d
G_EXEC eval 'echo -e '\''[Service]\nExecStartPre='\'' > /etc/systemd/system/mopidy.service.d/dietpi.conf'
Download_Test_Media
fi
if To_Install 31 # Kodi
then
# RPi repo
if [[ -f '/etc/apt/sources.list.d/raspi.list' ]]
then
# Purge DietPi Bullseye build with higher epoch version
[[ $G_DISTRO == 6 && $(dpkg-query -Wf '${Version}' kodi 2> /dev/null) == '3:19.1-dietpi'[1-3] ]] && G_EXEC_OUTPUT=1 G_EXEC dpkg -P kodi
if (( $G_HW_MODEL < 10 ))
then
# Enhance 4k support on RPi 4 and above with 512 MiB CMA
local cma=()
(( $G_HW_MODEL > 3 )) && cma=(512)
/boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-kms-v3d "${cma[@]}"
# Enable hardware codecs
/boot/dietpi/func/dietpi-set_hardware rpi-codec 1
fi
# Bookworm: Install Kodi 21
(( $G_DISTRO == 7 )) && aDEPS=('kodi21') || aDEPS=('kodi')
# Trixie: has now dedicated kodi-repository-kodi
(( $G_DISTRO > 7 )) && aDEPS+=('kodi-repository-kodi')
# Missing dependency
aDEPS+=('libgl1-mesa-dri')
# Bookworm: Apply missing dir workaround: https://github.com/RPi-Distro/repo/issues/153
(( $G_DISTRO == 7 )) && G_EXEC mkdir -p /etc/polkit-1/localauthority/50-local.d
# Everything else
else
aDEPS=('kodi' 'kodi-repository-kodi')
(( $G_HW_ARCH == 10 || $G_DISTRO > 6 )) || aDEPS+=('libgles2') # Missing ARM dependency until Bookworm
fi
# Desktop entry
G_EXEC mkdir -p /var/lib/dietpi/dietpi-software/installed/desktop/icons /usr/share/applications
G_THREAD_START curl -sSfLo /var/lib/dietpi/dietpi-software/installed/desktop/icons/kodi-icon.png "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/icons/kodi-icon.png"
G_THREAD_START curl -sSfLo /usr/share/applications/kodi.desktop "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/apps/kodi.desktop"
# Grant R/W access to all input and console devices for members of the "input" respectively "tty" group
dps_index=$software_id Download_Install '99-dietpi-kodi.rules' /etc/udev/rules.d/99-dietpi-kodi.rules
# Desktop shortcut
Create_Desktop_Shortcut kodi
fi
if To_Install 39 minidlna # ReadyMedia
then
# Config
[[ -f '/etc/minidlna.conf' ]] || dps_index=$software_id Download_Install 'minidlna.conf' /etc/minidlna.conf
G_AGI minidlna
G_EXEC systemctl stop minidlna
# Remove obsolete service file
Remove_SysV minidlna 1
# User: Make "dietpi" the primary group to enable cross-access to media files
Create_User -g dietpi -G minidlna -d /var/lib/minidlna minidlna
# Service: Debian patch enforces file logging, overriding "-S": https://github.com/MichaIng/DietPi/issues/4745
cat << '_EOF_' > /etc/systemd/system/minidlna.service
[Unit]
Description=ReadyMedia (DietPi)
Documentation=man:minidlnad(1) man:minidlna.conf(5)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=minidlna
LogsDirectory=minidlna
ExecStart=/usr/sbin/minidlnad -S -R -f /etc/minidlna.conf -P /dev/null
[Install]
WantedBy=multi-user.target
_EOF_
# Cache
G_EXEC mkdir -p /mnt/dietpi_userdata/.MiniDLNA_Cache
G_EXEC chown -R minidlna:root /mnt/dietpi_userdata/.MiniDLNA_Cache
Download_Test_Media
fi
if To_Install 108 # Amiberry
then
# RPi: Enable KMS/DRM
(( $G_HW_MODEL < 10 )) && /boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-kms-v3d
G_AGI amiberry
# Permissions: Enable access for kickstart uploads via file servers
local fp_roms='/mnt/dietpi_userdata/amiberry/roms'
# - ARMv6: Amiberry v5 used a different default dir for kickstart ROMs
(( $G_HW_ARCH == 1 )) && fp_roms='/mnt/dietpi_userdata/amiberry/kickstarts'
G_EXEC mkdir -p "$fp_roms"
G_EXEC chgrp dietpi "$fp_roms"
G_EXEC chmod g=rwx "$fp_roms"
# Pre-v6.26: Remove obsolete config file: https://github.com/BlitterStudio/amiberry/releases/tag/v2.25
[[ -f '/mnt/dietpi_userdata/amiberry/conf/adfdir.conf' ]] && G_EXEC rm /mnt/dietpi_userdata/amiberry/conf/adfdir.conf
# Pre-v8.5: Remove obsolete library and service files
if (( $G_HW_ARCH == 1 ))
then
[[ -e '/mnt/dietpi_userdata/amiberry/lib/libSDL2.so' ]] && G_EXEC rm -f /mnt/dietpi_userdata/amiberry/lib/libSDL2*.so{,.0.*}
else
[[ -d '/mnt/dietpi_userdata/amiberry/lib' ]] && G_EXEC rm -R /mnt/dietpi_userdata/amiberry/lib
fi
[[ -f '/etc/systemd/system/amiberry.service' ]] && G_EXEC rm /etc/systemd/system/amiberry.service
fi
if To_Install 10 # Amiberry-Lite
then
# RPi: Enable KMS/DRM
(( $G_HW_MODEL < 10 )) && /boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-kms-v3d
G_AGI amiberry-lite
# Permissions: Enable access for kickstart uploads via file servers
G_EXEC mkdir -p /mnt/dietpi_userdata/amiberry-lite/roms
G_EXEC chgrp dietpi /mnt/dietpi_userdata/amiberry-lite/roms
G_EXEC chmod g=rwx /mnt/dietpi_userdata/amiberry-lite/roms
fi
if To_Install 112 # DXX-Rebirth
then
aDEPS=('libsdl-mixer1.2' 'libsdl1.2debian' 'libphysfs1' 'libgl1' 'libglu1-mesa')
Download_Install 'https://dietpi.com/downloads/binaries/rpi/dxx-rebirth.7z' /mnt/dietpi_userdata
# Symlink savegames to root
# - Remove existing symlinks
G_EXEC rm -Rf /root/.d{1,2}x-rebirth
G_EXEC ln -sf /mnt/dietpi_userdata/dxx-rebirth/descent_1_profiles /root/.d1x-rebirth
G_EXEC ln -sf /mnt/dietpi_userdata/dxx-rebirth/descent_2_profiles /root/.d2x-rebirth
# +exe
G_EXEC chmod -R +x /mnt/dietpi_userdata/dxx-rebirth/*
# Desktop menu entry
G_EXEC mkdir -p /usr/share/applications
G_EXEC ln -sf /mnt/dietpi_userdata/dxx-rebirth/dxx-rebirth.desktop /usr/share/applications/dxx-rebirth.desktop
# Desktop shortcut
Create_Desktop_Shortcut dxx-rebirth
fi
if To_Install 111 urbackupsrv # UrBackup Server
then
# Pre-configure backup path: Read from database on reinstall and align all configs
if [[ -f '/var/urbackup/backup_server_settings.db' ]] && command -v sqlite3 > /dev/null
then
local backuppath=$(sqlite3 /var/urbackup/backup_server_settings.db 'select value from settings where key = "backupfolder"')
else
local backuppath=$(sed -n '/^[[:blank:]]*SOFTWARE_URBACKUP_BACKUPPATH=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
fi
[[ $backuppath ]] || backuppath='/mnt/dietpi_userdata/urbackup'
[[ -f '/etc/urbackup/backupfolder' ]] && G_EXEC eval "echo '$backuppath' > /etc/urbackup/backupfolder"
[[ -f '/var/urbackup/backupfolder' ]] && G_EXEC eval "echo '$backuppath' > /var/urbackup/backupfolder"
G_EXEC eval "debconf-set-selections <<< 'urbackup-server urbackup/backuppath string $backuppath'"
# APT distro
local distro='Debian_'
(( $G_HW_ARCH == 1 )) && distro='Raspbian_'
case $G_DISTRO in
6) distro+='11';;
7) distro+='12';;
*) (( $G_HW_ARCH == 1 )) && distro+='12' || distro+='13';;
esac
# APT key
G_EXEC mkdir -p /etc/apt/keyrings
G_EXEC curl -sSfLo /etc/apt/keyrings/dietpi-urbackup.asc "https://download.opensuse.org/repositories/home:/uroni/$distro/Release.key"
# APT source
G_EXEC eval "cat << '_EOF_' > /etc/apt/sources.list.d/dietpi-urbackup.sources
Types: deb
URIs: https://download.opensuse.org/repositories/home:/uroni/$distro
Suites: /
Signed-By: /etc/apt/keyrings/dietpi-urbackup.asc
_EOF_"
G_AGUP
# APT package
G_AGI urbackup-server
G_EXEC systemctl stop urbackupsrv
unset -v backuppath distro
fi
if To_Install 51 # OpenTyrian
then
aDEPS=('libsdl1.2debian' 'libsdl-net1.2')
Download_Install 'https://dietpi.com/downloads/binaries/rpi/opentyrian_armhf.zip' /
# Move to /usr/games
G_EXEC cp -a /usr/local/games/opentyrian /usr/games/
G_EXEC rm -R /usr/local/games/opentyrian
G_EXEC chmod +x /usr/games/opentyrian/opentyrian
# Desktop menu entry
G_THREAD_START curl -sSfL "https://raw.githubusercontent.com/$G_GITOWNER/DietPi/$G_GITBRANCH/.conf/desktop/apps/opentyrian.desktop" -o /usr/share/applications/opentyrian.desktop
# Copy the DietPi run file for OpenTyrian
dps_index=$software_id Download_Install 'run' /usr/games/opentyrian/run
G_EXEC chmod +x /usr/games/opentyrian/run
# Desktop shortcut
Create_Desktop_Shortcut opentyrian
G_THREAD_WAIT
fi
if To_Install 59 raspimjpeg # RPi Cam Web Interface
then
aDEPS=('gpac')
Download_Install 'https://github.com/silvanmelchior/RPi_Cam_Web_Interface/archive/master.tar.gz'
G_EXEC cd RPi_Cam_Web_Interface-master
# Remove files which are not to be installed
[[ -f 'www/status_mjpeg.txt' ]] && G_EXEC rm www/status_mjpeg.txt
# Web base dir
G_EXEC mkdir -p /var/www/rpicam
G_EXEC cp -dR www/. /var/www/rpicam/
[[ -e '/var/www/rpicam/FIFO' ]] || G_EXEC mknod /var/www/rpicam/FIFO p
[[ -e '/var/www/rpicam/FIFO1' ]] || G_EXEC mknod /var/www/rpicam/FIFO1 p
# - Dynamic cam preview and status
G_EXEC ln -sf /run/shm/mjpeg/cam.jpg /var/www/rpicam/cam.jpg
G_EXEC ln -sf /run/shm/mjpeg/status_mjpeg.txt /var/www/rpicam/status_mjpeg.txt
# Allow shutdown and reboot via web interface
G_EXEC cp etc/sudoers.d/RPI_Cam_Web_Interface /etc/sudoers.d/dietpi-rpi_cam_control
# Install RaspiMJPEG
# - Binary
G_EXEC cp {,/usr/local/}bin/raspimjpeg
G_EXEC chmod +x /usr/local/bin/raspimjpeg
# - Config
G_EXEC cp etc/raspimjpeg/raspimjpeg.1 /etc/raspimjpeg
G_EXEC ln -sf /etc/raspimjpeg /var/www/rpicam/raspimjpeg
# - Web base dir
grep -q '/rpicam' /etc/raspimjpeg || G_EXEC sed --follow-symlinks -i 's|/var/www|/var/www/rpicam|g' /etc/raspimjpeg
# - Service
dps_index=$software_id Download_Install 'raspimjpeg.sh' /var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh
G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh
cat << '_EOF_' > /etc/systemd/system/raspimjpeg.service
[Unit]
Description=RaspiMJPEG (DietPi)
[Service]
RemainAfterExit=yes
ExecStart=/var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh start
ExecStop=/var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh stop
[Install]
WantedBy=multi-user.target
_EOF_
# Cleanup
G_EXEC cd "$G_WORKING_DIR"
G_EXEC rm -R RPi_Cam_Web_Interface-master
# Data directory
G_EXEC mkdir -p /mnt/dietpi_userdata/rpicam
G_EXEC rm -Rf /var/www/rpicam/media
G_EXEC ln -s /mnt/dietpi_userdata/rpicam /var/www/rpicam/media
# Enable RPi camera
/boot/dietpi/func/dietpi-set_hardware rpi-camera enable
# Permissions
G_EXEC usermod -aG video www-data
G_EXEC chown -R www-data:www-data /var/www/rpicam /mnt/dietpi_userdata/rpicam
fi
if To_Install 45 deluged deluge-web # Deluge
then
# Packages
G_AGI deluged deluge-web deluge-console
(( $G_DISTRO > 6 )) && G_EXEC systemctl stop deluge-web
G_EXEC systemctl stop deluged
# Workaround for broken Bookworm package: https://github.com/MichaIng/DietPi/issues/6408
(( $G_DISTRO == 7 )) && G_EXEC_DESC='Fixing broken script in Bookworm package' G_EXEC sed --follow-symlinks -i '/bind_textdomain_codeset/d' /usr/lib/python3/dist-packages/deluge/i18n/util.py
# Remove SysV service leftovers, installed by Debian APT package
Remove_SysV deluged 1
[[ -d '/var/lib/deluged' ]] && G_EXEC rm -R /var/lib/deluged
[[ -d '/var/log/deluged' ]] && G_EXEC rm -R /var/log/deluged
# User
Create_User -g dietpi -G debian-deluged -d /mnt/dietpi_userdata/deluge debian-deluged
# Config
if [[ ! -f '/mnt/dietpi_userdata/deluge/.config/deluge/core.conf' ]]
then
G_EXEC mkdir -p /mnt/dietpi_userdata/deluge/.config/deluge
# Default configs
dps_index=$software_id Download_Install 'deluge.conf' /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
dps_index=$software_id Download_Install 'deluge_web.conf' /mnt/dietpi_userdata/deluge/.config/deluge/web.conf
dps_index=$software_id Download_Install 'hostlist.conf' /mnt/dietpi_userdata/deluge/.config/deluge/hostlist.conf
# Apply optimised settings
# - Cache size in 16 KiB units
G_CONFIG_INJECT '"cache_size":' " \"cache_size\": $(( $(Optimise_BitTorrent 0) * 1024 / 16 ))," /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
G_CONFIG_INJECT '"max_active_limit":' " \"max_active_limit\": $(Optimise_BitTorrent 1)," /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
G_CONFIG_INJECT '"max_active_downloading":' " \"max_active_downloading\": $(Optimise_BitTorrent 1)," /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
G_CONFIG_INJECT '"max_connections_global":' " \"max_connections_global\": $(Optimise_BitTorrent 2)," /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
G_CONFIG_INJECT '"max_upload_slots_global":' " \"max_upload_slots_global\": $(Optimise_BitTorrent 3)," /mnt/dietpi_userdata/deluge/.config/deluge/core.conf
# Web UI access
local salt=$(tr -dc '0-9a-f' < /dev/random | head -c40)
GCI_PASSWORD=1 G_CONFIG_INJECT '"pwd_salt":' " \"pwd_salt\": \"$salt\"," /mnt/dietpi_userdata/deluge/.config/deluge/web.conf
GCI_PASSWORD=1 G_CONFIG_INJECT '"pwd_sha1":' " \"pwd_sha1\": \"$(echo -n "$salt$GLOBAL_PW" | sha1sum | mawk '{print $1}')\"," /mnt/dietpi_userdata/deluge/.config/deluge/web.conf
unset -v salt
# Client access
local localpass=$(tr -dc '0-9a-f' < /dev/random | head -c40)
> /mnt/dietpi_userdata/deluge/.config/deluge/auth
G_EXEC chmod 0600 /mnt/dietpi_userdata/deluge/.config/deluge/auth
echo -e "root:$GLOBAL_PW:10\nlocalclient:$localpass:10" > /mnt/dietpi_userdata/deluge/.config/deluge/auth
# Web UI autoconnect
local id=$(tr -dc '0-9a-f' < /dev/random | head -c32)
G_EXEC chmod 0600 /mnt/dietpi_userdata/deluge/.config/deluge/hostlist.conf
G_CONFIG_INJECT '"[0-9a-f]{32}"' " \"$id\"," /mnt/dietpi_userdata/deluge/.config/deluge/hostlist.conf
sed --follow-symlinks -i "/\"[0-9a-f]\{40\}\"/c\ \"$localpass\"" /mnt/dietpi_userdata/deluge/.config/deluge/hostlist.conf
G_CONFIG_INJECT '"default_daemon":' " \"default_daemon\": \"$id\"," /mnt/dietpi_userdata/deluge/.config/deluge/web.conf
unset -v localpass id
RESTART_DELUGE_WEB=1
fi
# Permissions
G_EXEC chown -R debian-deluged:root /mnt/dietpi_userdata/deluge
# Service: https://github.com/deluge-torrent/deluge/blob/develop/packaging/systemd/deluged.service
cat << '_EOF_' > /etc/systemd/system/deluged.service
[Unit]
Description=Deluge Daemon (DietPi)
Documentation=man:deluged
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=debian-deluged
UMask=007
ExecStart=/usr/bin/deluged -d
[Install]
WantedBy=multi-user.target
_EOF_
# https://github.com/deluge-torrent/deluge/blob/develop/packaging/systemd/deluge-web.service
cat << _EOF_ > /etc/systemd/system/deluge-web.service
[Unit]
Description=Deluge Web UI (DietPi)
Documentation=man:deluge-web
Wants=network-online.target
After=network-online.target deluged.service
[Service]
User=debian-deluged
UMask=027
ExecStart=/usr/bin/deluge-web -d
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 115 webmin # Webmin: https://github.com/webmin/webmin/blob/master/webmin-setup-repo.sh
then
# APT key
G_EXEC curl -sSfLo '/etc/apt/trusted.gpg.d/dietpi-webmin.asc' 'https://webmin.com/developers-key.asc'
# APT list
G_EXEC eval 'echo '\''deb https://download.webmin.com/download/newkey/repository stable contrib'\'' > /etc/apt/sources.list.d/dietpi-webmin.list'
G_AGUP
# APT package
G_AGI webmin
G_EXEC systemctl stop webmin
fi
if To_Install 195 # youtube-dl
then
aDEPS=('python3')
Download_Install 'https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp' /usr/local/bin/yt-dlp
G_EXEC chmod +x /usr/local/bin/yt-dlp
G_EXEC ln -sf /usr/local/bin/yt-dlp /usr/local/bin/youtube-dl
# Remove obsolete /usr/local/bin/youtube-dl-py2 we used previously
[[ -f '/usr/local/bin/youtube-dl-py2' ]] && G_EXEC rm /usr/local/bin/youtube-dl-py2
fi
if To_Install 129 # O!MPD
then
Download_Install 'https://github.com/ArturSierzant/OMPD/archive/master.tar.gz'
# Replace existing installs but preserve local config override
if [[ -d '/var/www/ompd' ]]
then
[[ -f '/var/www/ompd/include/config.local.inc.php' ]] && G_EXEC mv /var/www/ompd/include/config.local.inc.php OMPD-master/include/
G_EXEC rm -R /var/www/ompd
fi
G_EXEC mv OMPD-master /var/www/ompd
# Preserve config file, create local override if non-existent
# Database will be migrated automatically, so only create new on fresh install, using local override config as flag
if [[ ! -f '/var/www/ompd/include/config.local.inc.php' ]]
then
G_EXEC systemctl start mariadb
# Create database user only, database will be created automatically
mysql -e "grant all privileges on ompd.* to ompd@localhost identified by '$GLOBAL_PW';"
G_EXEC systemctl stop mariadb
cat << _EOF_ > /var/www/ompd/include/config.local.inc.php
<?php
# Database
\$cfg['mysqli_host'] = '127.0.0.1';
\$cfg['mysqli_db'] = 'ompd';
\$cfg['mysqli_user'] = 'ompd';
\$cfg['mysqli_password'] = '$GLOBAL_PW';
\$cfg['mysqli_port'] = '';
\$cfg['mysqli_socket'] = '/run/mysqld/mysqld.sock';
\$cfg['mysqli_auto_create_db'] = true;
# Media dir
\$cfg['media_dir'] = '/mnt/dietpi_userdata/Music/';
?>
_EOF_
fi
# Webserver configs
# - Lighttpd
if (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
dps_index=$software_id Download_Install 'lighttpd.ompd.conf' /etc/lighttpd/conf-available/99-dietpi-ompd.conf
[[ -f '/etc/lighttpd/conf-enabled/99-dietpi-ompd.conf' ]] || G_EXEC lighty-enable-mod dietpi-ompd
fi
# Permissions: http://ompd.pl/configuration
G_EXEC chown -R www-data:www-data /var/www/ompd/{tmp,stream,cache,covers,include/config.local.inc.php}
fi
if To_Install 135 icecast2 darkice # Icecast + DarkIce
then
G_AGI darkice icecast2
# Trixie does not ship init.d service anymore
local darkice=()
(( $G_DISTRO < 8 )) && darkice=('darkice')
G_EXEC systemctl stop "${darkice[@]}" icecast2
# Icecast
# - Config: Set passwords if not yet done (default found)
# - HTML-encode characters with special meaning in XML. Icecast treats those correctly for authentication.
local password=${GLOBAL_PW//&/\&amp;}; password=${password//</\&lt;}
sed --follow-symlinks -i "/<source-password>hackme/c\ <source-password>$password</source-password>" /etc/icecast2/icecast.xml
sed --follow-symlinks -i "/<relay-password>hackme/c\ <relay-password>$password</relay-password>" /etc/icecast2/icecast.xml
sed --follow-symlinks -i "/<admin-password>hackme/c\ <admin-password>$password</admin-password>" /etc/icecast2/icecast.xml
unset -v password
# - Service: Replace init.d service with native systemd service
Remove_SysV icecast2 1
cat << _EOF_ > /etc/systemd/system/icecast2.service
[Unit]
Description=Icecast (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
User=icecast2
Group=icecast
ExecStart=/usr/bin/icecast2 -c /etc/icecast2/icecast.xml
[Install]
WantedBy=multi-user.target
_EOF_
# DarkIce
# - User
Create_User -g audio -G dietpi darkice
# - Config
[[ -f '/etc/darkice.cfg' ]] || > /etc/darkice.cfg
G_EXEC chown darkice:root /etc/darkice.cfg
G_EXEC chmod 0600 /etc/darkice.cfg
local input_device_index=$(arecord -l | mawk -F'[ :]' '/card/{print $2;exit}')
cat << _EOF_ > /etc/darkice.cfg
[general]
duration = 0
bufferSecs = 3
reconnect = yes
[input]
device = hw:${input_device_index:-0},0
sampleRate = 44100
bitsPerSample = 16
channel = 1
[icecast2-0]
bitrateMode = vbr
format = vorbis
quality = 0.8
server = localhost
port = 8000
password = $GLOBAL_PW
mountPoint = DietPi
name = DietPi
description = DarkIce on DietPi
url = http://localhost
genre = none
public = no
#localDumpFile = /mnt/dietpi_userdata/darkice_recording.ogg
_EOF_
# - Service: Replace init.d service with native systemd service
Remove_SysV darkice 1
cat << _EOF_ > /etc/systemd/system/darkice.service
[Unit]
Description=DarkIce (DietPi)
After=icecast2.service
[Service]
User=darkice
AmbientCapabilities=CAP_SYS_NICE
ExecStart=/usr/bin/darkice
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 63 # LinuxDash
then
Download_Install 'https://github.com/afaqurk/linux-dash/archive/master.tar.gz'
G_EXEC mkdir -p /var/www/linuxdash
G_EXEC cp -a linux-dash-master/* /var/www/linuxdash/
G_EXEC rm -R linux-dash-master
fi
if To_Install 184 tor # Tor Relay
then
G_AGI tor
G_EXEC systemctl stop tor
local nickname=
G_WHIP_BUTTON_CANCEL_TEXT='Skip'
G_WHIP_INPUTBOX 'Pick a nickname for your relay.\n\nNB: This is optional, select "Skip" to leave it empty.' && nickname=$G_WHIP_RETURNED_VALUE
local email=
G_WHIP_BUTTON_CANCEL_TEXT='Skip'
G_WHIP_INPUTBOX 'Pick a contact email for your relay.\n\nNB: This is optional but encouraged, and it will be published depending on your relay.' && email=$G_WHIP_RETURNED_VALUE
local orport=443
local invalid_text=
while :
do
G_WHIP_DEFAULT_ITEM=$orport G_WHIP_NOCANCEL=1
if G_WHIP_INPUTBOX "${invalid_text}Please enter the ORPort that people will use to connect to your Tor relay:
\nNB: This port needs to be forwarded by your router and/or opened in your firewall settings. Default value is: 443, which should be changed when running a webserver.
\nDo not use 9001, as this port is commonly associated with Tor and censors might scan for it."
then
disable_error=1 G_CHECK_VALIDINT "$G_WHIP_RETURNED_VALUE" 1 || { invalid_text='[ERROR] No valid entry found, value needs to be a sequence of integers. Please retry...\n\n'; continue; }
fi
orport=$G_WHIP_RETURNED_VALUE
break
done
local ipv6=
if G_GET_NET -6 -q gateway > /dev/null
then
for i in $(hostname -I)
do
[[ $i == *'.'* ]] && continue
G_WHIP_DEFAULT_ITEM=$i
break
done
G_WHIP_BUTTON_CANCEL_TEXT='Skip'
G_WHIP_INPUTBOX "Please enter the private IPv6 address of this device. Skip this if you only want to use IPv4, or if this device doesn't have an IPv6 address." && ipv6=$G_WHIP_RETURNED_VALUE
fi
while :
do
G_WHIP_MENU_ARRAY=(
'Bridge' ': Use this machine as an obfs4 bridge. This is the safest to run from home.'
'Guard/Middle' ': Use this machine as a beginning or middle relay. Some info will be made public.'
'Exit' ': Use this machine as an exit relay. This is most at risk for legal complaints.'
)
G_WHIP_DEFAULT_ITEM='Bridge' G_WHIP_NOCANCEL=1
G_WHIP_MENU "Please choose which type of relay this should be set up as:"
if [[ $G_WHIP_RETURNED_VALUE == 'Exit' ]]
then
G_WHIP_BUTTON_OK_TEXT='YES'
G_WHIP_BUTTON_CANCEL_TEXT='NO'
G_WHIP_YESNO 'WARNING: DO NOT RUN AN EXIT RELAY FROM HOME.
\nAlso, it is highly recommended that you setup exit relays on dedicated servers.
\nBefore running an exit relay, check with your hosting provider to make sure they allow it.
\nAlso, set a reverse DNS (PTR) record (tor-exit is a good name), and preferably edit your WHOIS record to show that this is a Tor exit node.
\nWould you still like to continue?' || continue
fi
break
done
if [[ $G_WHIP_RETURNED_VALUE == 'Bridge' ]]
then
G_AGI obfs4proxy
local obfs4port=80
invalid_text=
while :
do
G_WHIP_DEFAULT_ITEM=$obfs4port G_WHIP_NOCANCEL=1
if G_WHIP_INPUTBOX "${invalid_text}Please enter the obfs4 port that people will use to connect to your bridge:
\nNB: This port needs to be forwarded by your router and/or opened in your firewall settings. Default value is: 80. Do not use default value if running a webserver.
\nDo not use 9001, as this port is commonly associated with Tor and censors might scan for it."
then
disable_error=1 G_CHECK_VALIDINT "$G_WHIP_RETURNED_VALUE" 1 || { invalid_text='[ERROR] No valid entry found, value needs to be a sequence of integers. Please retry...\n\n'; continue; }
fi
obfs4port=$G_WHIP_RETURNED_VALUE
break
done
# Grant capability to bind to privileged ports below 1024
(( $obfs4port < 1024 )) && G_EXEC setcap cap_net_bind_service=+ep "$(command -v obfs4proxy)"
cat << _EOF_ > /etc/tor/torrc
BridgeRelay 1
ORPort $orport
ServerTransportPlugin obfs4 exec $(command -v obfs4proxy)
ServerTransportListenAddr obfs4 0.0.0.0:$obfs4port
ExtORPort auto
_EOF_
G_EXEC mkdir -p /etc/systemd/system/tor@.service.d
G_EXEC eval "echo -e '[Service]\nNoNewPrivileges=no' > /etc/systemd/system/tor@.service.d/dietpi-relay.conf"
elif [[ $G_WHIP_RETURNED_VALUE == 'Guard/Middle' ]]
then
cat << _EOF_ > /etc/tor/torrc
ORPort $orport
ExitRelay 0
SocksPort 0
ControlSocket 0
_EOF_
elif [[ $G_WHIP_RETURNED_VALUE == 'Exit' ]]
then
G_AGI unbound
aSOFTWARE_INSTALL_STATE[182]=1
cat << _EOF_ > /etc/tor/torrc
ORPort $orport
ExitRelay 1
SocksPort 0
ControlSocket 0
ExitPolicy accept *:20-21
ExitPolicy accept *:43
ExitPolicy accept *:53
ExitPolicy accept *:79
ExitPolicy accept *:80-81
ExitPolicy accept *:88
ExitPolicy accept *:110
ExitPolicy accept *:143
ExitPolicy accept *:220
ExitPolicy accept *:389
ExitPolicy accept *:443
ExitPolicy accept *:464
ExitPolicy accept *:531
ExitPolicy accept *:543-544
ExitPolicy accept *:554
ExitPolicy accept *:636
ExitPolicy accept *:706
ExitPolicy accept *:749
ExitPolicy accept *:873
ExitPolicy accept *:902-904
ExitPolicy accept *:981
ExitPolicy accept *:989-990
ExitPolicy accept *:991
ExitPolicy accept *:992
ExitPolicy accept *:993
ExitPolicy accept *:995
ExitPolicy accept *:1194
ExitPolicy accept *:1220
ExitPolicy accept *:1293
ExitPolicy accept *:1500
ExitPolicy accept *:1533
ExitPolicy accept *:1677
ExitPolicy accept *:1723
ExitPolicy accept *:1755
ExitPolicy accept *:1863
ExitPolicy accept *:2082
ExitPolicy accept *:2083
ExitPolicy accept *:2086-2087
ExitPolicy accept *:2095-2096
ExitPolicy accept *:2102-2104
ExitPolicy accept *:3690
ExitPolicy accept *:4321
ExitPolicy accept *:4643
ExitPolicy accept *:5050
ExitPolicy accept *:5190
ExitPolicy accept *:5222-5223
ExitPolicy accept *:5228
ExitPolicy accept *:8008
ExitPolicy accept *:8074
ExitPolicy accept *:8082
ExitPolicy accept *:8087-8088
ExitPolicy accept *:8232-8233
ExitPolicy accept *:8332-8333
ExitPolicy accept *:8443
ExitPolicy accept *:8888
ExitPolicy accept *:9418
ExitPolicy accept *:10000
ExitPolicy accept *:11371
ExitPolicy accept *:19294
ExitPolicy accept *:19638
ExitPolicy accept *:50002
ExitPolicy accept *:64738
ExitPolicy reject *:*
_EOF_
[[ $ipv6 ]] && echo 'IPv6Exit 1' >> /etc/tor/torrc
# Apply Unbound as local DNS resolver for privacy reasons
command -v resolvconf > /dev/null && G_EXEC sed --follow-symlinks -i 's/dns-nameservers.*$/dns-nameservers 127.0.0.1/' /etc/network/interfaces
G_EXEC eval "echo 'nameserver 127.0.0.1' > /etc/resolv.conf" # Unbound is running and configured in the very next config step, so it is safe to switch the DNS nameserver right now.
fi
G_EXEC eval 'echo '\''ControlPort 9051'\'' >> /etc/tor/torrc'
G_EXEC eval 'echo '\''CookieAuthentication 1'\'' >> /etc/tor/torrc'
[[ $nickname ]] && G_EXEC eval "echo 'Nickname $nickname' >> /etc/tor/torrc"
[[ $email ]] && G_EXEC eval "echo 'ContactInfo $email' >> /etc/tor/torrc"
[[ $ipv6 ]] && G_EXEC eval "echo 'ORPort [$ipv6]:$orport' >> /etc/tor/torrc"
fi
if To_Install 182 unbound # Unbound
then
G_DIETPI-NOTIFY 2 'Pre-configuring Unbound to avoid port binding conflicts'
# Download base configuration if it does not exist yet
[[ -f '/etc/unbound/unbound.conf.d/dietpi.conf' ]] || dps_index=$software_id Download_Install 'unbound.conf' /etc/unbound/unbound.conf.d/dietpi.conf
# Toggle IPv6 preference based whether there is an IPv6 default route
if G_GET_NET -6 -q gateway > /dev/null
then
G_CONFIG_INJECT 'do-ip6:[[:blank:]]' ' do-ip6: yes' /etc/unbound/unbound.conf.d/dietpi.conf 'server:'
else
G_CONFIG_INJECT 'do-ip6:[[:blank:]]' ' do-ip6: no' /etc/unbound/unbound.conf.d/dietpi.conf 'server:'
fi
# Since IP binding might be used, start after network interfaces have been configured, not when they just start to be configured
G_EXEC mkdir -p /etc/systemd/system/unbound.service.d
G_EXEC eval "echo -e '[Unit]\nWants=network-online.target\nAfter=network-online.target' > /etc/systemd/system/unbound.service.d/dietpi.conf"
# Pi-hole + AdGuard Home part 1
if (( ${aSOFTWARE_INSTALL_STATE[93]} > 0 || ${aSOFTWARE_INSTALL_STATE[126]} > 0 )) && grep -q '^[[:blank:]]*port:[[:blank:]][[:blank:]]*53$' /etc/unbound/unbound.conf.d/dietpi.conf
then
G_DIETPI-NOTIFY 2 'Configuring Unbound to work for your selected AdBlocker'
grep '^[[:blank:]]*nameserver[[:blank:]]' /etc/resolv.conf | grep -qvE '[[:blank:]]127.0.0.1(:53)?$' || echo 'nameserver 9.9.9.9' >> /etc/resolv.conf # Failsafe
G_CONFIG_INJECT 'port:[[:blank:]]' ' port: 5335' /etc/unbound/unbound.conf.d/dietpi.conf 'server:'
G_CONFIG_INJECT 'interface:[[:blank:]]' ' interface: 127.0.0.1' /etc/unbound/unbound.conf.d/dietpi.conf 'server:'
fi
# Reinstall: Restart to apply possible config changes
[[ -f '/lib/systemd/system/unbound.service' ]] && G_EXEC systemctl restart unbound
# Disable and mask unbound-resolvconf.service: https://github.com/MichaIng/DietPi/issues/5133
[[ -f '/lib/systemd/system/unbound-resolvconf.service' ]] && G_EXEC systemctl --no-reload disable --now unbound-resolvconf
G_EXEC systemctl --no-reload mask unbound-resolvconf
G_AGI unbound dns-root-data
Remove_SysV unbound
G_EXEC systemctl enable --now unbound # Failsafe
# Prevent resolvconf from disabling Unbound's exclusive recursive resolving or DoT by adding upstream resolvers of the host to Unbound's forward zone: https://salsa.debian.org/dns-team/unbound/-/blob/master/debian/resolvconf-forwards
[[ -f '/etc/resolvconf/update.d/unbound' ]] && G_EXEC chmod -x /etc/resolvconf/update.d/unbound
# Pi-hole part 2
if (( ${aSOFTWARE_INSTALL_STATE[93]} == 2 ))
then
G_DIETPI-NOTIFY 2 'Configuring Pi-hole to use Unbound'
# v6
if [[ $(pihole-FTL -v) == 'v6'* ]]
then
G_EXEC pihole-FTL --config dns.upstreams '[ "127.0.0.1#5335" ]'
fi
# v5
if [[ -f '/etc/dnsmasq.d/01-pihole.conf' ]]
then
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*server=/d' /etc/dnsmasq.d/01-pihole.conf
G_CONFIG_INJECT 'server=' 'server=127.0.0.1#5335' /etc/dnsmasq.d/01-pihole.conf
systemctl -q is-active pihole-FTL && G_EXEC systemctl restart pihole-FTL
fi
if [[ -f '/etc/pihole/setupVars.conf' ]]
then
G_CONFIG_INJECT 'PIHOLE_DNS_1=' 'PIHOLE_DNS_1=127.0.0.1#5335' /etc/pihole/setupVars.conf
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*PIHOLE_DNS_2=/d' /etc/pihole/setupVars.conf
fi
fi
# AdGuard Home part 2
if (( ${aSOFTWARE_INSTALL_STATE[126]} == 2 ))
then
G_DIETPI-NOTIFY 2 'Configuring AdGuard Home to use Unbound'
if [[ ! -f '/mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf' ]]
then
echo '127.0.0.1:5335' > /mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf
G_CONFIG_INJECT 'upstream_dns_file:[[:blank:]]' ' upstream_dns_file: /mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf' /mnt/dietpi_userdata/adguardhome/AdGuardHome.yaml
systemctl -q is-active adguardhome && G_EXEC systemctl restart adguardhome
fi
fi
# Plex Media Server: Fix secure remote access: https://dietpi.com/forum/t/cant-connect-to-plex-directly-due-to-unbound/5199
(( ${aSOFTWARE_INSTALL_STATE[42]} > 0 )) && echo -e 'server:\n\tprivate-domain: "plex.direct"' > /etc/unbound/unbound.conf.d/dietpi-plex.conf
fi
if To_Install 93 pihole-FTL # Pi-hole
then
# Download
G_EXEC curl -sSfLo install.sh 'https://raw.githubusercontent.com/pi-hole/pi-hole/master/automated%20install/basic-install.sh'
G_EXEC chmod +x install.sh
# Check free available memory. Increase swap size to prevent gravity running out of memory.
if (( $(free -m | mawk '/^Mem:/{print $7;exit}') < 512 && $(free -m | mawk '/^Swap:/{print $2;exit}') < 512 ))
then
G_DIETPI-NOTIFY 2 'Increasing swap size to 512 MiB for running gravity.sh, please wait...\n'
/boot/dietpi/func/dietpi-set_swapfile 512
fi
# Unbound: Switch port to 5335 if it was installed before, else it got just configured within its install step above
if (( ${aSOFTWARE_INSTALL_STATE[182]} == 2 )) && grep -q '^[[:blank:]]*port:[[:blank:]][[:blank:]]*53$' /etc/unbound/unbound.conf.d/dietpi.conf
then
G_DIETPI-NOTIFY 2 'Configuring Unbound to work for Pi-hole'
grep '^[[:blank:]]*nameserver[[:blank:]]' /etc/resolv.conf | grep -qvE '[[:blank:]]127.0.0.1(:53)?$' || echo 'nameserver 9.9.9.9' >> /etc/resolv.conf # Failsafe
G_CONFIG_INJECT 'port:[[:blank:]]' ' port: 5335' /etc/unbound/unbound.conf.d/dietpi.conf
G_CONFIG_INJECT 'interface:[[:blank:]]' ' interface: 127.0.0.1' /etc/unbound/unbound.conf.d/dietpi.conf
G_EXEC systemctl restart unbound
fi
# Install
# - Workaround "dialog" issues with "xterm" by faking "linux" terminal
local retry TERM_old=
[[ $TERM == 'xterm' ]] && { TERM_old=$TERM; export TERM=linux; }
# - Skip query logging dialogue and set to "false", which affects file logging only, not database>web UI.
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*setLogging$/d' install.sh
G_EXEC sed --follow-symlinks -i '/^QUERY_LOGGING=$/c\QUERY_LOGGING=false' install.sh
# - Skip final completion dialogue since we show our own
# shellcheck disable=SC2016
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*pihole setpassword "${pw}"/,/^[[:blank:]]*dialog --no-shadow --keep-tite/s/dialog --no-shadow --keep-tite/true/' install.sh
until ./install.sh
do
(( $G_INTERACTIVE )) && read -rp 'The Pi-hole installer failed. Do you want to retry? [yN]: ' retry
[[ $retry =~ ^[yY]([eE][sS])?$ ]] || { aSOFTWARE_INSTALL_STATE[$software_id]=0; break; }
done
G_EXEC rm install.sh
[[ $TERM_old ]] && TERM=$TERM_old
unset -v retry TERM_old
# Skip further setup if installer failed
if (( ${aSOFTWARE_INSTALL_STATE[$software_id]} == 1 ))
then
# Set web UI password: https://github.com/MichaIng/DietPi/issues/662
G_EXEC_PRE_FUNC(){ acommand[2]=$GLOBAL_PW; }
G_EXEC pihole setpassword "${GLOBAL_PW//?/X}"
# Disable pihole.log query logging: https://github.com/pi-hole/FTL/issues/614#issuecomment-510564476
G_EXEC pihole-FTL --config dns.queryLogging 'false'
# Unbound: Configure Pi-hole to use it
(( ${aSOFTWARE_INSTALL_STATE[182]} > 0 )) && G_EXEC pihole-FTL --config dns.upstreams '[ "127.0.0.1#5335" ]'
# Apply most resource friendly and officially recommended NULL blocking: https://docs.pi-hole.net/ftldns/blockingmode/
G_EXEC pihole-FTL --config dns.blocking.mode 'NULL'
# Reduce long-term database TTL: https://github.com/MichaIng/DietPi-Docs/issues/476
G_EXEC pihole-FTL --config database.maxDBdays 2
# Change web UI port to 8089 and 8489 (HTTPS) to avoid conflict with webservers and other web applications
G_EXEC pihole-FTL --config webserver.port 8089,8489s
# Inform user about differences to stock Pi-hole installation
G_WHIP_MSG "[ INFO ] Differences to stock Pi-hole installation
\nCompared to installing Pi-hole via official one-line installer, DietPi applied the following changes:
- The Pi-hole web UI can be reached on port 8089, i.e. via: http://$(G_GET_NET ip):8089/admin/
- Additionally it can be accessed via encrypted HTTPS at port 8489, i.e. via: https://$(G_GET_NET ip):8489/admin/. Note that browsers will show a warning until a TLS certificate from by a trusted public certificate authority is used.
- The Pi-hole web UI is secured with the global software password you chose during first run setup, default: \"dietpi\"
- DNS query logging to /var/log/pihole/pihole.log has been disabled. This does not affect the query logs in the web UI and database, but the \"pihole -t\"/\"pihole tail\" command does not show DNS queries anymore. If you want to use this command or need query logs in /var/log/pihole/pihole.log for other reasons, it can be re-enabled via web UI privacy settings or \"sudo pihole-FTL --config dns.queryLogging true\".
- DNS query logging to database (as shown in web UI) has been reduced to 2 days. This can be changed via web UI privacy settings or e.g. \"sudo pihole-FTL --config database.maxDBdays 7\" to raise it to 7 days.
\nFor further details, read our online docs: https://dietpi.com/docs/software/dns_servers/#pi-hole"
fi
fi
if To_Install 126 # AdGuard Home
then
# Deps: apache2-utils is required for htpasswd command to create bcrypt password hashes
aDEPS=('apache2-utils')
if [[ -f '/mnt/dietpi_userdata/adguardhome/AdGuardHome' ]]
then
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} executable \"/mnt/dietpi_userdata/adguardhome/AdGuardHome\" already exists. Download and install steps will be skipped.
- If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
- If you need to reinstall (e.g. broken instance), please manually remove the executable \"/mnt/dietpi_userdata/adguardhome/AdGuardHome\" and rerun \"dietpi-software (re)install $software_id\"."
G_AGI "${aDEPS[@]}"
aDEPS=()
else
case $G_HW_ARCH in
1) local arch='armv6';;
2) local arch='armv7';;
3) local arch='arm64';;
10) local arch='amd64';;
*) local arch='riscv64';;
esac
Download_Install "https://static.adguard.com/adguardhome/release/AdGuardHome_linux_$arch.tar.gz"
# Install to userdata until it is possible to split binary and data/config without breaking the updater: https://github.com/AdguardTeam/AdGuardHome/issues/3286
G_EXEC mkdir -p /mnt/dietpi_userdata/adguardhome
G_EXEC cp -a AdGuardHome/. /mnt/dietpi_userdata/adguardhome/
G_EXEC rm -R AdGuardHome
fi
# Unbound: Switch port to 5335 if it was installed before, else it got just configured within its install step above
if (( ${aSOFTWARE_INSTALL_STATE[182]} == 2 )) && grep -q '^[[:blank:]]*port:[[:blank:]][[:blank:]]*53$' /etc/unbound/unbound.conf.d/dietpi.conf
then
G_DIETPI-NOTIFY 2 'Configuring Unbound to work for AdGuard Home'
grep '^[[:blank:]]*nameserver[[:blank:]]' /etc/resolv.conf | grep -qvE '[[:blank:]]127.0.0.1(:53)?$' || echo 'nameserver 9.9.9.9' >> /etc/resolv.conf # Failsafe
G_CONFIG_INJECT 'port:[[:blank:]]' ' port: 5335' /etc/unbound/unbound.conf.d/dietpi.conf
G_CONFIG_INJECT 'interface:[[:blank:]]' ' interface: 127.0.0.1' /etc/unbound/unbound.conf.d/dietpi.conf
G_EXEC systemctl restart unbound
fi
# User
Create_User -d /mnt/dietpi_userdata/adguardhome adguardhome
# Config
if [[ ! -f '/mnt/dietpi_userdata/adguardhome/AdGuardHome.yaml' ]]
then
dps_index=$software_id Download_Install 'AdGuardHome.yaml' /mnt/dietpi_userdata/adguardhome/AdGuardHome.yaml
# shellcheck disable=SC2016
G_CONFIG_INJECT 'password:[[:blank:]]' " password: $(htpasswd -bnBC 10 '' "$GLOBAL_PW" | tr -d ':\n' | sed 's/\$2y/\$2a/')" /mnt/dietpi_userdata/adguardhome/AdGuardHome.yaml
fi
# Unbound: Configure AdGuard Home to use it
if [[ ${aSOFTWARE_INSTALL_STATE[182]} -gt 0 && ! -f '/mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf' ]]
then
G_DIETPI-NOTIFY 2 'Configuring AdGuard Home to use Unbound'
echo '127.0.0.1:5335' > /mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf
G_CONFIG_INJECT 'upstream_dns_file:[[:blank:]]' ' upstream_dns_file: /mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf' /mnt/dietpi_userdata/adguardhome/AdGuardHome.yaml
systemctl -q is-active adguardhome && G_EXEC systemctl restart adguardhome
fi
# Permissions
G_EXEC chown -R adguardhome:adguardhome /mnt/dietpi_userdata/adguardhome
G_EXEC chmod 0755 /mnt/dietpi_userdata/adguardhome/AdGuardHome
G_EXEC chmod 0600 /mnt/dietpi_userdata/adguardhome/AdGuardHome.yaml
# Service
cat << '_EOF_' > /etc/systemd/system/adguardhome.service
[Unit]
Description=AdGuard Home (DietPi)
Wants=network-online.target nss-lookup.target
After=network-online.target
Before=nss-lookup.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
User=adguardhome
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW
ExecStart=/mnt/dietpi_userdata/adguardhome/AdGuardHome
ExecReload=/mnt/dietpi_userdata/adguardhome/AdGuardHome -s reload
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
_EOF_
# AdGuard Home service needs to be started manually as dietpi-services must not control it to prevent it from killing the systems own DNS server.
aSTART_SERVICES+=('adguardhome')
fi
if To_Install 33 airsonic # Airsonic-Advanced
then
# Since v11 is not yet released, pull latest pre-release
local fallback_url='https://github.com/airsonic-advanced/airsonic-advanced/releases/download/11.0.0-SNAPSHOT.20240424015024/airsonic.war'
Download_Install "$(curl -sSfL 'https://api.github.com/repos/airsonic-advanced/airsonic-advanced/releases' | mawk -F\" '/^ *"browser_download_url": ".*\/airsonic\.war"$/{print $4}' | head -1)" /mnt/dietpi_userdata/airsonic/airsonic.war
# User
Create_User -g dietpi -G audio -d /mnt/dietpi_userdata/airsonic airsonic
# Service
cat << _EOF_ > /etc/systemd/system/airsonic.service
[Unit]
Description=Airsonic-Advanced (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
SyslogIdentifier=Airsonic
User=airsonic
WorkingDirectory=/mnt/dietpi_userdata/airsonic
ExecStart=$JAVA_PATH -mx${JAVA_MAX_HEAP_SIZE}m -Dairsonic.home=/mnt/dietpi_userdata/airsonic -Dserver.servlet.context-path=/airsonic -Dserver.port=8080 -jar /mnt/dietpi_userdata/airsonic/airsonic.war
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
_EOF_
# Enable FFmpeg transcode
G_EXEC mkdir -p /mnt/dietpi_userdata/airsonic/transcode
[[ -f '/mnt/dietpi_userdata/airsonic/transcode/ffmpeg' ]] || G_EXEC ln -sf "$(command -v ffmpeg)" /mnt/dietpi_userdata/airsonic/transcode
# Permissions
G_EXEC chmod +x /mnt/dietpi_userdata/airsonic/airsonic.war
G_EXEC chown airsonic:root /mnt/dietpi_userdata/airsonic
Download_Test_Media
fi
if To_Install 204 navidrome # Navidrome
then
case $G_HW_ARCH in
1) local arch='armv6';;
2) local arch='armv7';;
3) local arch='arm64';;
*) local arch='amd64';;
esac
local fallback_url="https://github.com/navidrome/navidrome/releases/download/v0.59.0/navidrome_0.59.0_linux_$arch.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/navidrome/navidrome/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/navidrome_[0-9.]*_linux_$arch\.tar\.gz\"$/{print \$4}")" /opt/navidrome
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/navidrome
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/navidrome navidrome
# Config: https://www.navidrome.org/docs/usage/configuration-options/#available-options
[[ -f '/mnt/dietpi_userdata/navidrome/navidrome.toml' ]] || cat << '_EOF_' > /mnt/dietpi_userdata/navidrome/navidrome.toml
DataFolder = '/mnt/dietpi_userdata/navidrome/data'
MusicFolder = '/mnt/dietpi_userdata/Music'
ImageCacheSize = '100MiB'
LogLevel = 'warn'
SessionTimeout = '24h'
AuthRequestLimit = '5'
AuthWindowLength = '20s'
ScanSchedule = '@every 1h'
EnableLogRedacting = 'true'
Scanner.Extractor = 'taglib'
CoverJpegQuality = '75'
_EOF_
# Service: https://www.navidrome.org/docs/installation/linux/#create-a-systemd-unit
cat << '_EOF_' > /etc/systemd/system/navidrome.service
[Unit]
Description=Navidrome (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
AssertPathExists=/mnt/dietpi_userdata/navidrome
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
User=navidrome
ExecStart=/opt/navidrome/navidrome --configfile "/mnt/dietpi_userdata/navidrome/navidrome.toml"
WorkingDirectory=/mnt/dietpi_userdata/navidrome
TimeoutStopSec=20
KillMode=process
Restart=on-failure
# See https://www.freedesktop.org/software/systemd/man/systemd.exec.html
DevicePolicy=closed
NoNewPrivileges=yes
PrivateTmp=yes
PrivateUsers=yes
ProtectControlGroups=yes
ProtectKernelModules=yes
ProtectKernelTunables=yes
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
RestrictNamespaces=yes
RestrictRealtime=yes
SystemCallFilter=~@clock @debug @module @mount @obsolete @reboot @setuid @swap
ReadWritePaths=/mnt/dietpi_userdata/navidrome
# You can uncomment the following line if you're not using the jukebox This
# will prevent navidrome from accessing any real (physical) devices
#PrivateDevices=yes
# You can change the following line to `strict` instead of `full` if you don't
# want navidrome to be able to write anything on your filesystem outside of
# /mnt/dietpi_userdata/navidrome.
ProtectSystem=full
# You can uncomment the following line if you don't have any media in /home/*.
# This will prevent navidrome from ever reading/writing anything there.
#ProtectHome=true
# You can customize some Navidrome config options by setting environment variables here. Ex:
#Environment=ND_BASEURL="/navidrome"
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chown -R root:root /opt/navidrome
G_EXEC chmod +x /opt/navidrome/navidrome
G_EXEC chown -R navidrome:root /mnt/dietpi_userdata/navidrome
Download_Test_Media
fi
if To_Install 212 kavita # Kavita
then
# .NET dependency: https://github.com/dotnet/docs/blob/main/docs/core/install/linux-debian.md#dependencies
case $G_DISTRO in
6) aDEPS=('libicu67');;
7) aDEPS=('libicu72');;
*) aDEPS=('libicu76');;
esac
case $G_HW_ARCH in
2) local arch='arm';;
3) local arch='arm64';;
*) local arch='x64';;
esac
local fallback_url="https://github.com/Kareadita/Kavita/releases/download/v0.8.8.3/kavita-linux-$arch.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/Kareadita/Kavita/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/kavita-linux-$arch\.tar\.gz\"$/{print \$4}")"
G_EXEC chmod +x Kavita/Kavita
# Change default port to 2036 to avoid conflict with Shairport Sync
G_CONFIG_INJECT '"Port":' ' "Port": 2036,' Kavita/config/appsettings-init.json
# Reinstall: Preserve config dir
if [[ -d '/opt/kavita' ]]
then
if [[ -d '/opt/kavita/config' ]]
then
G_EXEC rm -R Kavita/config
G_EXEC mv /opt/kavita/config Kavita/
fi
G_EXEC rm -R /opt/kavita
fi
G_EXEC mv Kavita /opt/kavita
# User
Create_User -g dietpi -d /opt/kavita kavita
G_EXEC chown -R kavita:root /opt/kavita
# Data
G_EXEC mkdir -p /mnt/dietpi_userdata/{ebooks,comics}
G_EXEC chown kavita:dietpi /mnt/dietpi_userdata/{ebooks,comics}
G_EXEC chmod 0775 /mnt/dietpi_userdata/{ebooks,comics}
# Service: https://wiki.kavitareader.com/en/install/linux-install
cat << '_EOF_' > /etc/systemd/system/kavita.service
[Unit]
Description=Kavita Server (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=Kavita
User=kavita
WorkingDirectory=/opt/kavita
ExecStart=/opt/kavita/Kavita
TimeoutStopSec=20
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
_EOF_
G_DIETPI-NOTIFY 2 'Please wait for a few minutes to let the application starts. By default it runs on port 2036.'
fi
if To_Install 68 # Remote.It
then
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='amd64';;
*) local arch='armhf'
esac
export R3_PLATFORM_CODE=1072
Download_Install "https://downloads.remote.it/remoteit/latest/remoteit.$arch.deb"
G_WHIP_MSG "[ INFO ] The installation of Remote.It is complete.
\nThe final step is to register your claim code with your desktop application.
See the manual below for instructions on how to do this.
\nhttps://docs.remote.it/software/device-package/installation#id-3.-claim-and-register-the-device
\nYour claim code: $(mawk -F\" '/claim/{print $4}' /etc/remoteit/config.json)
\nThen this DietPi device will be online as your Remote.It device and you can start using it."
if dpkg-query -s 'connectd' &> /dev/null || dpkg-query -s 'weavedconnectd' &> /dev/null
then
G_WHIP_BUTTON_CANCEL_TEXT='Skip' G_WHIP_YESNO '[WARNING] Legacy Remote.It package detected
\nOne of the legacy Remote.It packages "connectd" or "weavedconnectd" is installed on your system. These have been superseded by the new "remoteit" package.
\nDo you want the legacy packages to be uninstalled now?
\nNB: You can do this any time later, e.g. after connections with the new package have been set up:
G_AGP connectd weavedconnectd' && G_AGP connectd weavedconnectd
fi
fi
if To_Install 69 # Python 3 RPi.GPIO
then
if (( $G_DISTRO > 6 ))
then
G_AGI python3-rpi-lgpio
else
G_AGI python3-rpi.gpio
fi
fi
if To_Install 70 # WiringPi
then
aDEPS=('make' 'gcc' 'libc6-dev')
# RPi
if (( $G_HW_MODEL < 10 ))
then
Download_Install 'https://github.com/WiringPi/WiringPi/archive/master.tar.gz'
G_EXEC cd WiringPi-master
G_EXEC mkdir -p /usr/local/include
G_EXEC_OUTPUT=1 G_EXEC ./build
# Odroids
elif (( $G_HW_MODEL < 20 ))
then
aDEPS+=('automake' 'libtool' 'pkg-config' 'libgpiod-dev' 'libgpiod2') # Add libgpiod2 explicitly so purging *-dev packages, e.g. via dietpi-cleaner, does not break "gpio"
Download_Install 'https://github.com/hardkernel/wiringPi/archive/master.tar.gz'
G_EXEC mv {w,W}iringPi-master
G_EXEC cd WiringPi-master
G_EXEC_OUTPUT=1 G_EXEC ./autogen.sh
G_EXEC_OUTPUT=1 G_EXEC ./configure CFLAGS='-g0 -O3'
G_EXEC_OUTPUT=1 G_EXEC make "-j$(nproc)"
G_EXEC_OUTPUT=1 G_EXEC make install
# Orange Pi
else
# Requires /etc/orangepi-release or /etc/armbian-release with BOARD name: https://github.com/orangepi-xunlong/wiringOP/blob/next/wiringPi/wiringPi.c
local board=
case $G_HW_MODEL in
80) board='orangepi5';;
82) board='orangepi5plus';;
83) board='orangepizero3';;
87) board='orangepi3b';;
88) board='orangepizero2w';;
89) board='orangepi3-lts';;
91) board='orangepi5max';;
93) board='orangepi5pro';;
94) board='orangepi5ultra';;
95) board='orangepicm5';;
96) board='orangepi4a';;
97) board='orangepirv';;
98) board='orangepirv2';;
*) G_DIETPI-NOTIFY 1 "Invalid hardware model \"$G_HW_MODEL_NAME\" (ID \"$G_HW_MODEL\"). Aborting ..."; exit 1;;
esac
>> /etc/orangepi-release
GCI_PRESERVE=1 G_CONFIG_INJECT 'BOARD=' "BOARD=$board" /etc/orangepi-release
Download_Install 'https://github.com/orangepi-xunlong/wiringOP/archive/next.tar.gz'
G_EXEC mv wiringOP-next WiringPi-master
G_EXEC cd WiringPi-master
G_EXEC mkdir -p /usr/local/include
G_EXEC_OUTPUT=1 G_EXEC ./build
fi
G_EXEC strip --remove-section=.comment --remove-section=.note /usr/local/*bin/gpio
G_EXEC strip --strip-unneeded --remove-section=.comment --remove-section=.note /usr/local/lib/libwiringPi*.so
G_EXEC ldconfig
G_EXEC cd "$G_WORKING_DIR"
# Move source dir to disk, as it contains additional examples to compile
[[ -d '/root/wiringPi' ]] && G_EXEC rm -R /root/wiringPi # Pre-v7.2
[[ -d '/mnt/dietpi_userdata/WiringPi' ]] && G_EXEC rm -R /mnt/dietpi_userdata/WiringPi
G_EXEC mv WiringPi-master /mnt/dietpi_userdata/WiringPi
fi
if To_Install 72 # I2C
then
/boot/dietpi/func/dietpi-set_hardware i2c enable
fi
if To_Install 100 pijuice # PiJuice
then
# Pre-create config so we can preserve user customisations but override the package default
G_EXEC mkdir -p /var/lib/dietpi/dietpi-software/installed/pijuice /var/lib/pijuice
[[ -f '/var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func1.sh' ]] || G_EXEC eval 'echo -e '\''#!/bin/dash\npoweroff'\'' > /var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func1.sh'
G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func1.sh
[[ -f '/var/lib/pijuice/pijuice_config.JSON' ]] || cat << '_EOF_' > /var/lib/pijuice/pijuice_config.JSON
{
"system_events": {
"low_battery_voltage": {
"function": "SYS_FUNC_HALT",
"enabled": true
},
"low_charge": {
"function": "NO_FUNC",
"enabled": true
},
"button_power_off": {
"function": "USER_FUNC1",
"enabled": true
},
"forced_power_off": {
"function": "USER_FUNC2",
"enabled": false
},
"no_power": {
"function": "SYS_FUNC_HALT_POW_OFF",
"enabled": true
},
"forced_sys_power_off": {
"function": "USER_FUNC3",
"enabled": false
},
"watchdog_reset": {
"function": "USER_EVENT",
"enabled": true
}
},
"user_functions": {
"USER_FUNC1": "/var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func1.sh",
"USER_FUNC2": "/var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func2.sh",
"USER_FUNC3": "/var/lib/dietpi/dietpi-software/installed/pijuice/pijuice_func3.sh",
"USER_FUNC4": "",
"USER_FUNC5": "",
"USER_FUNC6": "",
"USER_FUNC7": "",
"USER_FUNC8": ""
},
"system_task": {
"watchdog": {
"enabled": true,
"period": "60"
},
"min_bat_voltage": {
"threshold": "1",
"enabled": true
},
"min_charge": {
"threshold": "1",
"enabled": true
},
"enabled": true,
"wakeup_on_charge": {
"enabled": true,
"trigger_level": "1"
}
}
}
_EOF_
# Trixie: https://archive.raspberrypi.com/debian/dists/trixie/main/binary-arm64/Packages, https://github.com/PiSupply/PiJuice/tree/master/Software/Install
if apt-cache dumpavail | grep -q '^Package: pijuice-base$'
then
G_AGI pijuice-base
else
Download_Install 'https://archive.raspberrypi.com/debian/pool/main/p/pijuice-base/pijuice-base_1.8_all.deb'
fi
G_EXEC systemctl stop pijuice
fi
if To_Install 171 # frp
then
case $G_HW_ARCH in
1) local arch='arm';;
2) local arch='arm_hf';;
3) local arch='arm64';;
10) local arch='amd64';;
*) local arch='riscv64';;
esac
# Download
local fallback_url="https://github.com/fatedier/frp/releases/download/v0.65.0/frp_0.65.0_linux_$arch.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/fatedier/frp/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/frp_[0-9.]*_linux_$arch\.tar\.gz\"$/{print \$4}")"
G_EXEC cd frp_*
# Mode choice
G_WHIP_MENU_ARRAY=(
'Server' ': Use this machine as a server, with a public IP'
'Client' ': Use this machine as a client, without a public IP'
'Both' ': Run the reverse proxy only on this machine'
)
G_WHIP_NOCANCEL=1
G_WHIP_DEFAULT_ITEM='Both'
G_WHIP_MENU 'Please choose how you are going to run frp:'
local mode=${G_WHIP_RETURNED_VALUE:-Both}
G_EXEC mkdir -p /etc/frp
Create_User frp -d /etc/frp
local token=
if [[ $mode == 'Server' || $mode == 'Both' ]]
then
G_EXEC mv frps /usr/local/bin/frps
cat << '_EOF_' > /etc/systemd/system/frps.service
[Unit]
Description=frp server (DietPi)
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
User=frp
AmbientCapabilities=CAP_NET_BIND_SERVICE
ExecStart=/usr/local/bin/frps -c /etc/frp/frps.toml
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
_EOF_
# Pre-v9.9: Inform about config file migration
if [[ -f '/etc/frp/frps.ini' ]]
then
G_WHIP_MSG '[WARNING] New toml config file will be generated
\nfrp deprecated the ini format for its config files, hence /etc/frp/frps.toml will be generated and used from now on.
\nAn automated conversion is not possible and hence need to be done manually if you did changes. A backup of the ini config it kept in place:
- /etc/frp/frps.ini.bak
\nA full overview of all config keys can be found here:
- https://github.com/fatedier/frp/blob/dev/conf/frps_full_example.toml'
G_EXEC mv /etc/frp/frps.ini{,.bak}
fi
# Pre-create config file to turn on dashboard
token=$(openssl rand -hex 15)
[[ -f '/etc/frp/frps.toml' ]] || cat << _EOF_ > /etc/frp/frps.toml
bindAddr = "0.0.0.0"
bindPort = 7000
webServer.addr = "0.0.0.0"
webServer.port = 7500
webServer.user = "admin"
webServer.password = "$GLOBAL_PW"
auth.method = "token"
auth.token = "$token"
_EOF_
G_EXEC chmod 0640 /etc/frp/frps.toml
G_EXEC chown root:frp /etc/frp/frps.toml
aENABLE_SERVICES+=('frps')
fi
if [[ $mode == 'Client' || $mode == 'Both' ]]
then
G_EXEC mv frpc /usr/local/bin/frpc
cat << '_EOF_' > /etc/systemd/system/frpc.service
[Unit]
Description=frp client (DietPi)
Wants=network-online.target
After=network-online.target frps.service
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
User=frp
ExecStart=/usr/local/bin/frpc -c /etc/frp/frpc.toml
ExecReload=/usr/local/bin/frpc reload -c /etc/frp/frpc.toml
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
_EOF_
# Pre-v9.9: Inform about config file migration
if [[ -f '/etc/frp/frpc.ini' ]]
then
G_WHIP_MSG '[WARNING] New toml config file will be generated
\nfrp deprecated the ini format for its config files, hence /etc/frp/frpc.toml will be generated and used from now on.
\nAn automated conversion is not possible and hence need to be done manually if you did changes. A backup of the ini config it kept in place:
- /etc/frp/frpc.ini.bak
\nA full overview of all config keys can be found here:
- https://github.com/fatedier/frp/blob/dev/conf/frpc_full_example.toml'
G_EXEC mv /etc/frp/frpc.ini{,.bak}
fi
# Pre-create config file to turn on admin UI
if [[ ! -f '/etc/frp/frpc.toml' ]]
then
local server_addr='127.0.0.1' server_port=7000
if [[ $G_WHIP_RETURNED_VALUE == 'Client' ]]
then
G_WHIP_NOCANCEL=1
G_WHIP_DEFAULT_ITEM="$server_addr:$server_port"
G_WHIP_INPUTBOX_REGEX='^[0-9.:]+$'
G_WHIP_INPUTBOX_REGEX_TEXT='be a valid IP address, optionally with appended network port number, like "192.168.1.100:7000"'
G_WHIP_INPUTBOX 'Please enter the IP address of your frp server, optionally including port (default 7000):'
[[ $G_WHIP_RETURNED_VALUE ]] && server_addr=${G_WHIP_RETURNED_VALUE%:*}
[[ $G_WHIP_RETURNED_VALUE =~ : ]] && server_port=${G_WHIP_RETURNED_VALUE##*:}
G_WHIP_NOCANCEL=1
G_WHIP_INPUTBOX_REGEX='*'
G_WHIP_INPUTBOX 'Please enter the authentication token of your frp server:'
token=$G_WHIP_RETURNED_VALUE
fi
cat << _EOF_ > /etc/frp/frpc.toml
serverAddr = "$server_addr"
serverPort = $server_port
webServer.addr = "0.0.0.0"
webServer.port = 7400
webServer.user = "admin"
webServer.password = "$GLOBAL_PW"
auth.method = "token"
auth.token = "$token"
_EOF_
fi
G_EXEC chmod 0660 /etc/frp/frpc.toml
G_EXEC chown root:frp /etc/frp/frpc.toml
aENABLE_SERVICES+=('frpc')
fi
# Cleanup
G_EXEC cd "$G_WORKING_DIR"
G_EXEC rm -R frp_*
fi
if To_Install 122 node-red # Node-RED
then
# APT deps
G_AGI python3
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/node-red
# User
Create_User -G dialout,gpio,i2c,spi -d /mnt/dietpi_userdata/node-red nodered
# - Allow sudo calls
G_EXEC eval 'echo '\''nodered ALL=NOPASSWD: ALL'\'' > /etc/sudoers.d/nodered'
# Permissions
G_EXEC chown -R nodered:nodered /mnt/dietpi_userdata/node-red
# Install as local instance for "nodered" user
G_EXEC cd /mnt/dietpi_userdata/node-red
# - Disable cache
local cache=$(runuser -u nodered -- mktemp -d)
# - Reinstall: Remove all locally installed modules, to work around update issues, but preserve installed plugins from package.json: https://github.com/MichaIng/DietPi/issues/7128
[[ -f 'package.json' ]] && G_EXEC rm -Rf node_modules .npm/_cacache
# - Install v3 on ARMv6, since the @node-rs/bcrypt dependency does not support it: https://github.com/MichaIng/DietPi/issues/7252
local version='latest'
(( $G_HW_ARCH == 1 )) && version=3
G_EXEC_OUTPUT=1 G_EXEC runuser -u nodered -- npm i --cache "$cache" --no-audit "node-red@$version"
G_EXEC rm -R "$cache"
G_EXEC cd "$G_WORKING_DIR"
# Service
cat << '_EOF_' > /etc/systemd/system/node-red.service
[Unit]
Description=Node-RED (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
User=nodered
ExecStart=/mnt/dietpi_userdata/node-red/node_modules/.bin/node-red -u /mnt/dietpi_userdata/node-red
[Install]
WantedBy=multi-user.target
_EOF_
# CLI alias
echo 'alias node-red-admin='\''sudo -u nodered /mnt/dietpi_userdata/node-red/node_modules/.bin/node-red-admin'\' > /etc/bashrc.d/dietpi-node-red.sh
fi
if To_Install 123 mosquitto # Mosquitto
then
# Use official APT repository where available: https://repo.mosquitto.org/debian/pool/main/m/mosquitto/
# - Current builds are not ARMv6 compatible: https://github.com/MichaIng/DietPi/issues/5140
if (( $G_DISTRO < 9 )) && (( $G_HW_ARCH == 2 || $G_HW_ARCH == 10 || ( $G_HW_ARCH == 3 && $G_DISTRO > 6 ) ))
then
# APT key
local url='https://repo.mosquitto.org/debian/mosquitto-repo.gpg'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-mosquitto.gpg --yes"
# APT list
G_EXEC eval "echo 'deb https://repo.mosquitto.org/debian $G_DISTRO_NAME main' > /etc/apt/sources.list.d/dietpi-mosquitto.list"
G_AGUP
fi
G_AGI mosquitto
G_EXEC systemctl stop mosquitto
Remove_SysV mosquitto
# Password file
if [[ ! -f '/etc/mosquitto/passwd' ]]
then
G_EXEC umask 0037 # relevant for passwd mode until v2.0.18
G_EXEC_PRE_FUNC(){ acommand[5]=$GLOBAL_PW; }
G_EXEC mosquitto_passwd -c -b /etc/mosquitto/passwd mosquitto "${GLOBAL_PW//?/X}"
G_EXEC chown root:mosquitto /etc/mosquitto/passwd
G_EXEC chmod 0640 /etc/mosquitto/passwd # from v2.0.18 on, 0600 is the default mode, hence the "mosquitto" user has no read access
G_EXEC umask 0022
fi
# Config: /etc/mosquitto/conf.d exists, but doubled settings do not override each other and lead to a startup failure instead, which breaks the purpose we want to use it for.
# - Disable PID file, not required for systemd handling
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*pid_file[[:blank:]]/d' /etc/mosquitto/mosquitto.conf
[[ -d '/run/mosquitto' ]] && G_EXEC rm -R /run/mosquitto
# - Log to default STDERR > systemd-journald
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*log_dest[[:blank:]]/d' /etc/mosquitto/mosquitto.conf
[[ -d '/var/log/mosquitto' ]] && G_EXEC rm -R /var/log/mosquitto
# - Add default password file for authenticated requests: https://github.com/MichaIng/DietPi/issues/4133
GCI_PRESERVE=1 G_CONFIG_INJECT 'password_file[[:blank:]]' 'password_file /etc/mosquitto/passwd' /etc/mosquitto/mosquitto.conf
# - Add default listener at port 1883 not bind to loopback IP: https://github.com/MichaIng/DietPi/issues/4133
GCI_PRESERVE=1 G_CONFIG_INJECT 'listener[[:blank:]]' 'listener 1883' /etc/mosquitto/mosquitto.conf
# Add/override default systemd unit: https://github.com/eclipse/mosquitto/tree/master/service/systemd
cat << '_EOF_' > /etc/systemd/system/mosquitto.service
[Unit]
Description=Mosquitto MQTT Broker (DietPi)
Documentation=man:mosquitto.conf(5) man:mosquitto(8)
Wants=network-online.target
After=network-online.target
[Service]
Type=notify
NotifyAccess=main
User=mosquitto
ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 131 blynkserver # Blynk Server
then
# RPi: Install build deps for the "onoff" Node module
(( $G_HW_MODEL > 9 )) || aDEPS=('python3' 'make' 'g++')
Download_Install 'https://github.com/Peterkn2001/blynk-server/releases/download/v0.41.17/server-0.41.17.jar' /mnt/dietpi_userdata/blynk/blynkserver.jar
# RPi: Install "onoff" for GPIO access
(( $G_HW_MODEL > 9 )) || G_EXEC_OUTPUT=1 G_EXEC npm i -g --no-audit onoff@latest
# Install Blynk JS library
G_EXEC_OUTPUT=1 G_EXEC npm i -g --no-audit blynk-library@latest
# Preserve existing config
if [[ ! -f '/mnt/dietpi_userdata/blynk/server.properties' ]]
then
G_EXEC curl -sSfL 'https://raw.githubusercontent.com/Peterkn2001/blynk-server/master/server/core/src/main/resources/server.properties' -o /mnt/dietpi_userdata/blynk/server.properties
G_EXEC chmod 0600 /mnt/dietpi_userdata/blynk/server.properties
G_CONFIG_INJECT 'logs.folder=' 'logs.folder=/var/log/blynk' /mnt/dietpi_userdata/blynk/server.properties
G_CONFIG_INJECT 'data.folder=' 'data.folder=/mnt/dietpi_userdata/blynk/data' /mnt/dietpi_userdata/blynk/server.properties
G_CONFIG_INJECT 'http.port=' 'http.port=8442' /mnt/dietpi_userdata/blynk/server.properties
GCI_PASSWORD=1 G_CONFIG_INJECT 'admin.pass=' "admin.pass=$GLOBAL_PW" /mnt/dietpi_userdata/blynk/server.properties
fi
# Example local TCP connection script
if [[ ! -f '/mnt/dietpi_userdata/blynk/client-tcp-local.js' ]]
then
G_EXEC curl -sSfL 'https://raw.githubusercontent.com/vshymanskyy/blynk-library-js/master/examples/nodejs/client-tcp-local.js' -o /mnt/dietpi_userdata/blynk/client-tcp-local.js
G_EXEC sed --follow-symlinks -i "s|require('blynk-library')|require('/usr/local/lib/node_modules/blynk-library')|" /mnt/dietpi_userdata/blynk/client-tcp-local.js
G_EXEC chmod +x /mnt/dietpi_userdata/blynk/client-tcp-local.js
fi
# User
Create_User -d /mnt/dietpi_userdata/blynk blynk
# Service
cat << _EOF_ > /etc/systemd/system/blynkserver.service
[Unit]
Description=Blynk Server (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
SyslogIdentifier=Blynk
User=blynk
LogsDirectory=blynk
WorkingDirectory=/mnt/dietpi_userdata/blynk
ExecStart=$JAVA_PATH -mx${JAVA_MAX_HEAP_SIZE}m -jar /mnt/dietpi_userdata/blynk/blynkserver.jar
SuccessExitStatus=143
# Hardening
PrivateTmp=true
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chown -R blynk:blynk /mnt/dietpi_userdata/blynk
fi
if To_Install 124 networkaudiod # NAA Daemon
then
# Base URL
local url="https://signalyst.com/bins/naa/linux/${G_DISTRO_NAME/forky/trixie}/"
# Check download dir
G_CHECK_URL "$url"
# Get latest version
local package=$(curl -sSfL "$url" | grep -Po "(?<=href=\")networkaudiod_[^\"]*_$(dpkg --print-architecture).deb(?=\")" | tail -1)
# Skip license
G_EXEC eval 'debconf-set-selections <<< '\''networkaudiod networkaudiod/license note false'\'
# Install
Download_Install "${package:+$url$package}"
G_EXEC systemctl stop networkaudiod
unset -v url package
fi
if To_Install 71 webiopi # WebIOPi
then
# Check for reinstall
local reinstall=0
[[ -f '/etc/webiopi/config' ]] && reinstall=1
aDEPS=('python3-dev' 'python3-setuptools' 'gcc' 'patch')
Download_Install 'https://github.com/Freenove/WebIOPi/archive/master.tar.gz'
G_EXEC cd WebIOPi-master/WebIOPi-*
# Apply patch
G_EXEC_OUTPUT=1 G_EXEC patch -p1 -i webiopi-pi2bplus.patch
# Install for Python 3 only
G_EXEC sed --follow-symlinks -i '/SEARCH="python python3"/c\SEARCH="python3"' setup.sh
# Skip Weaved install prompt
G_EXEC sed --follow-symlinks -i '/read response/c\response="n"' setup.sh
# Run setup script, skipping APT installs
G_EXEC_OUTPUT=1 G_EXEC ./setup.sh skip-apt
G_EXEC cd "$G_WORKING_DIR"
# Cleanup
G_EXEC rm -R WebIOPi-master
# On fresh installs, change port to 8002 to avoid conflict with Icecast
(( $reinstall )) || G_EXEC sed --follow-symlinks -i 's/^port = 8000$/port = 8002/' /etc/webiopi/config
# Service
Remove_SysV webiopi 1
cat << '_EOF_' > /etc/systemd/system/webiopi.service
[Unit]
Description=WebIOPi (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
SyslogIdentifier=WebIOPi
ExecStart=/usr/bin/python3 -m webiopi -c /etc/webiopi/config
# Hardening
PrivateTmp=true
ProtectHome=true
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 98 haproxy # HAProxy
then
# Dependencies
aDEPS=('make' 'gcc' 'libpcre2-dev' 'libssl-dev' 'zlib1g-dev' 'libsystemd-dev')
# Obtain latest version: Stick with v3. Once v4 is released, we will surely need to adjust below code.
local url=$(curl -sSfL 'https://www.haproxy.org/' | grep -Po '(?<=href=")/download/3\..*/src/haproxy-.*\.tar\.gz(?=")' | head -1)
local fallback_url='https://www.haproxy.org/download/3.3/src/haproxy-3.3.0.tar.gz'
Download_Install "${url:+https://www.haproxy.org$url}"
# Compile
G_EXEC cd haproxy-3.*
G_EXEC_OUTPUT=1 G_EXEC make -j "$G_HW_CPU_CORES" TARGET=linux-glibc USE_PCRE2=1 USE_OPENSSL=1 USE_ZLIB=1 USE_SYSTEMD=1 USE_PROMEX=1 DEBUG_CFLAGS='-g0 -O3' LDFLAGS='-Wl,-z,relro -Wl,-z,now -Wl,--as-needed'
# Strip binary size
G_EXEC strip --remove-section=.comment --remove-section=.note haproxy
# Install
G_EXEC_OUTPUT=1 G_EXEC make install
# - systemd unit
G_EXEC cd admin/systemd
G_EXEC_OUTPUT=1 G_EXEC make
G_EXEC mv {,/etc/systemd/system/}haproxy.service
# - Error pages
G_EXEC cd "$G_WORKING_DIR"
G_EXEC mkdir -p /etc/haproxy/errors
G_EXEC mv haproxy-3.*/examples/errorfiles/*.http /etc/haproxy/errors/
# Cleanup
G_EXEC rm -R haproxy-3.*
# Jail directory
G_EXEC mkdir -p /var/lib/haproxy
# Config
[[ -f '/etc/haproxy/haproxy.cfg' ]] || cat << _EOF_ > /etc/haproxy/haproxy.cfg
global
maxconn 64
# Jail directory
chroot /var/lib/haproxy
stats socket /run/haproxy.sock mode 660 level admin
stats timeout 30s
user root
group root
daemon
# Default SSL material locations
ca-base /etc/ssl/certs
crt-base /etc/ssl/private
# Default ciphers to use on SSL-enabled listening sockets.
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options prefer-client-ciphers no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
errorfile 400 /etc/haproxy/errors/400.http
errorfile 403 /etc/haproxy/errors/403.http
errorfile 408 /etc/haproxy/errors/408.http
errorfile 500 /etc/haproxy/errors/500.http
errorfile 502 /etc/haproxy/errors/502.http
errorfile 503 /etc/haproxy/errors/503.http
errorfile 504 /etc/haproxy/errors/504.http
frontend localnodes
bind *:80
mode http
default_backend nodes
backend nodes
mode http
balance roundrobin
option forwardfor
http-request set-header X-Forwarded-Port %[dst_port]
http-request add-header X-Forwarded-Proto https if { ssl_fc }
option httpchk HEAD / HTTP/1.1
http-check send meth HEAD uri / ver HTTP/1.1 hdr host localhost
server web01 127.0.0.1:9000 check
server web02 127.0.0.1:9001 check
server web03 127.0.0.1:9002 check
# Admin web page
listen stats
bind *:1338
stats enable
stats uri /
http-request use-service prometheus-exporter if { path /metrics }
stats hide-version
stats auth admin:$GLOBAL_PW
_EOF_
G_EXEC chmod 0600 /etc/haproxy/haproxy.cfg
fi
if To_Install 35 lyrionmusicserver # Lyrion Music Server
then
# Grab architecture
local arch='arm'
(( $G_HW_ARCH == 10 )) && arch='amd64'
# Grab latest package URL
local fallback_url="https://downloads.lms-community.org/nightly/lyrionmusicserver_9.0.4~1763927290_$arch.deb"
Download_Install "$(curl -sSfL 'https://raw.githubusercontent.com/LMS-Community/lms-server-repository/master/stable.xml' | grep -om1 "https://[^\"]*_$arch.deb")"
G_EXEC systemctl stop lyrionmusicserver
Remove_SysV lyrionmusicserver
# Grant user write access to DietPi media dirs for creating infobrowser.opml.
G_EXEC usermod -aG dietpi squeezeboxserver
Download_Test_Media
fi
if To_Install 55 # WordPress
then
Download_Install 'https://wordpress.org/latest.tar.gz' /var/www
# Permissions
G_EXEC chown -R www-data:www-data /var/www/wordpress
# Create MariaDB database
/boot/dietpi/func/create_mysql_db wordpress wordpress "$GLOBAL_PW"
fi
if To_Install 38 # FreshRSS
then
# Install required PHP modules: https://github.com/FreshRSS/FreshRSS#requirements
aDEPS=("php$PHP_VERSION-curl" "php$PHP_VERSION-gmp" "php$PHP_VERSION-intl" "php$PHP_VERSION-mbstring" "php$PHP_VERSION-xml" "php$PHP_VERSION-zip")
# - Add JSON module for PHP7, as it does not exist (embedded in core package) on PHP8
local json=()
[[ $PHP_VERSION == 8* ]] || aDEPS+=("php$PHP_VERSION-json") json=('json')
# Reinstall: Skip download and install, advice to use internal updater from web UI
if [[ -d '/opt/FreshRSS' ]]
then
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/opt/FreshRSS\" already exists. Download and install steps will be skipped.
- If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
- If you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\"."
G_AGI "${aDEPS[@]}"
aDEPS=()
else
# Bullseye: v1.25.0 requires PHP 8.1+: https://github.com/FreshRSS/FreshRSS/releases/tag/1.25.0
local version='1.24.3'
if (( $G_DISTRO > 6 ))
then
version=$(curl -sSfL 'https://api.github.com/repos/FreshRSS/FreshRSS/releases/latest' | mawk -F\" '/^ *"tag_name": "[^"]*",$/{print $4}')
[[ $version ]] || { version='1.27.1'; G_DIETPI-NOTIFY 1 "Automatic latest FreshRSS version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
fi
Download_Install "https://github.com/FreshRSS/FreshRSS/archive/$version.tar.gz"
G_EXEC mv "FreshRSS-$version" /opt/FreshRSS
fi
# Enable required PHP modules: https://github.com/FreshRSS/FreshRSS#requirements
G_EXEC phpenmod curl gmp iconv intl pdo_mysql ctype dom mbstring xml zip "${json[@]}"
# Apache config
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
# Required modules: https://github.com/FreshRSS/FreshRSS/blob/edge/p/.htaccess, https://github.com/FreshRSS/FreshRSS/blob/edge/p/api/.htaccess, https://github.com/FreshRSS/FreshRSS/blob/edge/p/themes/.htaccess
# - Do not enable mod_deflate since it may cause issues with concurrent ownCloud and Nextcloud installs: https://docs.nextcloud.com/server/latest/admin_manual/issues/general_troubleshooting.html#web-server-and-php-modules
G_EXEC a2enmod dir mime expires headers setenvif
# Better compatibility with mobile clients
cat << '_EOF_' > /etc/apache2/conf-available/dietpi-freshrss.conf
# Based on: https://freshrss.github.io/FreshRSS/en/admins/10_ServerConfig.html
<Directory /var/www/freshrss>
AllowOverride AuthConfig FileInfo Indexes Limit
Require all granted
Options FollowSymLinks
</Directory>
AllowEncodedSlashes On
_EOF_
G_EXEC a2enconf dietpi-freshrss
fi
# Create MariaDB database and user
# - Create random alphanumeric 30 characters password
local password=$(tr -dc '[:alnum:]' < /dev/random | head -c30)
if [[ -d '/mnt/dietpi_userdata/mysql/freshrss' ]]
then
G_DIETPI-NOTIFY 2 'FreshRSS MariaDB database found, will NOT overwrite.'
G_EXEC systemctl restart mariadb
# Update database password if do-install.php applies it to the FreshRSS config
[[ -f '/opt/FreshRSS/data/applied_migrations.txt' ]] || mysql -e "grant all privileges on \`freshrss\`.* to 'freshrss'@localhost identified by '$password';flush privileges"
else
/boot/dietpi/func/create_mysql_db freshrss freshrss "$password"
fi
# Set permissions for webserver access
G_EXEC chgrp -R www-data /opt/FreshRSS
G_EXEC chmod -R g+r+w /opt/FreshRSS
# CLI install: https://github.com/FreshRSS/FreshRSS/blob/master/cli/README.md#commands
G_EXEC cd /opt/FreshRSS
runuser -u www-data -- php ./cli/prepare.php
runuser -u www-data -- php ./cli/do-install.php --default_user dietpi --auth_type form --environment production --title FreshRSS --db-type mysql --db-host localhost --db-user freshrss --db-password "$password" --db-base freshrss --db-prefix freshrss
unset -v password
runuser -u www-data -- php ./cli/create-user.php --user dietpi --password "$GLOBAL_PW" --api_password "$GLOBAL_PW"
runuser -u www-data -- php ./cli/actualize-user.php --user dietpi
runuser -u www-data -- php ./cli/db-optimize.php --user dietpi
G_EXEC cd "$G_WORKING_DIR"
# Link web interface to webroot
G_EXEC rm -Rf /var/www/freshrss
G_EXEC ln -s /opt/FreshRSS/p /var/www/freshrss
# Create cron job for feed update every 30 minutes if it does not yet exist
crontab -u www-data -l 2>/dev/null | grep -q '/opt/FreshRSS/app/actualize_script.php' || { crontab -u www-data -l 2>/dev/null ; echo '*/30 * * * * php /opt/FreshRSS/app/actualize_script.php'; } | crontab -u www-data -
fi
if To_Install 28 # TigerVNC Server
then
# tigervnc-tools ships tigervncpasswd as dedicated package since Bookworm.
local apackages=()
(( $G_DISTRO > 6 )) && apackages=('tigervnc-tools')
G_AGI tigervnc-standalone-server tigervnc-scraping-server "${apackages[@]}"
fi
if To_Install 120 # RealVNC Server
then
local deps=()
# Bullseye: Depends on libbcm_host.so but does not pull libraspberrypi0 as dependency
(( $G_DISTRO < 7 )) && deps=('libraspberrypi0')
G_AGI realvnc-vnc-server "${deps[@]}"
fi
# TigerVNC/RealVNC Server: Shared setup
if (( ${aSOFTWARE_INSTALL_STATE[28]} == 1 || ${aSOFTWARE_INSTALL_STATE[120]} == 1 ))
then
# Service
cat << '_EOF_' > /etc/systemd/system/vncserver.service
[Unit]
Description=VNC Server (DietPi)
Before=xrdp.service xrdp-sesman.service
Wants=network-online.target
After=network-online.target
[Service]
RemainAfterExit=yes
PAMName=login
User=root
Environment=HOME=/root
ExecStart=/usr/local/bin/vncserver start
ExecStop=/usr/local/bin/vncserver stop
[Install]
WantedBy=multi-user.target
_EOF_
aSTART_SERVICES+=('vncserver')
cat << '_EOF_' > /usr/local/bin/vncserver
#!/bin/dash
if [ -f '/usr/bin/vncserver-virtual' ]
then
echo '[ OK ] RealVNC detected'
FP_BINARY='/usr/bin/vncserver-virtual -Authentication VncAuth'
FP_SHARED='exec /usr/bin/vncserver-x11 -service -Authentication VncAuth'
elif [ -f '/usr/bin/tigervncserver' ]
then
echo '[ OK ] TigerVNC detected'
FP_BINARY='/usr/bin/tigervncserver'
FP_SHARED="/usr/bin/X0tigervnc -display :0 -rfbauth $HOME/.vnc/passwd"
else
echo '[FAILED] No supported VNC server installed'
exit 1
fi
case "$1" in
start)
# Shared desktop mode
if grep -q '^[[:blank:]]*SOFTWARE_VNCSERVER_SHARE_DESKTOP=1' /boot/dietpi.txt
then
echo '[ INFO ] Waiting for X server...'
while :
do
until pgrep '^X' > /dev/null 2>&1; do sleep 1; done
echo '[ INFO ] Connecting to shared desktop'
$FP_SHARED
sleep 2
pgrep '^X' > /dev/null 2>&1 && exit 1
echo '[ INFO ] X server stopped, waiting for next session...'
done
# Virtual desktop mode
else
DISPLAY=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_DISPLAY_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
WIDTH=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_WIDTH=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
HEIGHT=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_HEIGHT=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
DEPTH=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_DEPTH=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
echo "[ INFO ] Starting virtual desktop at display :${DISPLAY:=1} in ${WIDTH:=1280}x${HEIGHT:=720}x${DEPTH:=16}"
export SHELL='/bin/bash'
exec $FP_BINARY ":$DISPLAY" -geometry "${WIDTH}x$HEIGHT" -depth "$DEPTH"
fi
;;
stop)
# Shared desktop mode
if grep -q '^[[:blank:]]*SOFTWARE_VNCSERVER_SHARE_DESKTOP=1' /boot/dietpi.txt
then
echo '[ INFO ] Disconnecting from shared desktop'
killall -qw vncserver-x11-core X0tigervnc
# Virtual desktop mode
else
DISPLAY=$(sed -n '/^[[:blank:]]*SOFTWARE_VNCSERVER_DISPLAY_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
echo "[ INFO ] Stopping virtual desktop at display :${DISPLAY:=1}"
$FP_BINARY -kill ":$DISPLAY"
fi
;;
*)
echo "[FAILED] Invalid command ($1), please use \"start\" or \"stop\""
exit 1
;;
esac
exit 0
_EOF_
G_EXEC chmod +x /usr/local/bin/vncserver
# TigerVNC
if (( ${aSOFTWARE_INSTALL_STATE[28]} == 1 ))
then
# TigerVNC: Permit remote connections which implies TLSVnc authentications being enabled additionally
# shellcheck disable=SC2016
GCI_PRESERVE=1 G_CONFIG_INJECT '\$localhost[[:blank:]]*=' '$localhost = "no";' /etc/tigervnc/vncserver-config-defaults
# Set control + read-only passwords
local path='/root/.vnc'
(( $G_DISTRO > 7 )) && path='/root/.config/tigervnc'
if [[ ! -f $path/passwd ]]
then
G_EXEC mkdir -p "$path"
tigervncpasswd -f <<< "$GLOBAL_PW
$GLOBAL_PW" > "$path/passwd"
G_EXEC chmod 0600 "$path/passwd"
fi
fi
# RealVNC
if (( ${aSOFTWARE_INSTALL_STATE[120]} == 1 ))
then
# Assure that its services are disabled when ours is enabled
G_EXEC systemctl --no-reload disable vncserver-virtuald vncserver-x11-serviced
# Set (+verify) virtual + shared desktop passwords: https://github.com/MichaIng/DietPi/pull/4679#issuecomment-908196511
if [[ ! -s '/root/.vnc/config.d/Xvnc' ]]
then
vncpasswd -virtual <<< "$GLOBAL_PW
$GLOBAL_PW"
vncpasswd -service <<< "$GLOBAL_PW
$GLOBAL_PW"
fi
[[ -f '/root/.vnc/config.d/.Xvnc-v5-marker' ]] || > /root/.vnc/config.d/.Xvnc-v5-marker
fi
fi
if To_Install 73 fail2ban # Fail2Ban
then
# Create jail.conf (backend = systemd) first, to prevent APT failure due to missing /var/log/auth.log: https://github.com/MichaIng/DietPi/issues/475#issuecomment-310873879
G_EXEC mkdir -p /etc/fail2ban/{fail2ban,filter,jail}.d
[[ -f '/etc/fail2ban/jail.conf' ]] || cat << '_EOF_' > /etc/fail2ban/jail.conf
[DEFAULT]
enabled = true
ignoreip = 127.0.0.1/8
ignorecommand =
backend = systemd
mode = normal
filter = %(__name__)s[mode=%(mode)s]
findtime = 600
maxretry = 3
bantime = 600
banaction = route
action = %(banaction)s[blocktype=blackhole]
[dropbear]
[sshd]
# Mode: normal (default), ddos, extra or aggressive (combines all)
# See "filter.d/sshd.conf" for details.
#mode = normal
_EOF_
# Log to systemd by default
G_EXEC eval 'echo -e '\''[Definition]\nlogtarget = SYSOUT'\'' > /etc/fail2ban/fail2ban.d/97_dietpi.conf'
# Fix Dropbear filter for STDOUT logging since Bookworm: https://github.com/fail2ban/fail2ban/pull/3597
[[ -f '/etc/fail2ban/filter.d/dropbear.local' ]] || G_EXEC curl -sSfo /etc/fail2ban/filter.d/dropbear.local 'https://raw.githubusercontent.com/fail2ban/fail2ban/master/config/filter.d/dropbear.conf'
# Trixie: Prevent defaults-debian.conf to override our default banaction with nftables
G_EXEC eval '> /etc/fail2ban/jail.d/defaults-debian.conf'
# Trixie: python3-systemd has become hard package dependency
local deps=()
(( $G_DISTRO > 7 )) || deps=('python3-systemd')
G_AGI "${deps[@]}" fail2ban
Remove_SysV fail2ban 1
G_EXEC rm /etc/fail2ban/jail.d/defaults-debian.conf
# Failsafe
G_EXEC systemctl unmask fail2ban
G_EXEC systemctl start fail2ban
fi
if To_Install 74 influxdb # InfluxDB
then
# APT key
local url='https://repos.influxdata.com/influxdata-archive_compat.key'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-influxdb.gpg --yes"
# APT list
local dist=$G_DISTRO_NAME
(( $G_DISTRO > 7 )) && dist='bookworm'
G_EXEC eval "echo 'deb https://repos.influxdata.com/debian $dist stable' > /etc/apt/sources.list.d/dietpi-influxdb.list"
G_AGUP
# APT package
G_AGI influxdb
G_EXEC systemctl stop influxdb
# Link database to userdata location
if [[ -d '/mnt/dietpi_userdata/influxdb' ]]
then
G_DIETPI-NOTIFY 2 'Existing database/plugin directory /mnt/dietpi_userdata/influxdb found. Will not overwrite...'
elif [[ -d '/var/lib/influxdb' ]]
then
G_EXEC mv /var/lib/influxdb /mnt/dietpi_userdata/
else
G_EXEC mkdir /mnt/dietpi_userdata/influxdb
G_EXEC chown -R influxdb:influxdb /mnt/dietpi_userdata/influxdb
fi
G_EXEC rm -Rf /var/lib/influxdb
G_EXEC ln -s /mnt/dietpi_userdata/influxdb /var/lib/influxdb
fi
if To_Install 77 grafana-server # Grafana: https://grafana.com/docs/grafana/latest/setup-grafana/installation/debian/#install-from-apt-repository
then
# ARMv6: Since Grafana repo is not compatible, install package from our repo, taken from here: https://grafana.com/grafana/download?platform=arm&edition=oss
if (( $G_HW_ARCH == 1 ))
then
G_AGI grafana-rpi
else
# APT key
local url='https://apt.grafana.com/gpg.key'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-grafana.gpg --yes"
# APT list
G_EXEC eval 'echo '\''deb https://apt.grafana.com stable main'\'' > /etc/apt/sources.list.d/grafana.list'
G_AGUP
# APT package
G_AGI grafana
fi
G_EXEC systemctl stop grafana-server
Remove_SysV grafana-server
# Link DB/plugins to userdata location
if [[ -d '/mnt/dietpi_userdata/grafana' ]]
then
G_DIETPI-NOTIFY 2 'Existing database/plugin directory /mnt/dietpi_userdata/grafana found. Will not overwrite...'
elif [[ -d '/var/lib/grafana' ]]
then
G_EXEC mv /var/lib/grafana /mnt/dietpi_userdata/
else
G_EXEC mkdir /mnt/dietpi_userdata/grafana
G_EXEC chown -R grafana:grafana /mnt/dietpi_userdata/grafana
fi
G_EXEC rm -Rf /var/lib/grafana
G_EXEC ln -s /mnt/dietpi_userdata/grafana /var/lib/grafana
# Config: Apply our defaults only if nothing was set before
# - Set password, wrap into tripled double quotes in case of ; or # being contained, according to docs: http://docs.grafana.org/installation/configuration/#password
GCI_PRESERVE=1 GCI_PASSWORD=1 G_CONFIG_INJECT 'admin_password[[:blank:]]*=' "admin_password = \"\"\"$GLOBAL_PW\"\"\"" /etc/grafana/grafana.ini
# - Set port to 3001 (away from default 3000) to avoid conflict with Gogs and Gitea
GCI_PRESERVE=1 G_CONFIG_INJECT 'http_port[[:blank:]]*=' 'http_port = 3001' /etc/grafana/grafana.ini
fi
if To_Install 64 # phpSysInfo
then
# Download
Download_Install 'https://github.com/phpsysinfo/phpsysinfo/archive/stable.tar.gz'
# Clean reinstall, but preserve previous config
if [[ -d '/var/www/phpsysinfo' ]]
then
G_EXEC mv /var/www/phpsysinfo/phpsysinfo.ini phpsysinfo-stable/phpsysinfo.ini
G_EXEC rm -R /var/www/phpsysinfo
fi
G_EXEC mv phpsysinfo-stable /var/www/phpsysinfo
[[ -f '/var/www/phpsysinfo/phpsysinfo.ini' ]] || dps_index=$software_id Download_Install 'phpsysinfo.ini' /var/www/phpsysinfo/phpsysinfo.ini
fi
if To_Install 80 ubooquity # Ubooquity
then
G_EXEC curl -sSfL 'https://vaemendis.net/ubooquity/service/download.php' -o Ubooquity.zip
command -v unzip > /dev/null || G_AGI unzip
G_EXEC unzip -o Ubooquity.zip
G_EXEC rm Ubooquity.zip
G_EXEC mkdir -p /mnt/dietpi_userdata/ubooquity
G_EXEC mv {,/mnt/dietpi_userdata/ubooquity/}Ubooquity.jar
# User
Create_User -G dietpi -d /mnt/dietpi_userdata/ubooquity ubooquity
# Data
G_EXEC mkdir -p /mnt/dietpi_userdata/{ebooks,comics}
# Logs
G_EXEC rm -Rf /mnt/dietpi_userdata/ubooquity/logs
G_EXEC ln -s /var/log/ubooquity /mnt/dietpi_userdata/ubooquity/logs
# Service
G_DIETPI-NOTIFY 2 "Generating systemd service to start ${aSOFTWARE_NAME[$software_id]} on boot"
cat << _EOF_ > /etc/systemd/system/ubooquity.service
[Unit]
Description=Ubooquity (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
SyslogIdentifier=Ubooquity
User=ubooquity
LogsDirectory=ubooquity
WorkingDirectory=/mnt/dietpi_userdata/ubooquity
ExecStart=$JAVA_PATH -mx${JAVA_MAX_HEAP_SIZE}m -jar /mnt/dietpi_userdata/ubooquity/Ubooquity.jar --headless --remoteadmin --adminport 2038 --libraryport 2039
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chmod +x /mnt/dietpi_userdata/ubooquity/Ubooquity.jar
G_EXEC chown -R ubooquity /mnt/dietpi_userdata/ubooquity
G_EXEC chown ubooquity:dietpi /mnt/dietpi_userdata/{ebooks,comics}
G_EXEC chmod 0775 /mnt/dietpi_userdata/{ebooks,comics}
fi
if To_Install 179 komga # Komga
then
local fallback_url='https://github.com/gotson/komga/releases/download/1.23.6/komga-1.23.6.jar'
Download_Install "$(curl -sSfL 'https://api.github.com/repos/gotson/komga/releases/latest' | mawk -F\" '/^ *"browser_download_url": ".*\/komga-[^"\/]*\.jar"$/{print $4}')" /mnt/dietpi_userdata/komga/komga.jar
# User
Create_User -G dietpi -d /mnt/dietpi_userdata/komga komga
# Data
G_EXEC mkdir -p /mnt/dietpi_userdata/{ebooks,comics,komga/.komga}
# Config
[[ -f '/mnt/dietpi_userdata/komga/application.yml' ]] || cat << _EOF_ > /mnt/dietpi_userdata/komga/application.yml
komga:
libraries-scan-startup: true # Scan libraries at startup
libraries-scan-cron: "* 2 * * * *" # Scan libraries periodically every hour at :02
database:
file: /mnt/dietpi_userdata/komga/database.sqlite
remember-me:
key: $(openssl rand -hex 32)
logging:
file:
name: "" # Disable file logging, use: "journalctl -u komga"
level:
root: WARN # TRACE DEBUG INFO WARN ERROR
server:
port: 2037
_EOF_
# Service
cat << _EOF_ > /etc/systemd/system/komga.service
[Unit]
Description=Komga (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
SyslogIdentifier=Komga
User=komga
WorkingDirectory=/mnt/dietpi_userdata/komga
ExecStart=$JAVA_PATH -mx${JAVA_MAX_HEAP_SIZE}m -jar komga.jar
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chmod +x /mnt/dietpi_userdata/komga/komga.jar
G_EXEC chown -R komga /mnt/dietpi_userdata/komga
G_EXEC chown komga:dietpi /mnt/dietpi_userdata/{ebooks,comics}
G_EXEC chmod 0775 /mnt/dietpi_userdata/{ebooks,comics}
fi
if To_Install 56 # Single File PHP Gallery
then
G_DIETPI-NOTIFY 2 'Obtaining latest download from: https://sye.dk/sfpg/?latest'
local file=$(curl -sSfL 'https://sye.dk/sfpg/?latest')
[[ $file ]] || { file='Single_File_PHP_Gallery_4.14.0.zip'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} download detection failed. Downloading the last known \"$file\" is attempted as fallback, but may fail if a newer version is available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
# Install required PHP modules: https://sye.dk/sfpg/
aDEPS=("php$PHP_VERSION-gd")
Download_Install "https://sye.dk/sfpg/$file" /var/www/gallery
# Enable required PHP modules
G_EXEC phpenmod gd
# Thumbnails and metadata cache: Remove on reinstalls
[[ -d '/var/www/gallery/_sfpg_data' ]] && G_EXEC rm -R /var/www/gallery/_sfpg_data
G_EXEC mkdir /var/www/gallery/_sfpg_data
G_EXEC chown www-data /var/www/gallery/_sfpg_data
# Apply a random security phrase
local phrase=$(tr -dc '[:alnum:]' < /dev/random | head -c30)
GCI_PASSWORD=1 G_CONFIG_INJECT "define\('SECURITY_PHRASE'" "define('SECURITY_PHRASE', '$phrase');" /var/www/gallery/index.php
unset -v phrase
# Create test galleries
G_EXEC mkdir -p /var/www/gallery/DietPi
[[ -f '/var/www/gallery/DietPi/logo_256.png' ]] || G_THREAD_START curl -sSf 'https://dietpi.com/images/dietpi-logo_256x256.png' -o /var/www/gallery/DietPi/logo_256.png
G_EXEC mkdir -p /var/www/gallery/Tr-Zero
[[ -f '/var/www/gallery/Tr-Zero/SS_0.jpg' ]] || G_THREAD_START curl -sSfL 'https://media.indiedb.com/images/games/1/25/24673/SS_0.jpg' -o /var/www/gallery/Tr-Zero/SS_0.jpg
[[ -f '/var/www/gallery/Tr-Zero/SS_1.jpg' ]] || G_THREAD_START curl -sSfL 'https://media.indiedb.com/images/games/1/25/24673/SS_44.jpg' -o /var/www/gallery/Tr-Zero/SS_1.jpg
[[ -f '/var/www/gallery/Tr-Zero/SS_2.jpg' ]] || G_THREAD_START curl -sSfL 'https://media.indiedb.com/images/games/1/25/24673/3.png' -o /var/www/gallery/Tr-Zero/SS_2.jpg
G_THREAD_WAIT
fi
if To_Install 40 # Ampache
then
# Required PHP modules: https://github.com/ampache/ampache/wiki/Installation#prerequisites
aDEPS=("php$PHP_VERSION-curl" "php$PHP_VERSION-intl" "php$PHP_VERSION-xml")
# - Add JSON module for PHP7, as it does not exist (embedded in core package) on PHP8
local json=()
[[ $PHP_VERSION == 8* ]] || aDEPS+=("php$PHP_VERSION-json") json=('json')
# Download: Ampache v7 requires PHP 8.2
if (( $G_DISTRO > 6 ))
then
local fallback_url="https://github.com/ampache/ampache/releases/download/7.7.2/ampache-7.7.2_all_php$PHP_VERSION.zip"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/ampache/ampache/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/ampache-[0-9\.]*_all_php$PHP_VERSION.zip\"$/{print \$4}")" ampache
else
local fallback_url="https://github.com/ampache/ampache/releases/download/6.6.7/ampache-6.6.7_all_php$PHP_VERSION.zip"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/ampache/ampache/releases' | mawk -F\" "/^ *\"browser_download_url\": \".*\/ampache-[0-9\.]*_all_php$PHP_VERSION.zip\"$/{print \$4}" | head -1)" ampache
fi
# Reinstall: Preserve configs from old and new paths
[[ -f '/var/www/ampache/config/ampache.cfg.php' ]] && G_EXEC mv /var/www/ampache/config/ampache.cfg.php ampache/config/
[[ -f '/var/www/ampache/config/registration_agreement.php' ]] && G_EXEC mv /var/www/ampache/config/registration_agreement.php ampache/config/
[[ -f '/mnt/dietpi_userdata/ampache/config/ampache.cfg.php' ]] && G_EXEC mv /mnt/dietpi_userdata/ampache/config/ampache.cfg.php ampache/config/
[[ -f '/mnt/dietpi_userdata/ampache/config/registration_agreement.php' ]] && G_EXEC mv /mnt/dietpi_userdata/ampache/config/registration_agreement.php ampache/config/
[[ -f '/var/www/ampache/channel/.htaccess' ]] && G_EXEC mv /var/www/ampache/channel/.htaccess ampache/public/channel/
[[ -f '/var/www/ampache/rest/.htaccess' ]] && G_EXEC mv /var/www/ampache/rest/.htaccess ampache/public/rest/
[[ -f '/var/www/ampache/play/.htaccess' ]] && G_EXEC mv /var/www/ampache/play/.htaccess ampache/public/play/
[[ -d '/var/www/ampache' || -L '/var/www/ampache' ]] && G_EXEC rm -R /var/www/ampache
[[ -d '/mnt/dietpi_userdata/ampache' ]] && G_EXEC rm -R /mnt/dietpi_userdata/ampache
G_EXEC mv {,/mnt/dietpi_userdata/}ampache
[[ -d '/mnt/dietpi_userdata/ampache/public' ]] && G_EXEC ln -s /mnt/dietpi_userdata/ampache/public /var/www/ampache
Download_Test_Media
# Enable required PHP modules: https://github.com/ampache/ampache/wiki/Installation#prerequisites
G_EXEC phpenmod curl intl xml "${json[@]}"
# Create random temporary alphanumeric 30 characters database password
local password=$(tr -dc '[:alnum:]' < /dev/random | head -c30)
# Fresh install: Create new config
if [[ ! -f '/mnt/dietpi_userdata/ampache/config/ampache.cfg.php' ]]
then
G_EXEC cp /mnt/dietpi_userdata/ampache/config/ampache.cfg.php{.dist,}
G_CONFIG_INJECT 'web_path[[:blank:]]+=' 'web_path = "/ampache"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'database_hostname[[:blank:]]+=' 'database_hostname = /run/mysqld/mysqld.sock' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'database_name[[:blank:]]+=' 'database_name = ampache' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'database_username[[:blank:]]+=' 'database_username = ampache' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
GCI_PASSWORD=1 G_CONFIG_INJECT 'database_password[[:blank:]]+=' "database_password = \"$password\"" /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'tmp_dir_path[[:blank:]]+=' 'tmp_dir_path = "/tmp"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_cmd[[:blank:]]+=' 'transcode_cmd = "ffmpeg"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_m4a[[:blank:]]+=' 'transcode_m4a = "required"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_flac[[:blank:]]+=' 'transcode_flac = "required"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_mpc[[:blank:]]+=' 'transcode_mpc = "required"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_ogg[[:blank:]]+=' 'transcode_ogg = "allowed"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_wav[[:blank:]]+=' 'transcode_wav = "required"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_avi[[:blank:]]+=' 'transcode_avi = "allowed"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_mkv[[:blank:]]+=' 'transcode_mkv = "allowed"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'transcode_mpg[[:blank:]]+=' 'transcode_mpg = "allowed"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'encode_target[[:blank:]]+=' 'encode_target = mp3' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'encode_video_target[[:blank:]]+=' 'encode_video_target = webm' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'generate_video_preview[[:blank:]]+=' 'generate_video_preview = "true"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
G_CONFIG_INJECT 'waveform[[:blank:]]+=' 'waveform = "true"' /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
# Update password as well in rare but possible case that config file still exists but database was lost
elif [[ ! -d '/mnt/dietpi_userdata/mysql/ampache' ]]
then
GCI_PASSWORD=1 G_CONFIG_INJECT 'database_password[[:blank:]]+=' "database_password = \"$password\"" /mnt/dietpi_userdata/ampache/config/ampache.cfg.php
fi
# Permissions: Permit config file updates via web UI
G_EXEC chown www-data /mnt/dietpi_userdata/ampache/config/ampache.cfg.php{.dist,}
# Fresh install: Generate database
G_EXEC systemctl start mariadb
if [[ ! -d '/mnt/dietpi_userdata/mysql/ampache' ]]
then
/boot/dietpi/func/create_mysql_db ampache ampache "$password"
# Import template
G_EXEC mysql ampache < /mnt/dietpi_userdata/ampache/resources/sql/ampache.sql
# Generate admin user: Replace password string internally to avoid printing it to console
G_EXEC_PRE_FUNC(){ acommand[6]=$GLOBAL_PW; }
G_EXEC php /mnt/dietpi_userdata/ampache/bin/cli admin:addUser -l 100 -p "${GLOBAL_PW//?/X}" dietpi
# Generate local music catalogue
G_EXEC_OUTPUT=1 G_EXEC php /mnt/dietpi_userdata/ampache/bin/cli run:addCatalog Music /mnt/dietpi_userdata/Music music
# Scan for music files
G_EXEC_OUTPUT=1 G_EXEC php /mnt/dietpi_userdata/ampache/bin/cli run:updateCatalog
# Reinstall: Update database
else
G_EXEC_OUTPUT=1 G_EXEC php /mnt/dietpi_userdata/ampache/bin/cli admin:updateDatabase
fi
unset -v password
fi
if To_Install 58 tailscaled # Tailscale
then
# APT key
G_EXEC curl -sSfL "https://pkgs.tailscale.com/stable/debian/$G_DISTRO_NAME.noarmor.gpg" -o /etc/apt/trusted.gpg.d/dietpi-tailscale.gpg
# APT list
G_EXEC eval "echo 'deb https://pkgs.tailscale.com/stable/debian $G_DISTRO_NAME main' > /etc/apt/sources.list.d/dietpi-tailscale.list"
G_AGUP
# APT package
G_AGI tailscale
Enable_IP_forwarding tailscale
fi
if To_Install 97 # OpenVPN Server
then
G_AGI openvpn iptables
Remove_SysV openvpn
Configure_iptables
aSTART_SERVICES+=('openvpn')
# Create initial server and client configs if not yet present
# - Wildcard config file detection via for loop
local i
for i in /etc/openvpn/*.conf
do
# Inform user if config exist, that config generation is skipped
if [[ -f $i ]]
then
G_DIETPI-NOTIFY 2 "Existing OpenVPN configuration found: $i
- Config generation will be skipped.
- If you need a fresh key/cert/config set, please remove $i
and run: dietpi-software reinstall 97"
break
fi
# Pre-v6.26 cleanup
[[ -f '/etc/openvpn/dh2048.pem' ]] && G_EXEC rm /etc/openvpn/dh2048.pem
dpkg-query -s easy-rsa &> /dev/null && G_AGP easy-rsa
# Download latest easy-rsa from GitHub
G_DIETPI-NOTIFY 2 'Downloading latest easy-rsa for certificate and key generation...'
local fallback_url='https://github.com/OpenVPN/easy-rsa/releases/download/v3.1.0/EasyRSA-3.1.0.tgz'
Download_Install "$(curl -sSfL 'https://api.github.com/repos/OpenVPN/easy-rsa/releases/latest' | mawk -F\" '/"browser_download_url": .*\/EasyRSA-[^"\/]*\.tgz"/{print $4}')"
[[ -d '/etc/openvpn/easy-rsa' ]] && G_EXEC rm -R /etc/openvpn/easy-rsa
G_EXEC mv EasyRSA-* /etc/openvpn/easy-rsa
# Cert and key generation via easy-rsa
G_DIETPI-NOTIFY 2 'Generating unique OpenVPN certificates and keys. Please wait...\n'
G_EXEC cd /etc/openvpn/easy-rsa
cat << '_EOF_' > vars
set_var EASYRSA_REQ_COUNTRY "UK"
set_var EASYRSA_REQ_PROVINCE "DietPi"
set_var EASYRSA_REQ_CITY "DietPi"
set_var EASYRSA_REQ_ORG "DietPi"
set_var EASYRSA_REQ_EMAIL "noreply@dietpi.com"
set_var EASYRSA_REQ_OU "DietPi"
set_var EASYRSA_BATCH "1"
_EOF_
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa gen-dh
./easyrsa build-server-full DietPi_OpenVPN_Server nopass
./easyrsa build-client-full DietPi_OpenVPN_Client nopass
# Server config
cp -a pki/{ca.crt,dh.pem,issued/DietPi_OpenVPN_Server.crt,private/DietPi_OpenVPN_Server.key} /etc/openvpn/
G_EXEC cd /etc/openvpn
cat << '_EOF_' > server.conf
port 1194
proto udp
dev tun
ca ca.crt
cert DietPi_OpenVPN_Server.crt
key DietPi_OpenVPN_Server.key
dh dh.pem
server 10.8.0.0 255.255.255.0
client-to-client
keepalive 10 60
comp-lzo
max-clients 10
user nobody
group nogroup
persist-key
persist-tun
verb 3
# Web Forwarding (uncomment to enable)
#push "redirect-gateway"
#push "dhcp-option DNS 10.8.0.1"
_EOF_
# Client config
cat << '_EOF_' > DietPi_OpenVPN_Client.ovpn
client
proto udp
dev tun
# IP/domain name of DietPi system, running OpenVPN server
remote mywebsite.com 1194
resolv-retry infinite
nobind
user nobody
group nogroup
persist-key
persist-tun
remote-cert-tls server
comp-lzo
verb 3
_EOF_
# - Add CA cert, client cert and key
echo -e "
<ca>\n$(<ca.crt)\n</ca>
<cert>\n$(<easy-rsa/pki/issued/DietPi_OpenVPN_Client.crt)\n</cert>
<key>\n$(<easy-rsa/pki/private/DietPi_OpenVPN_Client.key)\n</key>" >> DietPi_OpenVPN_Client.ovpn
# - Copy to userdata
G_EXEC cp DietPi_OpenVPN_Client.ovpn /mnt/dietpi_userdata/
# - and /boot partition
G_EXEC cp DietPi_OpenVPN_Client.ovpn /boot/
G_EXEC cd "$G_WORKING_DIR"
break # Always break loop which is only for single wildcard file detection
done
Enable_IP_forwarding openvpn
# ToDo: iptables NAT rules: Those need to run via pre-up and post-down rules when the OpenSSH server starts
#iptables -A FORWARD -i tun0 -j ACCEPT
#iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o "$(G_GET_NET iface)" -j MASQUERADE
#ip6tables -A FORWARD -i tun0 -j ACCEPT
#ip6tables -t nat -A POSTROUTING -o "$(G_GET_NET iface)" -j MASQUERADE
fi
if To_Install 117 # PiVPN
then
G_EXEC curl -sSfL 'https://raw.githubusercontent.com/pivpn/pivpn/master/auto_install/install.sh' -o install.bash
G_EXEC chmod +x install.bash
G_DIETPI-NOTIFY 2 'Preventing reboot from within PiVPN installer'
G_EXEC sed --follow-symlinks -i '/^Thank you for using PiVPN./a\exit 0' install.bash
G_DIETPI-NOTIFY 2 'Preventing install of unattended-upgrades'
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*askUnattendedUpgrades$/c\UNATTUPG=0' install.bash
G_DIETPI-NOTIFY 2 'Prevent dhcpcd from being installed'
G_EXEC sed --follow-symlinks -i '/^checkStaticIpSupported() {$/a\return 1' install.bash
# Unattended install
local options=()
if [[ -f '/boot/unattended_pivpn.conf' ]]
then
G_CONFIG_INJECT 'UNATTUPG=[[:digit:]]' 'UNATTUPG=0' /boot/unattended_pivpn.conf
options=('--unattended' '/boot/unattended_pivpn.conf')
fi
# APT deps
G_AGI bind9-dnsutils grepcidr net-tools bsdmainutils iptables-persistent # https://github.com/pivpn/pivpn/blob/master/auto_install/install.sh#L39
Configure_iptables
G_EXEC_OUTPUT=1 G_EXEC_NOEXIT=1 G_EXEC ./install.bash "${options[@]}" || aSOFTWARE_INSTALL_STATE[$software_id]=0
G_EXEC rm install.bash
[[ -f '/boot/unattended_pivpn.conf' ]] && G_EXEC mv /boot/unattended_pivpn.conf{,.applied}
fi
if To_Install 201 zerotier-one # ZeroTier
then
# APT key: Download from GitHub instead of https://download.zerotier.com/contact%40zerotier.com.gpg for enhanced protection against corrupted download server
local url='https://raw.githubusercontent.com/zerotier/ZeroTierOne/master/doc/contact%40zerotier.com.gpg'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-zerotier.gpg --yes"
# APT list
local dist=${G_DISTRO_NAME/forky/trixie}
G_EXEC eval "echo 'deb https://download.zerotier.com/debian/$dist $dist main' > /etc/apt/sources.list.d/dietpi-zerotier.list"
G_AGUP
# APT package
G_AGI zerotier-one
Remove_SysV zerotier-one
# Wait for and print ZeroTier address
CC_STOP=0 Create_Config '/var/lib/zerotier-one/identity.secret' 'zerotier-one' && G_DIETPI-NOTIFY 0 "Your ZeroTier address is: $(mawk -F: '{print $1}' /var/lib/zerotier-one/identity.public)"
fi
if To_Install 92 # Certbot
then
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
G_AGI certbot python3-certbot-apache
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
G_AGI certbot python3-certbot-nginx
else
G_AGI certbot
fi
fi
if To_Install 60 # WiFi Hotspot
then
local packages=('hostapd' 'isc-dhcp-server' 'iptables')
G_AGI "${packages[@]}"
G_EXEC systemctl stop hostapd isc-dhcp-server
aSTART_SERVICES+=('hostapd' 'isc-dhcp-server')
Configure_iptables
# Unmask hostapd since it is masked via postinst when no config exists yet
G_EXEC systemctl --no-reload unmask hostapd
# Enable WiFi modules
/boot/dietpi/func/dietpi-set_hardware wifimodules enable
local eth_iface=$(G_GET_NET -t eth iface)
local wifi_iface=$(G_GET_NET -t wlan iface)
# DHCP server config
G_BACKUP_FP /etc/dhcp/dhcpd.conf
cat << '_EOF_' > /etc/dhcp/dhcpd.conf
authoritative;
#default-lease-time 43200;
#max-lease-time 86400;
subnet 192.168.42.0 netmask 255.255.255.0 {
range 192.168.42.10 192.168.42.250;
option broadcast-address 192.168.42.255;
option routers 192.168.42.1;
option domain-name "local";
option domain-name-servers 9.9.9.9, 149.112.112.112;
}
_EOF_
# Assign detected WLAN interface
G_EXEC eval "echo 'INTERFACESv4=\"$wifi_iface\"' > /etc/default/isc-dhcp-server"
# Remove all entries below wlan, so we can recreate them
G_EXEC sed --follow-symlinks -Ei '/(allow-hotplug|auto)[[:blank:]]+wlan/q0' /etc/network/interfaces
# Enable up wlan
G_CONFIG_INJECT 'allow-hotplug wlan' "allow-hotplug $wifi_iface" /etc/network/interfaces
# Add WiFi settings to network interfaces config
cat << _EOF_ >> /etc/network/interfaces
iface $wifi_iface inet static
address 192.168.42.1
netmask 255.255.255.0
#gateway 192.168.0.1
#dns-nameservers 9.9.9.9 149.112.112.112
pre-up iw dev $wifi_iface set power_save off
post-down iw dev $wifi_iface set power_save on
# iptables NAT rules
up iptables-restore < /etc/iptables.ipv4.nat
up ip6tables-restore < /etc/iptables.ipv6.nat
_EOF_
# Remove wireless-power setting if not supported by adapter/firmware
# shellcheck disable=SC2015
iw dev "$wifi_iface" set power_save on 2> /dev/null && iw dev "$wifi_iface" set power_save off 2> /dev/null || G_EXEC sed --follow-symlinks -i '/ iw dev .* set power_save /d' /etc/network/interfaces
# Access point config
# - Parse dietpi.txt
local ssid=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_SSID=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local key=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_KEY=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local wifi4=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_WIFI4=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local wifi5=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_WIFI5=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local wifi6=0
(( $G_DISTRO > 6 )) && wifi6=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_WIFI6=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local freq5=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_5G=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
# - Sanity checks
[[ $wifi4 == [01] ]] || wifi4=0
[[ $wifi5 == [01] ]] || wifi5=0
[[ $wifi6 == [01] ]] || wifi6=0
[[ $freq5 == [01] ]] || freq5=0
(( ${#key} > 8 )) || { G_DIETPI-NOTIFY 1 'WiFi key from dietpi.txt is too short (less than 8 characters), falling back to default: "dietpihotspot"'; key='dietpihotspot'; }
# - WiFi 5 implies 5 GHz and 5 GHz requires at least WiFi 4
(( $wifi5 )) && freq5=1
(( $freq5 && ! $wifi5 && ! $wifi6 )) && wifi4=1
# - Enable WMM with QoS if WiFi 4 or 5 is enabled
local wmm_enabled=0
(( $wifi4 || $wifi5 || $wifi6 )) && wmm_enabled=1
# - Apply mode channel based on frequency
local mode='g'
if (( $freq5 ))
then
mode='a'
local channel=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_5G_CHANNEL=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
disable_error=1 G_CHECK_VALIDINT "$channel" 32 177 || channel=36
else
local channel=$(sed -n '/^[[:blank:]]*SOFTWARE_WIFI_HOTSPOT_CHANNEL=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
disable_error=1 G_CHECK_VALIDINT "$channel" 1 13 || channel=3
fi
# - Create config
G_BACKUP_FP /etc/hostapd/hostapd.conf
cat << _EOF_ > /etc/hostapd/hostapd.conf
interface=$wifi_iface
driver=nl80211
ssid=${ssid:-DietPi-Hotspot}
country_code=00
hw_mode=$mode
channel=$channel
ieee80211n=$wifi4
ieee80211ac=$wifi5
wmm_enabled=$wmm_enabled
macaddr_acl=0
auth_algs=1
ignore_broadcast_ssid=0
wpa=2
wpa_passphrase=$key
wpa_key_mgmt=WPA-PSK
wpa_pairwise=TKIP
rsn_pairwise=CCMP
_EOF_
(( $G_DISTRO > 6 )) && G_CONFIG_INJECT 'ieee80211ax=' "ieee80211ax=$wifi6" /etc/hostapd/hostapd.conf '^ieee80211ac'
G_EXEC chmod 0600 /etc/hostapd/hostapd.conf
# Set WiFi country code
/boot/dietpi/func/dietpi-set_hardware wificountrycode
# Enable access point config
G_EXEC eval 'echo '\''DAEMON_CONF="/etc/hostapd/hostapd.conf"'\'' > /etc/default/hostapd'
Enable_IP_forwarding wifihotspot
# Enable iptables NAT rules
iptables -t nat -A POSTROUTING -s 192.168.42.0/24 -o "$eth_iface" -j MASQUERADE
iptables -A FORWARD -i "$eth_iface" -o "$wifi_iface" -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A FORWARD -i "$wifi_iface" -o "$eth_iface" -j ACCEPT
ip6tables -t nat -A POSTROUTING -o "$eth_iface" -j MASQUERADE
ip6tables -A FORWARD -i "$eth_iface" -o "$wifi_iface" -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A FORWARD -i "$wifi_iface" -o "$eth_iface" -j ACCEPT
# Save iptables rules, applied via /etc/network/interfaces
iptables-save > /etc/iptables.ipv4.nat
ip6tables-save > /etc/iptables.ipv6.nat
# Bring up interface now
G_EXEC_NOHALT=1 G_EXEC ifup "$wifi_iface"
fi
if To_Install 61 tor # Tor Hotspot
then
G_AGI tor
G_EXEC systemctl stop tor
Remove_SysV tor 1
# Tor config
cat << '_EOF_' > /etc/tor/torrc
Log notice stdout
VirtualAddrNetwork 10.192.0.0/10
AutomapHostsSuffixes .onion,.exit
AutomapHostsOnResolve 1
TransPort 192.168.42.1:9040
DNSPort 192.168.42.1:53
_EOF_
# Flush iptables
iptables -F
iptables -t nat -F
ip6tables -F
ip6tables -t nat -F
# Generate Tor prerouting tables
local wifi_iface=$(G_GET_NET -t wlan iface)
iptables -t nat -A PREROUTING -i "$wifi_iface" -p tcp --dport 22 -j REDIRECT --to-ports 22
iptables -t nat -A PREROUTING -i "$wifi_iface" -p udp --dport 53 -j REDIRECT --to-ports 53
iptables -t nat -A PREROUTING -i "$wifi_iface" -p tcp --syn -j REDIRECT --to-ports 9040
ip6tables -t nat -A PREROUTING -i "$wifi_iface" -p tcp --dport 22 -j REDIRECT --to-ports 22
ip6tables -t nat -A PREROUTING -i "$wifi_iface" -p udp --dport 53 -j REDIRECT --to-ports 53
ip6tables -t nat -A PREROUTING -i "$wifi_iface" -p tcp --syn -j REDIRECT --to-ports 9040
# Save iptables rules, applied via /etc/network/interfaces
iptables-save > /etc/iptables.ipv4.nat
ip6tables-save > /etc/iptables.ipv6.nat
# User: Test tor is functional
#https://check.torproject.org
fi
if To_Install 189 # VSCodium
then
# APT key
local url='https://gitlab.com/paulcarroty/vscodium-deb-rpm-repo/raw/master/pub.gpg'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-vscodium.gpg --yes"
# APT list
G_EXEC eval 'echo '\''deb https://download.vscodium.com/debs vscodium main'\'' > /etc/apt/sources.list.d/dietpi-vscodium.list'
G_AGUP
# APT package
# - gnome-keyring is the backend daemon for the Secrets API used to store credentials, required e.g. for the GitHub PR and issues extension.
G_AGI codium gnome-keyring
# Desktop shortcut
Create_Desktop_Shortcut codium
fi
if To_Install 37 shairport-sync # Shairport Sync
then
# AirPlay 1 vs AirPlay 2 selection
local airplay2=$(sed -n '/^[[:blank:]]*SOFTWARE_SHAIRPORT_SYNC_AIRPLAY=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $airplay2 == 2 ]] && G_WHIP_DEFAULT_ITEM='ok'
if G_WHIP_BUTTON_OK_TEXT='AirPlay 2' G_WHIP_BUTTON_CANCEL_TEXT='AirPlay 1' G_WHIP_YESNO '[OPTION] AirPlay 2 support
\nSince Shairport Sync v4.1, an experimental AirPlay 2 build is available:\n- https://github.com/mikebrady/shairport-sync/blob/master/AIRPLAY2.md
\nIt causes higher CPU load, about 150 MiB additional disk space with dependencies, the NQPTP (Not Quite PTP) companion service, and not all source systems are supported (check out the above URL).
\nYou can switch between AirPlay 1 and AirPlay 2 build any time later via reinstall:\n- dietpi-software reinstall 37
\nDo you want to install the AirPlay 2 ready Shairport Sync build?'
then
G_CONFIG_INJECT 'SOFTWARE_SHAIRPORT_SYNC_AIRPLAY=' 'SOFTWARE_SHAIRPORT_SYNC_AIRPLAY=2' /boot/dietpi.txt
airplay2='-airplay2'
else
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*SOFTWARE_SHAIRPORT_SYNC_AIRPLAY=/c\#SOFTWARE_SHAIRPORT_SYNC_AIRPLAY=2' /boot/dietpi.txt
airplay2=
fi
G_AGI "shairport-sync$airplay2"
fi
if To_Install 48 # Pydio
then
# Install required PHP modules
aDEPS=("php$PHP_VERSION-apcu" "php$PHP_VERSION-gd" "php$PHP_VERSION-intl" "php$PHP_VERSION-mbstring" "php$PHP_VERSION-opcache" "php$PHP_VERSION-xml")
# Reinstall: Skip download and install, advice to use internal updater from web UI
if [[ -d '/var/www/pydio' ]]
then
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/var/www/pydio\" already exists. Download and install steps will be skipped.
- If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
- If you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\"."
G_AGI "${aDEPS[@]}"
aDEPS=()
else
Download_Install 'https://download.pydio.com/pub/core/ci/pydio-latest.tar.gz'
G_EXEC mv pydio-latest /var/www/pydio
fi
# PHP configuration
G_EXEC phpenmod apcu gd intl pdo_mysql dom mbstring xml
# Webserver config
# - Apache
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
# Enable Apache rewrite engine
a2enmod rewrite
# Move Pydio Apache config in place
dps_index=$software_id Download_Install 'apache.pydio.conf' /etc/apache2/sites-available/dietpi-pydio.conf
a2ensite dietpi-pydio
# - Lighttpd
elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
# Enable Lighttpd setenv, access and rewrite modules
G_CONFIG_INJECT '"mod_access",' ' "mod_access",' /etc/lighttpd/lighttpd.conf '"mod_.+",'
[[ -f '/etc/lighttpd/conf-enabled/05-setenv.conf' ]] || G_EXEC lighty-enable-mod setenv
# Move Pydio Lighttpd config in place
dps_index=$software_id Download_Install 'lighttpd.pydio.conf' /etc/lighttpd/conf-available/99-dietpi-pydio.conf
G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
G_EXEC lighty-enable-mod rewrite dietpi-pydio
# - Nginx
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
# Move Pydio Nginx config in place
dps_index=$software_id Download_Install 'nginx.pydio.conf' /etc/nginx/sites-dietpi/dietpi-pydio.conf
fi
# Database
/boot/dietpi/func/create_mysql_db pydio pydio "$GLOBAL_PW"
# Setup data directory
local data_dir='/mnt/dietpi_userdata/pydio_data'
# - Skip if already existent
if [[ -d $data_dir ]]
then
G_DIETPI-NOTIFY 2 "Existing $data_dir found, will migrate..."
[[ -e '/var/www/pydio/data' ]] && G_EXEC rm -R /var/www/pydio/data
else
# Move data structure
[[ -e $data_dir || -L $data_dir ]] && G_EXEC rm -R "$data_dir"
if [[ -d '/var/www/pydio/data' ]]
then
G_EXEC mv /var/www/pydio/data "$data_dir"
else
G_EXEC mkdir "$data_dir"
fi
fi
# - Create symlink
G_EXEC ln -sf "$data_dir" /var/www/pydio/data
# Permissions: Fix some files being 444 mode, breaking internal updater
G_EXEC chown -R www-data:www-data /var/www/pydio "$data_dir"
G_EXEC chmod -R u+w /var/www/pydio
fi
if To_Install 36 squeezelite # Squeezelite
then
G_AGI squeezelite
G_EXEC systemctl stop squeezelite
fi
if To_Install 66 rpimonitor # RPi-Monitor
then
Download_Install 'https://raw.githubusercontent.com/XavierBerger/RPi-Monitor-deb/develop/packages/rpimonitor_2.13-beta6_all.deb'
G_EXEC systemctl stop rpimonitor
# Update APT package status
G_EXEC /usr/share/rpimonitor/scripts/updatePackagesStatus.pl
# Fix issue to display CPU temperature correctly: https://github.com/XavierBerger/RPi-Monitor/issues/374
# shellcheck disable=SC2016
G_CONFIG_INJECT 'dynamic.1.postprocess=' 'dynamic.1.postprocess=int($1/10 + 0.5)/100' /etc/rpimonitor/template/temperature.conf
# USB drive stats implementation by Rich
if [[ $G_ROOTFS_DEV != '/dev/sda1' && ! -f '/etc/rpimonitor/template/usb_hdd.conf' ]] && findmnt -S /dev/sda1 > /dev/null
then
cat << '_EOF_' > /etc/rpimonitor/template/usb_hdd.conf
########################################################################
# Extract USB HDD (sda1) information
# Page: 1
# Information Status Statistics
# - USBHDD1 total - yes - yes
# - USBHDD1 used - yes - yes
########################################################################
static.10.name=usbhdd_total
static.10.source=df -m
static.10.regexp=^/dev/sda1\s+(\d+)
dynamic.14.name=usbhdd_used
dynamic.14.source=df -m
dynamic.14.regexp=^/dev/sda1\s+\d+\s+(\d+)
dynamic.14.rrd=GAUGE
web.status.1.content.9.name=USB HDD
web.status.1.content.9.icon=usb_hdd.png
web.status.1.content.9.line.1="<b>/sda1</b> Used: <b>"+KMG(data.usbhdd_used,'M')+"</b> (<b>"+Percent(data.usbhdd_used,data.usbhdd_total,'M')+"</b>) Free: <b>"+KMG(data.usbhdd_total-data.usbhdd_used,'M')+ "</b> Total: <b>"+ KMG(data.usbhdd_total,'M') +"</b>"
web.status.1.content.9.line.2=ProgressBar(data.usbhdd_used,data.usbhdd_total)
web.statistics.1.content.9.name=USB HDD
web.statistics.1.content.9.graph.1=usbhdd_total
web.statistics.1.content.9.graph.2=usbhdd_used
web.statistics.1.content.9.ds_graph_options.usbhdd_total.label=USB HDD total space (MiB)
web.statistics.1.content.9.ds_graph_options.usbhdd_total.color="#FF7777"
web.statistics.1.content.9.ds_graph_options.usbhdd_used.label=USB HDD used space (MiB)
web.statistics.1.content.9.ds_graph_options.usbhdd_used.lines={ fill: true }
web.statistics.1.content.9.ds_graph_options.usbhdd_used.color="#7777FF"
_EOF_
G_EXEC sed --follow-symlinks -i '\|include=/etc/rpimonitor/template/sdcard.conf|a\include=/etc/rpimonitor/template/usb_hdd.conf' /etc/rpimonitor/data.conf
fi
fi
if To_Install 65 netdata # Netdata
then
G_AGI netdata
G_EXEC systemctl stop netdata
fi
if To_Install 57 # Baïkal
then
# APT deps: https://github.com/sabre-io/Baikal/wiki/Baïkal-dependencies
aDEPS=("php$PHP_VERSION-xml" "php$PHP_VERSION-mbstring" "php$PHP_VERSION-mysql")
# Bullseye: v0.10 dropped support for PHP 7.4: https://github.com/MichaIng/DietPi/issues/7387
if (( $G_DISTRO < 7 ))
then
Download_Install 'https://github.com/sabre-io/Baikal/releases/download/0.9.5/baikal-0.9.5.zip'
else
local fallback_url='https://github.com/sabre-io/Baikal/releases/download/0.11.1/baikal-0.11.1.zip'
Download_Install "$(curl -sSfL 'https://api.github.com/repos/sabre-io/Baikal/releases/latest' | mawk -F\" '/^ *"browser_download_url": ".*\/baikal-[^"\/]*\.zip"$/{print $4}')"
fi
# Reinstall: https://sabre.io/baikal/upgrade/
if [[ -d '/var/www/baikal' ]]
then
[[ -d '/var/www/baikal/Specific' ]] && G_EXEC cp -a /var/www/baikal/Specific/. baikal/Specific/
[[ -d '/var/www/baikal/config' ]] && G_EXEC cp -a /var/www/baikal/config/. baikal/config/
G_EXEC rm -R /var/www/baikal
fi
G_EXEC mv baikal /var/www/baikal
# Enable required PHP modules: https://github.com/sabre-io/Baikal/wiki/Baïkal-dependencies
G_EXEC phpenmod xml mbstring pdo_mysql
# Database
/boot/dietpi/func/create_mysql_db baikal baikal "$GLOBAL_PW"
# Web server configs: http://sabre.io/baikal/install/ + https://github.com/bambocher/docker-baikal/blob/master/lighttpd.conf
# - Apache
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
dps_index=$software_id Download_Install 'apache.baikal.conf' /etc/apache2/sites-available/dietpi-baikal.conf
a2ensite dietpi-baikal
echo '# Redirect Cal/CardDAV requests to Baïkal endpoint:
Redirect 301 /.well-known/carddav /baikal/html/dav.php
Redirect 301 /.well-known/caldav /baikal/html/dav.php' > /etc/apache2/conf-available/dietpi-dav_redirect.conf
a2enconf dietpi-dav_redirect
# - Lighttpd
elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
dps_index=$software_id Download_Install 'lighttpd.baikal.conf' /etc/lighttpd/conf-available/99-dietpi-baikal.conf
echo '# Redirect Cal/CardDAV requests to Baïkal endpoint:
url.redirect += (
"^/.well-known/caldav" => "/baikal/html/dav.php",
"^/.well-known/carddav" => "/baikal/html/dav.php"
)' > /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
G_EXEC lighty-enable-mod dietpi-baikal dietpi-dav_redirect
# - Nginx
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
dps_index=$software_id Download_Install 'nginx.baikal.conf' /etc/nginx/sites-dietpi/dietpi-baikal.conf
# shellcheck disable=SC2016
echo '# Redirect Cal/CardDAV requests to Baïkal endpoint:
location = /.well-known/carddav { return 301 /baikal/html/dav.php; }
location = /.well-known/caldav { return 301 /baikal/html/dav.php; }' > /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf
fi
# Permissions
G_EXEC chown -R www-data:root /var/www/baikal/{Specific,config}
G_EXEC find /var/www/baikal/{Specific,config} \( -name '.ht*' -o -name '.git*' \) -exec chown root {} +
fi
if To_Install 43 mumble-server # Mumble Server
then
G_AGI mumble-server
G_EXEC systemctl stop mumble-server
# Trixie: config moved to sub dir and murmurd was renamed to mumble-server
local conf='/etc/mumble-server.ini' cli='murmurd'
(( $G_DISTRO > 7 )) && conf='/etc/mumble/mumble-server.ini' cli='mumble-server'
# Cap total connections
G_CONFIG_INJECT 'users=' "users=$(( $G_HW_CPU_CORES * 8 ))" "$conf"
# Name the root channel
G_CONFIG_INJECT 'registerName=' 'registerName=DietPi Mumble Server' "$conf"
# Disable DB logging
G_CONFIG_INJECT 'logdays=' 'logdays=-1' "$conf"
# Set Superuser passwd: https://dietpi.com/forum/t/1371
"$cli" -ini "$conf" -supw "$GLOBAL_PW"
fi
if To_Install 41 emby-server # Emby
then
case $G_HW_ARCH in
2) local arch='armhf';;
3) local arch='arm64';;
*) local arch='amd64';;
esac
local fallback_url="https://github.com/MediaBrowser/Emby.Releases/releases/download/4.9.1.90/emby-server-deb_4.9.1.90_$arch.deb"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/MediaBrowser/Emby.Releases/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/emby-server-deb_[^\"\/]*_$arch\.deb\"$/{print \$4}")"
G_EXEC systemctl stop emby-server
# User: The DEB package install overrides this, hence the method needs to be changed when using an APT repository!
Create_User -g dietpi -G emby,video,render -d /var/lib/emby emby
Download_Test_Media
fi
if To_Install 42 plexmediaserver # Plex Media Server
then
# APT key
local url='https://downloads.plex.tv/plex-keys/PlexSign.key'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-plexmediaserver.gpg --yes"
# APT list
G_EXEC eval 'echo '\''deb https://downloads.plex.tv/repo/deb public main'\'' > /etc/apt/sources.list.d/plexmediaserver.list'
G_AGUP
# APT package
G_AGI plexmediaserver
G_EXEC systemctl stop plexmediaserver
# User: Run service as "dietpi" group: https://github.com/MichaIng/DietPi/issues/350#issuecomment-423763518
Create_User -g dietpi -G plex,video,render -d /var/lib/plexmediaserver plex
# - Unset explicit group in service file (applied by DEB package) as this may override supplementary group permissions
G_EXEC mkdir -p /etc/systemd/system/plexmediaserver.service.d
G_EXEC eval 'echo -e '\''[Service]\nGroup='\'' > /etc/systemd/system/plexmediaserver.service.d/dietpi-group.conf'
# Unbound: Fix secure remote access: https://dietpi.com/forum/t/cant-connect-to-plex-directly-due-to-unbound/5199
(( ${aSOFTWARE_INSTALL_STATE[182]} == 2 )) && G_EXEC eval 'echo -e '\''server:\n\tprivate-domain: "plex.direct"'\'' > /etc/unbound/unbound.conf.d/dietpi-plex.conf'
Download_Test_Media
fi
if To_Install 52 cuberite # Cuberite
then
# https://github.com/cuberite/cuberite/blob/master/easyinstall.sh
case $G_HW_ARCH in
3) local arch='aarch64';;
10) local arch='x86_64';;
*) local arch='armhf-raspbian';;
esac
Download_Install "https://download.cuberite.org/linux-$arch/Cuberite.tar.gz" /mnt/dietpi_userdata/cuberite
# User
Create_User -d /mnt/dietpi_userdata/cuberite cuberite
# Service
cat << '_EOF_' > /etc/systemd/system/cuberite.service
[Unit]
Description=Cuberite (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
User=cuberite
WorkingDirectory=/mnt/dietpi_userdata/cuberite
ExecStart=/mnt/dietpi_userdata/cuberite/Cuberite --detached
[Install]
WantedBy=multi-user.target
_EOF_
# Web UI settings: Do not overwrite existing!
[[ -f '/mnt/dietpi_userdata/cuberite/webadmin.ini' ]] || > /mnt/dietpi_userdata/cuberite/webadmin.ini
G_EXEC chmod 0600 /mnt/dietpi_userdata/cuberite/webadmin.ini
[[ -s '/mnt/dietpi_userdata/cuberite/webadmin.ini' ]] || cat << _EOF_ > /mnt/dietpi_userdata/cuberite/webadmin.ini
[User:admin]
Password=$GLOBAL_PW
[WebAdmin]
Ports=1339
Enabled=1
_EOF_
# Permissions
G_EXEC chown -R cuberite:cuberite /mnt/dietpi_userdata/cuberite
G_EXEC chmod +x /mnt/dietpi_userdata/cuberite/Cuberite
fi
if To_Install 53 mineos # MineOS
then
# https://wiki.codeemo.com/install/debian_10.html
local url='https://github.com/hexparrot/mineos-node'
G_CHECK_URL "$url"
# APT deps
G_AGI rdiff-backup rsync screen make g++
# Download/Update MineOS
G_EXEC mkdir -p /mnt/dietpi_userdata/mineos
G_EXEC cd /mnt/dietpi_userdata/mineos
if [[ -d 'minecraft' ]]
then
# https://wiki.codeemo.com/maint/webui.html#updating-the-webui
G_EXEC cd minecraft
G_EXEC_OUTPUT=1 G_EXEC git pull origin master
else
G_EXEC_OUTPUT=1 G_EXEC git clone "$url" minecraft
G_EXEC cd minecraft
fi
# File modes
G_EXEC git config core.filemode false
G_EXEC chmod +x mineos_console.js webui.js update_webui.sh reset_webui.sh generate-sslcert.sh
# Bookworm: Workaround for failing userid 1.0.0-beta.9 build on weirdly Bookworm only. Latest stable 1.2.5 seems to work well: https://github.com/MichaIng/DietPi/issues/7265
G_CONFIG_INJECT '"userid"' ' "userid": "1.2.5",' package.json
# Workaround: Node.js posix needs to be installed alone first: https://github.com/MichaIng/DietPi/issues/5181
G_EXEC_OUTPUT=1 G_EXEC npm i --no-audit --no-package-lock posix@latest
# Install MineOS
G_EXEC_OUTPUT=1 G_EXEC npm i --no-audit
# Config: Preserve existing
[[ -f '/etc/mineos.conf' ]] || G_EXEC cp mineos.conf /etc/mineos.conf
# Create symlinks for console and userdata dir
G_EXEC ln -sf /mnt/dietpi_userdata/mineos/minecraft/mineos_console.js /usr/local/bin/mineos
G_EXEC mkdir -p /var/games ../serverdata
G_EXEC rm -Rf /var/games/minecraft
G_EXEC ln -s /mnt/dietpi_userdata/mineos/serverdata /var/games/minecraft
# Setup SSL cert
G_EXEC_OUTPUT=1 G_EXEC ./generate-sslcert.sh
G_EXEC cd "$G_WORKING_DIR"
# Service
cat << '_EOF_' > /etc/systemd/system/mineos.service
[Unit]
Description=MineOS (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
SyslogIdentifier=MineOS
WorkingDirectory=/mnt/dietpi_userdata/mineos/minecraft
Environment="SHELL=/bin/bash" "HOME=/root"
ExecStart=/usr/local/bin/node webui.js
KillMode=process
[Install]
WantedBy=multi-user.target
_EOF_
# User and permissions
local new_user=0
getent passwd mineos &> /dev/null || new_user=1
Create_User -d /mnt/dietpi_userdata/mineos/serverdata -p "$GLOBAL_PW" mineos
G_EXEC chown -R mineos /mnt/dietpi_userdata/mineos/serverdata
# - Assure SHA512 password hash algorithm, as MineOS does not support yescrypt, default since Bullseye: https://github.com/hexparrot/mineos-node/issues/441
(( $new_user )) && G_EXEC eval "chpasswd --crypt-method SHA512 <<< 'mineos:$GLOBAL_PW'"
fi
if To_Install 46 qbittorrent # qBittorrent
then
G_AGI qbittorrent-nox
# User
Create_User -g dietpi -d /home/qbittorrent qbittorrent
# Config: Create only on fresh install
if [[ ! -f '/home/qbittorrent/.config/qBittorrent/qBittorrent.conf' ]]; then
G_EXEC mkdir -p /home/qbittorrent/.config/qBittorrent
cat << _EOF_ > /home/qbittorrent/.config/qBittorrent/qBittorrent.conf
[General]
ported_to_new_savepath_system=true
[Application]
FileLogger\Enabled=true
FileLogger\Path=/var/log/qbittorrent
[AutoRun]
enabled=false
[LegalNotice]
Accepted=true
[Preferences]
Advanced\AnonymousMode=false
Advanced\IncludeOverhead=false
Bittorrent\DHT=true
Bittorrent\DHTPort=6881
Bittorrent\Encryption=1
Bittorrent\LSD=true
Bittorrent\MaxConnecs=$(Optimise_BitTorrent 2)
Bittorrent\MaxConnecsPerTorrent=$(Optimise_BitTorrent 2)
Bittorrent\MaxUploads=$(Optimise_BitTorrent 3)
Bittorrent\MaxUploadsPerTorrent=$(Optimise_BitTorrent 3)
Bittorrent\PeX=true
Bittorrent\sameDHTPortAsBT=true
Bittorrent\uTP=true
Bittorrent\uTP_rate_limited=false
Connection\GlobalDLLimit=-1
Connection\GlobalDLLimitAlt=10
Connection\GlobalUPLimit=-1
Connection\GlobalUPLimitAlt=10
Connection\PortRangeMin=6881
Connection\Proxy\Authentication=false
Connection\ProxyPeerConnections=false
Connection\ResolvePeerCountries=false
Connection\UPnP=true
Downloads\DiskWriteCacheSize=$(Optimise_BitTorrent 0)
Downloads\DiskWriteCacheTTL=60
Downloads\PreAllocation=false
Downloads\SavePath=/mnt/dietpi_userdata/downloads
Downloads\TempPath=/mnt/dietpi_userdata/downloads
Downloads\TempPathEnabled=false
Downloads\UseIncompleteExtension=false
DynDNS\Enabled=false
General\Locale=en
IPFilter\Enabled=false
MailNotification\enabled=false
Queueing\IgnoreSlowTorrents=false
Queueing\MaxActiveDownloads=$(Optimise_BitTorrent 1)
Queueing\MaxActiveTorrents=$(Optimise_BitTorrent 1)
Queueing\MaxActiveUploads=1
Queueing\QueueingEnabled=false
Scheduler\Enabled=false
WebUI\CSRFProtection=true
WebUI\ClickjackingProtection=true
WebUI\Enabled=true
WebUI\HTTPS\Enabled=false
WebUI\HostHeaderValidation=false
WebUI\LocalHostAuth=true
WebUI\Password_PBKDF2="@ByteArray(tpgNK76AcpP14rjOZP9vwg==:rQNtOB0P4HfNj20pJtxiTBi9miduS6L1Xqqazc4Y6Gpm3Rn02jMXnPPT3KH2JMDKhFQjAaTGVJz0dz5JVw2QUQ==)"
WebUI\Port=1340
WebUI\SecureCookie=true
WebUI\UseUPnP=true
WebUI\Username=qbittorrent
_EOF_
fi
# Service
cat << '_EOF_' > /etc/systemd/system/qbittorrent.service
[Unit]
Description=qBittorrent (DietPi)
Documentation=man:qbittorrent-nox(1)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=qbittorrent
UMask=002
LogsDirectory=qbittorrent
ExecStart=/usr/bin/qbittorrent-nox
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chown -R qbittorrent:root /home/qbittorrent
fi
if To_Install 107 rtorrent # rTorrent
then
aDEPS=('rtorrent' 'mediainfo')
# Install ruTorrent: Web UI for rTorrent
# - Grab current version
local version=$(curl -sSfL 'https://api.github.com/repos/Novik/ruTorrent/releases/latest' | mawk -F\" '/^ *"tag_name": "[^"]*",$/{print $4}')
[[ $version ]] || { version='v5.2.10'; G_DIETPI-NOTIFY 1 "Automatic latest ruTorrent version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
[[ $version == 'v5.'* ]] || version='v5.2.10'
Download_Install "https://github.com/Novik/ruTorrent/archive/$version.tar.gz"
# - Reinstall freshly with preserved configs and 3rd party plugins
if [[ -d '/var/www/rutorrent' ]]
then
# If old configs exist, preserve them and make new config files examples
[[ -f '/var/www/rutorrent/conf/config.php' ]] && { G_EXEC mv "ruTorrent-${version#v}/conf/config.php"{,.example}; G_EXEC cp -a {/var/www/rutorrent,"ruTorrent-${version#v}"}/conf/config.php; }
[[ -f '/var/www/rutorrent/conf/access.ini' ]] && { G_EXEC mv "ruTorrent-${version#v}/conf/access.ini"{,.example}; G_EXEC cp -a {/var/www/rutorrent,"ruTorrent-${version#v}"}/conf/access.ini; }
[[ -f '/var/www/rutorrent/conf/plugins.ini' ]] && { G_EXEC mv "ruTorrent-${version#v}/conf/plugins.ini"{,.example}; G_EXEC cp -a {/var/www/rutorrent,"ruTorrent-${version#v}"}/conf/plugins.ini; }
# Preserve 3rd party plugins
for i in /var/www/rutorrent/plugins/{,.??,.[^.]}*
do
[[ -e $i ]] || continue
[[ -e ruTorrent-${version#v}/plugins/${i#/var/www/rutorrent/plugins/} ]] && continue
G_EXEC cp -a "$i" "ruTorrent-${version#v}/plugins/"
done
# Preserve 3rd party themes
for i in /var/www/rutorrent/plugins/theme/themes/{,.??,.[^.]}*
do
[[ -e $i ]] || continue
[[ -e ruTorrent-${version#v}/plugins/theme/themes/${i#/var/www/rutorrent/plugins/theme/themes/} ]] && continue
G_EXEC cp -a "$i" "ruTorrent-${version#v}/plugins/theme/themes/"
done
# Reinstall freshly with preserved configs, 3rd party plugins and themes
G_EXEC rm -R /var/www/rutorrent
fi
G_EXEC mv "ruTorrent-${version#v}" /var/www/rutorrent
# Install DarkBetter theme manually: https://github.com/MichaIng/DietPi/issues/3271
if [[ -d '/var/www/rutorrent/plugins/theme/themes/DarkBetter' ]]
then
Download_Install 'https://github.com/chocolatkey/DarkBetter/archive/master.tar.gz'
G_EXEC rm -R /var/www/rutorrent/plugins/theme/themes/DarkBetter
G_EXEC mv DarkBetter-master /var/www/rutorrent/plugins/theme/themes/DarkBetter
fi
# ruTorrent: Enable HTTP authentication and RPC
# - Apache
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
# Enable SCGI module
G_EXEC a2enmod auth_basic authn_file authz_user proxy_scgi
# Enable HTTP authentication and rTorrent access via SCGI
[[ -f '/etc/.rutorrent-htaccess' ]] || htpasswd -cb /etc/.rutorrent-htaccess root "$GLOBAL_PW"
cat << '_EOF_' > /etc/apache2/sites-available/dietpi-rutorrent.conf
<Directory /var/www/rutorrent>
AllowOverride All
AuthType Basic
AuthName "ruTorrent login"
AuthBasicProvider file
AuthUserFile "/etc/.rutorrent-htaccess"
Require valid-user
</Directory>
<Location /RPC2>
AuthType Basic
AuthName "rTorrent login"
AuthBasicProvider file
AuthUserFile "/etc/.rutorrent-htaccess"
Require valid-user
</Location>
ProxyPass "/RPC2" "unix:/mnt/dietpi_userdata/downloads/.session/rpc.socket|scgi://127.0.0.1"
_EOF_
G_EXEC a2ensite dietpi-rutorrent
# - Lighttpd
elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
[[ -f '/etc/.rutorrent-htaccess' ]] || echo "root:rtorrent:$(echo -n "root:rtorrent:$GLOBAL_PW" | md5sum | mawk '{print $1}')" > /etc/.rutorrent-htaccess
# Pre-v7.2: Remove obsolete entries from /etc/lighttpd/lighttpd.conf
grep -q '^#RUTORRENT_DIETPI' /etc/lighttpd/lighttpd.conf && G_EXEC sed --follow-symlinks -i '/#RUTORRENT_DIETPI/,/#RUTORRENT_DIETPI/d' /etc/lighttpd/lighttpd.conf
cat << '_EOF_' > /etc/lighttpd/conf-available/98-dietpi-rtorrent.conf
server.modules += ( "mod_auth", "mod_authn_file", "mod_scgi" )
auth.backend = "htdigest"
auth.backend.htdigest.userfile = "/etc/.rutorrent-htaccess"
auth.require = ( "/rutorrent" =>
(
"method" => "digest",
"realm" => "rtorrent",
"require" => "valid-user"
),
"/RPC2" =>
(
"method" => "digest",
"realm" => "rtorrent",
"require" => "valid-user"
)
)
scgi.server = ( "/RPC2" =>
( "rtorrent" =>
(
"socket" => "/mnt/dietpi_userdata/downloads/.session/rpc.socket",
"check-local" => "disable"
)
)
)
$HTTP["url"] =~ "^/rutorrent/(conf|share)($|/)" {
url.access-deny = ("")
}
_EOF_
[[ -f '/etc/lighttpd/conf-enabled/98-dietpi-rtorrent.conf' ]] || G_EXEC lighty-enable-mod dietpi-rtorrent
# - Nginx
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
# "openssl passwd -5" (sha256, even "-6" sha512) can be used from Buster on.
[[ -f '/etc/.rutorrent-htaccess' ]] || echo "root:$(openssl passwd -apr1 "$GLOBAL_PW")" > /etc/.rutorrent-htaccess
cat << '_EOF_' > /etc/nginx/sites-dietpi/dietpi-rutorrent.conf
location ^~ /rutorrent {
auth_basic "ruTorrent login";
auth_basic_user_file /etc/.rutorrent-htaccess;
location ~ ^/rutorrent/(?:conf|share)(?:/|$) { return 404; }
location ~ \.php(?:$|/) {
include snippets/fastcgi-php.conf;
fastcgi_pass php;
}
}
location ^~ /RPC2 {
auth_basic "rTorrent login";
auth_basic_user_file /etc/.rutorrent-htaccess;
include scgi_params;
scgi_pass unix:/mnt/dietpi_userdata/downloads/.session/rpc.socket;
}
_EOF_
fi
G_EXEC chmod 0400 /etc/.rutorrent-htaccess
G_EXEC chown www-data:www-data /etc/.rutorrent-htaccess
# ruTorrent config
# shellcheck disable=SC2016
G_CONFIG_INJECT '\$scgi_port[[:blank:]]' ' $scgi_port = 0;' /var/www/rutorrent/conf/config.php
# shellcheck disable=SC2016
G_CONFIG_INJECT '\$scgi_host[[:blank:]]' ' $scgi_host = "unix:///mnt/dietpi_userdata/downloads/.session/rpc.socket";' /var/www/rutorrent/conf/config.php
G_CONFIG_INJECT '"curl"[[:blank:]]' ' "curl" => "/usr/bin/curl",' /var/www/rutorrent/conf/config.php
# Session dir
G_EXEC mkdir -p /mnt/dietpi_userdata/downloads/.session
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/rtorrent rtorrent
# Service
cat << _EOF_ > /etc/systemd/system/rtorrent.service
[Unit]
Description=rTorrent (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=rtorrent
ExecStart=$(command -v rtorrent)
[Install]
WantedBy=multi-user.target
_EOF_
# rTorrent config: Do not overwrite if existent
# - Example: https://github.com/rakshasa/rtorrent/blob/master/doc/rtorrent.rc
# - Deprecated commands:
# https://github.com/rakshasa/rtorrent/wiki/rTorrent-0.9-Comprehensive-Command-list-(WIP)
# https://github.com/rakshasa/rtorrent/blob/master/doc/scripts/update_commands_0.9.sed
G_EXEC mkdir -p /mnt/dietpi_userdata/rtorrent
if [[ -f '/mnt/dietpi_userdata/rtorrent/.rtorrent.rc' ]]
then
# In case dist-upgraded systems, assure that daemon mode is enabled
G_CONFIG_INJECT 'system.daemon.set[[:blank:]=]' 'system.daemon.set = true' /mnt/dietpi_userdata/rtorrent/.rtorrent.rc
# Reinstall: Assure that rpi.socket is used, else ruTorrent connection would fail
G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*network.scgi.open_port[[:blank:]=]/d' /mnt/dietpi_userdata/rtorrent/.rtorrent.rc
G_CONFIG_INJECT 'network.scgi.open_local[[:blank:]=]' 'network.scgi.open_local = /mnt/dietpi_userdata/downloads/.session/rpc.socket' /mnt/dietpi_userdata/rtorrent/.rtorrent.rc
G_CONFIG_INJECT 'execute.nothrow[[:blank:]]*=[[:blank:]]*chmod,666,/mnt/dietpi_userdata/downloads/.session/rpc.socket' 'execute.nothrow = chmod,666,/mnt/dietpi_userdata/downloads/.session/rpc.socket' /mnt/dietpi_userdata/rtorrent/.rtorrent.rc 'network.scgi.open_local[[:blank:]=]'
else
cat << _EOF_ > /mnt/dietpi_userdata/rtorrent/.rtorrent.rc
### Miscellaneous settings
system.daemon.set = true
# Default download dir
directory.default.set = /mnt/dietpi_userdata/downloads
# Session dir
session.path.set = /mnt/dietpi_userdata/downloads/.session
# Save session every 5 minutes
schedule2 = session_save, 240, 300, ((session.save))
# Close torrents on low diskspace, check every minute
schedule2 = monitor_diskspace, 15, 60, ((close_low_diskspace, 1000M))
system.umask.set = 002
# Max memory mapping size, not max physical RAM usage!
pieces.memory.max.set = ${RAM_PHYS}M
pieces.hash.on_completion.set = no
### Connection settings
# Incoming connection ports
network.port_open.set = yes
network.port_random.set = no
network.port_range.set = 49164-49164
# SCGI connection, e.g. for ruTorrent web UI
network.scgi.open_local = /mnt/dietpi_userdata/downloads/.session/rpc.socket
execute.nothrow = chmod,666,/mnt/dietpi_userdata/downloads/.session/rpc.socket
### Network limits
network.http.max_open.set = $(Optimise_BitTorrent 2)
network.max_open_files.set = $(( $(Optimise_BitTorrent 2) * 2 ))
network.max_open_sockets.set = $(Optimise_BitTorrent 2)
# Global max download/upload rate in KiB, "0" for unlimited
throttle.global_down.max_rate.set_kb = 0
throttle.global_up.max_rate.set_kb = 0
### Peer settings
# Max downloads/uploads accross all torrents
throttle.max_downloads.global.set = $(Optimise_BitTorrent 2)
throttle.max_uploads.global.set = $(Optimise_BitTorrent 2)
# Max downloads/uploads per torrent
throttle.max_downloads.set = $(( $(Optimise_BitTorrent 2) / 2 ))
throttle.max_uploads.set = $(( $(Optimise_BitTorrent 2) / 2 ))
# Min/Max connected peers
throttle.min_peers.normal.set = $(( $(Optimise_BitTorrent 2) - 1))
throttle.max_peers.normal.set = $(Optimise_BitTorrent 2)
throttle.min_peers.seed.set = -1
throttle.max_peers.seed.set = -1
trackers.numwant.set = $(Optimise_BitTorrent 2)
# Public tracker support
trackers.use_udp.set = yes
dht.mode.set = on
#dht.port.set = 6881
protocol.pex.set = yes
protocol.encryption.set = allow_incoming,try_outgoing,enable_retry
_EOF_
fi
# Permissions
G_EXEC chown -R rtorrent:root /mnt/dietpi_userdata/rtorrent /mnt/dietpi_userdata/downloads/.session
# - ruTorrent: https://github.com/Novik/ruTorrent/wiki/Config
G_EXEC chown -R www-data:root /var/www/rutorrent/share
G_EXEC chown root:root /var/www/rutorrent/share/.htaccess
fi
if To_Install 132 aria2 # Aria2
then
G_AGI aria2
# Web UI: Settings are stored client-wise, web UI files are never written by webserver. Thus root:root 022 permissions existing dir removal on reinstall can be done.
Download_Install 'https://github.com/ziahamza/webui-aria2/archive/master.tar.gz'
[[ -d '/var/www/aria2' ]] && G_EXEC rm -R /var/www/aria2
G_EXEC mv webui-aria2-master /var/www/aria2
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/aria2 aria2
# Config
G_EXEC mkdir -p /mnt/dietpi_userdata/aria2
[[ -f '/mnt/dietpi_userdata/aria2/aria2.conf' ]] || cat << _EOF_ > /mnt/dietpi_userdata/aria2/aria2.conf || exit 1
# DietPi default aria2c options served as aria2.conf file via --conf-path
# https://aria2.github.io/manual/en/html/aria2c.html#options
# Logging
console-log-level=notice
# Session
always-resume=true
continue=true
input-file=/mnt/dietpi_userdata/downloads/aria2.session
save-session=/mnt/dietpi_userdata/downloads/aria2.session
save-session-interval=60
# Connection
listen-port=6881-6999
ftp-pasv=true
check-certificate=false
# - Count
max-concurrent-downloads=$(Optimise_BitTorrent 1)
split=$(Optimise_BitTorrent 1)
max-connection-per-server=$(Optimise_BitTorrent 1)
bt-max-peers=$(Optimise_BitTorrent 2)
# - Bandwidth
max-overall-upload-limit=0
max-upload-limit=0
max-overall-download-limit=0
max-download-limit=0
# - Retries
max-file-not-found=3
max-tries=5
retry-wait=60
# RPC
enable-rpc=true
rpc-listen-all=true
rpc-listen-port=6800
rpc-secret=$GLOBAL_PW
rpc-allow-origin-all=true
pause=false
# Store
dir=/mnt/dietpi_userdata/downloads
allow-overwrite=false
auto-file-renaming=false
file-allocation=none
check-integrity=true
# Seeding
seed-ratio=0.1
seed-time=0
_EOF_
# Pre-create input file
local fp_input=$(sed -n '/^[[:blank:]]*input-file=/{s/^[^=]*=//p;q}' /mnt/dietpi_userdata/aria2/aria2.conf)
[[ ${fp_input//\"} && ! -f $fp_input ]] && > "$fp_input"
# Permissions
G_EXEC chown -R aria2:0 /mnt/dietpi_userdata/aria2 "$fp_input"
G_EXEC chmod 0600 /mnt/dietpi_userdata/aria2/aria2.conf
unset -v fp_input
# Service
cat << _EOF_ > /etc/systemd/system/aria2.service
[Unit]
Description=Aria2 (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=aria2
UMask=002
ExecStart=$(command -v aria2c) --conf-path=/mnt/dietpi_userdata/aria2/aria2.conf
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 116 medusa # Medusa
then
aDEPS=('mediainfo' 'python3')
# Reinstall: Skip download and install, advice to use internal updater from web UI
if [[ -d '/mnt/dietpi_userdata/medusa' ]]
then
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/mnt/dietpi_userdata/medusa\" already exists. Download and install steps will be skipped.
- If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
- If you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\"."
G_AGI "${aDEPS[@]}"
aDEPS=()
else
Download_Install 'https://github.com/pymedusa/Medusa/archive/master.tar.gz'
G_EXEC mv Medusa-master /mnt/dietpi_userdata/medusa
fi
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/medusa medusa
# Service: https://github.com/pymedusa/Medusa/blob/master/runscripts/init.systemd
cat << '_EOF_' > /etc/systemd/system/medusa.service || exit 1
[Unit]
Description=Medusa (DietPi)
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=Medusa
User=medusa
UMask=002
ExecStart=/usr/bin/python3 /mnt/dietpi_userdata/medusa/start.py -q --nolaunch --datadir=/mnt/dietpi_userdata/medusa
TimeoutStopSec=25
KillMode=process
Restart=on-failure
# Hardening
ProtectSystem=strict
ProtectHome=1
PrivateTmp=1
PrivateDevices=1
ProtectKernelTunables=1
ProtectControlGroups=1
ReadWritePaths=-/mnt /media -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chown -R medusa:0 /mnt/dietpi_userdata/medusa
fi
if To_Install 50 syncthing # Syncthing
then
# Reinstall: Skip download and install, advice to use internal updater from web UI
if [[ -d '/opt/syncthing' ]]
then
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/opt/syncthing\" already exists. Download and install steps will be skipped.
- If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
- If you need to reinstall (e.g. broken instance), please manually remove the install dir and rerun \"dietpi-software (re)install $software_id\"."
else
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='amd64';;
11) local arch='riscv64';;
*) local arch='arm';;
esac
local fallback_url="https://github.com/syncthing/syncthing/releases/download/v2.0.12/syncthing-linux-$arch-v2.0.12.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/syncthing/syncthing/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/syncthing-linux-$arch-[^\"\/]*\.tar\.gz\"$/{print \$4}")"
G_EXEC mv syncthing-* /opt/syncthing
fi
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/syncthing syncthing
# Permit self-updates
G_EXEC chown -R syncthing /opt/syncthing
# Generate and edit config only on fresh installs
if [[ ! -f '/mnt/dietpi_userdata/syncthing/config.xml' ]]
then
# Failsafe: Assure that "syncthing" (its "dietpi" group) has permission to create the /mnt/dietpi_userdata/syncthing directory
G_EXEC chgrp dietpi /mnt/dietpi_userdata
G_EXEC chmod 'g+rwx' /mnt/dietpi_userdata
# Generate config
G_EXEC_OUTPUT=1 G_EXEC runuser -u syncthing -- /opt/syncthing/syncthing generate --home=/mnt/dietpi_userdata/syncthing
# Allow remote access: https://docs.syncthing.net/users/faq.html#how-do-i-access-the-web-gui-from-another-computer
G_EXEC sed --follow-symlinks -i '\|:8384</address>|c\ <address>0.0.0.0:8384</address>' /mnt/dietpi_userdata/syncthing/config.xml
# Set default data directory
G_EXEC mkdir -p /mnt/dietpi_userdata/syncthing_data
G_EXEC chown -R syncthing: /mnt/dietpi_userdata/syncthing_data
G_EXEC chmod 0775 /mnt/dietpi_userdata/syncthing_data
if grep -q '<folder id="default"' /mnt/dietpi_userdata/syncthing/config.xml
then
G_EXEC sed --follow-symlinks -i '\|<folder id="default"|s|label="[^"]*"|label="Syncthing Data"|' /mnt/dietpi_userdata/syncthing/config.xml
G_EXEC sed --follow-symlinks -i '\|<folder id="default"|s|path="[^"]*"|path="/mnt/dietpi_userdata/syncthing_data"|' /mnt/dietpi_userdata/syncthing/config.xml
else
G_CONFIG_INJECT '<folder id="default"' ' <folder id="default" label="Syncthing Data" path="/mnt/dietpi_userdata/syncthing_data"></folder>' /mnt/dietpi_userdata/syncthing/config.xml '^<configuration'
fi
fi
# Service: https://github.com/syncthing/syncthing/blob/main/etc/linux-systemd/system/syncthing@.service
cat << '_EOF_' > /etc/systemd/system/syncthing.service
[Unit]
Description=Syncthing (DietPi)
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
User=syncthing
UMask=002
ExecStart=/opt/syncthing/syncthing --no-browser --no-restart --log-level=WARN --home=/mnt/dietpi_userdata/syncthing
Restart=on-failure
RestartSec=1
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
# Hardening
ProtectSystem=full
PrivateTmp=true
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
_EOF_
# Increase fs watcher limit: https://docs.syncthing.net/users/faq.html#inotify-limits
# - 8192 until Linux 5.11, 7x RAM size in MiB since Linux 5.11, up to 1048576
# Raise UDP buffer sizes: https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Sizes
if (( $G_HW_MODEL == 75 ))
then
(( $(sysctl -n fs.inotify.max_user_watches) >= 204800 && $(sysctl -n net.core.rmem_max) >= 7500000 && $(sysctl -n net.core.wmem_max) >= 7500000 )) || G_WHIP_MSG '[WARNING] Filesystem watcher and UDP buffer size limits might be too low
\nSyncthing uses filesystem (inotify) watchers to detect changes in your folders. Depending on the amount of files, the kernel version and physical RAM size of the host system, the current limit might be too low for Syncthing to start all watchers.
\nAdditionally, for HTTP3/QUIC connections, the bandwidth can be limited by UDP buffer sizes.
\nBoth limits can only be raised on the host of this container. E.g. add the following lines to a new sysctl config file like /etc/sysctl.d/dietpi-syncthing.conf:
fs.inotify.max_user_watches=204800
net.core.rmem_max=7500000
net.core.wmem_max=7500000
\nApply them without reboot like this:
sudo sysctl -p /etc/sysctl.d/dietpi-syncthing.confs
\nMore info:
- https://docs.syncthing.net/users/faq.html#inotify-limits
- https://github.com/quic-go/quic-go/wiki/UDP-Buffer-Size'
else
G_EXEC eval 'echo -e '\''fs.inotify.max_user_watches=204800\nnet.core.rmem_max=7500000\nnet.core.wmem_max=7500000'\'' > /etc/sysctl.d/dietpi-syncthing.conf'
local dryrun=()
[[ ! -w '/proc/sys/net/core/rmem_max' ]] && systemd-detect-virt -cq && dryrun=('--dry-run')
G_EXEC sysctl -p "${dryrun[@]}" /etc/sysctl.d/dietpi-syncthing.conf
fi
fi
if To_Install 113 # Chromium
then
# libpam-systemd: Required for non-root users to start Chromium from console via "startx"
if (( $G_HW_MODEL > 9 ))
then
G_AGI chromium libpam-systemd
Create_Desktop_Shortcut chromium
# RPi: Use RPi repo package including non-free FFmpeg codecs integration
else
# From Bookworm on, "chromium-browser" is a dummy package only
if (( $G_DISTRO < 7 ))
then
G_AGI chromium-browser chromium-codecs-ffmpeg-extra libpam-systemd
Create_Desktop_Shortcut chromium-browser
else
G_AGI chromium libpam-systemd
Create_Desktop_Shortcut chromium
fi
# Enable KMS
grep -Eq '^[[:blank:]]*dtoverlay=vc4-f?kms-v3d' /boot/config.txt || /boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-kms-v3d
# Enable hardware codecs
/boot/dietpi/func/dietpi-set_hardware rpi-codec 1
fi
# Flags: Allow root (disable sandbox) and minimise CPU usage: https://peter.sh/experiments/chromium-command-line-switches/
G_EXEC eval "echo 'export CHROMIUM_FLAGS=\"\$CHROMIUM_FLAGS --no-sandbox --test-type --disable-smooth-scrolling --disable-low-res-tiling --enable-low-end-device-mode --num-raster-threads=$G_HW_CPU_CORES --disable-composited-antialiasing\"' > /etc/chromium.d/dietpi"
# Autostart script for kiosk mode, based on @AYapejian: https://github.com/MichaIng/DietPi/issues/1737#issue-318697621
G_EXEC mkdir -p /var/lib/dietpi/dietpi-software/installed
cat << '_EOF_' > /var/lib/dietpi/dietpi-software/installed/chromium-autostart.sh
#!/bin/dash
# Autostart script for kiosk mode, based on @AYapejian: https://github.com/MichaIng/DietPi/issues/1737#issue-318697621
# Resolution to use for kiosk mode, should ideally match current system resolution
RES_X=$(sed -n '/^[[:blank:]]*SOFTWARE_CHROMIUM_RES_X=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
RES_Y=$(sed -n '/^[[:blank:]]*SOFTWARE_CHROMIUM_RES_Y=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
# Command line switches: https://peter.sh/experiments/chromium-command-line-switches/
# - Review and add custom flags in: /etc/chromium.d
CHROMIUM_OPTS="--kiosk --window-size=${RES_X:-1280},${RES_Y:-720} --window-position=0,0"
# If you want tablet mode, uncomment the next line.
#CHROMIUM_OPTS="$CHROMIUM_OPTS --force-tablet-mode --tablet-ui"
# Home page
URL=$(sed -n '/^[[:blank:]]*SOFTWARE_CHROMIUM_AUTOSTART_URL=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
# RPi or Debian Chromium package
FP_CHROMIUM=$(command -v chromium-browser)
[ "$FP_CHROMIUM" ] || FP_CHROMIUM=$(command -v chromium)
# Use "startx" as non-root user to get required permissions via systemd-logind
STARTX='xinit'
[ "$USER" = 'root' ] || STARTX='startx'
exec "$STARTX" "$FP_CHROMIUM" $CHROMIUM_OPTS "${URL:-https://dietpi.com/}"
_EOF_
G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/chromium-autostart.sh
fi
if To_Install 136 motioneye # motionEye
then
# Dependencies
Python_Deps -i pillow pycurl
# RPi: Enable camera module
(( $G_HW_MODEL > 9 )) || /boot/dietpi/func/dietpi-set_hardware rpi-camera 1
# motionEye
G_EXEC_OUTPUT=1 G_EXEC pip3 install -U motioneye
G_EXEC_OUTPUT=1 G_EXEC motioneye_init --skip-apt-update
G_EXEC systemctl stop motioneye
# Prevent the conflicting motion daemon from starting
G_EXEC systemctl --no-reload disable --now motion
G_EXEC systemctl --no-reload mask motion
# Data directory
G_EXEC mkdir -p /mnt/dietpi_userdata/motioneye
G_EXEC chown -R motion /mnt/dietpi_userdata/motioneye
G_CONFIG_INJECT 'media_path' 'media_path /mnt/dietpi_userdata/motioneye' /etc/motioneye/motioneye.conf
fi
if To_Install 137 mjpg-streamer # mjpg-streamer
then
# Build dependencies
aDEPS=('make' 'cmake' 'gcc' 'libc6-dev' 'libjpeg62-turbo-dev')
# - RPi Bullseye: Add RPi Camera support by default
(( $G_HW_MODEL > 9 || $G_DISTRO > 6 )) || aDEPS+=('libraspberrypi-dev')
# Download sources
Download_Install 'https://github.com/jacksonliam/mjpg-streamer/archive/master.tar.gz'
# Compile
G_EXEC cd mjpg-streamer-master/mjpg-streamer-experimental
G_EXEC_OUTPUT=1 G_EXEC make CFLAGS='-g0 -O3' -j "$(nproc)"
G_EXEC strip --remove-section=.comment --remove-section=.note _build/mjpg_streamer
# Move all plugin libraries into the executable's directory to avoid the need for LD_LIBRARY_PATH
G_EXEC mv _build/plugins/*/*.so _build/
# Remove all left build files
G_EXEC rm -R _build/{{,C,c}[Mm]ake*,plugins}
# Install to system, in case remove previous installs
[[ -d '/opt/mjpg-streamer' ]] && G_EXEC rm -R /opt/mjpg-streamer
G_EXEC mv _build /opt/mjpg-streamer
# Cleanup
G_EXEC cd "$G_WORKING_DIR"
G_EXEC rm -R mjpg-streamer-master
# User
Create_User -g video -d /opt/mjpg-streamer mjpg-streamer
# Service
# - RPi Bullseye: If the RPi legacy camera stack is enabled already, use it by default.
local input='input_uvc.so -d /dev/video0'
[[ $G_HW_MODEL -gt 9 || $G_DISTRO -gt 6 || -f '/etc/modprobe.d/dietpi-disable_rpi_camera.conf' ]] || input='input_raspicam.so'
cat << _EOF_ > /etc/systemd/system/mjpg-streamer.service
[Unit]
Description=mjpg-streamer (DietPi)
Documentation=https://github.com/jacksonliam/mjpg-streamer/tree/master/mjpg-streamer-experimental
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=mjpg-streamer
WorkingDirectory=/opt/mjpg-streamer
ExecStart=/opt/mjpg-streamer/mjpg_streamer -i '$input' -o 'output_http.so -p 8082 -n'
# Hardening
ProtectSystem=strict
PrivateTmp=true
ProtectHome=true
ProtectKernelTunables=true
ProtectControlGroups=true
[Install]
WantedBy=multi-user.target
_EOF_
# OctoPrint: Configure it to use mjpg-streamer if installed
if [[ ${aSOFTWARE_INSTALL_STATE[153]} == 2 && -f '/mnt/dietpi_userdata/octoprint/.octoprint/config.yaml' ]]
then
G_DIETPI-NOTIFY 2 'Configuring OctoPrint to use mjpg-streamer for webcam support'
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.stream "http://$(G_GET_NET ip):8082/?action=stream"
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.snapshot 'http://127.0.0.1:8082/?action=snapshot'
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.ffmpeg "$(command -v ffmpeg)"
fi
fi
if To_Install 138 virtualhere # VirtualHere
then
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='x86_64';;
*) local arch='arm';;
esac
Download_Install "https://virtualhere.com/sites/default/files/usbserver/vhusbd$arch" /opt/virtualhere/vhusbd
G_EXEC chmod +x /opt/virtualhere/vhusbd
# Config
if [[ ! -f '/opt/virtualhere/config.ini' ]]
then
if [[ -f '/etc/vhusbd/config.ini' ]]
then
G_EXEC mv /etc/vhusbd/config.ini /opt/virtualhere/config.ini
else
# shellcheck disable=SC2016
echo 'ServerName=$HOSTNAME$' > /opt/virtualhere/config.ini
fi
fi
# Service
cat << '_EOF_' > /etc/systemd/system/virtualhere.service
[Unit]
Description=VirtualHere (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
ExecStart=/opt/virtualhere/vhusbd
[Install]
WantedBy=multi-user.target
_EOF_
# Pre-v8.4
[[ -d '/etc/vhusbd' ]] && G_EXEC rm -R /etc/vhusbd
[[ -f '/var/log/virtualhere.log' ]] && G_EXEC rm /var/log/virtualhere.log
fi
if To_Install 139 sabnzbd # SABnzbd: https://sabnzbd.org/wiki/installation/install-off-modules
then
# APT deps
aDEPS=('par2')
(( $G_DISTRO > 7 )) && aDEPS+=('7zip') || aDEPS+=('p7zip-full')
Python_Deps cffi cryptography sabctools ujson
Download_Install 'https://github.com/sabnzbd/sabnzbd/archive/master.tar.gz'
# Reinstall: Remove old install dir
if [[ -d '/etc/sabnzbd' ]]
then
# Preserve old config file
[[ -f '/etc/sabnzbd/sabnzbd.ini' ]] && G_EXEC mv /etc/sabnzbd/sabnzbd.ini sabnzbd-master/sabnzbd.ini
G_EXEC rm -R /etc/sabnzbd
fi
# Install
G_EXEC mv sabnzbd-master /etc/sabnzbd
# Python deps
G_EXEC cd /etc/sabnzbd
G_EXEC_OUTPUT=1 G_EXEC pip3 install -Ur requirements.txt
# User
Create_User -g dietpi -d /etc/sabnzbd sabnzbd
# Permissions
G_EXEC chown -R sabnzbd:root .
# Service: https://github.com/sabnzbd/sabnzbd/blob/master/linux/sabnzbd%40.service
# - Options: https://sabnzbd.org/wiki/advanced/command-line-parameters
# "-OO": Optimise code and remove doc lines (default shebang of SABnzbd.py, but we run python3 explicitly to avoid version conflicts)
# "-d": Run in daemon mode without terminal and browser start (requires "-f /path/to/config.ini")
# NB: In systemd unit leads to unreliable long taking webserver start. A new process is forked which allows web access, but sometimes after very long time, sometimes never: https://github.com/sabnzbd/sabnzbd/issues/1283
# "-n": Do no start browser with daemon
cat << '_EOF_' > /etc/systemd/system/sabnzbd.service
[Unit]
Description=SABnzbd (DietPi)
Documentation=https://sabnzbd.org/wiki/
Wants=network-online.target
After=network-online.target remote-fs.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=SABnzbd
User=sabnzbd
ExecStart=/usr/bin/python3 -OO /etc/sabnzbd/SABnzbd.py -f /etc/sabnzbd/sabnzbd.ini -n --disable-file-log
Restart=on-failure
[Install]
WantedBy=multi-user.target
_EOF_
# Config
# - Touch only if it does not yet exist, assume reinstall otherwise and preserve custom changes
# - API keys and initial config are only generated during 1st run
# - We need to launch program, then apply our config tweaks, else, wizard setup in web interface simply loops without API keys.
if [[ ! -f '/etc/sabnzbd/sabnzbd.ini' ]] && CREATE_CONFIG_CONTENT='[misc]' Create_Config /etc/sabnzbd/sabnzbd.ini sabnzbd
then
G_CONFIG_INJECT 'log_level[[:blank:]]+=' 'log_level = 0' /etc/sabnzbd/sabnzbd.ini # Warnings and errors only
G_CONFIG_INJECT 'auto_browser[[:blank:]]+=' 'auto_browser = 0' /etc/sabnzbd/sabnzbd.ini
G_CONFIG_INJECT 'host[[:blank:]]+=' 'host = 0.0.0.0' /etc/sabnzbd/sabnzbd.ini
G_CONFIG_INJECT 'admin_dir[[:blank:]]+=' 'admin_dir = /mnt/dietpi_userdata/downloads/sabnzbd_admin' /etc/sabnzbd/sabnzbd.ini
G_CONFIG_INJECT 'download_dir[[:blank:]]+=' 'download_dir = /mnt/dietpi_userdata/downloads/incomplete' /etc/sabnzbd/sabnzbd.ini
G_CONFIG_INJECT 'complete_dir[[:blank:]]+=' 'complete_dir = /mnt/dietpi_userdata/downloads/complete' /etc/sabnzbd/sabnzbd.ini
G_CONFIG_INJECT 'nzb_backup_dir[[:blank:]]+=' 'nzb_backup_dir = /mnt/dietpi_userdata/downloads/sabnzbd_nzb_backup' /etc/sabnzbd/sabnzbd.ini
G_CONFIG_INJECT 'permissions[[:blank:]]+=' 'permissions = 0775' /etc/sabnzbd/sabnzbd.ini
G_CONFIG_INJECT 'refresh_rate[[:blank:]]+=' 'refresh_rate = 2' /etc/sabnzbd/sabnzbd.ini
[[ -d '/mnt/dietpi_userdata/downloads/sabnzbd_admin' ]] && G_EXEC chown -R sabnzbd:root /mnt/dietpi_userdata/downloads/sabnzbd_admin
[[ -d '/mnt/dietpi_userdata/downloads/sabnzbd_nzb_backup' ]] && G_EXEC chown -R sabnzbd:root /mnt/dietpi_userdata/downloads/sabnzbd_nzb_backup
[[ -d '/mnt/dietpi_userdata/downloads/incomplete' ]] && G_EXEC chown -R sabnzbd:dietpi /mnt/dietpi_userdata/downloads/incomplete
[[ -d '/mnt/dietpi_userdata/downloads/complete' ]] && G_EXEC chown -R sabnzbd:dietpi /mnt/dietpi_userdata/downloads/complete
fi
# Install language packs: https://github.com/MichaIng/DietPi/issues/1917#issue-340631943
G_EXEC python3 -OO tools/make_mo.py
G_EXEC chown -R sabnzbd:root .
G_EXEC cd "$G_WORKING_DIR"
fi
if To_Install 183 vaultwarden # vaultwarden
then
G_AGI vaultwarden
G_EXEC systemctl stop vaultwarden
fi
if To_Install 193 k3s # K3s
then
# APT deps
local disable_apparmor=1
systemctl -q is-active apparmor && disable_apparmor=0
G_AGI apparmor iptables
(( $disable_apparmor )) && G_EXEC systemctl --no-reload disable --now apparmor
Configure_iptables
# Fetch config file if it exists
if [[ -f '/boot/dietpi-k3s.yaml' && ! -f '/etc/rancher/k3s/config.yaml' ]]
then
G_EXEC mkdir -p '/etc/rancher/k3s'
G_EXEC cp '/boot/dietpi-k3s.yaml' '/etc/rancher/k3s/config.yaml'
fi
# Install
G_EXEC curl -sSfL 'https://get.k3s.io/' -o install.sh
G_EXEC chmod +x install.sh
export INSTALL_K3S_SKIP_ENABLE=true
export INSTALL_K3S_EXEC=$(sed -n '/^[[:blank:]]*SOFTWARE_K3S_EXEC=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
if G_EXEC_NOEXIT=1 G_EXEC_OUTPUT=1 G_EXEC ./install.sh
then
# Do not enter into a server restart loop on failure
G_EXEC mkdir -p /etc/systemd/system/k3s.service.d
G_EXEC eval 'echo -e '\''[Service]\nRestart=on-success'\'' > /etc/systemd/system/k3s.service.d/dietpi.conf'
Enable_memory_cgroup
else
aSOFTWARE_INSTALL_STATE[$software_id]=-1
fi
G_EXEC rm install.sh
fi
if To_Install 142 # MicroK8s
then
# MicroK8s itself is a snap, hence snapd is needed
G_AGI snapd
# Update snapd itself, needed on older Debian to install core20 snap. Both are pulled by the MicroK8s snap anyway, but while older snapd runs, installing MicroK8s fails at core20 before snapd is updated.
if snap list snapd &> /dev/null
then
G_EXEC_OUTPUT=1 G_EXEC snap refresh snapd
else
G_EXEC_OUTPUT=1 G_EXEC snap install snapd
fi
# Install or update microk8s snap
if snap list microk8s &> /dev/null
then
G_EXEC_OUTPUT=1 G_EXEC snap refresh microk8s
else
G_EXEC_OUTPUT=1 G_EXEC snap install microk8s --classic
fi
Enable_memory_cgroup
fi
if To_Install 143 koel # Koel
then
# APT dependencies: https://docs.koel.dev/guide/getting-started > https://laravel.com/docs/11.x/deployment#server-requirements / https://laravel.com/docs/10.x/deployment#server-requirements / https://laravel.com/docs/8.x/deployment#server-requirements
# - Skip JSON module from PHP 8.0 on, where it is embedded into core
# - pdo_sqlite module required for "artisan koel:init" < "artisan db:seed", despite MySQL database engine used
aDEPS=("php$PHP_VERSION-bcmath" "php$PHP_VERSION-mbstring" "php$PHP_VERSION-sqlite3" "php$PHP_VERSION-xml")
# Grab latest supported release: Koel v6 requires PHP 8.0 or higher for Laravel 9
if (( ${PHP_VERSION::1} > 7 ))
then
aDEPS+=("php$PHP_VERSION-curl")
local fallback_url='https://github.com/koel/koel/releases/download/v8.1.0/koel-v8.1.0.tar.gz' aphp_deps=('curl' 'dom')
Download_Install "$(curl -sSfL 'https://api.github.com/repos/koel/koel/releases/latest' | mawk -F\" '/^ *"browser_download_url": ".*\/koel-[^"\/]*\.tar\.gz"$/{print $4}')"
else
aDEPS+=("php$PHP_VERSION-json")
local aphp_deps=('json')
Download_Install 'https://github.com/koel/koel/releases/download/v5.1.14/koel-v5.1.14.tar.gz'
fi
# Reinstall: Clear previous install, but keep existing config file
if [[ -d '/mnt/dietpi_userdata/koel' ]]
then
[[ -f '/mnt/dietpi_userdata/koel/.env' ]] && G_EXEC mv /mnt/dietpi_userdata/koel/.env koel/
G_EXEC rm -R /mnt/dietpi_userdata/koel
fi
[[ -f 'koel/.env' ]] || G_EXEC cp koel/.env{.example,}
G_EXEC mv koel /mnt/dietpi_userdata/koel
# Enable required PHP modules
G_EXEC phpenmod bcmath ctype fileinfo mbstring pdo_mysql pdo_sqlite tokenizer xml "${aphp_deps[@]}"
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/koel koel
# Permissions
G_EXEC cd /mnt/dietpi_userdata/koel
G_EXEC chmod 0600 .env
G_EXEC chown -R koel:root .
# Create database and apply to config file if it does not exist yet
if [[ ! -d '/mnt/dietpi_userdata/mysql/koel' ]]
then
# Create random alphanumeric 30 characters password
local password=$(tr -dc '[:alnum:]' < /dev/random | head -c30)
/boot/dietpi/func/create_mysql_db koel koel "$password"
GCI_PASSWORD=1 G_CONFIG_INJECT 'DB_PASSWORD=' "DB_PASSWORD=$password" .env
unset -v password
G_CONFIG_INJECT 'DB_CONNECTION=' 'DB_CONNECTION=mysql' .env
G_CONFIG_INJECT 'DB_HOST=' 'DB_HOST=localhost' .env
G_CONFIG_INJECT 'DB_DATABASE=' 'DB_DATABASE=koel' .env
G_CONFIG_INJECT 'DB_USERNAME=' 'DB_USERNAME=koel' .env
G_CONFIG_INJECT 'MEDIA_PATH=' 'MEDIA_PATH=/mnt/dietpi_userdata/Music' .env
G_CONFIG_INJECT 'FFMPEG_PATH=' "FFMPEG_PATH=$(command -v ffmpeg)" .env
# Else assure database server runs
else
G_EXEC systemctl restart mariadb
fi
Download_Test_Media
# Init
G_EXEC_OUTPUT=1 G_EXEC runuser -u koel -- "php$PHP_VERSION" artisan koel:init -n --no-assets
G_EXEC_OUTPUT=1 G_EXEC runuser -u koel -- "php$PHP_VERSION" artisan koel:sync
G_EXEC cd "$G_WORKING_DIR"
# Service: Run on port 8003 by default to avoid conflict with Icecast
cat << _EOF_ > /etc/systemd/system/koel.service
[Unit]
Description=Koel (DietPi)
Documentation=https://docs.koel.dev/
Wants=network-online.target
After=network-online.target mariadb.service
[Service]
SyslogIdentifier=Koel
User=koel
WorkingDirectory=/mnt/dietpi_userdata/koel
ExecStart=$(command -v "php$PHP_VERSION") /mnt/dietpi_userdata/koel/artisan serve --host 0.0.0.0 --port 8003
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 144 sonarr # Sonarr
then
# Sonarr v4 does not support ARMv6
if (( $G_HW_ARCH == 1 ))
then
# https://sonarr.tv/#downloads-v3-linux-debian
# APT key
G_EXEC eval 'curl -sSf '\''https://keyserver.ubuntu.com/pks/lookup?search=0x2009837CBFFD68F45BC180471F4F90DE2A9B4BF8&op=get'\'' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-sonarr.gpg --yes'
# APT list: Buster is the latest available suite: https://apt.sonarr.tv/debian/dists/
G_EXEC eval 'echo '\''deb https://apt.sonarr.tv/debian buster main'\'' > /etc/apt/sources.list.d/sonarr.list'
G_AGUP
# Pre-configure DEB package
G_EXEC eval "debconf-set-selections <<< 'sonarr sonarr/owning_group string dietpi'"
G_EXEC eval "debconf-set-selections <<< 'sonarr sonarr/config_directory string /mnt/dietpi_userdata/sonarr'"
# Install Sonarr and mediainfo
G_AGI sonarr mediainfo
G_EXEC systemctl stop sonarr
local install_dir='/usr/lib/sonarr'
local exe="$(command -v mono) $install_dir/bin/Sonarr.exe"
else
# Pre-v9.9 cleanup
[[ -f '/etc/apt/sources.list.d/sonarr.list' ]] && G_EXEC rm /etc/apt/sources.list.d/sonarr.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-sonarr.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-sonarr.gpg
if dpkg-query -s 'sonarr' &> /dev/null
then
[[ -d '/mnt/dietpi_userdata/sonarr' ]] && G_EXEC mv /mnt/dietpi_userdata/sonarr{,_bak}
G_AGP sonarr
[[ -d '/mnt/dietpi_userdata/sonarr_bak' ]] && G_EXEC mv /mnt/dietpi_userdata/sonarr{_bak,}
# Offer to uninstall Mono
if (( ${aSOFTWARE_INSTALL_STATE[150]} == 2 ))
then
G_WHIP_BUTTON_OK_TEXT='Yes' G_WHIP_BUTTON_CANCEL_TEXT='No' G_WHIP_DEFAULT_ITEM='Yes'
G_WHIP_YESNO '[ INFO ] Mono is no dependency of Sonarr v4 anymore
\nA migration from Sonarr v3 to Sonarr v4 is done, which does not require Mono anymore. If you do not need Mono for something else, it can hence be uninstalled to free up some space.
\nDo you want to uninstall Mono now? It can be done any time later via "dietpi-software".' && aSOFTWARE_INSTALL_STATE[150]=-1
fi
fi
# APT dependencies
# - .NET: https://github.com/dotnet/docs/blob/main/docs/core/install/linux-debian.md#dependencies
case $G_DISTRO in
6) aDEPS=('libicu67');;
7) aDEPS=('libicu72');;
*) aDEPS=('libicu76');;
esac
# Download
case $G_HW_ARCH in
2) local arch='arm';;
3) local arch='arm64';;
*) local arch='x64';;
esac
local fallback_url="https://github.com/Sonarr/Sonarr/releases/download/v4.0.16.2944/Sonarr.main.4.0.16.2944.linux-$arch.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/Sonarr/Sonarr/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*linux-$arch\.tar\.gz\"$/{print \$4}")"
# Install: Remove previous instance on reinstall
[[ -d '/opt/sonarr' ]] && G_EXEC rm -R /opt/sonarr
G_EXEC mv Sonarr /opt/sonarr
local install_dir='/opt/sonarr'
local exe="$install_dir/Sonarr"
fi
# Pre-v7.1: Remove Sonarr v2 key, PID file and database backups from DietPi-Arr_to_RAM
if [[ -f '/etc/apt/trusted.gpg' && $(apt-key --keyring /etc/apt/trusted.gpg list 'A236C58F409091A18ACA53CBEBFF6B99D9B78493' 2> /dev/null) ]]
then
G_EXEC apt-key --keyring /etc/apt/trusted.gpg del 'A236C58F409091A18ACA53CBEBFF6B99D9B78493'
[[ $(apt-key --keyring /etc/apt/trusted.gpg list 2> /dev/null) ]] || G_EXEC rm /etc/apt/trusted.gpg
fi
[[ -f '/mnt/dietpi_userdata/sonarr/nzbdrone.pid' ]] && G_EXEC rm /mnt/dietpi_userdata/sonarr/nzbdrone.pid
[[ -f '/mnt/dietpi_userdata/sonarr/nzbdrone.db.bak' ]] && G_EXEC rm /mnt/dietpi_userdata/sonarr/nzbdrone.db.bak
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/sonarr
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/sonarr sonarr
# Service: https://github.com/Sonarr/Sonarr/blob/phantom-develop/distribution/debian/sonarr.service
cat << _EOF_ > /etc/systemd/system/sonarr.service
[Unit]
Description=Sonarr (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target transmission-daemon.service qbittorrent.service rtorrent.service nzbget.service deluged.service aria2.service sabnzbd.service
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=Sonarr
User=sonarr
UMask=002
LogsDirectory=sonarr
ExecStart=$exe -nobrowser -data=/mnt/dietpi_userdata/sonarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=-$install_dir -/mnt -/media -/var/log/sonarr -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
# Logs to RAM
G_EXEC rm -Rf /mnt/dietpi_userdata/sonarr/logs*
G_EXEC ln -s /var/log/sonarr /mnt/dietpi_userdata/sonarr/logs
G_EXEC ln -s /var/log/sonarr/logs.db /mnt/dietpi_userdata/sonarr/logs.db
G_EXEC ln -s /var/log/sonarr/logs.db-shm /mnt/dietpi_userdata/sonarr/logs.db-shm
G_EXEC ln -s /var/log/sonarr/logs.db-wal /mnt/dietpi_userdata/sonarr/logs.db-wal
# Permissions
G_EXEC chown -R sonarr:dietpi /mnt/dietpi_userdata/sonarr "$install_dir"
fi
if To_Install 145 radarr # Radarr
then
# APT dependencies
if (( $G_HW_ARCH == 1 ))
then
aDEPS=('mediainfo')
else
# .NET: https://github.com/dotnet/docs/blob/main/docs/core/install/linux-debian.md#dependencies
case $G_DISTRO in
6) aDEPS=('libicu67');;
7) aDEPS=('libicu72');;
*) aDEPS=('libicu76');;
esac
fi
# Download
# - ARMv6: Radarr v4 does not support Mono anymore
local url='https://github.com/Radarr/Radarr/releases/download/v3.2.2.5080/Radarr.master.3.2.2.5080.linux.tar.gz'
if (( $G_HW_ARCH != 1 ))
then
case $G_HW_ARCH in
2) local arch='arm';;
3) local arch='arm64';;
*) local arch='x64';;
esac
url=$(curl -sSfL 'https://api.github.com/repos/Radarr/Radarr/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*linux-core-$arch\.tar\.gz\"$/{print \$4}")
local fallback_url="https://github.com/Radarr/Radarr/releases/download/v6.0.4.10291/Radarr.master.6.0.4.10291.linux-core-$arch.tar.gz"
fi
Download_Install "$url"
# Bullseye: Use system SQLite library: https://wiki.servarr.com/radarr/faq#radarr-wont-start-on-debian-11-or-older-systems-due-to-sqlite-version
(( $G_DISTRO < 7 && $G_HW_ARCH != 1 )) && G_EXEC ln -sf /usr/lib/*/libsqlite3.so.0 Radarr/libe_sqlite3.so
# Install: Remove previous instance on reinstall
[[ -d '/opt/radarr' ]] && G_EXEC rm -R /opt/radarr
G_EXEC mv Radarr /opt/radarr
# Radarr v2: Remove old PID file and database backups from DietPi-Arr_to_RAM
[[ -f '/mnt/dietpi_userdata/radarr/nzbdrone.pid' ]] && G_EXEC rm /mnt/dietpi_userdata/radarr/nzbdrone.pid
[[ -f '/mnt/dietpi_userdata/radarr/nzbdrone.db.bak' ]] && G_EXEC rm /mnt/dietpi_userdata/radarr/nzbdrone.db.bak
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/radarr
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/radarr radarr
# Service: https://github.com/Radarr/Radarr/wiki/Autostart-on-Linux#service-file
cat << '_EOF_' > /etc/systemd/system/radarr.service
[Unit]
Description=Radarr (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target transmission-daemon.service qbittorrent.service rtorrent.service nzbget.service deluged.service aria2.service sabnzbd.service
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=Radarr
User=radarr
UMask=002
LogsDirectory=radarr
ExecStart=/opt/radarr/Radarr -nobrowser -data=/mnt/dietpi_userdata/radarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=-/opt/radarr -/mnt -/media -/var/log/radarr -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
# - ARMv6 devices use Mono
(( $G_HW_ARCH == 1 )) && G_CONFIG_INJECT 'ExecStart=' "ExecStart=$(command -v mono) /opt/radarr/Radarr.exe -nobrowser -data=/mnt/dietpi_userdata/radarr" /etc/systemd/system/radarr.service
# Logs to RAM
G_EXEC rm -Rf /mnt/dietpi_userdata/radarr/logs*
G_EXEC ln -s /var/log/radarr /mnt/dietpi_userdata/radarr/logs
G_EXEC ln -s /var/log/radarr/logs.db /mnt/dietpi_userdata/radarr/logs.db
G_EXEC ln -s /var/log/radarr/logs.db-shm /mnt/dietpi_userdata/radarr/logs.db-shm
G_EXEC ln -s /var/log/radarr/logs.db-wal /mnt/dietpi_userdata/radarr/logs.db-wal
# Permissions
G_EXEC chown -R radarr:dietpi /mnt/dietpi_userdata/radarr /opt/radarr
fi
if To_Install 106 lidarr # Lidarr
then
# APT dependencies
aDEPS=('mediainfo')
# - .NET: https://github.com/dotnet/docs/blob/main/docs/core/install/linux-debian.md#dependencies
if (( $G_HW_ARCH != 1 ))
then
case $G_DISTRO in
6) aDEPS+=('libicu67');;
7) aDEPS+=('libicu72');;
*) aDEPS+=('libicu76');;
esac
fi
# Pre-v7.5 ARMv6: Migrate old install dir
[[ -d '/opt/Lidarr' && ! -d '/opt/lidarr' ]] && G_EXEC mv /opt/{L,l}idarr
# Download
# - ARMv6: Lidarr v1 does not support Mono anymore
local url='https://github.com/Lidarr/Lidarr/releases/download/v0.8.1.2135/Lidarr.master.0.8.1.2135.linux.tar.gz'
if (( $G_HW_ARCH != 1 ))
then
case $G_HW_ARCH in
2) local arch='arm';;
3) local arch='arm64';;
*) local arch='x64';;
esac
url=$(curl -sSfL 'https://api.github.com/repos/Lidarr/Lidarr/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*linux-core-$arch\.tar\.gz\"$/{print \$4}")
local fallback_url="https://github.com/Lidarr/Lidarr/releases/download/v3.1.0.4875/Lidarr.master.3.1.0.4875.linux-core-$arch.tar.gz"
fi
Download_Install "$url"
# Bullseye: Use system SQLite library: https://wiki.servarr.com/lidarr/faq#lidarr-wont-start-on-debian-11-or-older-systems-due-to-sqlite-version
(( $G_DISTRO < 7 && $G_HW_ARCH != 1 )) && G_EXEC ln -sf /usr/lib/*/libsqlite3.so.0 Lidarr/libe_sqlite3.so
# Install: Remove previous instance on reinstall
[[ -d '/opt/lidarr' ]] && G_EXEC rm -R /opt/lidarr
G_EXEC mv Lidarr /opt/lidarr
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/lidarr
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/lidarr lidarr
# Service: https://github.com/lidarr/Lidarr/wiki/Autostart-on-Linux#systemd
cat << _EOF_ > /etc/systemd/system/lidarr.service
[Unit]
Description=Lidarr (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target transmission-daemon.service qbittorrent.service rtorrent.service nzbget.service deluged.service aria2.service sabnzbd.service
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=Lidarr
User=lidarr
UMask=002
LogsDirectory=lidarr
ExecStart=/opt/lidarr/Lidarr -nobrowser -data=/mnt/dietpi_userdata/lidarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=-/opt/lidarr -/mnt -/media -/var/log/lidarr -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
# - ARMv6 devices use Mono
(( $G_HW_ARCH == 1 )) && G_CONFIG_INJECT 'ExecStart=' "ExecStart=$(command -v mono) /opt/lidarr/Lidarr.exe -nobrowser -data=/mnt/dietpi_userdata/lidarr" /etc/systemd/system/lidarr.service
# Logs to RAM
G_EXEC rm -Rf /mnt/dietpi_userdata/lidarr/logs*
G_EXEC ln -s /var/log/lidarr /mnt/dietpi_userdata/lidarr/logs
G_EXEC ln -s /var/log/lidarr/logs.db /mnt/dietpi_userdata/lidarr/logs.db
G_EXEC ln -s /var/log/lidarr/logs.db-shm /mnt/dietpi_userdata/lidarr/logs.db-shm
G_EXEC ln -s /var/log/lidarr/logs.db-wal /mnt/dietpi_userdata/lidarr/logs.db-wal
# Permissions
G_EXEC chown -R lidarr:dietpi /mnt/dietpi_userdata/lidarr /opt/lidarr
fi
if To_Install 180 bazarr # Bazarr: https://wiki.bazarr.media/Getting-Started/Installation/Linux/linux/
then
# APT deps
aDEPS=('unzip')
(( $G_DISTRO > 7 )) && aDEPS+=('7zip') || aDEPS+=('p7zip')
# - ARMv6 does not support unrar-nonfree and Bazarr does not support unrar-free, so we need to use "unar": https://github.com/morpheus65535/bazarr/issues/2172
(( $G_HW_ARCH == 1 )) && aDEPS+=('unar')
Python_Deps lxml numpy pillow
# Download
Download_Install 'https://github.com/morpheus65535/bazarr/releases/latest/download/bazarr.zip' bazarr
# Python deps
# - Allow Pillow to be compiled
G_EXEC sed --follow-symlinks -i 's/ --only-binary=Pillow$//' bazarr/requirements.txt
G_EXEC_OUTPUT=1 G_EXEC pip3 install -Ur bazarr/requirements.txt
# Install: Remove previous instance on reinstall
[[ -d '/opt/bazarr' ]] && G_EXEC rm -R /opt/bazarr
G_EXEC mv bazarr /opt
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/bazarr
# Log to RAM
G_EXEC rm -Rf /mnt/dietpi_userdata/bazarr/log
G_EXEC ln -s /var/log/bazarr /mnt/dietpi_userdata/bazarr/log
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/bazarr bazarr
# Permissions
G_EXEC chown -R bazarr:0 /mnt/dietpi_userdata/bazarr /opt/bazarr
# Service: https://wiki.bazarr.media/Getting-Started/Autostart/Linux/linux/
cat << '_EOF_' > /etc/systemd/system/bazarr.service
[Unit]
Description=Bazarr (DietPi)
Wants=network-online.target
After=network-online.target sonarr.service radarr.service
[Service]
SyslogIdentifier=Bazarr
User=bazarr
UMask=002
LogsDirectory=bazarr
WorkingDirectory=/mnt/dietpi_userdata/bazarr
ExecStart=/usr/bin/python3 /opt/bazarr/bazarr.py -c /mnt/dietpi_userdata/bazarr
KillSignal=SIGINT
TimeoutStopSec=20
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
PrivateTmp=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=-/opt/bazarr -/mnt -/media -/var/log/bazarr -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
# On fresh install, pre-configure for Sonarr and Radarr when found
if (( ${aSOFTWARE_INSTALL_STATE[144]} + ${aSOFTWARE_INSTALL_STATE[145]} > 0 )) && CREATE_CONFIG_CONTENT='^sonarr:' Create_Config /mnt/dietpi_userdata/bazarr/config/config.yaml bazarr
then
# Sonarr
if (( ${aSOFTWARE_INSTALL_STATE[144]} > 0 )) && CREATE_CONFIG_CONTENT='ApiKey' Create_Config /mnt/dietpi_userdata/sonarr/config.xml sonarr
then
local port=$(sed -nE '/<Port>[0-9]+<\/Port>/{s/^.*<Port>([0-9]+)<\/Port>.*$/\1/p;q}' /mnt/dietpi_userdata/sonarr/config.xml)
local apikey=$(sed -nE '/<ApiKey>.+<\/ApiKey>/{s/^.*<ApiKey>(.+)<\/ApiKey>.*$/\1/p;q}' /mnt/dietpi_userdata/sonarr/config.xml)
G_EXEC sed --follow-symlinks -i '/^sonarr:/,/^[^[:blank:]]/s/ ip: .*$/ ip: 127.0.0.1/' /mnt/dietpi_userdata/bazarr/config/config.yaml
G_EXEC sed --follow-symlinks -i "/^sonarr:/,/^[^[:blank:]]/s/ port = .*$/ port: $port/" /mnt/dietpi_userdata/bazarr/config/config.yaml
G_EXEC sed --follow-symlinks -i '/^sonarr:/,/^[^[:blank:]]/s/ base_url: .*$/ base_url: \//' /mnt/dietpi_userdata/bazarr/config/config.yaml
G_EXEC sed --follow-symlinks -i "/^sonarr:/,/^[^[:blank:]]/s/ apikey: .*$/ apikey: $apikey/" /mnt/dietpi_userdata/bazarr/config/config.yaml
G_CONFIG_INJECT 'use_sonarr:' ' use_sonarr: true' /mnt/dietpi_userdata/bazarr/config/config.yaml '^general:'
fi
# Radarr
if (( ${aSOFTWARE_INSTALL_STATE[145]} > 0 )) && CREATE_CONFIG_CONTENT='ApiKey' Create_Config /mnt/dietpi_userdata/radarr/config.xml radarr
then
local port=$(sed -nE '/<Port>[0-9]+<\/Port>/{s/^.*<Port>([0-9]+)<\/Port>.*$/\1/p;q}' /mnt/dietpi_userdata/radarr/config.xml)
local apikey=$(sed -nE '/<ApiKey>.+<\/ApiKey>/{s/^.*<ApiKey>(.+)<\/ApiKey>.*$/\1/p;q}' /mnt/dietpi_userdata/radarr/config.xml)
G_EXEC sed --follow-symlinks -i '/^radarr:/,/^[^[:blank:]]/s/ ip: .*$/ ip: 127.0.0.1/' /mnt/dietpi_userdata/bazarr/config/config.yaml
G_EXEC sed --follow-symlinks -i "/^radarr:/,/^[^[:blank:]]/s/ port: .*$/ port: $port/" /mnt/dietpi_userdata/bazarr/config/config.yaml
G_EXEC sed --follow-symlinks -i '/^radarr:/,/^[^[:blank:]]/s/ base_url: .*$/ base_url: \//' /mnt/dietpi_userdata/bazarr/config/config.yaml
G_EXEC sed --follow-symlinks -i "/^radarr:/,/^[^[:blank:]]/s/ apikey: .*$/ apikey: $apikey/" /mnt/dietpi_userdata/bazarr/config/config.yaml
G_CONFIG_INJECT 'use_radarr:' ' use_radarr: true' /mnt/dietpi_userdata/bazarr/config/config.yaml '^general:'
fi
fi
fi
if To_Install 146 tautulli # Tautulli
then
# Download
local url='https://github.com/Tautulli/Tautulli.git'
G_CHECK_URL "$url"
G_THREAD_START git clone --depth 1 "$url"
G_AGI python3-pkg-resources
G_THREAD_WAIT
# Install: Remove previous instance on reinstall
[[ -d '/opt/tautulli' ]] && G_EXEC rm -R /opt/tautulli
G_EXEC mv Tautulli /opt/tautulli
# User
Create_User -d /mnt/dietpi_userdata/tautulli tautulli
# Directory
G_EXEC mkdir -p /mnt/dietpi_userdata/tautulli
# Service: https://github.com/Tautulli/Tautulli/blob/master/init-scripts/init.systemd
cat << '_EOF_' > /etc/systemd/system/tautulli.service
[Unit]
Description=Tautulli (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
SyslogIdentifier=Tautulli
User=tautulli
ExecStart=/usr/bin/python3 /opt/tautulli/Tautulli.py -q --nolaunch --config /mnt/dietpi_userdata//tautulli/config.ini --datadir /mnt/dietpi_userdata/tautulli
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chown -R tautulli:tautulli /{opt,mnt/dietpi_userdata}/tautulli
fi
if To_Install 147 jackett # Jackett
then
# ARMv6 requires Mono: https://github.com/Jackett/Jackett#installation-on-linux-armv6-or-below
# .NET dependency: https://github.com/dotnet/docs/blob/main/docs/core/install/linux-debian.md#dependencies
if (( $G_HW_ARCH != 1 ))
then
case $G_DISTRO in
6) aDEPS=('libicu67');;
7) aDEPS=('libicu72');;
*) aDEPS=('libicu76');;
esac
fi
# Download
case $G_HW_ARCH in
1) local arch='Mono';;
2) local arch='LinuxARM32';;
3) local arch='LinuxARM64';;
*) local arch='LinuxAMDx64';;
esac
local fallback_url="https://github.com/Jackett/Jackett/releases/download/v0.24.445/Jackett.Binaries.$arch.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/Jackett/Jackett/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/Jackett\.Binaries\.$arch\.tar\.gz\"$/{print \$4}")"
# Move existing configs to unpacked install dir
[[ -d '/opt/jackett/Jackett' ]] && G_EXEC mv /opt/jackett/Jackett Jackett/
[[ -d '/opt/jackett/.mono' ]] && G_EXEC mv /opt/jackett/.mono Jackett/
# Install: Remove previous instance on reinstall
[[ -d '/opt/jackett' ]] && G_EXEC rm -R /opt/jackett
G_EXEC mv Jackett /opt/jackett
# User
Create_User -d /opt/jackett jackett
# Permissions
G_EXEC chown -R jackett:jackett /opt/jackett
# Service: https://github.com/Jackett/Jackett/wiki/systemd-service
# - Wrap execution into shell to work around: https://github.com/Jackett/Jackett/issues/5208
cat << '_EOF_' > /etc/systemd/system/jackett.service
[Unit]
Description=Jackett (DietPi)
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=Jackett
User=jackett
WorkingDirectory=/opt/jackett
Environment=XDG_CONFIG_HOME=/opt/jackett
ExecStart=/bin/dash -c '/opt/jackett/jackett --NoRestart; ec=$?; while pgrep -u jackett JackettUpdater > /dev/null; do sleep 1; done; exit $ec'
Restart=always
RestartSec=5
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
PrivateTmp=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
NoNewPrivileges=true
ReadWritePaths=-/opt/jackett
[Install]
WantedBy=multi-user.target
_EOF_
# - ARMv6 devices use Mono
(( $G_HW_ARCH == 1 )) && G_CONFIG_INJECT 'ExecStart=' "ExecStart=$(command -v mono) /opt/jackett/JackettConsole.exe --NoRestart" /etc/systemd/system/jackett.service
fi
if To_Install 149 nzbget # NZBGet
then
local reinstall=0
[[ -f '/mnt/dietpi_userdata/nzbget/nzbget.conf' ]] && reinstall=1
local fallback_url='https://github.com/nzbgetcom/nzbget/releases/download/v25.4/nzbget-25.4-bin-linux.run'
Download_Install "$(curl -sSfL 'https://api.github.com/repos/nzbgetcom/nzbget/releases/latest' | mawk -F\" '/^ *"browser_download_url": ".*\/nzbget-[^"/]*-bin-linux.run"$/{print $4}')" nzbget.run
G_EXEC mkdir -p /mnt/dietpi_userdata/nzbget
G_EXEC_OUTPUT=1 G_EXEC dash nzbget.run --destdir /mnt/dietpi_userdata/nzbget
G_EXEC rm nzbget.run
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/nzbget nzbget
# Permissions
G_EXEC chmod 0600 /mnt/dietpi_userdata/nzbget/nzbget.conf
G_EXEC chown -R nzbget:root /mnt/dietpi_userdata/nzbget
# Config
if (( ! $reinstall ))
then
G_CONFIG_INJECT 'MainDir=' 'MainDir=/mnt/dietpi_userdata/downloads' /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'DestDir=' 'DestDir=/mnt/dietpi_userdata/downloads/complete' /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'WriteLog=' 'WriteLog=none' /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'ControlUsername=' 'ControlUsername=admin' /mnt/dietpi_userdata/nzbget/nzbget.conf
GCI_PASSWORD=1 G_CONFIG_INJECT 'ControlPassword=' "ControlPassword=$GLOBAL_PW" /mnt/dietpi_userdata/nzbget/nzbget.conf
# Umask: https://github.com/MichaIng/DietPi/issues/1999
G_CONFIG_INJECT 'UMask=' 'UMask=0002' /mnt/dietpi_userdata/nzbget/nzbget.conf
# Optimisations
G_CONFIG_INJECT 'CrcCheck=' 'CrcCheck=no' /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'ParScan=' 'ParScan=limited' /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'ParThreads=' "ParThreads=$G_HW_CPU_CORES" /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'DebugTarget=' 'DebugTarget=none' /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'DetailTarget=' 'DetailTarget=none' /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'CrashTrace=' 'CrashTrace=no' /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'ParBuffer=' "ParBuffer=$(Optimise_BitTorrent 0)" /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'ArticleCache=' "ArticleCache=$(Optimise_BitTorrent 0)" /mnt/dietpi_userdata/nzbget/nzbget.conf
G_CONFIG_INJECT 'WriteBuffer=' "WriteBuffer=$(Optimise_BitTorrent 0)" /mnt/dietpi_userdata/nzbget/nzbget.conf
fi
# Service
cat << '_EOF_' > /etc/systemd/system/nzbget.service
[Unit]
Description=NZBGet (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=nzbget
WorkingDirectory=/mnt/dietpi_userdata/nzbget
ExecStart=/mnt/dietpi_userdata/nzbget/nzbget -so OutputMode=log
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 151 prowlarr # Prowlarr
then
# .NET dependency: https://github.com/dotnet/docs/blob/main/docs/core/install/linux-debian.md#dependencies
case $G_DISTRO in
6) aDEPS=('libicu67');;
7) aDEPS=('libicu72');;
*) aDEPS=('libicu76');;
esac
# Download
case $G_HW_ARCH in
2) local arch='arm';;
3) local arch='arm64';;
*) local arch='x64';;
esac
local fallback_url="https://github.com/Prowlarr/Prowlarr/releases/download/v2.3.0.5236/Prowlarr.master.2.3.0.5236.linux-core-$arch.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/Prowlarr/Prowlarr/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*linux-core-$arch\.tar\.gz\"$/{print \$4}")"
# Bullseye: Use system SQLite library: https://wiki.servarr.com/prowlarr/faq#prowlarr-wont-start-on-debian-11-or-older-systems-due-to-sqlite-version
(( $G_DISTRO < 7 )) && G_EXEC ln -sf /usr/lib/*/libsqlite3.so.0 Prowlarr/libe_sqlite3.so
# Install: Remove previous instance on reinstall
[[ -d '/opt/prowlarr' ]] && G_EXEC rm -R /opt/prowlarr
G_EXEC mv Prowlarr /opt/prowlarr
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/prowlarr
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/prowlarr prowlarr
# Service: https://wiki.servarr.com/prowlarr/installation
cat << '_EOF_' > /etc/systemd/system/prowlarr.service
[Unit]
Description=Prowlarr (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target transmission-daemon.service qbittorrent.service rtorrent.service nzbget.service deluged.service aria2.service sabnzbd.service
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=Prowlarr
User=prowlarr
UMask=002
LogsDirectory=prowlarr
ExecStart=/opt/prowlarr/Prowlarr -nobrowser -data=/mnt/dietpi_userdata/prowlarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=-/opt/prowlarr -/mnt -/media -/var/log/prowlarr -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
# Logs to RAM
G_EXEC rm -Rf /mnt/dietpi_userdata/prowlarr/logs*
G_EXEC ln -s /var/log/prowlarr /mnt/dietpi_userdata/prowlarr/logs
G_EXEC ln -s /var/log/prowlarr/logs.db /mnt/dietpi_userdata/prowlarr/logs.db
G_EXEC ln -s /var/log/prowlarr/logs.db-shm /mnt/dietpi_userdata/prowlarr/logs.db-shm
G_EXEC ln -s /var/log/prowlarr/logs.db-wal /mnt/dietpi_userdata/prowlarr/logs.db-wal
# Permissions
G_EXEC chown -R prowlarr:dietpi /mnt/dietpi_userdata/prowlarr /opt/prowlarr
fi
if To_Install 203 readarr # Readarr
then
# .NET dependency: https://github.com/dotnet/docs/blob/main/docs/core/install/linux-debian.md#dependencies
case $G_DISTRO in
6) aDEPS=('libicu67');;
7) aDEPS=('libicu72');;
*) aDEPS=('libicu76');;
esac
# Download
case $G_HW_ARCH in
2) local arch='arm';;
3) local arch='arm64';;
*) local arch='x64';;
esac
local fallback_url="https://github.com/Readarr/Readarr/releases/download/v0.4.18.2805/Readarr.develop.0.4.18.2805.linux-core-$arch.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/Readarr/Readarr/releases' | mawk -F\" "/^ *\"browser_download_url\": \".*linux-core-$arch\.tar\.gz\"$/{print \$4}" | head -1)"
# Install: Remove previous instance on reinstall
[[ -d '/opt/readarr' ]] && G_EXEC rm -R /opt/readarr
G_EXEC mv Readarr /opt/readarr
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/readarr
# User
Create_User -g dietpi -d /mnt/dietpi_userdata/readarr readarr
# Service: https://wiki.servarr.com/readarr/installation
cat << '_EOF_' > /etc/systemd/system/readarr.service
[Unit]
Description=Readarr (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target transmission-daemon.service qbittorrent.service rtorrent.service nzbget.service deluged.service aria2.service sabnzbd.service
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=Readarr
User=readarr
UMask=002
LogsDirectory=readarr
ExecStart=/opt/readarr/Readarr -nobrowser -data=/mnt/dietpi_userdata/readarr
TimeoutStopSec=20
KillMode=process
Restart=on-failure
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=-/opt/readarr -/mnt -/media -/var/log/readarr -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
# Logs to RAM
G_EXEC rm -Rf /mnt/dietpi_userdata/readarr/logs*
G_EXEC ln -s /var/log/readarr /mnt/dietpi_userdata/readarr/logs
G_EXEC ln -s /var/log/readarr/logs.db /mnt/dietpi_userdata/readarr/logs.db
G_EXEC ln -s /var/log/readarr/logs.db-shm /mnt/dietpi_userdata/readarr/logs.db-shm
G_EXEC ln -s /var/log/readarr/logs.db-wal /mnt/dietpi_userdata/readarr/logs.db-wal
# Permissions
G_EXEC chown -R readarr:dietpi /mnt/dietpi_userdata/readarr /opt/readarr
fi
if To_Install 155 htpc-manager # HTPC Manager
then
local url='https://github.com/HTPC-Manager/HTPC-Manager'
G_CHECK_URL "$url"
# APT deps
Python_Deps -i cryptography pillow psutil pynacl
if [[ -d '/mnt/dietpi_userdata/htpc-manager/.git' ]]
then
G_EXEC cd /mnt/dietpi_userdata/htpc-manager
G_EXEC_OUTPUT=1 G_EXEC git remote set-url origin "$url"
G_EXEC_OUTPUT=1 G_EXEC git fetch --depth=1 origin
G_EXEC_OUTPUT=1 G_EXEC git reset --hard origin
G_EXEC_OUTPUT=1 G_EXEC git clean -dxfe '/userdata'
else
G_EXEC_OUTPUT=1 G_EXEC git clone --depth=1 "$url"
G_EXEC mkdir -p /mnt/dietpi_userdata/htpc-manager
G_EXEC cp -a HTPC-Manager/. /mnt/dietpi_userdata/htpc-manager/
G_EXEC rm -R HTPC-Manager
G_EXEC cd /mnt/dietpi_userdata/htpc-manager
G_EXEC_OUTPUT=1 G_EXEC git reset --hard origin
G_EXEC_OUTPUT=1 G_EXEC git clean -dxfe '/userdata'
fi
# Python deps
G_EXEC_OUTPUT=1 G_EXEC pip3 install -Ur requirements.txt
# Service
cat << '_EOF_' > /etc/systemd/system/htpc-manager.service
[Unit]
Description=HTPC Manager (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
SyslogIdentifier=HTPC Manager
ExecStart=/usr/bin/python3 -OO /mnt/dietpi_userdata/htpc-manager/Htpc.py
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 153 octoprint # OctoPrint
then
# Pre-create user and its home directory to allow user-level (Rust and) Python instance
Create_User -G dialout,tty,video -d /mnt/dietpi_userdata/octoprint octoprint
G_EXEC mkdir -p /mnt/dietpi_userdata/octoprint
G_EXEC chown -R octoprint:octoprint /mnt/dietpi_userdata/octoprint
# Deps
Python_Deps -i -u octoprint cffi netifaces psutil pydantic
# Clear pip cache in case it got somehow created
[[ -d '/mnt/dietpi_userdata/octoprint/.cache' ]] && G_EXEC rm -R /mnt/dietpi_userdata/octoprint/.cache
# Install OctoPrint
# shellcheck disable=SC2016
G_EXEC_OUTPUT=1 G_EXEC runuser -u octoprint -- dash -c 'PATH="$HOME/.cargo/bin:$PATH" pip3 install -U --user --no-warn-script-location octoprint'
# Service: https://github.com/OctoPrint/OctoPrint/blob/master/scripts/octoprint.service
cat << '_EOF_' > /etc/systemd/system/octoprint.service
[Unit]
Description=OctoPrint (DietPi)
Documentation=https://dietpi.com/docs/software/printing/#octoprint
Wants=network-online.target
After=network-online.target mjpg-streamer.service
[Service]
Environment="LC_ALL=C.UTF-8" "LANG=C.UTF-8"
User=octoprint
ExecStart=/mnt/dietpi_userdata/octoprint/.local/bin/octoprint serve
[Install]
WantedBy=multi-user.target
_EOF_
# CLI alias
echo "alias octoprint='sudo -u octoprint /mnt/dietpi_userdata/octoprint/.local/bin/octoprint'" > /etc/bashrc.d/dietpi-octoprint.sh
# On fresh installs, change listening port to 5001 to avoid conflict with Shairport Sync.
[[ -f '/mnt/dietpi_userdata/octoprint/.octoprint/config.yaml' ]] || G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set server.port '5001'
# Apply service and system commands: Allow execution via specific sudoers config, use "which" for "reboot" and "poweroff", since we create shell functions in dietpi-globals to bypass logind calls if logind is not running.
# shellcheck disable=SC2230
echo "octoprint ALL=NOPASSWD: $(command -v systemctl) restart octoprint, $(which reboot), $(which poweroff)" > /etc/sudoers.d/octoprint
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set server.commands.serverRestartCommand 'sudo systemctl restart octoprint'
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set server.commands.systemRestartCommand 'sudo reboot'
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set server.commands.systemShutdownCommand 'sudo poweroff'
# mjpg-streamer: Configure OctoPrint to use it if installed
if (( ${aSOFTWARE_INSTALL_STATE[137]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Configuring OctoPrint to use mjpg-streamer for webcam support'
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.stream "http://$(G_GET_NET ip):8082/?action=stream"
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.snapshot 'http://127.0.0.1:8082/?action=snapshot'
G_EXEC runuser -u octoprint -- /mnt/dietpi_userdata/octoprint/.local/bin/octoprint config set webcam.ffmpeg "$(command -v ffmpeg)"
fi
fi
if To_Install 187 cups # CUPS
then
# Download base configuration if it does not exist yet
[[ -f '/etc/cups/cupsd.conf' ]] || dps_index=$software_id Download_Install 'cupsd.conf' /etc/cups/cupsd.conf
G_AGI cups
G_EXEC systemctl stop cups
G_EXEC chown root:lp /etc/cups/ /etc/cups/ppd/ /etc/cups/ssl/
fi
if To_Install 154 roonserver # Roon Server: https://help.roonlabs.com/portal/en/kb/articles/linux-install#Manual_Install
then
# .NET dependency: https://github.com/dotnet/docs/blob/main/docs/core/install/linux-debian.md#dependencies
case $G_DISTRO in
6) aDEPS=('libicu67');;
7) aDEPS=('libicu72');;
*) aDEPS=('libicu76');;
esac
G_WHIP_BUTTON_OK_TEXT='Early Access' G_WHIP_BUTTON_CANCEL_TEXT='Stable'
G_WHIP_DEFAULT_ITEM=$(grep -cm1 '^[[:blank:]]*SOFTWARE_ROONSERVER_EARLYACCESS=1' /boot/dietpi.txt)
if G_WHIP_YESNO 'Do you want to install regular stable Roon Server builds, or early access builds?
\nOfficial info about the Roon Early Access Program can be found here:
- https://help.roonlabs.com/portal/en/kb/articles/roon-early-access-program
\nNB: Early access builds have a higher chance to contain bugs, and you may need to restore a database backup when reverting from early access to stable builds.'
then
Download_Install 'https://download.roonlabs.net/builds/earlyaccess/RoonServer_linuxx64.tar.bz2'
G_CONFIG_INJECT 'SOFTWARE_ROONSERVER_EARLYACCESS=' 'SOFTWARE_ROONSERVER_EARLYACCESS=1' /boot/dietpi.txt
else
Download_Install 'https://download.roonlabs.net/builds/RoonServer_linuxx64.tar.bz2'
G_CONFIG_INJECT 'SOFTWARE_ROONSERVER_EARLYACCESS=' 'SOFTWARE_ROONSERVER_EARLYACCESS=0' /boot/dietpi.txt
fi
# Reinstall: Replace old instance
[[ -d '/opt/roonserver' ]] && G_EXEC rm -R /opt/roonserver
G_EXEC mv RoonServer /opt/roonserver
# Log to /var/log/roonserver
G_EXEC mkdir -p /mnt/dietpi_userdata/roonserver/{RoonServer,RAATServer}
G_EXEC rm -Rf /mnt/dietpi_userdata/roonserver/{RoonServer,RAATServer}/Logs
G_EXEC ln -s /var/log/roonserver /mnt/dietpi_userdata/roonserver/RoonServer/Logs
G_EXEC ln -s /var/log/roonserver /mnt/dietpi_userdata/roonserver/RAATServer/Logs
# User: Grant sudo permissions to create a mount point and mount SMB shares
Create_User -G dietpi,audio -d /mnt/dietpi_userdata/roonserver roonserver
G_EXEC eval 'echo '\''roonserver ALL=NOPASSWD:SETENV: /bin/mkdir -p /mnt/RoonStorage_*, /sbin/mount.cifs'\'' > /etc/sudoers.d/roonserver'
# Permissions
G_EXEC chmod 0755 /opt/roonserver
G_EXEC chown -R roonserver:root /{mnt/dietpi_userdata,opt}/roonserver
# Service
cat << '_EOF_' > /etc/systemd/system/roonserver.service
[Unit]
Description=Roon Server (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
SyslogIdentifier=Roon Server
User=roonserver
AmbientCapabilities=CAP_SYS_NICE
LogsDirectory=roonserver
Environment=ROON_DATAROOT=/mnt/dietpi_userdata/roonserver
Environment=ROON_ID_DIR=/mnt/dietpi_userdata/roonserver
ExecStart=/opt/roonserver/start.sh
# Hardening
PrivateTmp=true
[Install]
WantedBy=multi-user.target
_EOF_
Download_Test_Media
fi
if To_Install 156 # Steam
then
# Allow non-interactive install
G_EXEC eval "debconf-set-selections <<< 'steam steam/question select I AGREE'"
# x86_64: Install Debian i386 package
if [[ $G_HW_ARCH == 10 ]]
then
# Add i386 arch: https://packages.debian.org/steam
[[ $(dpkg --print-foreign-architectures) == *'i386'* ]] || { G_EXEC dpkg --add-architecture i386; G_AGUP; }
G_AGI steam
# Reapply GPU drivers to install required i386 items
local gpu_current=$(sed -n '/^[[:blank:]]*CONFIG_GPU_DRIVER=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
/boot/dietpi/func/dietpi-set_hardware gpudriver "$gpu_current"
# ARM: Install repacked Debian i386 package for armhf
elif [[ $G_HW_ARCH == 2 ]]
then
Download_Install "https://dietpi.com/downloads/binaries/$G_DISTRO_NAME/steam_$G_HW_ARCH_NAME.deb"
fi
# Move data dir to dietpi_userdata
if [[ -d '/mnt/dietpi_userdata/steam' ]]
then
G_EXEC rm -Rf /root/.steam
elif [[ -d '/root/.steam' ]]
then
G_EXEC mv /root/.steam /mnt/dietpi_userdata/steam
else
G_EXEC mkdir /mnt/dietpi_userdata/steam
fi
G_EXEC ln -s /mnt/dietpi_userdata/steam /root/.steam
# Desktop shortcut
Create_Desktop_Shortcut steam
fi
if To_Install 158 minio # MinIO
then
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='amd64';;
*) local arch='arm';;
esac
G_EXEC curl -sSfLo /usr/local/bin/minio "https://dl.minio.io/server/minio/release/linux-$arch/minio"
G_EXEC chmod +x /usr/local/bin/minio
# Service
G_EXEC curl -sSfLo /etc/systemd/system/minio.service 'https://github.com/minio/minio-service/raw/master/linux-systemd/minio.service'
# User
Create_User -d /mnt/dietpi_userdata/minio-data minio-user
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/minio-data
G_EXEC chown -R minio-user:minio-user /mnt/dietpi_userdata/minio-data
# Config
[[ -f '/etc/default/minio' ]] || cat << '_EOF_' > /etc/default/minio
# Default file path
MINIO_VOLUMES="/mnt/dietpi_userdata/minio-data"
# Use if you want to run MinIO on a custom port.
MINIO_OPTS="--address :9004 --console-address :9001"
# Access key of the server.
#MINIO_ACCESS_KEY=Server-Access-Key
# Secret key of the server.
#MINIO_SECRET_KEY=Server-Secret-Key
_EOF_
fi
if To_Install 162 docker # Docker
then
# APT packages
if (( $G_HW_ARCH == 11 ))
then
# RISC-V: Use "docker.io" from Debian repo as the official Docker repo does not support RISC-V yet: https://download.docker.com/linux/debian/dists/
local packages=('docker.io')
# Trixie: Add split "docker-cli" package: https://packages.debian.org/trixie/docker-cli
(( $G_DISTRO > 7 )) && packages+=('docker-cli')
else
# Detect distro
local distro='debian' suite=${G_DISTRO_NAME/forky/trixie}
(( $G_RASPBIAN )) && distro='raspbian' suite=${suite/trixie/bookworm}
# APT key
local url="https://download.docker.com/linux/$distro/gpg"
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-docker.gpg --yes"
# APT list
G_EXEC eval "echo 'deb https://download.docker.com/linux/$distro $suite stable' > /etc/apt/sources.list.d/docker.list"
G_AGUP
# APT package name
packages=('docker-ce')
fi
# APT package
# - Mask service to prevent iptables related startup failure: https://github.com/MichaIng/DietPi/issues/6013
G_EXEC systemctl mask --now docker
G_AGI "${packages[@]}"
G_EXEC systemctl unmask docker
G_EXEC systemctl start docker.socket
# Change Docker service type to "simple": https://github.com/MichaIng/DietPi/issues/2238#issuecomment-439474766
G_EXEC mkdir -p /lib/systemd/system/docker.service.d
G_EXEC eval "echo -e '[Service]\nType=simple' > /lib/systemd/system/docker.service.d/dietpi-simple.conf"
# Config: https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file
# - Move Docker containers to dietpi_userdata
# - Log to systemd-journald (journalctl) by default with reduced log level: https://github.com/MichaIng/DietPi/issues/2388
# + containerd: https://github.com/docker/docker.github.io/issues/9091
G_EXEC mkdir -p /mnt/dietpi_userdata/docker-data
if [[ -f '/etc/docker/daemon.json' ]]
then
GCI_PRESERVE=1 G_CONFIG_INJECT '"data-root":' ' "data-root": "/mnt/dietpi_userdata/docker-data",' /etc/docker/daemon.json '^\{([[:space:]]|$)'
GCI_PRESERVE=1 G_CONFIG_INJECT '"log-driver":' ' "log-driver": "journald",' /etc/docker/daemon.json '^\{([[:space:]]|$)'
GCI_PRESERVE=1 G_CONFIG_INJECT '"log-level":' ' "log-level": "warn",' /etc/docker/daemon.json '^\{([[:space:]]|$)'
GCI_PRESERVE=1 G_CONFIG_INJECT '"debug":' ' "debug": false,' /etc/docker/daemon.json '^\{([[:space:]]|$)'
else
G_EXEC mkdir -p /etc/docker
echo '{
"data-root": "/mnt/dietpi_userdata/docker-data",
"log-driver": "journald",
"log-level": "warn",
"debug": false
}' > /etc/docker/daemon.json
fi
G_CONFIG_INJECT '\[debug\]' '[debug]' /etc/containerd/config.toml
GCI_PRESERVE=1 G_CONFIG_INJECT 'level[[:blank:]]*=' ' level = "warn"' /etc/containerd/config.toml '^\[debug\]'
Enable_memory_cgroup
Configure_iptables
fi
if To_Install 134 # Docker Compose
then
# RISC-V: Official APT repo does not support RISC-V yet
if (( $G_HW_ARCH == 11 ))
then
G_AGI docker-compose
else
G_AGI docker-compose-plugin
fi
fi
if To_Install 161 bdd # FuguHub
then
local arch='RaspberryPi'
(( $G_HW_ARCH == 10 )) && arch='linux-x64'
# Download
Download_Install "https://fuguhub.com/FuguHub.$arch.tar.gz" bd
# - Remove first run wizard
G_EXEC rm bd/applications/Config-Wizard.zip
# - SSL addon: https://fuguhub.com/SSL-Certificate-Wizard.lsp
G_THREAD_START curl -sSfL 'https://fuguhub.com/unix/sslcert.zip' -o bd/applications/sslcert.zip
# - Drop Box addon: https://fuguhub.com/dropbox.lsp
G_THREAD_START curl -sSfL 'https://fuguhub.com/box.zip' -o bd/applications/box.zip
G_THREAD_WAIT
# Reinstall: Do not overwrite config and data
if [[ -d '/home/bd' ]]
then
G_EXEC rm -R bd/{cmsdocs,data,disk}
G_EXEC cp -a bd/. /home/bd/
else
G_EXEC rm -R bd/disk
G_EXEC mv bd /home/bd
fi
# File server root directory
if [[ ! -d '/mnt/dietpi_userdata/fuguhub-data' ]]
then
if [[ -d '/home/bd/disk' ]]
then
G_EXEC mv /home/bd/disk /mnt/dietpi_userdata/fuguhub-data # Pre-v8.0
else
G_EXEC mkdir /mnt/dietpi_userdata/fuguhub-data
fi
fi
G_EXEC rm -Rf /home/bd/disk # Pre-v8.0
[[ -e '/mnt/dietpi_userdata/fuguhub-data/cmsdocs' ]] || G_EXEC ln -s /home/bd/cmsdocs /mnt/dietpi_userdata/fuguhub-data/cmsdocs
[[ -e '/mnt/dietpi_userdata/fuguhub-data/applications' ]] || G_EXEC ln -s /home/bd/applications /mnt/dietpi_userdata/fuguhub-data/applications
# Create admin account
if [[ ! -f '/home/bd/user.dat' ]]
then
> /home/bd/user.dat
G_EXEC chmod 0600 /home/bd/user.dat
echo -n "{'v':{'dietpi':{'name':'dietpi','pwd':'$GLOBAL_PW','maxUsers':15,'recycle':true,'inactive':1200,'roles':{}}}}" > /home/bd/user.dat
fi
[[ -f '/home/bd/drvcnstr.dat' ]] || echo -n '{"v":{"admin":{"roles":["admin"],"urls":["\/*"],"methods":["GET","POST","PUT","PROPFIND","PROPPATCH","MKCOL","DELETE","MOVE","COPY"]}}}' > /home/bd/drvcnstr.dat
[[ -f '/home/bd/tuncnstr.dat' ]] || echo -n '{"v":{"TunFullAcc":{"roles":["TunFullAcc"],"urls":["\/*"],"methods":["GET"]}}}' > /home/bd/tuncnstr.dat
# User
Create_User -d /home/bd bd
# Permissions
G_EXEC chown -R bd:bd /home/bd /mnt/dietpi_userdata/fuguhub-data
Remove_SysV bdd # Pre-v8.0: https://github.com/MichaIng/DietPi/issues/5058
cat << '_EOF_' > /etc/systemd/system/bdd.service
[Unit]
Description=FuguHub (DietPi)
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=bd
AmbientCapabilities=CAP_NET_BIND_SERVICE
WorkingDirectory=/home/bd
ExecStart=/home/bd/FuguHub -h/home/bd -r/mnt/dietpi_userdata/fuguhub-data --threads 3 --sessions 100 --connections 80
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 164 nukkit # Nukkit
then
Download_Install 'https://ci.opencollab.dev/job/NukkitX/job/Nukkit/job/master/lastStableBuild/artifact/target/nukkit-1.0-SNAPSHOT.jar' /usr/local/bin/nukkit/nukkit.jar
# Config
[[ -f '/usr/local/bin/nukkit/nukkit.yml' ]] || G_EXEC curl -sSfL 'https://raw.githubusercontent.com/CloudburstMC/Languages/master/eng/nukkit.yml' -o /usr/local/bin/nukkit/nukkit.yml
# Service
cat << _EOF_ > /etc/systemd/system/nukkit.service
[Unit]
Description=Nukkit (DietPi)
Wants=network-online.target
After=network-online.target
[Service]
SyslogIdentifier=Nukkit
WorkingDirectory=/usr/local/bin/nukkit
ExecStart=$JAVA_PATH -jar /usr/local/bin/nukkit/nukkit.jar
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 49 gogs # Gogs
then
# ARMv6/ARMv7/RISC-V: No pre-compiled binaries available, so we host our own.
if (( $G_HW_ARCH == 1 || $G_HW_ARCH == 2 || $G_HW_ARCH == 11 ))
then
local url="https://dietpi.com/downloads/binaries/$G_DISTRO_NAME/gogs_$G_HW_ARCH_NAME.7z"
# Else install latest binaries from GitHub
else
case $G_HW_ARCH in
3) local arch='armv8';;
*) local arch='amd64';;
esac
local fallback_url="https://github.com/gogs/gogs/releases/download/v0.13.3/gogs_0.13.3_linux_$arch.tar.gz"
local url=$(curl -sSfL 'https://api.github.com/repos/gogs/gogs/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/gogs_[^\"\/]*_linux_$arch.tar.gz\"$/{print \$4}")
fi
Download_Install "$url"
G_EXEC mkdir -p /etc/gogs
G_EXEC mv gogs/gogs /etc/gogs/gogs
G_EXEC rm -R gogs
# Pre-v8.14: Remove unnecessary init scripts
[[ -d '/etc/gogs/scripts' ]] && G_EXEC rm -R /etc/gogs/scripts
# User
Create_User -d /etc/gogs -s /bin/dash gogs
# Directories + permissions
G_EXEC mkdir -p /mnt/dietpi_userdata/gogs-repo
G_EXEC chown -R gogs:gogs /etc/gogs /mnt/dietpi_userdata/gogs-repo
# Database
/boot/dietpi/func/create_mysql_db gogs gogs "$GLOBAL_PW"
# Service: https://github.com/gogs/gogs/blob/main/scripts/systemd/gogs.service
cat << '_EOF_' > /etc/systemd/system/gogs.service
[Unit]
Description=Gogs (DietPi)
Wants=network-online.target
After=network-online.target mariadb.service
[Service]
User=gogs
LogsDirectory=gogs
WorkingDirectory=/etc/gogs
ExecStart=/etc/gogs/gogs web
# Hardening
ProtectSystem=full
PrivateDevices=yes
PrivateTmp=yes
NoNewPrivileges=true
ReadWritePaths=-/etc/gogs
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 165 gitea # Gitea
then
# ARMv7: As of v1.8 there were issues with ARMv7 binaries on Raspbian which are hence not provided anymore: https://github.com/go-gitea/gitea/issues/6700
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='amd64';;
11) local arch='riscv64';;
*) local arch='arm-6';;
esac
local fallback_url="https://github.com/go-gitea/gitea/releases/download/v1.25.2/gitea-1.25.2-linux-$arch.xz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/go-gitea/gitea/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/gitea-[^\"\/]*-linux-$arch\.xz\"$/{print \$4}")" /mnt/dietpi_userdata/gitea/gitea
# User
Create_User -d /mnt/dietpi_userdata/gitea -s /bin/dash gitea
# Permissions
G_EXEC chown -R gitea:gitea /mnt/dietpi_userdata/gitea
G_EXEC chmod +x /mnt/dietpi_userdata/gitea/gitea
# Database
/boot/dietpi/func/create_mysql_db gitea gitea "$GLOBAL_PW"
# Service
cat << '_EOF_' > /etc/systemd/system/gitea.service
[Unit]
Description=Gitea (DietPi)
Wants=network-online.target
After=network-online.target mariadb.service
[Service]
User=gitea
LogsDirectory=gitea
WorkingDirectory=/mnt/dietpi_userdata/gitea
ExecStart=/mnt/dietpi_userdata/gitea/gitea web
# Hardening
ProtectSystem=full
PrivateDevices=yes
PrivateTmp=yes
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
_EOF_
# Pre-v7.9: Migrate run user setting: https://github.com/MichaIng/DietPi/issues/5516
[[ -f '/mnt/dietpi_userdata/gitea/custom/conf/app.ini' ]] && G_CONFIG_INJECT 'RUN_USER[[:blank:]]' 'RUN_USER = gitea' /mnt/dietpi_userdata/gitea/custom/conf/app.ini
fi
if To_Install 177 forgejo # Forgejo
then
# ARMv7: Dedicated binaries are not provided: https://codeberg.org/forgejo/forgejo/releases
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='amd64';;
*) local arch='arm-6';;
esac
# Bullseye: Forgejo 13 requires Git >=2.34.1: https://codeberg.org/forgejo/forgejo/pulls/8328. This is also practically true, the daemon fails right after initial web UI setup steps.
if (( $G_DISTRO > 6 ))
then
local fallback_url="https://codeberg.org/forgejo/forgejo/releases/download/v13.0.3/forgejo-13.0.3-linux-$arch.xz"
Download_Install "$(curl -sSfL 'https://codeberg.org/api/v1/repos/forgejo/forgejo/releases/latest' | mawk -v RS=, -F\" "/^\"browser_download_url\":\".*-linux-$arch\.xz\"$/{print \$4;exit}")" /mnt/dietpi_userdata/forgejo/forgejo
else
local fallback_url="https://codeberg.org/forgejo/forgejo/releases/download/v12.0.4/forgejo-12.0.4-linux-$arch.xz"
Download_Install "$(curl -sSfL 'https://codeberg.org/api/v1/repos/forgejo/forgejo/releases' | mawk -v RS=, -F\" "/^\"browser_download_url\":\".*-12\..*-linux-$arch\.xz\"$/{print \$4}" | head -1)" /mnt/dietpi_userdata/forgejo/forgejo
fi
# User
Create_User -d /mnt/dietpi_userdata/forgejo -s /bin/dash forgejo
# Permissions
G_EXEC chown -R forgejo:forgejo /mnt/dietpi_userdata/forgejo
G_EXEC chmod +x /mnt/dietpi_userdata/forgejo/forgejo
# Database
/boot/dietpi/func/create_mysql_db forgejo forgejo "$GLOBAL_PW"
# Service
cat << '_EOF_' > /etc/systemd/system/forgejo.service
[Unit]
Description=Forgejo (DietPi)
Wants=network-online.target
After=network-online.target mariadb.service
[Service]
User=forgejo
LogsDirectory=forgejo
WorkingDirectory=/mnt/dietpi_userdata/forgejo
ExecStart=/mnt/dietpi_userdata/forgejo/forgejo web
# Hardening
ProtectSystem=full
PrivateDevices=yes
PrivateTmp=yes
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 163 gmediarender # GMediaRender
then
G_AGI gmediarender
G_EXEC systemctl stop gmediarender
fi
if To_Install 160 # Allo GUI
then
# Download
Download_Install 'https://github.com/MichaIng/DietPi-AlloGUI/archive/v15.tar.gz'
G_EXEC mv DietPi-AlloGUI-15 allo
# Permissions
G_EXEC chmod -R 'o=,g-w' allo
G_EXEC chgrp -R www-data allo
G_EXEC chmod -R 'g=' allo/storage allo/bootstrap/cache
G_EXEC find allo/{storage,bootstrap/cache} -type d -exec chown www-data {} +
# Create database when missing, else reapply random database password
local db_password=$(tr -dc '[:alnum:]' < /dev/random | head -c32)
if [[ -d '/mnt/dietpi_userdata/mysql/allo' ]]
then
G_EXEC systemctl start mariadb
mysql -e "grant all privileges on allo.* to allo@localhost identified by '$db_password';"
else
/boot/dietpi/func/create_mysql_db allo allo "$db_password"
G_EXEC mysql -e 'create table allo.users(id tinyint unsigned primary key auto_increment, name tinytext not null, email tinytext not null, password varchar(60) not null, remember_token varchar(60), created_at timestamp, updated_at timestamp default current_timestamp);'
mysql -e "insert into allo.users(name, email, password) values ('admin', 'admin@allo.com', '$(php -r "echo password_hash('$GLOBAL_PW',PASSWORD_BCRYPT);")');"
fi
GCI_PASSWORD=1 G_CONFIG_INJECT 'DB_PASSWORD=' "DB_PASSWORD=$db_password" allo/.env
unset -v db_password
# Apply new app key
GCI_PASSWORD=1 G_CONFIG_INJECT 'APP_KEY=' "APP_KEY=base64:$(base64 < <(tr -dc '[:graph:]' < /dev/random | head -c32))" allo/.env
# Install cleanly
[[ -d '/opt/allo' ]] && G_EXEC rm -R /opt/allo
G_EXEC mv allo /opt
# Grant sudo permissions
G_EXEC eval 'echo '\''www-data ALL=NOPASSWD: ALL'\'' > /etc/sudoers.d/allo'
# Link to webroot
G_EXEC ln -snf /opt/allo/public /var/www/allo
# Redirect to web interface by default
G_EXEC rm -f /var/www/index\.*
cat << '_EOF_' > /var/www/index.php || exit 1
<?php
header('Location: /allo/index.php');
exit;
_EOF_
fi
if To_Install 166 pi-spc # Audiophonics PI-SPC
then
# https://github.com/audiophonics/Raspberry-pwr-management
G_EXEC mkdir -p /var/lib/dietpi/dietpi-software/installed/pi-spc
cat << '_EOF_' > /var/lib/dietpi/dietpi-software/installed/pi-spc/sds.sh
#!/bin/dash
echo 'Audiophonics PI-SPC: Shutdown script starting...
Asserting pins:
ShutDown : GPIO17=in, Low
BootOK : GPIO22=out, High
SoftShutDown : GPIO04=out, Low'
[ -f '/proc/cpuinfo' ] || { echo 'Oops: Unable to determine board revision from /proc/cpuinfo: /proc/cpuinfo does not exist. Exiting ...'; exit 1; }
grep -q '^Hardware' /proc/cpuinfo || { echo 'Oops: Unable to determine board revision from /proc/cpuinfo: No "Hardware" line. Exiting ...'; exit 1; }
gpio -g mode 04 out
gpio -g write 04 0
gpio -g mode 17 in
gpio -g write 17 0
gpio -g mode 22 out
gpio -g write 22 1
until [ "$(gpio -g read 17)" = 1 ]
do
sleep 0.25
done
echo 'Audiophonics PI-SPC: Shutting down system as requested...'
systemctl start poweroff.target
exit 0
_EOF_
G_EXEC chmod +x /var/lib/dietpi/dietpi-software/installed/pi-spc/sds.sh
cat << '_EOF_' > /etc/systemd/system/pi-spc.service
[Unit]
Description=Audiophonics PI-SPC (DietPi)
[Service]
StandardOutput=tty
ExecStart=/var/lib/dietpi/dietpi-software/installed/pi-spc/sds.sh
[Install]
WantedBy=multi-user.target
_EOF_
# Alternative: Use native GPIO shutdown and poweroff device tree overlays
# systemd-logind < dbus is required for the gpio-shutdown device tree overlay to trigger the shutdown.
#G_AGI dbus
#G_EXEC systemctl unmask systemd-logind
#G_EXEC systemctl start systemd-logind
#G_CONFIG_INJECT 'dtoverlay=gpio-shutdown' 'dtoverlay=gpio-shutdown,gpio_pin=17,active_low=0,gpio_pull=down' /boot/config.txt
#G_CONFIG_INJECT 'dtoverlay=gpio-poweroff' 'dtoverlay=gpio-poweroff,gpiopin=22,active_low' /boot/config.txt
fi
if To_Install 167 raspotify # Raspotify: https://github.com/dtcooper/raspotify/blob/master/install.sh
then
# ARMv6: 0.31.8.1 is the last version supporting ARMv6: https://github.com/dtcooper/raspotify/wiki/Raspotify-on-Pi-v1's-and-Pi-Zero-v1.x, https://github.com/dtcooper/raspotify/commit/345f15c
if (( $G_HW_ARCH == 1 ))
then
Download_Install 'https://github.com/dtcooper/raspotify/releases/download/0.31.8.1/raspotify_0.31.8.1.librespot.v0.3.1-54-gf4be9bb_armhf.deb'
else
# APT key
Download_Install 'https://dtcooper.github.io/raspotify/key.asc' /etc/apt/trusted.gpg.d/dietpi-raspotify.asc
# APT list
G_EXEC eval 'echo '\''deb https://dtcooper.github.io/raspotify raspotify main'\'' > /etc/apt/sources.list.d/dietpi-raspotify.list'
G_AGUP
# APT package
G_AGI raspotify
fi
# Stop service
G_EXEC systemctl stop raspotify
fi
if To_Install 86 roon-extension-manager # Roon Extension Manager
then
# Data dir
G_EXEC mkdir -p /mnt/dietpi_userdata/roon-extension-manager
# Pre-v8.2 migration
if [[ -f '/etc/systemd/system/roon-extension-manager.service' ]]
then
G_EXEC systemctl stop roon-extension-manager
G_EXEC rm /etc/systemd/system/roon-extension-manager.service
fi
[[ -d '/root/.roon-extension-manager' && ! -d '/mnt/dietpi_userdata/roon-extension-manager/.roon-extension-manager' ]] && G_EXEC mv /{root,mnt/dietpi_userdata/roon-extension-manager}/.roon-extension-manager
# User: Create with login shell first, since the REM installer uses "su"
Create_User -G docker -d /mnt/dietpi_userdata/roon-extension-manager -s /bin/dash roon-extension-manager
# Permissions
G_EXEC chown -R roon-extension-manager:root /mnt/dietpi_userdata/roon-extension-manager
# Store installer to data dir, so we can reuse it on uninstall
aDEPS=('wget')
Download_Install 'https://raw.githubusercontent.com/TheAppgineer/roon-extension-manager/v1.x/rem-setup.sh' /mnt/dietpi_userdata/roon-extension-manager/rem-setup.sh
G_EXEC cd /mnt/dietpi_userdata/roon-extension-manager
G_EXEC chmod +x rem-setup.sh
# Let the installer create everything for "roon-extension-manager" to run the service: https://github.com/MichaIng/DietPi/issues/5236
SUDO_USER='roon-extension-manager' G_EXEC_OUTPUT=1 G_EXEC ./rem-setup.sh
G_EXEC systemctl stop roon-extension-manager
G_EXEC usermod -s "$(command -v nologin)" roon-extension-manager
fi
if To_Install 178 jellyfin # Jellyfin
then
# RPi: Enable hardware codecs
(( $G_HW_MODEL > 9 )) || /boot/dietpi/func/dietpi-set_hardware rpi-codec 1
# APT key
local url='https://repo.jellyfin.org/jellyfin_team.gpg.key'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-jellyfin.gpg --yes"
# APT list
G_EXEC eval "echo 'deb https://repo.jellyfin.org/debian ${G_DISTRO_NAME/forky/trixie} main' > /etc/apt/sources.list.d/dietpi-jellyfin.list"
G_AGUP
# Mask service to prevent potential XML parsing issue due to bad termination timing
G_EXEC systemctl --no-reload mask jellyfin
# APT meta package: Server, web component and FFmpeg implementation
G_AGI jellyfin
G_EXEC systemctl --no-reload unmask jellyfin
# Grant dietpi group permissions and assure video access
Create_User -G dietpi,video,render -d /mnt/dietpi_userdata/jellyfin jellyfin
# Config: Only apply on fresh install, assumed when /mnt/dietpi_userdata/jellyfin does not yet exist
if [[ ! -d '/mnt/dietpi_userdata/jellyfin' ]]
then
# Data dir
# shellcheck disable=SC2015
[[ -d '/var/lib/jellyfin' ]] && G_EXEC mv /var/lib/jellyfin /mnt/dietpi_userdata/jellyfin || G_EXEC mkdir /mnt/dietpi_userdata/jellyfin
G_CONFIG_INJECT 'JELLYFIN_DATA_DIR=' 'JELLYFIN_DATA_DIR=/mnt/dietpi_userdata/jellyfin' /etc/default/jellyfin
# Change default WorkingDirectory
G_EXEC mkdir -p /etc/systemd/system/jellyfin.service.d
G_EXEC eval 'echo -e '\''[Service]\nWorkingDirectory=/mnt/dietpi_userdata/jellyfin'\'' > /etc/systemd/system/jellyfin.service.d/dietpi.conf'
# Cache dir
[[ -d '/var/cache/jellyfin' ]] && G_EXEC mv /var/cache/jellyfin /mnt/dietpi_userdata/jellyfin/cache
G_CONFIG_INJECT 'JELLYFIN_CACHE_DIR=' 'JELLYFIN_CACHE_DIR=/mnt/dietpi_userdata/jellyfin/cache' /etc/default/jellyfin
# Change default port due to conflict with Emby
# - This config file is not generated at service start, but only when saving network settings via web UI. This also complements any existing config with defaults, hence we pre-create one with only the changes we need.
cat << '_EOF_' > /etc/jellyfin/network.xml
<NetworkConfiguration>
<InternalHttpPort>8097</InternalHttpPort>
<PublicHttpPort>8097</PublicHttpPort>
</NetworkConfiguration>
_EOF_
G_EXEC chown jellyfin: /etc/jellyfin/network.xml
fi
# Permissions
G_EXEC chown -R jellyfin: /mnt/dietpi_userdata/jellyfin
Download_Test_Media
fi
if To_Install 62 # Box86
then
# APT deps
aDEPS=('cmake' 'make' 'gcc' 'libc6-dev' 'python3-minimal')
# Download
local version=$(curl -sSfL 'https://api.github.com/repos/ptitSeb/box86/releases/latest' | mawk -F\" '/^ *"tag_name": "[^"]*",$/{print $4}')
[[ $version ]] || { version='v0.3.8'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
Download_Install "https://github.com/ptitSeb/box86/archive/$version.tar.gz"
# Build for targets: https://github.com/ptitSeb/box86/blob/master/CMakeLists.txt
G_EXEC mkdir "box86-${version#v}/build"
G_EXEC cd "box86-${version#v}/build"
# - RPi 2
if (( $G_HW_MODEL == 2 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRPI2=1
# - RPi 3
elif (( $G_HW_MODEL == 3 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRPI3=1
# - RPi 4
elif (( $G_HW_MODEL == 4 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRPI4=1
# - Odroid XU4
elif (( $G_HW_MODEL == 11 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DODROIDXU4=1
# - ASUS Tinker Board
elif (( $G_HW_MODEL == 52 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRK3288=1
# - Others
else
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DARM_DYNAREC=1
fi
G_EXEC_OUTPUT=1 G_EXEC make CFLAGS='-g0 -O3' "-j$(nproc)"
G_EXEC strip --remove-section=.comment --remove-section=.note box86
G_EXEC make install
# Cleanup
G_EXEC cd "$G_WORKING_DIR"
G_EXEC rm -R "box86-${version#v}"
# Reload binfmt if kernel module is available to have i386 binaries executed via box86 automatically from now on
modprobe binfmt_misc 2> /dev/null && G_EXEC systemctl restart systemd-binfmt
fi
if To_Install 197 # Box64
then
# APT deps
aDEPS=('cmake' 'make' 'gcc' 'libc6-dev' 'python3-minimal')
# Download
local version=$(curl -sSfL 'https://api.github.com/repos/ptitSeb/box64/releases/latest' | mawk -F\" '/^ *"tag_name": "[^"]*",$/{print $4}')
[[ $version ]] || { version='v0.3.8'; G_DIETPI-NOTIFY 1 "Automatic latest ${aSOFTWARE_NAME[$software_id]} version detection failed. Version \"$version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
Download_Install "https://github.com/ptitSeb/box64/archive/$version.tar.gz"
# Build for targets: https://github.com/ptitSeb/box64/blob/main/CMakeLists.txt
G_EXEC mkdir "box64-${version#v}/build"
G_EXEC cd "box64-${version#v}/build"
# - RISC-V
if (( $G_HW_ARCH == 11 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRV64=1
# - RPi 2/3
elif (( $G_HW_MODEL == 2 || $G_HW_MODEL == 3 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRPI3ARM64=1
# - RPi 4
elif (( $G_HW_MODEL == 4 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRPI4ARM64=1
# - RPi 5
elif (( $G_HW_MODEL == 5 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRPI5ARM64=1
# - Odroid N2
elif (( $G_HW_MODEL == 15 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DODROIDN2=1
# - RK3399
elif (( $G_HW_CPUID == 3 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRK3399=1
# - RK3588
elif (( $G_HW_CPUID == 11 ))
then
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DRK3588=1
# - Generic ARMv8
else
G_EXEC cmake .. -DNOGIT=1 -DCMAKE_BUILD_TYPE=Release -DARM64=1
fi
G_EXEC_OUTPUT=1 G_EXEC make CFLAGS='-g0 -O3' "-j$(nproc)"
G_EXEC strip --remove-section=.comment --remove-section=.note box64
G_EXEC make install
# Cleanup
G_EXEC cd "$G_WORKING_DIR"
G_EXEC rm -R "box64-${version#v}"
# Reload binfmt if kernel module is available to have x86_64 binaries executed via box64 automatically from now on
modprobe binfmt_misc 2> /dev/null && G_EXEC systemctl restart systemd-binfmt
fi
if To_Install 207 # Moonlight (CLI): https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-embedded/setup.deb.sh
then
# APT key
local url='https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-embedded/gpg.5AEE46706CF0453E.key'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-moonlight.gpg --yes"
# APT list
local dist=$G_DISTRO_NAME
(( $G_DISTRO > 7 )) && dist='bookworm'
G_EXEC eval "echo 'deb https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-embedded/deb/raspbian $dist main' > /etc/apt/sources.list.d/dietpi-moonlight.list"
G_AGUP
# APT deps
aDEPS=('libcec6')
(( $G_DISTRO > 7 )) && aDEPS=('libcec7')
# APT package
G_AGI moonlight-embedded "${aDEPS[@]}"
aDEPS=()
# Enable required fake KMS driver: https://github.com/moonlight-stream/moonlight-embedded/wiki/Packages
/boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-fkms-v3d
fi
if To_Install 208 # Moonlight (GUI): https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-qt/setup.deb.sh
then
# APT key
local url='https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-qt/gpg.2F6AE14E1C660D44.key'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-moonlight-qt.gpg --yes"
# APT list
local distro='debian' suite=${G_DISTRO_NAME/forky/trixie}
(( $G_HW_MODEL < 10 )) && distro='raspbian'
G_EXEC eval "echo 'deb https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-qt/deb/$distro $suite main' > /etc/apt/sources.list.d/dietpi-moonlight-qt.list"
G_AGUP
# APT package
G_AGI moonlight-qt
# Raspberry Pi video setup: https://github.com/moonlight-stream/moonlight-docs/wiki/Installing-Moonlight-Qt-on-Raspberry-Pi-4#hevc-and-hdr-support
# - Usually requires vc4-fkms-v3d which also serves best performance
# - HEVC requires rpivid-v4l2, but it is deprecated and enabled OOTB since Bullseye
# - HDR requires vc4-kms-v3d, works on RPi 4 only (?) and breaks starting Moonlight from desktop
# => Compatible arrangement with HEVC but no HDR
if (( $G_HW_MODEL < 10 ))
then
/boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-fkms-v3d
/boot/dietpi/func/dietpi-set_hardware rpi-codec 1
fi
fi
if To_Install 11 # GZDoom
then
G_AGI gzdoom
# Enable KMS/DRM on RPi
(( $G_HW_MODEL > 9 )) || /boot/dietpi/func/dietpi-set_hardware rpi-opengl vc4-kms-v3d
fi
if To_Install 27 # TasmoAdmin
then
# Install required PHP modules: https://github.com/TasmoAdmin/TasmoAdmin/wiki/Guide-for-Debian-Server-10-(Buster)
aDEPS=("php$PHP_VERSION-curl" "php$PHP_VERSION-zip" "php$PHP_VERSION-mbstring" "php$PHP_VERSION-xml")
local adeps=("${aDEPS[@]#*-}")
# Reinstall: Skip download and install, advice to use internal updater from web UI
if [[ -d '/var/www/tasmoadmin' ]]
then
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install dir \"/var/www/tasmoadmin\" already exists. Download and install steps will be skipped.
- If you want to update ${aSOFTWARE_NAME[$software_id]}, please use the internal updater from web UI.
- If you need to reinstall (e.g. broken instance), please manually backup your config files+data, remove the install dir and rerun \"dietpi-software (re)install $software_id\"."
G_AGI "${aDEPS[@]}"
aDEPS=()
else
# v3 drops PHP 7.4 support: https://github.com/TasmoAdmin/TasmoAdmin/releases/tag/v3.0.0
if (( $G_DISTRO > 6 ))
then
local fallback_url='https://github.com/TasmoAdmin/TasmoAdmin/releases/download/v4.3.2/tasmoadmin_v4.3.2.tar.gz'
Download_Install "$(curl -sSfL 'https://api.github.com/repos/TasmoAdmin/TasmoAdmin/releases/latest' | mawk -F\" '/^ *"browser_download_url": ".*\/tasmoadmin_v[^"\/]*\.tar\.gz"$/{print $4}')"
else
Download_Install 'https://github.com/TasmoAdmin/TasmoAdmin/releases/download/v2.4.2/tasmoadmin_v2.4.2.tar.gz'
fi
G_EXEC chown -R www-data:www-data tasmoadmin
G_EXEC mv tasmoadmin /var/www/
fi
# Webserver config
# - Config file Version
local version=
(( $G_DISTRO > 6 )) && version='.3'
if (( ${aSOFTWARE_INSTALL_STATE[83]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Apache webserver found, enabling TasmoAdmin specific configuration.'
G_EXEC a2enmod setenvif rewrite authz_core authn_core authn_file
local tasmoadmin_conf='/etc/apache2/sites-available/dietpi-tasmoadmin.conf'
if [[ -f $tasmoadmin_conf ]]
then
tasmoadmin_conf+='.dietpi-new'
G_WHIP_MSG "Existing TasmoAdmin Apache configuration found, will preserve the old one and save the new one for review and comparison to: $tasmoadmin_conf"
fi
dps_index=$software_id Download_Install 'apache.tasmoadmin.conf' "$tasmoadmin_conf"
G_EXEC a2ensite dietpi-tasmoadmin
elif (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Lighttpd webserver found, enabling TasmoAdmin specific configuration.'
local tasmoadmin_conf='/etc/lighttpd/conf-available/99-dietpi-tasmoadmin.conf'
if [[ -f $tasmoadmin_conf ]]
then
tasmoadmin_conf+='.dietpi-new'
G_WHIP_MSG "Existing TasmoAdmin Lighttpd configuration found, will preserve the old one and save the new one for review and comparison to: $tasmoadmin_conf"
fi
dps_index=$software_id Download_Install "lighttpd.tasmoadmin$version.conf" "$tasmoadmin_conf"
G_EXEC_POST_FUNC(){ [[ $exit_code == 2 ]] && exit_code=0; } # Do not fail if modules are enabled already
G_EXEC lighty-enable-mod rewrite dietpi-tasmoadmin
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
G_DIETPI-NOTIFY 2 'Nginx webserver found, enabling TasmoAdmin specific configuration.'
local tasmoadmin_conf='/etc/nginx/sites-dietpi/dietpi-tasmoadmin.conf'
if [[ -f $tasmoadmin_conf ]]
then
owncloud_conf+='.dietpi-new'
G_WHIP_MSG "Existing TasmoAdmin Nginx configuration found, will preserve the old one and save the new one for review and comparison to: $tasmoadmin_conf"
fi
dps_index=$software_id Download_Install "nginx.tasmoadmin$version.conf" "$tasmoadmin_conf"
fi
# Enable required PHP modules
G_EXEC phpenmod "${adeps[@]}"
fi
if To_Install 206 openhab # openHAB: https://www.openhab.org/docs/installation/linux.html#package-repository-installation
then
# APT key
local url='https://openhab.jfrog.io/artifactory/api/gpg/key/public'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-openhab.gpg --yes"
# APT list
# - Enable RISC-V support by explicitly defining arch=all, which contains exactly the same packages (well, it's Java...)
local arch=
(( $G_HW_ARCH == 11 )) && arch=' [arch=all]'
G_EXEC eval "echo 'deb$arch https://openhab.jfrog.io/artifactory/openhab-linuxpkg stable main' > /etc/apt/sources.list.d/dietpi-openhab.list"
G_AGUP
# openHAB 5 requires Java 21
(( $JAVA_VERSION < 21 )) && G_EXEC eval 'cat << '\''_EOF_'\'' > /etc/apt/preferences.d/dietpi-openhab
Package: src:openhab*
Pin: version 5.*
Pin-Priority: -1
_EOF_'
# APT package
G_AGI openhab
G_EXEC systemctl stop openhab
# Disable plain HTTP and change HTTPS TCP port to avoid conflicts with SABnzbd, Airsonic-Advanced (8000) and MineOS (8443)
G_CONFIG_INJECT 'OPENHAB_HTTPS_PORT=' 'OPENHAB_HTTPS_PORT=8444' /etc/default/openhab
G_CONFIG_INJECT 'OPENHAB_HTTP_PORT=' 'OPENHAB_HTTP_PORT=0' /etc/default/openhab
# Do all logs to STDOUT => journalctl -u openhab
G_EXEC sed --follow-symlinks -i 's|<AppenderRef ref=".*"/>|<AppenderRef ref="STDOUT"/>|' /var/lib/openhab/etc/log4j2.xml
local line='' last=''
grep -n '<AppenderRef ref="STDOUT"/>' /var/lib/openhab/etc/log4j2.xml | while read -r line
do
line=${line%%:*}
(( $line == ${last:-$line} + 1 )) && G_EXEC sed --follow-symlinks -i "${last}d" /var/lib/openhab/etc/log4j2.xml
last=$line
done
G_EXEC rm -f /var/log/openhab/*
fi
if To_Install 157 home-assistant # Home Assistant
then
local ha_user='homeassistant'
local ha_home="/home/$ha_user"
local ha_pyenv_activation=". $ha_home/pyenv-activate.sh"
# Obtain latest Python 3.13.y version supported by pyenv
local ha_python_version=$(curl -sSfL 'https://api.github.com/repos/pyenv/pyenv/contents/plugins/python-build/share/python-build?ref=master' | mawk -F\" '/^ *"name": "3\.13\.[0-9]*",$/{print $4}' | sort -Vr | head -1)
[[ $ha_python_version ]] || { ha_python_version='3.13.11'; G_DIETPI-NOTIFY 1 "Automatic latest Python version detection failed. Version \"$ha_python_version\" will be installed as fallback, but a newer version might be available. Please report this at: https://github.com/MichaIng/DietPi/issues"; }
G_DIETPI-NOTIFY 2 "Home Assistant user: $ha_user"
G_DIETPI-NOTIFY 2 "Home Assistant home: $ha_home"
G_DIETPI-NOTIFY 2 "pyenv activation: \"$ha_pyenv_activation\""
G_DIETPI-NOTIFY 2 "pyenv Python version: $ha_python_version"
# User
Create_User -G dialout,gpio,i2c -d "$ha_home" "$ha_user"
G_EXEC mkdir -p "$ha_home"
G_EXEC chown "$ha_user:$ha_user" "$ha_home"
# Dependencies
# - Bullseye ARMv6/7: Depends on old bcrypt and cryptography versions which have no AMMv7 wheels yet, hence add dependency manually
local rust=()
(( $G_DISTRO < 7 && $G_HW_ARCH < 3 )) && rust=('-r') aDEPS=('pkg-config' 'libssl-dev')
PYTHON_VERSION=$ha_python_version Python_Deps -u "$ha_user" "${rust[@]}" av bcrypt cryptography numpy pillow pyenv pymicro-vad
# - Custom dependencies, e.g. MariaDB support: G_AGI libmariadb-dev; pip3 install mysqlclient|PyMySQL
local custom_apt_deps=$(sed -n '/^[[:blank:]]*SOFTWARE_HOMEASSISTANT_APT_DEPS=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
mapfile -t -d' ' -O "${#aDEPS[@]}" aDEPS < <(echo -n "$custom_apt_deps")
# - Install isal compression library as faster drop-in replacement for zlib, to address a warning by aiohttp_fast_zlib: https://github.com/MichaIng/DietPi/issues/7525
local custom_pip_deps="isal $(sed -n '/^[[:blank:]]*SOFTWARE_HOMEASSISTANT_PIP_DEPS=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)"
# Install pyenv to $ha_home
Download_Install 'https://github.com/pyenv/pyenv/archive/master.tar.gz'
G_EXEC chown -R "$ha_user:$ha_user" pyenv-master
# - Start with fresh instance, to allow clean pyenv and Python updates and fix broken instances. All userdata and configs are preserved in: /mnt/dietpi_userdata/homeassistant
[[ -d $ha_home/.pyenv ]] && G_EXEC rm -R "$ha_home/.pyenv"
[[ -f $ha_home/.python-version ]] && G_EXEC rm "$ha_home/.python-version" # pre-v9.13: remove "pyenv local" info as we use "pyenv global" now, stored to $ha_home/.python/version instead
[[ -d '/srv/homeassistant' ]] && G_EXEC rm -R /srv/homeassistant # pre-v6-27
G_EXEC mv pyenv-master "$ha_home/.pyenv"
# Link config and data to DietPi userdata
if [[ ! -d '/mnt/dietpi_userdata/homeassistant' ]]
then
if [[ -d $ha_home/.homeassistant ]]
then
G_EXEC mv "$ha_home/.homeassistant" /mnt/dietpi_userdata/homeassistant
else
G_EXEC mkdir /mnt/dietpi_userdata/homeassistant
fi
fi
[[ -d $ha_home/.homeassistant ]] && G_EXEC rm -R "$ha_home/.homeassistant"
G_EXEC ln -sf /mnt/dietpi_userdata/homeassistant "$ha_home/.homeassistant"
# Reset and merge pyenv and HA Python environment to avoid version mismatches on Python upgrades: https://github.com/MichaIng/DietPi/issues/6117
[[ -d '/mnt/dietpi_userdata/homeassistant/deps' ]] && G_EXEC rm -R /mnt/dietpi_userdata/homeassistant/deps
G_EXEC ln -sf "$ha_home/.pyenv/versions/$ha_python_version/lib/python${ha_python_version%.*}/site-packages" /mnt/dietpi_userdata/homeassistant/deps
# Configure pip
# - Disable cache
G_EXEC mkdir -p "$ha_home/.pip"
G_EXEC eval "echo -e '[global]\nno-cache-dir=true' > '$ha_home/.pip/pip.conf'"
# - ARMv6/7 Trixie/Forky: Add piwheels: The Python version needs to match the Debian version, else dynamically linked shared libraries may not match
(( $G_HW_ARCH < 3 && $G_DISTRO > 7 )) && G_CONFIG_INJECT 'extra-index-url[[:blank:]]*=' 'extra-index-url=https://www.piwheels.org/simple/' "$ha_home/.pip/pip.conf" '\[global\]'
# Generate script to activate pyenv: This must be sourced from the originating shell, hence it does not require execute permissions.
echo "#!/bin/dash
if [ \$(whoami) != '$ha_user' ]; then
echo '[FAILED] This pyenv must be activated as user \"$ha_user\". Aborting...'
kill -INT \$\$
fi
cd $ha_home
# Permit uv to install into system Python: https://github.com/astral-sh/uv/issues/7907
export PATH=\"$ha_home/.pyenv/bin:\$PATH\" UV_SYSTEM_PYTHON=1
eval \"\$(pyenv init -)\"
[ -f '.cargo/env' ] && . .cargo/env" > "$ha_home/pyenv-activate.sh"
local version=
# Bullseye: v2025.1.4 is the last which supports old SQLite: https://github.com/MichaIng/DietPi/issues/7374
(( $G_DISTRO < 7 )) && version='==2025.1.4'
G_EXEC_DESC="Compiling and installing Python $ha_python_version into pyenv, which will take a while ..."
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$ha_user" -- dash -c "$ha_pyenv_activation; exec pyenv install $ha_python_version"
G_EXEC_DESC="Set Python $ha_pyenv_activation as global version for this pyenv instance"
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$ha_user" -- dash -c "$ha_pyenv_activation; exec pyenv global $ha_python_version"
G_EXEC_DESC='Upgrading base modules: pip setuptools wheel'
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$ha_user" -- dash -c "$ha_pyenv_activation; exec pip3 install -U pip setuptools wheel"
G_EXEC_DESC="Installing additional dependencies: $custom_pip_deps"
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$ha_user" -- dash -c "$ha_pyenv_activation; exec pip3 install $custom_pip_deps"
G_EXEC_DESC='Installing Home Assistant core module'
G_EXEC_OUTPUT=1 G_EXEC runuser -u "$ha_user" -- dash -c "$ha_pyenv_activation; exec pip3 install homeassistant$version"
# Generate script to launch HA using pyenv
echo "#!/bin/dash
$ha_pyenv_activation
exec hass -c '/mnt/dietpi_userdata/homeassistant'" > "$ha_home/homeassistant-start.sh"
G_EXEC chmod +x "$ha_home/homeassistant-start.sh"
# Generate script to update HA within pyenv
echo "#!/bin/dash
exec sudo -u $ha_user dash -c '$ha_pyenv_activation; exec pip3 install -U homeassistant$version'" > "$ha_home/homeassistant-update.sh"
G_EXEC chmod +x "$ha_home/homeassistant-update.sh"
# Service
cat << _EOF_ > /etc/systemd/system/home-assistant.service
[Unit]
Description=Home Assistant (DietPi)
Wants=network-online.target
After=network-online.target mariadb.service
[Service]
SyslogIdentifier=Home Assistant
User=$ha_user
ExecStart=$ha_home/homeassistant-start.sh
RestartForceExitStatus=100
[Install]
WantedBy=multi-user.target
_EOF_
# Download HACS
Download_Install 'https://github.com/hacs/integration/releases/latest/download/hacs.zip' hacs
[[ -d '/mnt/dietpi_userdata/homeassistant/custom_components/hacs' ]] && G_EXEC rm -R /mnt/dietpi_userdata/homeassistant/custom_components/hacs
G_EXEC mkdir -p /mnt/dietpi_userdata/homeassistant/custom_components
G_EXEC mv hacs /mnt/dietpi_userdata/homeassistant/custom_components/
G_EXEC chown -R "$ha_user:$ha_user" /mnt/dietpi_userdata/homeassistant
G_DIETPI-NOTIFY 2 "Home Assistant Community Store (HACS) has been installed in addition. To activate it, follow this guide: https://hacs.xyz/docs/configuration/basic/"
fi
if To_Install 181 papermc # PaperMC
then
# Make sure user agrees to the EULA
if [[ -f '/mnt/dietpi_userdata/papermc/eula.txt' ]] || G_WHIP_BUTTON_OK_TEXT='Yes' G_WHIP_BUTTON_CANCEL_TEXT='Abort' G_WHIP_YESNO 'Do you agree to the Minecraft EULA found at:\n\nhttps://account.mojang.com/documents/minecraft_eula'
then
# Collect latest build of latest supported version of PaperMC
local url='https://api.papermc.io/v2/projects/paper'
# - Minecraft 1.20.5 and above requires Java 21: https://minecraft.wiki/w/Java_Edition_1.20.5
local version='1.20.4'
(( $JAVA_VERSION < 21 )) || { version=$(curl -sSfL "$url"); version=${version%\"*} version=${version##*\"}; }
local build=$(curl -sSfL "$url/versions/$version"); build=${build%]*} build=${build##*[,[]}
#local file=$(curl -sSfL "$url/versions/$version/builds/$build"); file=${file##*\"name\":\"} file=${file%%\"*}
# Download and install PaperMC
Download_Install "$url/versions/$version/builds/$build/downloads/paper-$version-$build.jar" /opt/papermc/paperclip.jar
G_EXEC mkdir -p /mnt/dietpi_userdata/papermc
G_EXEC eval 'echo '\''eula=true'\'' > /mnt/dietpi_userdata/papermc/eula.txt'
# User
Create_User -d /mnt/dietpi_userdata/papermc papermc
# Bedrock compatibility
G_WHIP_BUTTON_OK_TEXT='Yes'
G_WHIP_BUTTON_CANCEL_TEXT='Skip'
[[ -f '/mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot.jar' ]] && G_WHIP_DEFAULT_ITEM='Yes'
if G_WHIP_YESNO 'Would you like to install the Geyser and Floodgate plugins for compatibility with Bedrock Edition?\n\nNote that this may be buggy.'
then
local version='2.2.3'
(( $JAVA_VERSION < 21 )) || version='latest'
Download_Install "https://download.geysermc.org/v2/projects/geyser/versions/$version/builds/latest/downloads/spigot" /mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot.jar
Download_Install 'https://download.geysermc.org/v2/projects/floodgate/versions/latest/builds/latest/downloads/spigot' /mnt/dietpi_userdata/papermc/plugins/floodgate-spigot.jar
fi
# Minecraft rcon client for remote administration and server maintenance scripts
aDEPS=('gcc' 'libc6-dev')
Download_Install 'https://github.com/Tiiffi/mcrcon/archive/master.tar.gz'
G_EXEC gcc -g0 -O3 mcrcon-master/mcrcon.c -o /usr/local/bin/mcrcon
G_EXEC rm -R mcrcon-master
G_EXEC strip --remove-section=.comment --remove-section=.note /usr/local/bin/mcrcon
# Link logs to RAM
G_EXEC rm -Rf /mnt/dietpi_userdata/papermc/logs
G_EXEC ln -s /var/log/papermc /mnt/dietpi_userdata/papermc/logs
# Permissions
G_EXEC chown -R papermc:papermc /mnt/dietpi_userdata/papermc
# Assure 512 MiB heap size and 1.5 GiB overall memory (-100 MiB to avoid tiny swap space)
local memory_limit=$JAVA_MAX_HEAP_SIZE
if (( $memory_limit < 1024 ))
then
memory_limit=512
if (( $RAM_TOTAL < 1436 ))
then
G_DIETPI-NOTIFY 2 'Stable PaperMC operation requires at least 1.5 GiB system memory. We will now increase your swap size to satisfy this requirement.'
/boot/dietpi/func/dietpi-set_swapfile $(( 1536 - $RAM_PHYS ))
# Apply 1 GiB heap size on 2 GiB+ physical RAM devices
elif (( $RAM_PHYS > 1848 ))
then
memory_limit=1024
fi
fi
# Service
cat << _EOF_ > /etc/systemd/system/papermc.service
[Unit]
Description=PaperMC (DietPi)
Documentation=https://paper.readthedocs.io/
Wants=network-online.target
After=network-online.target
[Service]
SyslogIdentifier=PaperMC
User=papermc
LogsDirectory=papermc
WorkingDirectory=/mnt/dietpi_userdata/papermc
ExecStart=$JAVA_PATH -mx${memory_limit}m -jar /opt/papermc/paperclip.jar --nogui --noconsole
SuccessExitStatus=143
[Install]
WantedBy=multi-user.target
_EOF_
# Config
if [[ -f '/mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot.jar' ]]
then
Create_Config /mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot/config.yml papermc 1800 1 &&
G_CONFIG_INJECT 'auth-type:[[:blank:]]' ' auth-type: floodgate' /mnt/dietpi_userdata/papermc/plugins/Geyser-Spigot/config.yml
else
Create_Config /mnt/dietpi_userdata/papermc/permissions.yml papermc 1800 1
fi
G_CONFIG_INJECT 'enable-rcon=' 'enable-rcon=true' /mnt/dietpi_userdata/papermc/server.properties
GCI_PASSWORD=1 G_CONFIG_INJECT 'rcon.password=' "rcon.password=$GLOBAL_PW" /mnt/dietpi_userdata/papermc/server.properties
else
aSOFTWARE_INSTALL_STATE[$software_id]=0
G_DIETPI-NOTIFY 2 "${aSOFTWARE_NAME[$software_id]} install skipped due to outstanding EULA agreement"
fi
fi
if To_Install 140 domoticz # Domoticz
then
G_AGI domoticz
fi
if To_Install 202 # Rclone
then
# RISC-V: No upstream packages yet, hence use Debian repo
if (( $G_HW_ARCH == 11 ))
then
G_AGI rclone
else
case $G_HW_ARCH in
1) local arch='arm-v6';;
2) local arch='arm-v7';;
3) local arch='arm64';;
*) local arch='amd64';;
esac
local fallback_url="https://github.com/rclone/rclone/releases/download/v1.72.1/rclone-v1.72.1-linux-$arch.deb"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/rclone/rclone/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/rclone-v[^\"\/]*-linux-$arch.deb\"$/{print \$4}")"
fi
fi
if To_Install 209 # Restic
then
case $G_HW_ARCH in
3) local arch='arm64';;
10) local arch='amd64';;
11) local arch='riscv64';;
*) local arch='arm';;
esac
local fallback_url="https://github.com/restic/restic/releases/download/v0.18.1/restic_0.18.1_linux_$arch.bz2"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/restic/restic/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/restic_[^\"\/]*_linux_$arch\.bz2\"$/{print \$4}")" /usr/local/bin/restic
G_EXEC chmod +x /usr/local/bin/restic
fi
if To_Install 210 # MediaWiki
then
# Install required PHP modules: https://www.mediawiki.org/wiki/Manual:Installation_requirements#PHP
aDEPS=("php$PHP_VERSION-gd" "php$PHP_VERSION-intl")
# - Add JSON module for PHP7, as it does not exist (embedded in core package) on PHP8
[[ $PHP_VERSION == 8* ]] || aDEPS+=("php$PHP_VERSION-json")
local php_modules=("${aDEPS[@]##*-}")
# Create MariaDB database and user
if [[ -d '/mnt/dietpi_userdata/mysql/mediawiki' ]]
then
G_DIETPI-NOTIFY 2 'MediaWiki MariaDB database found, will NOT overwrite.'
else
/boot/dietpi/func/create_mysql_db mediawiki mediawiki "$GLOBAL_PW"
fi
# Get latest version
local fallback_url='https://releases.wikimedia.org/mediawiki/1.45/mediawiki-1.45.1.tar.gz'
Download_Install "$(curl -sSfL 'https://www.mediawiki.org/wiki/Download' | grep -o 'https://releases\.wikimedia\.org/mediawiki/[^/"]*/mediawiki-[^"]*\.tar\.gz' | head -1)"
# Enable required PHP modules
G_EXEC phpenmod "${php_modules[@]}"
# Webserver configs
# - Lighttpd
if (( ${aSOFTWARE_INSTALL_STATE[84]} > 0 ))
then
[[ -f '/etc/lighttpd/conf-enabled/05-setenv.conf' ]] || G_EXEC lighty-enable-mod setenv
cat << '_EOF_' > /etc/lighttpd/conf-available/98-dietpi-mediawiki.conf
$HTTP["url"] =~ "^/wiki/images($|/)" {
setenv.set-response-header = ("X-Content-Type-Options" => "nosniff")
}
_EOF_
[[ -f '/etc/lighttpd/conf-enabled/98-dietpi-mediawiki.conf' ]] || lighty-enable-mod dietpi-mediawiki
# - Nginx
elif (( ${aSOFTWARE_INSTALL_STATE[85]} > 0 ))
then
cat << '_EOF_' > /etc/nginx/sites-dietpi/dietpi-mediawiki.conf
location ^~ /wiki/images {
add_header X-Content-Type-Options "nosniff" always;
}
_EOF_
fi
# Reinstall: Clean install but preserve existing config and uploaded images
if [[ -f '/var/www/wiki/LocalSettings.php' ]]
then
G_EXEC mv /var/www/wiki/LocalSettings.php mediawiki-*/
G_EXEC chmod 600 mediawiki-*/LocalSettings.php
fi
if [[ -d '/var/www/wiki/images' ]]
then
G_EXEC cp -a mediawiki-*/images/{.htaccess,README} /var/www/wiki/images/
G_EXEC cp -a /var/www/wiki/images/. mediawiki-*/images/
fi
G_EXEC chown -R www-data:www-data mediawiki-*
[[ -d '/var/www/wiki' ]] && G_EXEC rm -R /var/www/wiki
# Move new instance in place
G_EXEC mv mediawiki-* /var/www/wiki
fi
if To_Install 211 homebridge # Homebridge
then
# APT key
local url='https://repo.homebridge.io/KEY.gpg'
G_CHECK_URL "$url"
G_EXEC eval "curl -sSfL '$url' | gpg --dearmor -o /etc/apt/trusted.gpg.d/dietpi-homebridge.gpg --yes"
# APT list
G_EXEC eval 'echo '\''deb https://repo.homebridge.io stable main'\'' > /etc/apt/sources.list.d/dietpi-homebridge.list'
G_AGUP
G_AGI homebridge
G_EXEC systemctl stop homebridge
# Workaround for bug with wrong home directory /nonexistent
G_EXEC usermod -d /var/lib/homebridge homebridge
fi
if To_Install 205 # Homer
then
# Download the latest release
Download_Install 'https://github.com/bastienwirtz/homer/releases/latest/download/homer.zip' homer
# Backup existing instance
if [[ -d '/var/www/homer' ]]
then
G_DIETPI-NOTIFY 2 'Existing Homer instance found, backing up to /mnt/dietpi_userdata/homer_backup ...'
[[ -d '/mnt/dietpi_userdata/homer_backup' ]] && G_EXEC rm -R /mnt/dietpi_userdata/homer_backup
G_EXEC cp -a /var/www/homer /mnt/dietpi_userdata/homer_backup
G_DIETPI-NOTIFY 2 'Removing old assets with hash in file name ...'
G_EXEC rm -Rf /var/www/homer/{workbox-*.js,resources}
fi
# Install latest release, preserve existing config
G_EXEC cp -a homer/. /var/www/homer/
G_EXEC rm -R homer
[[ -f '/var/www/homer/assets/config.yml' ]] || G_EXEC cp /var/www/homer/assets/config.yml{.dist,}
fi
if To_Install 198 filebrowser # File Browser
then
case $G_HW_ARCH in
1) local arch='armv6';;
2) local arch='armv7';;
3) local arch='arm64';;
11) local arch='riscv64';;
*) local arch='amd64';;
esac
local fallback_url="https://github.com/filebrowser/filebrowser/releases/download/v2.52.0/linux-$arch-filebrowser.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/filebrowser/filebrowser/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/linux-$arch-filebrowser\.tar\.gz\"$/{print \$4}")" ./filebrowser/
# Reinstall
[[ -d '/opt/filebrowser' ]] && G_EXEC rm -R /opt/filebrowser
# Install
G_EXEC mv filebrowser /opt/
# User
G_EXEC mkdir -p /mnt/dietpi_userdata/filebrowser
Create_User -g dietpi -d /mnt/dietpi_userdata/filebrowser filebrowser
# Config if not exist
if [[ ! -f '/mnt/dietpi_userdata/filebrowser/filebrowser.db' ]]
then
G_EXEC cd /mnt/dietpi_userdata/filebrowser
G_EXEC /opt/filebrowser/filebrowser config init
# Reduce minimum password length to match default software password if it is shorter
local extra_args=()
(( ${#GLOBAL_PW} < 12 )) && extra_args+=('--minimum-password-length' "${#GLOBAL_PW}")
G_EXEC /opt/filebrowser/filebrowser config set -a 0.0.0.0 -p 8084 -r /mnt "${extra_args[@]}"
G_EXEC_DESC='Setting up File Browser login user "dietpi"' G_EXEC /opt/filebrowser/filebrowser users add dietpi "$GLOBAL_PW" --perm.admin
fi
# Permissions
G_EXEC chown -R filebrowser:root /mnt/dietpi_userdata/filebrowser /opt/filebrowser
# Service
cat << '_EOF_' > /etc/systemd/system/filebrowser.service
[Unit]
Description=File Browser (DietPi)
Documentation=https://filebrowser.org/
Wants=network-online.target
After=network-online.target remote-fs.target
[Service]
User=filebrowser
UMask=002
ExecStart=/opt/filebrowser/filebrowser -d /mnt/dietpi_userdata/filebrowser/filebrowser.db
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 199 spotifyd # Spotifyd
then
# Obtain supported version, variant and related dependencies. It became very complicated: https://github.com/MichaIng/DietPi/issues/6419
local arch='' variant='full' version='' aDEPS=('libdbus-1-3' 'libpulse0')
case $G_HW_ARCH in
1) arch='armv6' variant='slim' version='0.3.5' aDEPS=();; # Last supported version and only supported variant for ARMv6
2) arch='armv7';;
3) arch='aarch64';;
*) arch='x86_64';;
esac
# Download specific version if given, else obtain latest version from GitHub API
if [[ $version ]]
then
Download_Install "https://github.com/Spotifyd/spotifyd/releases/download/v$version/spotifyd-linux-$arch-$variant.tar.gz" /opt/spotifyd
else
local fallback_url="https://github.com/Spotifyd/spotifyd/releases/download/v0.4.2/spotifyd-linux-$arch-$variant.tar.gz"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/Spotifyd/spotifyd/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/spotifyd-linux-$arch-$variant\.tar\.gz\"$/{print \$4}")" /opt/spotifyd
fi
# User
Create_User -G audio -d /mnt/dietpi_userdata/spotifyd spotifyd
# Config: Do not touch on reinstall
if [[ ! -f '/mnt/dietpi_userdata/spotifyd/spotifyd.conf' ]]
then
G_EXEC mkdir -p /mnt/dietpi_userdata/spotifyd/cache
dps_index=$software_id Download_Install 'spotifyd.conf' /mnt/dietpi_userdata/spotifyd/spotifyd.conf
G_EXEC chmod 0600 /mnt/dietpi_userdata/spotifyd/spotifyd.conf
fi
# Service: https://github.com/Spotifyd/spotifyd/blob/master/contrib/spotifyd.service
cat << '_EOF_' > /etc/systemd/system/spotifyd.service
[Unit]
Description=Spotifyd (DietPi)
Wants=network-online.target
After=network-online.target sound.target
[Service]
User=spotifyd
WorkingDirectory=/mnt/dietpi_userdata/spotifyd
ExecStart=/opt/spotifyd/spotifyd --no-daemon --config-path=/mnt/dietpi_userdata/spotifyd/spotifyd.conf
[Install]
WantedBy=multi-user.target
_EOF_
# Permissions
G_EXEC chown -R spotifyd:root /mnt/dietpi_userdata/spotifyd
G_EXEC chmod +x /opt/spotifyd/spotifyd
fi
if To_Install 185 # Portainer
then
# Remove existing container and image, including CE, BE, and old v1, and store image repo in variable to preserve BE instances
local container=$(docker container ls -aqf 'ancestor=portainer/portainer' -f 'ancestor=portainer/portainer-ce' -f 'ancestor=portainer/portainer-ee') image repo
[[ $container ]] && G_EXEC docker container rm -f "$container"
read -r image repo < <(docker image ls -af 'reference=portainer/portainer' -f 'reference=portainer/portainer-ce' -f 'reference=portainer/portainer-ee' --format '{{.ID}} {{.Repository}}')
[[ $image ]] && G_EXEC docker image rm "$image"
# Create volume if it does not exist yet
[[ $(docker volume ls -qf 'name=^portainer_data$') ]] || G_EXEC docker volume create portainer_data
# Deploy new Portainer container, migrate v1 to CE and preserve BE
[[ $repo == 'portainer/portainer-ee' ]] || repo='portainer/portainer-ce'
G_EXEC_OUTPUT=1 G_EXEC docker run -d -p '9002:9000' -p '9442:9443' --name=portainer --restart=always -v '/run/docker.sock:/var/run/docker.sock' -v '/etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro' -v 'portainer_data:/data' "$repo"
fi
if To_Install 141 adsb-setup adsb-docker # ADS-B Feeder
then
# clone the adsb-feeder repo into /tmp
G_EXEC_OUTPUT=1 G_EXEC git clone -b dietpi 'https://github.com/dirkhh/adsb-feeder-image' /tmp/adsb-feeder
# remove the service that isn't needed for an app install on DietPi and install the rest
G_EXEC cd /tmp/adsb-feeder/src/modules/adsb-feeder/filesystem/root
G_EXEC rm ./usr/lib/systemd/system/adsb-bootstrap.service
G_EXEC rm ./usr/lib/systemd/system/adsb-update.service
G_EXEC rm ./usr/lib/systemd/system/adsb-update.timer
# Fix bash path on potentially non-usr-merged systems
[[ -f '/usr/bin/bash' ]] || G_EXEC sed --follow-symlinks -i 's|/usr/bin/bash|/bin/bash|' ./usr/lib/systemd/system/adsb-*.service
G_EXEC mv ./usr/lib/systemd/system/* /etc/systemd/system/
# determine the version
local ADSB_FEEDER_DATE_COMPONENT=$(git log -20 --date='format:%y%m%d' --format='%ad' | uniq -c | mawk '{print $2"."$1;exit}')
local ADSB_FEEDER_TAG_COMPONENT=$(git describe --match 'v[0-9]*' --long | sed 's/-[0-9]*-g[0-9a-f]*//')
local ADSB_FEEDER_VERSION="$ADSB_FEEDER_TAG_COMPONENT(dietpi)-$ADSB_FEEDER_DATE_COMPONENT"
# create the target directory for the app and populated with the code from the git checkout
[[ -d '/opt/adsb' ]] && G_EXEC rm -R /opt/adsb
G_EXEC mv /tmp/adsb-feeder/src/modules/adsb-feeder/filesystem/root/opt/adsb /opt/
G_EXEC cd /opt/adsb
# remove the git clone of the repo we installed from
G_EXEC rm -R /tmp/adsb-feeder
# create a symlink so the config files reside where they should be in /mnt/dietpi_userdata/adsb-feeder
G_EXEC mkdir -p /mnt/dietpi_userdata/adsb-feeder/config
G_EXEC ln -s /mnt/dietpi_userdata/adsb-feeder/config .
# set the 'image name' and version that are shown in the footer of the Web UI and mark as app, not image
G_EXEC eval 'echo '\''ADSB Feeder app running on DietPi'\'' > feeder-image.name'
G_EXEC eval "echo '$ADSB_FEEDER_VERSION' > adsb.im.version"
G_EXEC rm -f os.adsb.feeder.image
G_EXEC touch app.adsb.feeder.image
# get the Python modules
G_EXEC_OUTPUT=1 G_EXEC pip3 install -U flask requests
# finally ensure that /run allows executables (this is needed for the containers to be able to not do excessive
# writes to physical storage - which might be an SD card)
G_EXEC mount -o remount,exec /run
fi
if To_Install 172 # WireGuard
then
# Pre-v8.12: Purge DKMS as it is not required and might mess with available kernel module
dpkg-query -s wireguard-dkms &> /dev/null && G_AGP wireguard-dkms
# Install user space tools
G_AGI wireguard-tools iptables qrencode
Configure_iptables
# Server/Client choice
G_WHIP_MENU_ARRAY=(
'Server' ': Use this machine as VPN server and allow clients to connect to it.'
'Client' ': Use this machine as VPN client to connect to another VPN server or service provider.'
)
G_WHIP_DEFAULT_ITEM=$(sed -n '/^[[:blank:]]*SOFTWARE_WIREGUARD_MODE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ ${G_WHIP_DEFAULT_ITEM,,} == 'client' ]] || G_WHIP_DEFAULT_ITEM='Server'
G_WHIP_NOCANCEL=1
G_WHIP_MENU 'Please choose if this machine should be set up as VPN server or client:'
G_CONFIG_INJECT 'SOFTWARE_WIREGUARD_MODE=' "SOFTWARE_WIREGUARD_MODE=$G_WHIP_RETURNED_VALUE" /boot/dietpi.txt
# Server choice
if [[ $G_WHIP_RETURNED_VALUE == 'Server' ]]
then
# Public IP/domain and desired WireGuard server port
G_WHIP_DEFAULT_ITEM=$(sed -n '/^[[:blank:]]*SOFTWARE_PUBLIC_DOMAIN_NAME=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $G_WHIP_DEFAULT_ITEM ]] || G_WHIP_DEFAULT_ITEM=$(find /etc/letsencrypt/live -mindepth 1 -maxdepth 1 -type d -print -quit 2> /dev/null)
[[ $G_WHIP_DEFAULT_ITEM ]] || G_WHIP_DEFAULT_ITEM=$(hostname -f)
G_WHIP_NOCANCEL=1
G_WHIP_INPUTBOX 'Please enter your servers public IP/domain for WireGuard client access:'
local domain=${G_WHIP_RETURNED_VALUE#http*://}
GCI_PRESERVE=1 G_CONFIG_INJECT 'SOFTWARE_PUBLIC_DOMAIN_NAME=' "SOFTWARE_PUBLIC_DOMAIN_NAME=$domain" /boot/dietpi.txt
G_WHIP_DEFAULT_ITEM=$(sed -n '/^[[:blank:]]*SOFTWARE_WIREGUARD_PORT=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
disable_error=1 G_CHECK_VALIDINT "$G_WHIP_DEFAULT_ITEM" 0 || G_WHIP_DEFAULT_ITEM=51820
G_WHIP_NOCANCEL=1 G_WHIP_INPUTBOX_REGEX='^[1-9][0-9]*$' G_WHIP_INPUTBOX_REGEX_TEXT='a valid port number'
G_WHIP_INPUTBOX 'Please enter the network port that will be used to access your WireGuard server:\n
NB: This port needs to be forwarded by your router and/or opened in your firewall settings. Default value is: 51820'
local port=$G_WHIP_RETURNED_VALUE
# Create everything inside WireGuard config dir
G_EXEC cd /etc/wireguard
# For security reasons set umask to 0077
G_EXEC umask 0077
# Create server and client keys
[[ -f 'server_private.key' ]] || wg genkey > server_private.key
[[ -f 'server_public.key' ]] || wg pubkey < server_private.key > server_public.key
[[ -f 'client_private.key' ]] || wg genkey > client_private.key
[[ -f 'client_public.key' ]] || wg pubkey < client_private.key > client_public.key
# Server config
[[ -f 'wg0.conf' ]] || cat << _EOF_ > wg0.conf || exit 1
[Interface]
Address = 10.9.0.1/24
PrivateKey = $(<server_private.key)
ListenPort = $port
PostUp = sysctl net.ipv4.conf.%i.forwarding=1 net.ipv4.conf.\$(ip r l 0/0 | mawk '{print \$5;exit}').forwarding=1
PostUp = sysctl net.ipv6.conf.\$(ip r l 0/0 | mawk '{print \$5;exit}').accept_ra=2
PostUp = sysctl net.ipv6.conf.%i.forwarding=1 net.ipv6.conf.\$(ip r l 0/0 | mawk '{print \$5;exit}').forwarding=1
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -s 10.9.0.0/24 -o \$(ip r l 0/0 | mawk '{print \$5;exit}') -j MASQUERADE
PostUp = ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o \$(ip r l 0/0 | mawk '{print \$5;exit}') -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -s 10.9.0.0/24 -o \$(ip r l 0/0 | mawk '{print \$5;exit}') -j MASQUERADE
PostDown = ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o \$(ip r l 0/0 | mawk '{print \$5;exit}') -j MASQUERADE
# Client 1
[Peer]
PublicKey = $(<client_public.key)
AllowedIPs = 10.9.0.2/32
# Client 2
#[Peer]
#PublicKey = XXXX
#AllowedIPs = 10.9.0.3/32
_EOF_
# Server local network IP
local server_ip=$(G_GET_NET ip)
# Server DNS nameserver
local server_dns=$(mawk '/nameserver/{print $2;exit}' /etc/resolv.conf)
# - Replace "127.0.0.1"/"localhost" loopback entries by server wg0 IP: https://github.com/MichaIng/DietPi/issues/2482
server_dns=${server_dns//127.0.0.1/10.9.0.1}
server_dns=${server_dns//localhost/10.9.0.1}
# Client config
[[ -f 'wg0-client.conf' ]] || cat << _EOF_ > wg0-client.conf
[Interface]
Address = 10.9.0.2/24
PrivateKey = $(<client_private.key)
# Comment the following to preserve the clients default DNS server, or force a desired one.
DNS = $server_dns
# Kill switch: Uncomment the following if the client should stop any network traffic, when disconnected from the VPN server
# NB: This requires "iptables" to be installed, thus will not work on most mobile phones.
#PostUp = iptables -I OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT; ip6tables -I OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
#PreDown = iptables -D OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT; ip6tables -D OUTPUT ! -o %i -m mark ! --mark \$(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
[Peer]
PublicKey = $(<server_public.key)
# Tunnel all network traffic through the VPN:
# AllowedIPs = 0.0.0.0/0, ::/0
# Tunnel access to server-side local network only:
# AllowedIPs = ${server_ip%.*}.0/24
# Tunnel access to VPN server only:
# AllowedIPs = $server_ip/32
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = $domain:$port
# Uncomment the following if you're behind a NAT and want the connection to be kept alive.
#PersistentKeepalive = 25
_EOF_
unset -v domain port server_ip server_dns
# Set umask back to default 0022
G_EXEC umask 0022
# Navigate back to DietPi-Software working dir
G_EXEC cd "$G_WORKING_DIR"
# Use wg-quick@.service to start VPN on boot
G_EXEC systemctl enable --now wg-quick@wg0
# Client choice
elif [[ $G_WHIP_RETURNED_VALUE == 'Client' ]]
then
G_AGI resolvconf # Use by wg-quick to apply DNS in client setup
G_WHIP_MSG '[ INFO ] WireGuard client setup has been chosen\n
Please follow the instructions of your VPN provider to configure WireGuard.\n
If no WireGuard (auto)start is included, but you require it, please do the following:
1. Check for the created config file/interface name:
ls -Al /etc/wireguard/
It has a ".conf" file ending, lets assume "wg0-client.conf".
2. To start the VPN interface, run:
systemctl start wg-quick@wg0-client
3. To autostart the VPN interface on boot, run:
systemctl enable wg-quick@wg0-client
4. To disable autostart again, run:
systemctl disable wg-quick@wg0-client'
fi
fi
if To_Install 213 soju # soju
then
# Dependencies
aDEPS=('make' 'scdoc' 'gcc' 'libsqlite3-dev' 'libpam-dev')
# Download
local fallback_url='https://github.com/emersion/soju/releases/download/v0.8.0/soju-0.8.0.tar.gz'
Download_Install "$(curl -sSfL 'https://api.github.com/repos/emersion/soju/releases/latest' | mawk -F\" '/^ *"browser_download_url": ".*\/soju-[^"\/]*\.tar\.gz"$/{print $4}')"
# Build
G_EXEC cd soju-*
G_EXEC_OUTPUT=1 G_EXEC make all "-j$(nproc)" config_path='/mnt/dietpi_userdata/soju/config' GOFLAGS='-tags=pam,libsqlite3'
# Install
local commands=('soju' 'sojuctl' 'sojudb')
local manpages=('doc/soju.1' 'doc/sojuctl.1')
G_EXEC cp "${commands[@]}" /usr/local/bin/
G_EXEC mkdir -p /usr/local/share/man/man1
G_EXEC cp "${manpages[@]}" /usr/local/share/man/man1/
# Config
if [[ ! -f '/mnt/dietpi_userdata/soju/config' ]]
then
# We shall default to on-disk chat logs
# It's a lossy format, as stated under `message-store` subsection in https://soju.im/doc/soju.1.html#CONFIG_FILE
# But it's the recommended setting as per https://git.sr.ht/~emersion/soju/tree/master/doc/getting-started.md
G_EXEC mkdir -p /mnt/dietpi_userdata/soju
cat <<- '_EOF_' > /mnt/dietpi_userdata/soju/config
db sqlite3 /mnt/dietpi_userdata/soju/data.db
message-store fs /mnt/dietpi_userdata/soju/logs
listen irc+insecure://
listen unix+admin://
_EOF_
fi
# User
Create_User -d /mnt/dietpi_userdata/soju soju
G_EXEC chown 'soju:soju' -R /mnt/dietpi_userdata/soju
G_EXEC chmod 0660 -R /mnt/dietpi_userdata/soju
G_EXEC chmod 0770 /mnt/dietpi_userdata/soju
# Service: https://github.com/emersion/soju/blob/master/contrib/soju.service
# - CAP_NET_BIND_SERVICE needed for builtin identd server: https://github.com/emersion/soju/blob/master/doc/packaging.md#binding-to-privileged-ports
cat << '_EOF_' > /etc/systemd/system/soju.service
[Unit]
Description=soju IRC bouncer service
Documentation=https://soju.im/
Documentation=man:soju(1) man:sojuctl(1)
Wants=network-online.target
After=network-online.target
[Service]
User=soju
RuntimeDirectory=soju
WorkingDirectory=/mnt/dietpi_userdata/soju
AmbientCapabilities=CAP_NET_BIND_SERVICE
ExecStart=/usr/local/bin/soju
ExecReload=/bin/kill -HUP $MAINPID
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 169 lazylibrarian # LazyLibrarian
then
# Dependencies
aDEPS=('python3-venv')
Python_Deps cryptography pillow
# Download
Download_Install 'https://gitlab.com/LazyLibrarian/LazyLibrarian/-/archive/master/LazyLibrarian-master.tar.bz2'
# Add dummy unbundled.libs to prevent unnecessary attempt to remove bundled libraries which do not exist. It is falsely determined since the venv is inside the install dir.
> LazyLibrarian-master/unbundled.libs
# Install: Clear existing instance
[[ -d '/opt/lazylibrarian' ]] && G_EXEC rm -R /opt/lazylibrarian
G_EXEC mv LazyLibrarian-master /opt/lazylibrarian
# Python venv
G_EXEC python3 -m venv /opt/lazylibrarian/venv
G_EXEC_OUTPUT=1 G_EXEC /opt/lazylibrarian/venv/bin/pip install /opt/lazylibrarian
# User
Create_User -g dietpi lazylibrarian
# Data directory
G_EXEC mkdir -p /mnt/dietpi_userdata/lazylibrarian
G_EXEC chmod 0750 /mnt/dietpi_userdata/lazylibrarian
# Config: Preserve existing
[[ -f '/mnt/dietpi_userdata/lazylibrarian/config.ini' ]] || G_EXEC eval 'cat << '\''_EOF_'\'' > /mnt/dietpi_userdata/lazylibrarian/config.ini
[GENERAL]
ebook_dir = /mnt/dietpi_userdata/ebooks
audio_dir = /mnt/dietpi_userdata/audiobooks
download_dir = /mnt/dietpi_userdata/downloads
[LOGGING]
logdir = /var/log/lazylibrarian
_EOF_'
# Add version info to prevent unnecessary update attempt on first service start of fresh install and make update checks meaningful
G_EXEC mkdir -p /mnt/dietpi_userdata/lazylibrarian/cache
curl -sSfL 'https://gitlab.com/api/v4/projects/9317860/repository/commits?per_page=1' | mawk -F\" '{print $8}' > /mnt/dietpi_userdata/lazylibrarian/cache/version.txt
# Permissions
G_EXEC chown -R 'lazylibrarian' /mnt/dietpi_userdata/lazylibrarian
# Service: https://gitlab.com/LazyLibrarian/LazyLibrarian/-/blob/master/init
cat << '_EOF_' > /etc/systemd/system/lazylibrarian.service
[Unit]
Description=LazyLibrarian
Documentation=https://lazylibrarian.gitlab.io
Wants=network-online.target
After=network-online.target remote-fs.target transmission-daemon.service qbittorrent.service rtorrent.service nzbget.service deluged.service aria2.service sabnzbd.service
[Service]
SyslogIdentifier=LazyLibrarian
User=lazylibrarian
UMask=002
LogsDirectory=lazylibrarian
ExecStart=/opt/lazylibrarian/venv/bin/python /opt/lazylibrarian/LazyLibrarian.py --datadir=/mnt/dietpi_userdata/lazylibrarian --nolaunch
ExitType=cgroup
# Hardening
ProtectSystem=strict
ProtectHome=true
PrivateDevices=true
PrivateTmp=true
ProtectKernelTunables=true
ProtectControlGroups=true
ReadWritePaths=-/opt/lazylibrarian -/mnt -/media -/var/log/lazylibrarian -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 127 birdnet # BirdNET-Go: https://github.com/tphakala/birdnet-go/blob/main/install.sh
then
local bnet_vers='nightly-20251028'
local bnet_inst='/opt/birdnet'
local bnet_data='/mnt/dietpi_userdata/birdnet'
local bnet_conf="$bnet_data/.config/birdnet-go/config.yaml"
local bnet_user='birdnet'
local bnet_port=8127
# Dependencies
aDEPS=('sox' 'patchelf')
# - Add yq only on first install
local install_yq=0
[[ -f $bnet_conf ]] || command -v yq > /dev/null || install_yq=1 aDEPS+=('yq')
case $G_HW_ARCH in
3) local arch='arm64';;
*) local arch='amd64';;
esac
# Download
Download_Install "https://github.com/tphakala/birdnet-go/releases/download/$bnet_vers/birdnet-go-linux-$arch.tar.gz" birdnet
# Change rpath for birdnet-go to make use of libtensorflowlite_c.so in its own dir
G_EXEC patchelf --set-rpath "$bnet_inst" birdnet/birdnet-go
# Install: Remove old dir on reinstall
[[ -d $bnet_inst ]] && G_EXEC rm -R "$bnet_inst"
G_EXEC mv birdnet "$bnet_inst"
# User
Create_User -G audio -d "$bnet_data" "$bnet_user"
# Data dir
G_EXEC mkdir -p "$bnet_data"
G_EXEC chown -R "$bnet_user:$bnet_user" "$bnet_data"
# Config
# - Checks hard-coded paths in this order: ~/.config/birdnet-go/config.yaml > /etc/birdnet-go/config.yaml
if [[ ! -f $bnet_conf ]]
then
# Initial call to generate ~/.config/birdnet-go/config.yaml
G_EXEC runuser -u "$bnet_user" -- "$bnet_inst/birdnet-go"
# Replace paths, otherwise defaults to the working directory
# Disable MQTT and CPU/memory/disk monitoring
# Enable new web UI at $bnet_port
G_EXEC yq -yi "
.output.sqlite.path = \"$bnet_data/birdnet.db\" |
.main.log.path = \"$bnet_data/logs/birdnet.log\" |
.realtime.log.path = \"$bnet_data/logs/birdnet.txt\" |
.realtime.audio.export.path = \"$bnet_data/clips\" |
.realtime.output.file.path = \"$bnet_data/output\" |
.realtime.mqtt.enabled = false |
.realtime.monitoring.enabled = false |
.realtime.webserver.enabled = true |
.realtime.dashboard.newui = true |
.webserver.port = $bnet_port" "$bnet_conf"
# Purge yq if it was installed for BirdNET-Go only
(( $install_yq )) && G_AGP yq
fi
# Service
cat << _EOF_ > /etc/systemd/system/birdnet.service || exit 1
[Unit]
Description=BirdNet-Go
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
User=$bnet_user
WorkingDirectory=$bnet_data
ExecStart=$bnet_inst/birdnet-go realtime
Restart=on-failure
RestartSec=5
# Hardening
ProtectSystem=strict
ProtectHome=1
PrivateTmp=1
ProtectKernelTunables=1
ProtectControlGroups=1
ReadWritePaths=-$bnet_data -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
fi
if To_Install 12 # RustDesk Server
then
local rd_inst='/opt/rustdesk'
local rd_data='/mnt/dietpi_userdata/rustdesk'
case "$G_HW_ARCH" in
2) local arch='armv7';;
3) local arch='arm64v8';;
*) local arch='amd64';;
esac
# Download
local fallback_url="https://github.com/rustdesk/rustdesk-server/releases/download/1.1.14/rustdesk-server-linux-$arch.zip"
Download_Install "$(curl -sSfL 'https://api.github.com/repos/rustdesk/rustdesk-server/releases/latest' | mawk -F\" "/^ *\"browser_download_url\": \".*\/rustdesk-server-linux-$arch\.zip\"$/{print \$4}")" rustdesk
# Install: remove old install dir
[[ -d $rd_inst ]] && G_EXEC rm -R "$rd_inst"
G_EXEC mv "rustdesk/$arch" "$rd_inst"
G_EXEC rm -R rustdesk
# User
Create_User -d "$rd_data" rustdesk
# Data dir and config files:
# - https://github.com/rustdesk/rustdesk-server/blob/c6502179/README.md
# - https://github.com/rustdesk/rustdesk-server/discussions/371#discussioncomment-13971105
G_EXEC mkdir -p "$rd_data"
[[ -f $rd_data/signal.env ]] || cat << _EOF_ > "$rd_data/signal.env" || exit 1
# RustDesk Signal Server settings: "$rd_data/signal.env
# TCP/UDP listening port (default: 21116)
#PORT=21116
# Client authentication key, else the auto-generated $rd_data/id_ed25519.pub is used
#KEY=
# Log level error|warn|info|debug|trace (default: info)
# NB: "info" logs client IPs, hence we suggest to switch to "warn" for production.
#RUST_LOG=info
# Database path relative to $rd_data (default: db_v2.sqlite3)
#DB_URL=db_v2.sqlite3
# Set to "Y" to disallows direct P2P connections (default: N)
#ALWAYS_USE_RELAY=N
_EOF_
[[ -f $rd_data/relay.env ]] || cat << _EOF_ > "$rd_data/relay.env" || exit 1
# RustDesk Relay Server settings: $rd_data/relay.env
# TCP listening port (default: 21117)
#PORT=21117
# Client authentication key, else the auto-generated $rd_data/id_ed25519.pub is used
#KEY=
# Log level error|warn|info|debug|trace (default: info)
# NB: "info" logs client IPs, hence we suggest to switch to "warn" for production.
#RUST_LOG=info
# Total bandwidth limit in MiB/s (default: 1024)
#TOTAL_BANDWIDTH=1024
# Single connection bandwidth limit in MiB/s (default: 128)
#SINGLE_BANDWIDTH=128
# Bandwidth limit for clients listed in $rd_data/blacklist.txt in MiB/s (default: 32)
#LIMIT_SPEED=32
# Delay before limiting bandwidth in seconds (default: 1800)
#DOWNGRADE_START_CHECK=1800
# Threshold before limiting bandwidth in bit/ms (default: 0.66)
#DOWNGRADE_THRESHOLD=0.66
_EOF_
G_EXEC chmod 0700 "$rd_data"
G_EXEC chmod 0600 "$rd_data"/*
G_EXEC chown -R rustdesk:0 "$rd_data"
# Service hbbs: https://github.com/rustdesk/rustdesk-server/blob/master/systemd/rustdesk-hbbs.service
cat << _EOF_ > /etc/systemd/system/rustdesksignal.service
[Unit]
Description=RustDesk Signal Server
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=RustDesk Signal Server
User=rustdesk
WorkingDirectory=$rd_data
EnvironmentFile=$rd_data/signal.env
ExecStart=$rd_inst/hbbs
Restart=on-failure
RestartSec=5
# Hardening
ProtectSystem=strict
ProtectHome=1
PrivateUsers=1
PrivateTmp=1
PrivateDevices=1
ProtectKernelTunables=1
ProtectControlGroups=1
ProtectKernelModules=1
ProtectKernelLogs=1
ProtectClock=1
ReadWritePaths=-$rd_data -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
# Service hbbr: https://github.com/rustdesk/rustdesk-server/blob/master/systemd/rustdesk-hbbr.service
cat << _EOF_ > /etc/systemd/system/rustdeskrelay.service
[Unit]
Description=RustDesk Relay Server
Wants=network-online.target
After=network-online.target
StartLimitIntervalSec=60
StartLimitBurst=3
[Service]
SyslogIdentifier=RustDesk Relay Server
User=rustdesk
WorkingDirectory=$rd_data
EnvironmentFile=$rd_data/relay.env
ExecStart=$rd_inst/hbbr
Restart=on-failure
RestartSec=5
LimitNOFILE=4096
# Hardening
ProtectSystem=strict
ProtectHome=1
PrivateUsers=1
PrivateTmp=1
PrivateDevices=1
ProtectKernelTunables=1
ProtectControlGroups=1
ProtectKernelModules=1
ProtectKernelLogs=1
ProtectClock=1
ReadWritePaths=-$rd_data -/tmp
[Install]
WantedBy=multi-user.target
_EOF_
aSTART_SERVICES+=('rustdesksignal' 'rustdeskrelay')
unset -v rd_inst rd_data
fi
}
# $1: 0=None -1=Dropbear -2=OpenSSH
Apply_SSHServer_Choices()
{
if (( $1 == 0 ))
then
[[ ${aSOFTWARE_INSTALL_STATE[104]} == [01] ]] && aSOFTWARE_INSTALL_STATE[104]=0 || aSOFTWARE_INSTALL_STATE[104]=-1
[[ ${aSOFTWARE_INSTALL_STATE[105]} == [01] ]] && aSOFTWARE_INSTALL_STATE[105]=0 || aSOFTWARE_INSTALL_STATE[105]=-1
elif (( $1 == -2 ))
then
[[ ${aSOFTWARE_INSTALL_STATE[104]} == [01] ]] && aSOFTWARE_INSTALL_STATE[104]=0 || aSOFTWARE_INSTALL_STATE[104]=-1
[[ ${aSOFTWARE_INSTALL_STATE[105]} == [01] ]] && aSOFTWARE_INSTALL_STATE[105]=1 || aSOFTWARE_INSTALL_STATE[105]=2
else
[[ ${aSOFTWARE_INSTALL_STATE[104]} == [01] ]] && aSOFTWARE_INSTALL_STATE[104]=1 || aSOFTWARE_INSTALL_STATE[104]=2
[[ ${aSOFTWARE_INSTALL_STATE[105]} == [01] ]] && aSOFTWARE_INSTALL_STATE[105]=0 || aSOFTWARE_INSTALL_STATE[105]=-1
fi
}
# $1: 0=None -1=DietPi-RAMlog #1 -2=DietPi-RAMlog #2 -3=Full
Apply_Logging_Choices()
{
if (( $1 == 0 ))
then
[[ ${aSOFTWARE_INSTALL_STATE[101]} == [01] ]] && aSOFTWARE_INSTALL_STATE[101]=0 || aSOFTWARE_INSTALL_STATE[101]=-1
[[ ${aSOFTWARE_INSTALL_STATE[102]} == [01] ]] && aSOFTWARE_INSTALL_STATE[102]=0 || aSOFTWARE_INSTALL_STATE[102]=-1
[[ ${aSOFTWARE_INSTALL_STATE[103]} == [01] ]] && aSOFTWARE_INSTALL_STATE[103]=0 || aSOFTWARE_INSTALL_STATE[103]=-1
elif (( $1 == -2 ))
then
[[ ${aSOFTWARE_INSTALL_STATE[101]} == [01] ]] && aSOFTWARE_INSTALL_STATE[101]=0 || aSOFTWARE_INSTALL_STATE[101]=-1
[[ ${aSOFTWARE_INSTALL_STATE[102]} == [01] ]] && aSOFTWARE_INSTALL_STATE[102]=0 || aSOFTWARE_INSTALL_STATE[102]=-1
[[ ${aSOFTWARE_INSTALL_STATE[103]} == [01] ]] && aSOFTWARE_INSTALL_STATE[103]=1 || aSOFTWARE_INSTALL_STATE[103]=2
elif (( $1 == -3 ))
then
[[ ${aSOFTWARE_INSTALL_STATE[101]} == [01] ]] && aSOFTWARE_INSTALL_STATE[101]=1 || aSOFTWARE_INSTALL_STATE[101]=2
[[ ${aSOFTWARE_INSTALL_STATE[102]} == [01] ]] && aSOFTWARE_INSTALL_STATE[102]=1 || aSOFTWARE_INSTALL_STATE[102]=2
[[ ${aSOFTWARE_INSTALL_STATE[103]} == [01] ]] && aSOFTWARE_INSTALL_STATE[103]=0 || aSOFTWARE_INSTALL_STATE[103]=-1
else
set -- '-1'
[[ ${aSOFTWARE_INSTALL_STATE[101]} == [01] ]] && aSOFTWARE_INSTALL_STATE[101]=0 || aSOFTWARE_INSTALL_STATE[101]=-1
[[ ${aSOFTWARE_INSTALL_STATE[102]} == [01] ]] && aSOFTWARE_INSTALL_STATE[102]=0 || aSOFTWARE_INSTALL_STATE[102]=-1
[[ ${aSOFTWARE_INSTALL_STATE[103]} == [01] ]] && aSOFTWARE_INSTALL_STATE[103]=1 || aSOFTWARE_INSTALL_STATE[103]=2
fi
INDEX_LOGGING=$1
# If DietPi-RAMlog is and stays installed, apply logging index now to make the change permanent for DietPi-LogClear even if no actual install is required.
[[ $INDEX_LOGGING == -[12] && ${aSOFTWARE_INSTALL_STATE[103]} == 2 ]] && G_CONFIG_INJECT 'INDEX_LOGGING=' "INDEX_LOGGING=$INDEX_LOGGING" /boot/dietpi/.installed
}
Install_Apply_GPU_Settings()
{
# Offer to install GPU driver when Xserver installed (x86_64 support currently only)
if (( $G_HW_ARCH == 10 && $G_HW_MODEL == 21 && ${aSOFTWARE_INSTALL_STATE[6]} == 1 ))
then
local gpu_current=$(sed -n '/^[[:blank:]]*CONFIG_GPU_DRIVER=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ ${gpu_current,,} == 'none' ]] && G_WHIP_YESNO 'No GPU Driver is currently installed.\n\nWould you like to select a GPU driver for installation now?' && /boot/dietpi/dietpi-config 2
fi
# RPi: Disable headless mode and raise memory split to default for GUI applications
(( $G_HW_MODEL < 10 )) || return 0
# Kodi, Jellyfin, DXX-Rebirth, RPi Cam Web Interface, Amiberry, Chromium, Desktops, OpenTyrian, Moonlight (CLI), Moonlight (GUI), GZDoom
(( ${aSOFTWARE_INSTALL_STATE[31]} == 1 ||
${aSOFTWARE_INSTALL_STATE[178]} == 1 ||
${aSOFTWARE_INSTALL_STATE[112]} == 1 ||
${aSOFTWARE_INSTALL_STATE[59]} == 1 ||
${aSOFTWARE_INSTALL_STATE[108]} == 1 ||
${aSOFTWARE_INSTALL_STATE[113]} == 1 ||
${aSOFTWARE_INSTALL_STATE[23]} == 1 ||
${aSOFTWARE_INSTALL_STATE[24]} == 1 ||
${aSOFTWARE_INSTALL_STATE[25]} == 1 ||
${aSOFTWARE_INSTALL_STATE[26]} == 1 ||
${aSOFTWARE_INSTALL_STATE[51]} == 1 ||
${aSOFTWARE_INSTALL_STATE[173]} == 1 ||
${aSOFTWARE_INSTALL_STATE[207]} == 1 ||
${aSOFTWARE_INSTALL_STATE[208]} == 1 ||
${aSOFTWARE_INSTALL_STATE[11]} == 1 )) || return 0
# Disable headless mode
/boot/dietpi/func/dietpi-set_hardware headless 0
# Raise memory split to default
local default_gpu_mem=76
(( $G_HW_MEMORY_SIZE < 1024 )) && default_gpu_mem=64
local current_gpu_mem=$(sed -n '/^[[:blank:]]*gpu_mem_1024=/{s/^[^=]*=//p;q}' /boot/config.txt)
[[ $current_gpu_mem ]] || current_gpu_mem=$default_gpu_mem
(( $current_gpu_mem < $default_gpu_mem )) && /boot/dietpi/func/dietpi-set_hardware gpumemsplit "$default_gpu_mem"
}
Uninstall_Software()
{
# $1: Service name
# $2: Remove user named $2 or $1 if $2 == 1 (optional)
# $3: Remove group named $3 or $1 if $3 == 1 (optional)
Remove_Service()
{
local unmasked disabled
if [[ -f '/etc/systemd/system/'$1'.service' ]]
then
G_EXEC systemctl --no-reload disable --now "$1" && disabled=1
G_EXEC rm "/etc/systemd/system/$1.service" && unmasked=1
elif [[ -f '/lib/systemd/system/'$1'.service' ]]
then
G_EXEC systemctl --no-reload unmask "$1" && unmasked=1
G_EXEC systemctl --no-reload disable --now "$1" && disabled=1
fi
if [[ -f '/etc/init.d/'$1 ]]
then
[[ $unmasked ]] || G_EXEC systemctl --no-reload unmask "$1"
[[ $disabled ]] || G_EXEC systemctl --no-reload disable "$1"
[[ $disabled ]] || G_EXEC systemctl stop "$1" # --now does not work with generated wrapper units
G_EXEC rm "/etc/init.d/$1"
G_EXEC update-rc.d "$1" remove
fi
[[ -d '/etc/systemd/system/'$1'.service.d' ]] && G_EXEC rm -R "/etc/systemd/system/$1.service.d"
[[ $2 ]] && getent passwd "${2/#1/$1}" > /dev/null && G_EXEC userdel "${2/#1/$1}"
[[ $3 ]] && getent group "${3/#1/$1}" > /dev/null && G_EXEC groupdel "${3/#1/$1}"
}
# $1: Database and username
Remove_Database()
{
systemctl start mariadb || return 1
mysqladmin -f drop "$1"
mysql -e "drop user $1@localhost;"
}
# NB: "systemctl daemon-reload" is executed at the end of this function
G_NOTIFY_3_MODE='Step'
local software_id
To_Uninstall()
{
(( ${aSOFTWARE_INSTALL_STATE[$1]} == -1 )) || return 1
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" "Uninstalling ${aSOFTWARE_NAME[$1]}: ${aSOFTWARE_DESC[$1]}"
software_id=$1
}
if To_Uninstall 23 # LXDE
then
apt-mark auto lxhotkey-plugin-openbox upower librsvg2-common 2> /dev/null
G_AGP lxde 'lxde-*'
G_EXEC rm -Rf /{root,home/*}/.{config,cache}/{lxpanel,lxsession,lxterminal,openbox,pcmanfm,dconf}
fi
if To_Uninstall 173 # LXQt
then
apt-mark auto qterminal xarchiver lxde-icon-theme upower xscreensaver leafpad featherpad speedcrunch 2> /dev/null
G_AGP lxqt
G_EXEC rm -Rf /{root,home/*}/.config/lxqt
fi
if To_Uninstall 174 # GIMP
then
G_AGP gimp
fi
if To_Uninstall 175 # Xfce Power Manager
then
G_AGP xfce4-power-manager
fi
if To_Uninstall 24 # MATE
then
apt-mark auto upower policykit-1 2> /dev/null
G_AGP mate-desktop-environment-core mate-media
fi
if To_Uninstall 26 # GNUstep
then
G_AGP wmaker gnustep gnustep-devel gnustep-games
G_EXEC rm -Rf /{root,home/*}/GNUstep
fi
if To_Uninstall 25 # Xfce
then
apt-mark auto gnome-icon-theme tango-icon-theme upower policykit-1 polkitd 2> /dev/null
G_AGP xfce4 xfce4-terminal
fi
if To_Uninstall 22 # QuiteRSS
then
G_AGP quiterss
fi
if To_Uninstall 30 # NoMachine
then
G_AGP nomachine
G_EXEC rm -Rf /{root,home/*}/NoMachine
fi
if To_Uninstall 29 # XRDP
then
G_AGP xrdp xorgxrdp
fi
if To_Uninstall 44 # Transmission
then
Remove_Service transmission-daemon debian-transmission debian-transmission
G_AGP transmission-daemon
fi
if To_Uninstall 47 # ownCloud
then
# Remove background cron job
crontab -u www-data -l | grep -v '/var/www/owncloud/.*cron' | crontab -u www-data -
# Disable and remove PHP modules
command -v phpdismod > /dev/null && G_EXEC phpdismod dietpi-owncloud
G_EXEC rm -f /etc/php/*/mods-available/dietpi-owncloud.ini
# Disable and remove webserver configs
command -v a2dissite > /dev/null && a2dissite dietpi-owncloud
[[ -f '/etc/apache2/sites-available/dietpi-owncloud.conf' ]] && G_EXEC rm /etc/apache2/sites-available/dietpi-owncloud.conf
[[ -f '/etc/nginx/sites-dietpi/dietpi-owncloud.conf' ]] && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-owncloud.conf
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-owncloud
[[ -f '/etc/lighttpd/conf-available/99-dietpi-owncloud.conf' ]] && G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-owncloud.conf
G_WHIP_MSG "DietPi will perform an automated backup of your ownCloud database and installation directory, which will be stored inside your ownCloud data directory.\n\nThe data directory won't be removed. So you can recover your whole ownCloud instance any time later.\n\nRemove the data directory manually if you don't need it anymore."
# Find datadir for backups
local datadir=$(grep -m1 "^[[:blank:]]*'datadirectory'" /var/www/owncloud/config/config.php | mawk '{print $3}' | sed "s/[',]//g")
[[ $datadir ]] || datadir='/mnt/dietpi_userdata/owncloud_data'
# Drop MariaDB users and database
if systemctl start mariadb
then
local dbuser=$(grep -m1 "^[[:blank:]]*'dbuser'" /var/www/owncloud/config/config.php | mawk '{print $3}' | sed 's/,//')
local dbhost=$(grep -m1 "^[[:blank:]]*'dbhost'" /var/www/owncloud/config/config.php | mawk '{print $3}' | sed 's/,//')
mysql -e "drop user $dbuser@$dbhost;"
mysql -e "drop user $dbuser;" 2> /dev/null
# Perform database backup if existent, otherwise skip to not overwrite existing one
[[ -d '/mnt/dietpi_userdata/mysql/owncloud' ]] && mysqldump owncloud > "$datadir/dietpi-owncloud-database-backup.sql"
mysqladmin drop owncloud -f
fi
if [[ -d '/var/www/owncloud' ]]
then
# Backup ownCloud installation dir
G_EXEC cp -a /var/www/owncloud/. "$datadir/dietpi-owncloud-installation-backup/"
# Remove ownCloud installation dir
G_EXEC rm -R /var/www/owncloud
fi
# Remove redirect configs
if grep -q 'owncloud' /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf 2> /dev/null
then
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-dav_redirect
G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
fi
if grep -q 'owncloud' /etc/apache2/conf-available/dietpi-dav_redirect.conf 2> /dev/null
then
command -v a2disconf > /dev/null && a2disconf dietpi-dav_redirect
G_EXEC rm /etc/apache2/conf-available/dietpi-dav_redirect.conf
fi
grep -q 'owncloud' /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf 2> /dev/null && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf
fi
if To_Uninstall 168 # Nextcloud Talk + Coturn server
then
Remove_Service coturn
G_AGP coturn
[[ -f '/etc/turnserver.conf' ]] && G_EXEC rm /etc/turnserver.conf
systemctl start redis-server
if systemctl start mariadb
then
runuser -u www-data -- php /var/www/nextcloud/occ maintenance:mode --off
runuser -u www-data -- php /var/www/nextcloud/occ app:disable spreed
fi
G_DIETPI-NOTIFY 2 'Disabled Nextcloud Talk app, but you need to remove it manually from Nextcloud web UI if desired.'
fi
if To_Uninstall 114 # Nextcloud
then
crontab -u www-data -l | grep -v '/var/www/nextcloud/cron.php' | crontab -u www-data -
# Disable and remove PHP modules
command -v phpdismod > /dev/null && G_EXEC phpdismod dietpi-nextcloud
G_EXEC rm -f /etc/php/*/mods-available/dietpi-nextcloud.ini
# Disable and remove webserver configs
command -v a2dissite > /dev/null && a2dissite dietpi-nextcloud
[[ -f '/etc/apache2/sites-available/dietpi-nextcloud.conf' ]] && G_EXEC rm /etc/apache2/sites-available/dietpi-nextcloud.conf
[[ -f '/etc/nginx/sites-dietpi/dietpi-nextcloud.conf' ]] && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-nextcloud.conf
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-nextcloud
[[ -f '/etc/lighttpd/conf-available/99-dietpi-nextcloud.conf' ]] && G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-nextcloud.conf
G_WHIP_MSG "DietPi will perform an automated backup of your Nextcloud database and installation directory, which will be stored inside your Nextcloud data directory.\n\nThe data directory won't be removed. So you can recover your whole Nextcloud instance any time later.\n\nRemove the data directory manually if you don't need it anymore."
# Find datadir for backups
local datadir=$(grep -m1 "^[[:blank:]]*'datadirectory'" /var/www/nextcloud/config/config.php | mawk '{print $3}' | sed "s/[',]//g")
[[ $datadir ]] || datadir='/mnt/dietpi_userdata/nextcloud_data'
# Drop MariaDB users and database
if systemctl start mariadb
then
local dbuser=$(grep -m1 "^[[:blank:]]*'dbuser'" /var/www/nextcloud/config/config.php | mawk '{print $3}' | sed 's/,//')
local dbhost=$(grep -m1 "^[[:blank:]]*'dbhost'" /var/www/nextcloud/config/config.php | mawk '{print $3}' | sed 's/,//')
mysql -e "drop user $dbuser@$dbhost;"
mysql -e "drop user $dbuser;" 2> /dev/null
# Perform database backup if existent, otherwise skip to not overwrite existing one
[[ -d '/mnt/dietpi_userdata/mysql/nextcloud' ]] && mysqldump nextcloud > "$datadir/dietpi-nextcloud-database-backup.sql"
mysqladmin drop nextcloud -f
fi
if [[ -d '/var/www/nextcloud' ]]
then
# Backup Nextcloud installation dir
G_EXEC cp -a /var/www/nextcloud/. "$datadir/dietpi-nextcloud-installation-backup/"
# Remove Nextcloud installation dir
G_EXEC rm -R /var/www/nextcloud
fi
# Remove redirect configs
if grep -q 'nextcloud' /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf 2> /dev/null
then
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-dav_redirect
G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
fi
if grep -q 'nextcloud' /etc/apache2/conf-available/dietpi-dav_redirect.conf 2> /dev/null
then
command -v a2disconf > /dev/null && a2disconf dietpi-dav_redirect
G_EXEC rm /etc/apache2/conf-available/dietpi-dav_redirect.conf
fi
grep -q 'nextcloud' /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf 2> /dev/null && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf
fi
if To_Uninstall 63 # LinuxDash
then
[[ -d '/var/www/linuxdash' ]] && G_EXEC rm -R /var/www/linuxdash
fi
if To_Uninstall 27 # TasmoAdmin
then
[[ -d '/var/www/tasmoadmin' ]] && G_EXEC rm -R /var/www/tasmoadmin
# Remove webserver configs
command -v a2dissite > /dev/null && a2dissite dietpi-tasmoadmin
[[ -f '/etc/apache2/conf-available/dietpi-tasmoadmin.conf' ]] && G_EXEC rm /etc/apache2/conf-available/dietpi-tasmoadmin.conf
[[ -f '/etc/nginx/sites-dietpi/dietpi-tasmoadmin.conf' ]] && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-tasmoadmin.conf
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-tasmoadmin
[[ -f '/etc/lighttpd/conf-available/99-dietpi-tasmoadmin.conf' ]] && G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-tasmoadmin.conf
fi
if To_Uninstall 206 # openHAB
then
G_AGP openhab
[[ -f '/etc/apt/sources.list.d/dietpi-openhab.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-openhab.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-openhab.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-openhab.gpg
[[ -f '/etc/apt/preferences.d/dietpi-openhab' ]] && G_EXEC rm /etc/apt/preferences.d/dietpi-openhab
fi
if To_Uninstall 83 # Apache
then
Remove_Service apache2
G_AGP apache2 libapache2-mod-php*
G_EXEC rm -Rf /{etc,var/{lib,log}}/apache2 /{etc/php,var/lib/php/modules}/*/apache2
fi
if To_Uninstall 85 # Nginx
then
Remove_Service nginx
G_AGP nginx 'nginx-*'
fi
if To_Uninstall 84 # Lighttpd
then
Remove_Service lighttpd
G_AGP lighttpd
# Config
[[ -d '/etc/lighttpd' ]] && G_EXEC rm -R /etc/lighttpd
[[ -d '/var/www/index.lighttpd.html' ]] && G_EXEC rm /var/www/index.lighttpd.html
# Certbot hook
[[ -f '/etc/letsencrypt/renewal-hooks/deploy/dietpi-lighttpd.sh' ]] && G_EXEC rm /etc/letsencrypt/renewal-hooks/deploy/dietpi-lighttpd.sh
# - Pre-v8.0
[[ -f 'etc/systemd/system/certbot.service.d/dietpi-lighttpd.conf' ]] && G_EXEC rm /etc/systemd/system/certbot.service.d/dietpi-lighttpd.conf
[[ -d '/etc/systemd/system/certbot.service.d' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /etc/systemd/system/certbot.service.d
fi
if To_Uninstall 91 # Redis
then
Remove_Service redis-server
G_AGP redis-server redis-tools 'php*-redis'
G_EXEC rm -f /etc/sysctl.d/98-dietpi-redis.conf
fi
if To_Uninstall 34 # PHP Composer
then
[[ -f '/usr/local/bin/composer' ]] && G_EXEC rm /usr/local/bin/composer
fi
if To_Uninstall 89 # PHP
then
Remove_Service "php$PHP_VERSION-fpm"
G_AGP 'php*-*' 'libapache2-mod-php*'
G_EXEC rm -Rf /{etc,var/lib}/php /var/log/php*-fpm.log
[[ -f '/etc/tmpfiles.d/dietpi-php_sessions.conf' ]] && G_EXEC rm /etc/tmpfiles.d/dietpi-php_sessions.conf
[[ -d '/var/tmp/php_upload_tmp' ]] && G_EXEC rm -R /var/tmp/php_upload_tmp # Pre-v6.32
fi
if To_Uninstall 90 # phpMyAdmin
then
G_EXEC rm -Rf /var/www/phpmyadmin
if [[ -f '/etc/apache2/sites-available/dietpi-phpmyadmin.conf' ]]
then
command -v a2dissite > /dev/null && a2dissite dietpi-phpmyadmin
G_EXEC rm /etc/apache2/sites-available/dietpi-phpmyadmin.conf
fi
if [[ -f '/etc/lighttpd/conf-available/98-dietpi-phpmyadmin.conf' ]]
then
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-phpmyadmin
G_EXEC rm /etc/lighttpd/conf-available/98-dietpi-phpmyadmin.conf
fi
[[ -f '/etc/apache2/sites-dietpi/dietpi-phpmyadmin.conf' ]] && G_EXEC rm /etc/apache2/sites-dietpi/dietpi-phpmyadmin.conf
Remove_Database phpmyadmin
fi
if To_Uninstall 184 # Tor Relay
then
Remove_Service tor
G_AGP tor obfs4proxy
fi
if To_Uninstall 54 # phpBB
then
[[ -d '/var/www/phpbb' ]] && G_EXEC rm -R /var/www/phpbb
[[ -d '/var/www/phpBB3' ]] && G_EXEC rm -R /var/www/phpBB3 # Pre-v6.33
Remove_Database phpbb
Remove_Database phpbb3 # Pre-v6.33
fi
if To_Uninstall 115 # Webmin
then
Remove_Service webmin
G_AGP webmin
G_EXEC rm -f /etc/apt/sources.list.d/dietpi-webmin.list /etc/apt/trusted.gpg.d/dietpi-webmin.asc
fi
if To_Uninstall 32 # ympd
then
G_AGP ympd
fi
if To_Uninstall 148 # myMPD
then
Remove_Service mympd 1 1 # user and group for pre-v8.12
if [[ -f '/usr/local/share/ca-certificates/DietPi_myMPD_CA.crt' || -L '/usr/local/share/ca-certificates/DietPi_myMPD_CA.crt' ]]
then
G_EXEC rm /usr/local/share/ca-certificates/DietPi_myMPD_CA.crt
G_EXEC update-ca-certificates -f
fi
G_AGP mympd
[[ -f '/etc/apt/sources.list.d/dietpi-mympd.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-mympd.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-mympd.asc' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-mympd.asc
[[ -d '/var/lib/mympd' ]] && G_EXEC rm -R /var/lib/mympd
[[ -d '/var/lib/private/mympd' ]] && G_EXEC rm -R /var/lib/private/mympd
[[ -d '/var/cache/mympd' ]] && G_EXEC rm -R /var/cache/mympd
[[ -d '/var/cache/private/mympd' ]] && G_EXEC rm -R /var/cache/private/mympd
# pre-v8.0
[[ -f '/lib/systemd/system/mympd.service' ]] && G_EXEC rm /lib/systemd/system/mympd.service
command -v mympd > /dev/null && G_EXEC rm "$(command -v mympd)"
command -v mympd-config > /dev/null && G_EXEC rm "$(command -v mympd-config)" # myMPD pre-v8.0.0
command -v mympd-script > /dev/null && G_EXEC rm "$(command -v mympd-script)"
[[ -d '/usr/share/doc/mympd' ]] && G_EXEC rm -R /usr/share/doc/mympd
G_EXEC rm -f /usr/share/man/man1/mympd* /etc/mympd.conf* # /etc: myMPD pre-v8.0.0
fi
if To_Uninstall 128 # MPD
then
Remove_Service mpd 1 1 # group for pre-v6.29
G_AGP mpd
[[ -d '/var/log/mpd' ]] && G_EXEC rm -R /var/log/mpd
[[ -d '/mnt/dietpi_userdata/.mpd_cache' ]] && G_EXEC rm -R /mnt/dietpi_userdata/.mpd_cache
[[ -f '/etc/mpd.conf' ]] && G_EXEC rm /etc/mpd.conf
[[ -f '/usr/local/etc/mpd.conf' ]] && G_EXEC rm /usr/local/etc/mpd.conf && G_EXEC rmdir --ignore-fail-on-non-empty /usr/local/etc # pre-v6.29
[[ -f '/etc/default/mpd' ]] && G_EXEC rm /etc/default/mpd # pre-v6.20
fi
if To_Uninstall 122 # Node-RED
then
Remove_Service node-red nodered nodered
# Configs
[[ -f '/etc/sudoers.d/nodered' ]] && G_EXEC rm /etc/sudoers.d/nodered
[[ -f '/etc/bashrc.d/dietpi-node-red.sh' ]] && G_EXEC rm /etc/bashrc.d/dietpi-node-red.sh
# Data
[[ -d '/mnt/dietpi_userdata/node-red' ]] && G_EXEC rm -R /mnt/dietpi_userdata/node-red
G_EXEC rm -Rf /{root,home/*}/.node-red # Pre-v6.25
# Pre-v7.0
command -v npm > /dev/null && npm r -g node-red
[[ -f '/usr/local/bin/node-red' ]] && G_EXEC rm /usr/local/bin/node-red
[[ -f '/usr/local/bin/node-red-pi' ]] && G_EXEC rm /usr/local/bin/node-red-pi
fi
if To_Uninstall 195 # youtube-dl
then
G_EXEC rm -f /usr/local/bin/{yt-dlp,youtube-dl,youtube-dl-py2}
fi
if To_Uninstall 123 # Mosquitto
then
Remove_Service mosquitto
G_AGP mosquitto
[[ -d '/etc/mosquitto' ]] && G_EXEC rm -R /etc/mosquitto
[[ -f '/etc/apt/sources.list.d/dietpi-mosquitto.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-mosquitto.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-mosquitto.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-mosquitto.gpg
fi
if To_Uninstall 124 # NAA Daemon
then
G_AGP networkaudiod
fi
if To_Uninstall 129 # O!MPD
then
[[ -d '/var/www/ompd' ]] && G_EXEC rm -R /var/www/ompd
[[ -f '/etc/lighttpd/conf-enabled/99-dietpi-ompd.conf' ]] && G_EXEC lighty-disable-mod dietpi-ompd
[[ -f '/etc/lighttpd/conf-available/99-dietpi-ompd.conf' ]] && G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-ompd.conf
Remove_Database ompd
fi
if To_Uninstall 131 # Blynk Server
then
Remove_Service blynkserver blynk blynk
[[ -d '/mnt/dietpi_userdata/blynk' ]] && G_EXEC rm -R /mnt/dietpi_userdata/blynk
[[ -d '/var/log/blynk' ]] && G_EXEC rm -R /var/log/blynk
command -v npm > /dev/null && npm r -g blynk-library
[[ -f '/usr/local/bin/blynk-ctrl' ]] && G_EXEC rm /usr/local/bin/blynk-ctrl
[[ -f '/usr/local/bin/blynk-client' ]] && G_EXEC rm /usr/local/bin/blynk-client
[[ -d '/etc/blynkserver' ]] && G_EXEC rm -R /etc/blynkserver # Pre-v6.19
fi
if To_Uninstall 125 # Synapse
then
Remove_Service synapse 1 1
[[ -d '/mnt/dietpi_userdata/synapse' ]] && G_EXEC rm -R /mnt/dietpi_userdata/synapse
command -v pip3 > /dev/null && G_EXEC_OUTPUT=1 G_EXEC pip3 uninstall -y matrix-synapse
command -v dropdb > /dev/null && runuser -u postgres -- dropdb synapse
command -v dropuser > /dev/null && runuser -u postgres -- dropuser synapse
fi
if To_Uninstall 132 # Aria2
then
[[ -d '/var/www/aria2' ]] && G_EXEC rm -R /var/www/aria2
Remove_Service aria2 1
G_AGP aria2
# Remove input file
if [[ -f '/mnt/dietpi_userdata/aria2/aria2.conf' ]]
then
local fp_input=$(sed -n '/^[[:blank:]]*input-file=/{s/^[^=]*=//p;q}' /mnt/dietpi_userdata/aria2/aria2.conf)
[[ ${fp_input//\"} ]] && G_EXEC rm -f "$fp_input"
unset -v fp_input
fi
G_EXEC rm -Rf /mnt/dietpi_userdata/aria2
[[ -f '/mnt/dietpi_userdata/downloads/aria2.session' ]] && G_EXEC rm /mnt/dietpi_userdata/downloads/aria2.session
fi
if To_Uninstall 133 # YaCy
then
Remove_Service yacy
[[ -d '/etc/yacy' ]] && G_EXEC rm -R /etc/yacy
fi
if To_Uninstall 141 # ADS-B Feeder
then
G_EXEC_NOHALT=1 G_EXEC_OUTPUT=1 G_EXEC /opt/adsb/docker-compose-adsb down
Remove_Service adsb-docker
Remove_Service adsb-feeder-update
Remove_Service adsb-setup
[[ -f '/opt/adsb/pre-uninstall-cleanup' ]] && G_EXEC /opt/adsb/pre-uninstall-cleanup
G_EXEC rm -Rf /opt/adsb /mnt/dietpi_userdata/adsb-feeder /opt/adsb-feeder-update
fi
if To_Uninstall 2 # Folding@Home
then
# Un-check out all work units, so they can be picked up by other donors prior to timeout: https://github.com/FoldingAtHome/fah-issues/issues/1255
FAHClient --chdir /mnt/dietpi_userdata/fahclient --dump all
Remove_Service fahclient
G_AGP fahclient
[[ -d '/mnt/dietpi_userdata/fahclient' ]] && G_EXEC rm -R /mnt/dietpi_userdata/fahclient
fi
if To_Uninstall 135
then
Remove_Service darkice
G_AGP darkice icecast2
fi
if To_Uninstall 136 # motionEye
then
Remove_Service motioneye
command -v pip3 > /dev/null && G_EXEC_OUTPUT=1 G_EXEC pip3 uninstall -y motioneye
G_AGP motion
[[ -d '/etc/motioneye' ]] && G_EXEC rm -R /etc/motioneye
[[ -d '/var/log/motioneye' ]] && G_EXEC rm -R /var/log/motioneye
[[ -d '/var/lib/motioneye' ]] && G_EXEC rm -R /var/lib/motioneye
[[ -d '/mnt/dietpi_userdata/motioneye' ]] && G_DIETPI-NOTIFY 2 'The motionEye media directory is left in place. You can manually remove it via:\n rm -R /mnt/dietpi_userdata/motioneye'
fi
if To_Uninstall 137 # mjpg-streamer
then
Remove_Service mjpg-streamer 1
[[ -d '/opt/mjpg-streamer' ]] && G_EXEC rm -R /opt/mjpg-streamer
fi
if To_Uninstall 127 # BirdNET-Go
then
Remove_Service birdnet 1 1
G_AGP sox
G_EXEC rm -Rf /opt/birdnet
G_EXEC rm -Rf /mnt/dietpi_userdata/birdnet
fi
if To_Uninstall 138 # VirtualHere
then
Remove_Service virtualhere
[[ -d '/opt/virtualhere' ]] && G_EXEC rm -R /opt/virtualhere
[[ -d '/etc/vhusbd' ]] && G_EXEC rm -R /etc/vhusbd # Pre-v8.4
[[ -f '/var/log/virtualhere.log' ]] && G_EXEC rm /var/log/virtualhere.log # Pre-v8.4
fi
if To_Uninstall 139 # SABnzbd
then
Remove_Service sabnzbd 1 1 # group for pre-v6.33
[[ -d '/etc/sabnzbd' ]] && G_EXEC rm -R /etc/sabnzbd
[[ -d '/mnt/dietpi_userdata/downloads/sabnzbd_admin' ]] && G_EXEC rm -R /mnt/dietpi_userdata/downloads/sabnzbd_admin
[[ -d '/mnt/dietpi_userdata/downloads/sabnzbd_nzb_backup' ]] && G_EXEC rm -R /mnt/dietpi_userdata/downloads/sabnzbd_nzb_backup
[[ -d '/var/log/sabnzbd' ]] && G_EXEC rm -R /var/log/sabnzbd # Pre-v7.9
fi
if To_Uninstall 183 # vaultwarden
then
Remove_Service vaultwarden 1 1 # Pre-v8.7
G_AGP vaultwarden
[[ -d '/mnt/dietpi_userdata/vaultwarden' ]] && G_EXEC rm -R /mnt/dietpi_userdata/vaultwarden
[[ -d '/opt/vaultwarden' ]] && G_EXEC rm -R /opt/vaultwarden # Pre-v8.7
fi
if To_Uninstall 193 # K3s
then
[[ -f '/usr/local/bin/k3s-uninstall.sh' ]] && G_EXEC_OUTPUT=1 G_EXEC_NOEXIT=1 G_EXEC /usr/local/bin/k3s-uninstall.sh
[[ -f '/usr/local/bin/k3s-agent-uninstall.sh' ]] && G_EXEC_OUTPUT=1 G_EXEC_NOEXIT=1 G_EXEC /usr/local/bin/k3s-agent-uninstall.sh
[[ -d '/etc/systemd/system/k3s.service.d' ]] && G_EXEC rm -R /etc/systemd/system/k3s.service.d
[[ -d '/etc/rancher' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /etc/rancher
[[ -d '/var/lib/rancher' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /var/lib/rancher
fi
if To_Uninstall 142 # MicroK8s
then
command -v snap > /dev/null && G_EXEC_OUTPUT=1 G_EXEC snap remove microk8s
fi
if To_Uninstall 182 # Unbound
then
# Pi-hole: Assure that it does not resolve via Unbound anymore
# - v6
if [[ $(pihole-FTL -v) == 'v6'* ]]
then
G_DIETPI-NOTIFY 2 'Removing Unbound from Pi-hole upstream DNS servers, and in case add Quad9 as new one.'
local upstreams=() i
read -ra upstreams < <(pihole-FTL --config dns.upstreams)
# Remove array brackets
unset -v 'upstreams[0]' 'upstreams[${#upstreams[@]}]'
for i in "${!upstreams[@]}"
do
# Remove loopback IP (Unbound)
[[ ${upstreams[i]} == '127.0.0.1'* ]] && { unset -v 'upstreams[i]'; break; }
# Remove trailing comma and readd double-quotes removed from pihole-FTL output
upstreams[i]="\"${upstreams[i]%,}\""
done
# Add Quad9 if no upstream DNS server is left
(( ${#upstreams[@]} )) || upstreams=('"9.9.9.9"')
upstreams[0]=${upstreams[*]}
G_EXEC pihole-FTL --config dns.upstreams "[ ${upstreams[0]// /,} ]"
fi
# - v5
if [[ -f '/etc/dnsmasq.d/01-pihole.conf' ]] && grep -q '^[[:blank:]]*server=127.0.0.1' /etc/dnsmasq.d/01-pihole.conf
then
G_DIETPI-NOTIFY 2 'The Pi-hole upstream DNS server has been changed to Quad9 due to Unbound being uninstalled.'
G_CONFIG_INJECT 'server=127.0.0.1' 'server=9.9.9.9' /etc/dnsmasq.d/01-pihole.conf
systemctl -q is-active pihole-FTL && G_EXEC_NOEXIT=1 G_EXEC systemctl restart pihole-FTL
fi
[[ -f '/etc/pihole/setupVars.conf' ]] && grep -q '^[[:blank:]]*PIHOLE_DNS_1=127.0.0.1' /etc/pihole/setupVars.conf && G_CONFIG_INJECT 'PIHOLE_DNS_1=' 'PIHOLE_DNS_1=9.9.9.9' /etc/pihole/setupVars.conf
# AdGuard Home: Assure that it does not resolve via Unbound anymore
if [[ -f '/mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf' ]]
then
G_DIETPI-NOTIFY 2 'AdGuard Home upstream DNS server has been changed back to default settings due to Unbound being uninstalled.'
G_CONFIG_INJECT 'upstream_dns_file:[[:blank:]]' ' upstream_dns_file: ""' /mnt/dietpi_userdata/adguardhome/AdGuardHome.yaml
[[ -f '/mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf' ]] && G_EXEC rm /mnt/dietpi_userdata/adguardhome/dietpi-unbound.conf
systemctl -q is-active adguardhome && G_EXEC_NOEXIT=1 G_EXEC systemctl restart adguardhome
fi
# Failsafe: Add Quad9 to system resolver if only Unbound was used
if [[ -f '/etc/unbound/unbound.conf.d/dietpi.conf' ]] && grep -Eq '^[[:blank:]]*port:[[:blank:]]+53$' /etc/unbound/unbound.conf.d/dietpi.conf
then
grep '^[[:blank:]]*nameserver[[:blank:]]' /etc/resolv.conf | grep -qvE '[[:blank:]]127.0.0.1(:53)?$' || echo 'nameserver 9.9.9.9' >> /etc/resolv.conf # Failsafe
fi
G_AGP unbound
dpkg-query -s 'dns-root-data' &> /dev/null && G_EXEC apt-mark auto dns-root-data
[[ -d '/etc/unbound' ]] && G_EXEC rm -R /etc/unbound
[[ -d '/var/cache/unbound' ]] && G_EXEC rm -R /var/cache/unbound
fi
if To_Uninstall 143 # Koel
then
Remove_Service koel 1 1 # group for pre-v6.33
[[ -d '/mnt/dietpi_userdata/koel' ]] && G_EXEC rm -R /mnt/dietpi_userdata/koel
Remove_Database koel
fi
if To_Uninstall 144 # Sonarr
then
Remove_Service sonarr 1 1 # group for pre-v6.29
G_AGP sonarr nzbdrone # Pre-v7.1
[[ -d '/var/log/sonarr' ]] && G_EXEC rm -R /var/log/sonarr
G_EXEC rm -Rf /opt/sonarr /mnt/dietpi_userdata/sonarr /opt/NzbDrone # Pre-v7.1
[[ -f '/etc/apt/sources.list.d/sonarr.list' ]] && G_EXEC rm /etc/apt/sources.list.d/sonarr.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-sonarr.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-sonarr.gpg
# Pre-v7.1: Remove Sonarr v2 key
if [[ -f '/etc/apt/trusted.gpg' && $(apt-key --keyring /etc/apt/trusted.gpg list 'A236C58F409091A18ACA53CBEBFF6B99D9B78493' 2> /dev/null) ]]
then
G_EXEC apt-key --keyring /etc/apt/trusted.gpg del 'A236C58F409091A18ACA53CBEBFF6B99D9B78493'
[[ $(apt-key --keyring /etc/apt/trusted.gpg list 2> /dev/null) ]] || G_EXEC rm /etc/apt/trusted.gpg
fi
fi
if To_Uninstall 145 # Radarr
then
Remove_Service radarr 1 1 # group for pre-v6.29
G_EXEC rm -Rf /opt/radarr /mnt/dietpi_userdata/radarr /var/log/radarr
fi
if To_Uninstall 106 # Lidarr
then
Remove_Service lidarr 1 1 # group for pre-v6.29
G_EXEC rm -Rf /opt/{L,l}idarr /mnt/dietpi_userdata/lidarr /var/log/lidarr
fi
if To_Uninstall 180 # Bazarr
then
Remove_Service bazarr 1
G_EXEC rm -Rf /opt/bazarr /mnt/dietpi_userdata/bazarr /var/log/bazarr
fi
if To_Uninstall 200 # DietPi-Dashboard
then
Remove_Service dietpi-dashboard # Pre-v9.18
Remove_Service dietpi-dashboard-backend
Remove_Service dietpi-dashboard-frontend
[[ -d '/opt/dietpi-dashboard' ]] && G_EXEC rm -R /opt/dietpi-dashboard
fi
if To_Uninstall 99 # Prometheus Node Exporter
then
if [[ -f '/etc/systemd/system/raspberrypi_exporter.timer' ]]
then
G_EXEC systemctl --no-reload disable --now raspberrypi_exporter.timer
G_EXEC rm /etc/systemd/system/raspberrypi_exporter.timer
fi
[[ -d '/etc/systemd/system/raspberrypi_exporter.timer.d' ]] && G_EXEC rm -R /etc/systemd/system/raspberrypi_exporter.timer.d
Remove_Service raspberrypi_exporter
Remove_Service node_exporter 1 1
[[ -d '/opt/node_exporter' ]] && G_EXEC rm -R /opt/node_exporter
fi
if To_Uninstall 146 # Tautulli
then
Remove_Service tautulli 1 1
[[ -d '/opt/tautulli' ]] && G_EXEC rm -R /opt/tautulli
[[ -d '/mnt/dietpi_userdata/tautulli' ]] && G_EXEC rm -R /mnt/dietpi_userdata/tautulli
fi
if To_Uninstall 147 # Jackett
then
Remove_Service jackett 1 1
[[ -d '/opt/jackett' ]] && G_EXEC rm -R /opt/jackett
fi
if To_Uninstall 149 # NZBGet
then
Remove_Service nzbget 1 1 # group for pre-v6.33
[[ -d '/mnt/dietpi_userdata/nzbget' ]] && G_EXEC rm -R /mnt/dietpi_userdata/nzbget
fi
if To_Uninstall 151 # Prowlarr
then
Remove_Service prowlarr 1
G_EXEC rm -Rf /opt/prowlarr /mnt/dietpi_userdata/prowlarr /var/log/prowlarr
fi
if To_Uninstall 203 # Readarr
then
Remove_Service readarr 1
G_EXEC rm -Rf /opt/readarr /mnt/dietpi_userdata/readarr /var/log/readarr
fi
if To_Uninstall 155 # HTPC Manager
then
Remove_Service htpc-manager
[[ -d '/mnt/dietpi_userdata/htpc-manager' ]] && G_EXEC rm -R /mnt/dietpi_userdata/htpc-manager
fi
if To_Uninstall 150 # Mono
then
# shellcheck disable=SC2046
apt-mark auto $(dpkg --get-selections 'mono-*' 'libmono-*' 2> /dev/null | mawk '{print $1}') 2> /dev/null
[[ -f '/etc/apt/sources.list.d/dietpi-mono.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-mono.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-mono.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-mono.gpg
fi
if To_Uninstall 37 # Shairport Sync
then
G_AGP shairport-sync shairport-sync-airplay2
fi
if To_Uninstall 152 # Avahi-Daemon
then
G_AGP avahi-daemon
fi
if To_Uninstall 153 # OctoPrint
then
Remove_Service octoprint 1 1
# CLI alias
[[ -f '/etc/bashrc.d/dietpi-octoprint.sh' ]] && G_EXEC rm /etc/bashrc.d/dietpi-octoprint.sh
# Data
[[ -d '/mnt/dietpi_userdata/octoprint' ]] && G_EXEC rm -R /mnt/dietpi_userdata/octoprint
# sudoers
[[ -f '/etc/sudoers.d/octoprint' ]] && G_EXEC rm /etc/sudoers.d/octoprint
fi
if To_Uninstall 187 # CUPS
then
G_AGP cups
[[ -d '/etc/cups' ]] && G_EXEC rm -R /etc/cups
fi
if To_Uninstall 121 # Roon Bridge
then
Remove_Service roonbridge 1 1
[[ -d '/opt/roonbridge' ]] && G_EXEC rm -R /opt/roonbridge
[[ -d '/var/log/roonbridge' ]] && G_EXEC rm -R /var/log/roonbridge
[[ -d '/mnt/dietpi_userdata/roonbridge' ]] && G_EXEC rm -R /mnt/dietpi_userdata/roonbridge
# Pre-v8.2
[[ -d '/etc/roonbridge' ]] && G_EXEC rm -R /etc/roonbridge
[[ -d '/var/log/roon' ]] && G_EXEC rm -R /var/log/roon
[[ -d '/mnt/dietpi_userdata/roon' ]] && G_EXEC rm -R /mnt/dietpi_userdata/roon
fi
if To_Uninstall 154 # Roon Server
then
Remove_Service roonserver 1 1
[[ -f '/etc/sudoers.d/roonserver' ]] && G_EXEC rm /etc/sudoers.d/roonserver
[[ -d '/opt/roonserver' ]] && G_EXEC rm -R /opt/roonserver
[[ -d '/var/log/roonserver' ]] && G_EXEC rm -R /var/log/roonserver
[[ -d '/mnt/dietpi_userdata/roonserver' ]] && G_EXEC rm -R /mnt/dietpi_userdata/roonserver
fi
if To_Uninstall 156 # Steam
then
G_AGP steam
G_EXEC rm -Rf /{root,home/*}/{.steam{,path,pid},Desktop/steam.desktop} /mnt/dietpi_userdata/steam
fi
if To_Uninstall 62 # Box86
then
[[ -f '/etc/binfmt.d/box86.conf' ]] && G_EXEC rm /etc/binfmt.d/box86.conf
[[ -d '/proc/sys/fs/binfmt_misc' ]] && G_EXEC systemctl restart systemd-binfmt
[[ -f '/usr/local/bin/box86' ]] && G_EXEC rm /usr/local/bin/box86
for i in /usr/lib/i386-linux-gnu/lib{gcc_s,png12,stdc++,unwind}.so*
do
[[ -e $i ]] && ! dpkg-query -S "$i" &> /dev/null && G_EXEC rm "$i"
done
[[ -d '/usr/lib/i386-linux-gnu' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /usr/lib/i386-linux-gnu
fi
if To_Uninstall 197 # Box64
then
[[ -f '/etc/binfmt.d/box64.conf' ]] && G_EXEC rm /etc/binfmt.d/box64.conf
[[ -d '/proc/sys/fs/binfmt_misc' ]] && G_EXEC systemctl restart systemd-binfmt
[[ -f '/usr/local/bin/box64' ]] && G_EXEC rm /usr/local/bin/box64
for i in /usr/lib/x86_64-linux-gnu/lib{crypto,gcc_s,mbed,png12,ssl,stdc++,unwind}*
do
[[ -e $i ]] && ! dpkg-query -S "$i" &> /dev/null && G_EXEC rm "$i"
done
[[ -d '/usr/lib/x86_64-linux-gnu' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /usr/lib/x86_64-linux-gnu
fi
if To_Uninstall 207 # Moonlight (CLI)
then
G_AGP moonlight-embedded
[[ -f '/etc/apt/sources.list.d/dietpi-moonlight.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-moonlight.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-moonlight.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-moonlight.gpg
fi
if To_Uninstall 208 # Moonlight (GUI)
then
dpkg-query -s 'libgl1' &> /dev/null && G_EXEC apt-mark auto libgl1
G_AGP moonlight-qt
[[ -f '/etc/apt/sources.list.d/dietpi-moonlight-qt.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-moonlight-qt.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-moonlight-qt.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-moonlight-qt.gpg
fi
if To_Uninstall 11 # GZDoom
then
G_AGP gzdoom
fi
if To_Uninstall 119 # CAVA
then
G_AGP cava
G_EXEC rm -Rf /{root,home/*}/.config/cava
fi
if To_Uninstall 118 # Mopidy
then
Remove_Service mopidy
G_AGP 'mopidy*'
[[ -f '/etc/apt/sources.list.d/dietpi-mopidy.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-mopidy.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-mopidy.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-mopidy.gpg
command -v pip3 > /dev/null && G_EXEC_OUTPUT=1 G_EXEC pip3 uninstall -y Mopidy-MusicBox-Webclient
getent passwd mopidy > /dev/null && G_EXEC userdel mopidy
[[ -d '/mnt/dietpi_userdata/mopidy' ]] && G_EXEC rm -R /mnt/dietpi_userdata/mopidy
fi
if To_Uninstall 31 # Kodi
then
G_AGP kodi kodi-odroid kodi-aml-fbdev-odroid
G_EXEC rm -Rf /{root,home/*}/{.kodi,Desktop/kodi.desktop}
[[ -f '/usr/share/applications/kodi.desktop' ]] && G_EXEC rm /usr/share/applications/kodi.desktop
[[ -f '/etc/udev/rules.d/99-dietpi-kodi.rules' ]] && G_EXEC rm /etc/udev/rules.d/99-dietpi-kodi.rules
[[ -f '/etc/modules-load.d/dietpi-n2-kodi.conf' ]] && G_EXEC rm /etc/modules-load.d/dietpi-n2-kodi.conf
[[ -f '/etc/modules-load.d/dietpi-c4-kodi.conf' ]] && G_EXEC rm /etc/modules-load.d/dietpi-c4-kodi.conf
[[ -f '/var/lib/dietpi/dietpi-software/installed/desktop/icons/kodi-icon.png' ]] && G_EXEC rm /var/lib/dietpi/dietpi-software/installed/desktop/icons/kodi-icon.png
fi
if To_Uninstall 39 # ReadyMedia
then
Remove_Service minidlna 1 1
G_AGP minidlna
[[ -d '/mnt/dietpi_userdata/.MiniDLNA_Cache' ]] && G_EXEC rm -R /mnt/dietpi_userdata/.MiniDLNA_Cache
[[ -d '/var/log/minidlna' ]] && G_EXEC rm -R /var/log/minidlna
fi
if To_Uninstall 51 # OpenTyrian
then
# shellcheck disable=SC2046
apt-mark auto $(dpkg --get-selections libsdl1.2debian libsdl-net1.2 2> /dev/null | mawk '{print $1}') 2> /dev/null
G_EXEC rm -f /{usr/share/applications,{root,home/*}/Desktop}/opentyrian.desktop
[[ -d '/usr/games/opentyrian' ]] && G_EXEC rm -R /usr/games/opentyrian
fi
if To_Uninstall 59 # RPi Cam Web Interface
then
Remove_Service raspimjpeg
[[ -f '/var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh' ]] && G_EXEC rm /var/lib/dietpi/dietpi-software/installed/raspimjpeg.sh
[[ -f '/usr/local/bin/raspimjpeg' ]] && G_EXEC rm /usr/local/bin/raspimjpeg
[[ -f '/etc/raspimjpeg' ]] && G_EXEC rm /etc/raspimjpeg
G_AGP gpac
[[ -d '/var/www/rpicam' ]] && G_EXEC rm -R /var/www/rpicam
[[ -f '/etc/sudoers.d/dietpi-rpi_cam_control' ]] && G_EXEC rm /etc/sudoers.d/dietpi-rpi_cam_control
fi
if To_Uninstall 45 # Deluge
then
Remove_Service deluge-web
Remove_Service deluged debian-deluged debian-deluged
[[ -d '/mnt/dietpi_userdata/deluge' ]] && G_EXEC rm -R /mnt/dietpi_userdata/deluge
G_AGP deluged deluge-web deluge-console
fi
if To_Uninstall 94 # ProFTPD
then
G_AGP proftpd-basic
fi
if To_Uninstall 96 # Samba Server
then
G_AGP samba samba-common-bin
getent passwd samba > /dev/null && G_EXEC userdel samba
[[ -f '/etc/tmpfiles.d/dietpi-samba_cache.conf' ]] && G_EXEC rm /etc/tmpfiles.d/dietpi-samba_cache.conf
[[ -d '/run/samba-cache' ]] && G_EXEC rm -R /run/samba-cache
G_EXEC rm -Rf /var/cache/samba # Symlink
fi
if To_Uninstall 95 # vsftpd
then
G_AGP vsftpd
fi
if To_Uninstall 109 # NFS Server
then
G_AGP nfs-kernel-server
G_EXEC rm -Rf /etc/exports{,.d}
fi
if To_Uninstall 93 # Pi-hole
then
# Uninstaller
# - Skip dialogue
[[ -f '/opt/pihole/uninstall.sh' ]] && G_EXEC sed --follow-symlinks -i '/^[[:blank:]]*read -rp .*Are you sure you would like to remove.* answer$/c\answer=y' /opt/pihole/uninstall.sh
command -v pihole > /dev/null && G_EXEC_OUTPUT=1 G_EXEC pihole uninstall
# - Call script directly if pihole command was not available (anymore)
[[ -f '/opt/pihole/uninstall.sh' ]] && G_EXEC_OUTPUT=1 G_EXEC /opt/pihole/uninstall.sh
# Cleanup if uninstaller was not available (anymore)
# - Service
Remove_Service pihole-FTL pihole pihole
# - Files
[[ -f '/usr/bin/pihole-FTL' ]] && G_EXEC rm /usr/bin/pihole-FTL
[[ -e '/usr/local/bin/pihole' ]] && G_EXEC rm /usr/local/bin/pihole
# - Dirs
[[ -d '/etc/pihole' ]] && G_EXEC rm -R /etc/pihole
[[ -d '/etc/.pihole' ]] && G_EXEC rm -R /etc/.pihole
[[ -d '/opt/pihole' ]] && G_EXEC rm -R /opt/pihole
[[ -d '/var/www/html/admin' ]] && G_EXEC rm -R /var/www/html/admin
[[ -d '/var/www/html/pihole' ]] && G_EXEC rm -R /var/www/html/pihole
# v5
# - Symlinks
[[ -L '/var/www/pihole' ]] && G_EXEC rm /var/www/pihole
[[ -L '/var/www/admin' ]] && G_EXEC rm /var/www/admin
# - Webserver configs
[[ -d '/etc/apache2/sites-available' ]] && G_EXEC rm -f /etc/apache2/sites-{available,enabled}/dietpi-pihole*
[[ -d '/etc/lighttpd/conf-available' ]] && G_EXEC rm -f /etc/lighttpd/conf-{available,enabled}/99-dietpi-pihole*
[[ -d '/etc/nginx/sites-dietpi' ]] && G_EXEC rm -f /etc/nginx/sites-dietpi/dietpi-pihole*
# Unbound: Switch port to 53 if it is still installed
if (( ${aSOFTWARE_INSTALL_STATE[182]} == 2 )) && grep -Eq '^[[:blank:]]*port:[[:blank:]]+5335$' /etc/unbound/unbound.conf.d/dietpi.conf
then
G_DIETPI-NOTIFY 2 'Reconfiguring Unbound to work on its own'
G_CONFIG_INJECT 'port:[[:blank:]]' ' port: 53' /etc/unbound/unbound.conf.d/dietpi.conf
G_CONFIG_INJECT 'interface:[[:blank:]]' ' interface: 0.0.0.0' /etc/unbound/unbound.conf.d/dietpi.conf
grep -E '^[[:blank:]]*nameserver[[:blank:]]+127.0.0.1:5335$' /etc/resolv.conf && sed --follow-symlinks -Ei '/^[[:blank:]]*nameserver[[:blank:]]+127.0.0.1:5335$/c\nameserver 127.0.0.1' /etc/resolv.conf # Failsafe
G_EXEC_NOEXIT=1 G_EXEC systemctl restart unbound
else
grep '^[[:blank:]]*nameserver[[:blank:]]' /etc/resolv.conf | grep -qvE '[[:blank:]]127.0.0.1(:53)?$' || echo 'nameserver 9.9.9.9' >> /etc/resolv.conf # Failsafe
fi
fi
if To_Uninstall 126 # AdGuard Home
then
Remove_Service adguardhome 1
# Data directory
[[ -d '/mnt/dietpi_userdata/adguardhome' ]] && G_EXEC rm -R /mnt/dietpi_userdata/adguardhome
# Unbound: Switch port to 53 if it is still installed
if (( ${aSOFTWARE_INSTALL_STATE[182]} == 2 )) && grep -Eq '^[[:blank:]]*port:[[:blank:]]+5335$' /etc/unbound/unbound.conf.d/dietpi.conf
then
G_DIETPI-NOTIFY 2 'Reconfiguring Unbound to work on its own'
G_CONFIG_INJECT 'port:[[:blank:]]' ' port: 53' /etc/unbound/unbound.conf.d/dietpi.conf
G_CONFIG_INJECT 'interface:[[:blank:]]' ' interface: 0.0.0.0' /etc/unbound/unbound.conf.d/dietpi.conf
grep -E '^[[:blank:]]*nameserver[[:blank:]]+127.0.0.1:5335$' /etc/resolv.conf && sed --follow-symlinks -Ei '/^[[:blank:]]*nameserver[[:blank:]]+127.0.0.1:5335$/c\nameserver 127.0.0.1' /etc/resolv.conf # Failsafe
G_EXEC_NOEXIT=1 G_EXEC systemctl restart unbound
else
grep '^[[:blank:]]*nameserver[[:blank:]]' /etc/resolv.conf | grep -qvE '[[:blank:]]127.0.0.1(:53)?$' || echo 'nameserver 9.9.9.9' >> /etc/resolv.conf # Failsafe
fi
fi
if To_Uninstall 33 # Airsonic-Advanced
then
Remove_Service airsonic 1
[[ -d '/mnt/dietpi_userdata/airsonic' ]] && G_EXEC rm -R /mnt/dietpi_userdata/airsonic
fi
if To_Uninstall 204 # Navidrome
then
Remove_Service navidrome 1
G_EXEC rm -Rf /opt/navidrome /mnt/dietpi_userdata/navidrome
fi
if To_Uninstall 212 # Kavita
then
Remove_Service kavita 1
G_EXEC rm -Rf /opt/kavita
fi
if To_Uninstall 71 # WebIOPi
then
Remove_Service webiopi
[[ -f '/usr/bin/webiopi' ]] && G_EXEC rm /usr/bin/webiopi # Executable
[[ -f '/usr/bin/webiopi-passwd' ]] && G_EXEC rm /usr/bin/webiopi-passwd # Password generator
[[ -d '/etc/webiopi' ]] && G_EXEC rm -R /etc/webiopi # Config dir
[[ -d '/usr/share/webiopi' ]] && G_EXEC rm -R /usr/share/webiopi # HTML resources
G_EXEC rm -Rf /usr/local/lib/python*/dist-packages/WebIOPi* # Python packages
fi
if To_Uninstall 68 # Remote.It
then
G_AGP remoteit connectd weavedconnectd # connectd: pre-v8.10, weavedconnectd: pre-v6.22
fi
if To_Uninstall 186 # Kubo
then
Remove_Service ipfs 1 1
G_EXEC rm -Rf /usr/local/bin/ipfs /etc/bashrc.d/dietpi-ipfs.sh /etc/sysctl.d/dietpi-ipfs.conf /mnt/dietpi_userdata/ipfs
fi
if To_Uninstall 16 # microblog.pub
then
Remove_Service microblog-pub 1 1
[[ -f '/etc/bashrc.d/microblog-pub.sh' ]] && G_EXEC rm /etc/bashrc.d/microblog-pub.sh
[[ -d '/opt/microblog-pub' ]] && G_EXEC rm -R /opt/microblog-pub
G_DIETPI-NOTIFY 2 'All microblog.pub data and settings are still retained.\n - You can remove these manually: rm -R /mnt/dietpi_userdata/microblog-pub'
fi
if To_Uninstall 98 # HAProxy
then
Remove_Service haproxy
G_EXEC rm -Rf /{etc,var/lib,/usr/local/{doc,sbin}}/haproxy /usr/local/share/man/man1/haproxy.1
[[ -d '/usr/local/share/man/man1' ]] && G_EXEC rmdir -p --ignore-fail-on-non-empty /usr/local/share/man/man1
fi
if To_Uninstall 35 # Lyrion Music Server
then
# Stop systemd service, which is not done by postinst, failing to remove user then
Remove_Service lyrionmusicserver
G_AGP lyrionmusicserver
[[ -d '/var/lib/squeezeboxserver' ]] && G_EXEC rm -R /var/lib/squeezeboxserver
fi
if To_Uninstall 55 # WordPress
then
[[ -d '/var/www/wordpress' ]] && G_EXEC rm -R /var/www/wordpress
Remove_Database wordpress
fi
if To_Uninstall 38 # FreshRSS
then
crontab -u www-data -l | grep -v '/opt/FreshRSS/app/actualize_script.php' | crontab -u www-data -
a2disconf dietpi-freshrss 2> /dev/null
[[ -f '/etc/apache2/conf-available/dietpi-freshrss.conf' ]] && G_EXEC rm /etc/apache2/conf-available/dietpi-freshrss.conf
G_EXEC rm -Rf /var/www/freshrss # symlink
[[ -d '/opt/FreshRSS' ]] && G_EXEC rm -R /opt/FreshRSS
[[ -f '/var/log/freshrss.log' ]] && G_EXEC rm /var/log/freshrss.log
Remove_Database freshrss
fi
if To_Uninstall 28 || To_Uninstall 120 # TigerVNC/RealVNC
then
# RealVNC services
Remove_Service vncserver-x11-serviced
Remove_Service vncserver-virtuald
G_AGP 'tigervnc-*' x11vnc realvnc-vnc-server
Remove_Service vncserver
[[ -f '/usr/local/bin/vncserver' ]] && G_EXEC rm /usr/local/bin/vncserver
G_EXEC rm -Rf /{root,home/*}/.vnc
fi
if To_Uninstall 73 # Fail2Ban
then
G_AGP fail2ban
dpkg-query -s 'python3-systemd' &> /dev/null && G_EXEC apt-mark auto python3-systemd
[[ -d '/etc/fail2ban' ]] && G_EXEC rm -R /etc/fail2ban
fi
if To_Uninstall 64 # phpSysInfo
then
[[ -d '/var/www/phpsysinfo' ]] && G_EXEC rm -R /var/www/phpsysinfo
fi
if To_Uninstall 56 # Single File PHP Gallery
then
[[ -f '/var/www/gallery/index.php' ]] && G_EXEC rm /var/www/gallery/index.php
[[ -f '/var/www/gallery/readme.txt' ]] && G_EXEC rm /var/www/gallery/readme.txt
[[ -d '/var/www/gallery/_sfpg_data' ]] && G_EXEC rm -R /var/www/gallery/_sfpg_data
G_DIETPI-NOTIFY 2 'The plain gallery image files are not removed. You may do this manually via "rm -R /var/www/gallery".'
fi
if To_Uninstall 40 # Ampache
then
[[ -d '/var/www/ampache' || -L '/var/www/ampache' ]] && G_EXEC rm -R /var/www/ampache
[[ -d '/mnt/dietpi_userdata/ampache' ]] && G_EXEC rm -R /mnt/dietpi_userdata/ampache
Remove_Database ampache
fi
if To_Uninstall 117 # PiVPN
then
[[ -f '/opt/pivpn/uninstall.sh' ]] && G_EXEC sed --follow-symlinks -i 's/ askreboot;//' /opt/pivpn/uninstall.sh
command -v pivpn > /dev/null && G_EXEC_OUTPUT=1 G_EXEC pivpn -u
getent passwd pivpn > /dev/null && G_EXEC userdel pivpn
getent group pivpn > /dev/null && G_EXEC groupdel pivpn
[[ -f '/etc/apt/sources.list.d/swupdate.openvpn.net.list' ]] && G_EXEC rm /etc/apt/sources.list.d/swupdate.openvpn.net.list # Not part of current installer anymore
fi
if To_Uninstall 201 # ZeroTier
then
G_AGP zerotier-one
[[ -d '/etc/systemd/system/zerotier-one.service.d' ]] && G_EXEC rm -R /etc/systemd/system/zerotier-one.service.d
[[ -d '/var/lib/zerotier-one' ]] && G_EXEC rm -R /var/lib/zerotier-one
[[ -f '/etc/apt/sources.list.d/dietpi-zerotier.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-zerotier.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-zerotier.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-zerotier.gpg
fi
if To_Uninstall 97 # OpenVPN Server
then
G_AGP openvpn
[[ -d '/etc/systemd/system/openvpn.service.d' ]] && G_EXEC rm -R /etc/systemd/system/openvpn.service.d
[[ -d '/etc/openvpn' ]] && G_EXEC rm -R /etc/openvpn
[[ -f '/etc/sysctl.d/dietpi-openvpn.conf' ]] && G_EXEC rm /etc/sysctl.d/dietpi-openvpn.conf
fi
if To_Uninstall 172 # WireGuard
then
G_AGP 'wireguard*'
[[ -d '/etc/systemd/system/wg-quick@wg0.service.d' ]] && G_EXEC rm -R /etc/systemd/system/wg-quick@wg0.service.d
[[ -d '/etc/wireguard' ]] && G_EXEC rm -R /etc/wireguard
[[ -f '/etc/apt/sources.list.d/dietpi-wireguard.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-wireguard.list
fi
if To_Uninstall 58 # Tailscale
then
G_AGP tailscale
[[ -d '/etc/systemd/system/tailscaled.service.d' ]] && G_EXEC rm -R /etc/systemd/system/tailscaled.service.d
[[ -d '/var/lib/tailscale' ]] && G_EXEC rm -R /var/lib/tailscale
[[ -f '/etc/sysctl.d/dietpi-tailscale.conf' ]] && G_EXEC rm /etc/sysctl.d/dietpi-tailscale.conf
[[ -f '/etc/apt/sources.list.d/dietpi-tailscale.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-tailscale.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-tailscale.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-tailscale.gpg
fi
if To_Uninstall 92 # Certbot
then
G_AGP python3-certbot-apache python3-certbot-nginx certbot
[[ -d '/etc/systemd/system/certbot.service.d' ]] && G_EXEC rm -R /etc/systemd/system/certbot.service.d
fi
if To_Uninstall 69 # Python 3 RPi.GPIO
then
G_AGP python3-rpi-lgpio python3-rpi.gpio
fi
if To_Uninstall 72 # I2C
then
G_AGP i2c-tools
/boot/dietpi/func/dietpi-set_hardware i2c disable
fi
if To_Uninstall 70 # WiringPi
then
if [[ -d '/mnt/dietpi_userdata/WiringPi' ]]
then
G_EXEC cd /mnt/dietpi_userdata/WiringPi
if [[ -f ./build ]]
then
G_EXEC_NOEXIT=1 G_EXEC_OUTPUT=1 G_EXEC ./build uninstall # RPi and pre-v8.5 Odroids
elif [[ -f ./Makefile ]]
then
G_EXEC_NOEXIT=1 G_EXEC_OUTPUT=1 G_EXEC make uninstall # Odroids
fi
G_EXEC cd "$G_WORKING_DIR"
G_EXEC rm -R /mnt/dietpi_userdata/WiringPi
fi
# Pre-v7.2
if [[ -d '/root/wiringPi' ]]
then
G_EXEC cd /root/wiringPi
G_EXEC_NOEXIT=1 G_EXEC_OUTPUT=1 G_EXEC ./build uninstall
G_EXEC cd "$G_WORKING_DIR"
G_EXEC rm -R /root/wiringPi
fi
# GPIO utility
[[ -f '/usr/local/bin/gpio' ]] && G_EXEC rm /usr/local/bin/gpio
[[ -f '/usr/local/sbin/gpio' ]] && G_EXEC rm /usr/local/sbin/gpio
[[ -f '/usr/local/share/man/man1/gpio.1' ]] && G_EXEC rm /usr/local/share/man/man1/gpio.1
[[ -d '/usr/local/share/man/man1' ]] && G_EXEC rmdir -p --ignore-fail-on-non-empty /usr/local/share/man/man1
# Library
G_EXEC rm -f /usr/local/lib/libwiringPi.so*
# Device library
G_EXEC rm -f /usr/local/lib/libwiringPiDev.so*
# Headers
G_EXEC rm -f /usr/local/include/wiringPi*.h
# Orange Pi
[[ -f '/etc/orangepi-release' ]] && grep -q '^# Added by DietPi:$' /etc/orangepi-release && G_EXEC rm /etc/orangepi-release
fi
if To_Uninstall 61 # Tor Hotspot
then
G_AGP tor
[[ -d '/var/log/tor' ]] && G_EXEC rm -R /var/log/tor # pre-v6.27
# Uninstall WiFi Hotspot too, due to iptables needing reset
aSOFTWARE_INSTALL_STATE[60]=-1
fi
if To_Uninstall 60 # WiFi Hotspot
then
G_AGP hostapd isc-dhcp-server
[[ -f '/etc/dhcp/dhcpd.conf' ]] && G_EXEC rm /etc/dhcp/dhcpd.conf
[[ -f '/etc/hostapd/hostapd.conf' ]] && G_EXEC rm /etc/hostapd/hostapd.conf
[[ -f '/etc/default/isc-dhcp-server' ]] && G_EXEC rm /etc/default/isc-dhcp-server
[[ -f '/etc/default/hostapd' ]] && G_EXEC rm /etc/default/hostapd
[[ -f '/etc/iptables.ipv4.nat' ]] && G_EXEC rm /etc/iptables.ipv4.nat
[[ -f '/etc/iptables.ipv6.nat' ]] && G_EXEC rm /etc/iptables.ipv6.nat
[[ -f '/etc/sysctl.d/dietpi-wifihotspot.conf' ]] && G_EXEC rm /etc/sysctl.d/dietpi-wifihotspot.conf
# Set WiFi interface back to inactive and ready for use with dietpi-config.
local wifi_iface=$(G_GET_NET -t wlan iface)
# - Remove WLAN block, so we can recreate it
G_EXEC sed --follow-symlinks -Ei '/(allow-hotplug|auto)[[:blank:]]+wlan/q0' /etc/network/interfaces
# - Disable
G_EXEC sed --follow-symlinks -i "/allow-hotplug wlan/c\#allow-hotplug $wifi_iface" /etc/network/interfaces
# - Add default WiFi settings to network interfaces config
cat << _EOF_ >> /etc/network/interfaces
iface $wifi_iface inet dhcp
address 192.168.0.101
netmask 255.255.255.0
#gateway 192.168.0.1
#dns-nameservers 9.9.9.9 149.112.112.112
pre-up iw dev $wifi_iface set power_save off
post-down iw dev $wifi_iface set power_save on
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
_EOF_
# shellcheck disable=SC2015
iw dev "$wifi_iface" set power_save on 2> /dev/null && iw dev "$wifi_iface" set power_save off 2> /dev/null || G_EXEC sed --follow-symlinks -i '/ iw dev .* set power_save /d' /etc/network/interfaces
# - Flush iptables
iptables -F
iptables -t nat -F
ip6tables -F
ip6tables -t nat -F
fi
if To_Uninstall 48 # Pydio
then
[[ -d '/var/www/pydio' ]] && G_EXEC rm -R /var/www/pydio
# Remove webserver configs
command -v a2dissite > /dev/null && a2dissite dietpi-pydio
[[ -f '/etc/apache2/conf-available/dietpi-pydio.conf' ]] && G_EXEC rm /etc/apache2/conf-available/dietpi-pydio.conf
[[ -f '/etc/nginx/sites-dietpi/dietpi-pydio.conf' ]] && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-pydio.conf
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-pydio
[[ -f '/etc/lighttpd/conf-available/99-dietpi-pydio.conf' ]] && G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-pydio.conf
Remove_Database pydio
fi
if To_Uninstall 36 # Squeezelite
then
G_AGP squeezelite
Remove_Service squeezelite 1 1 # Pre-v7.3
fi
if To_Uninstall 66 # RPi-Monitor
then
G_AGP rpimonitor
[[ -d '/etc/rpimonitor' ]] && G_EXEC rm -R /etc/rpimonitor
[[ -d '/var/lib/rpimonitor' ]] && G_EXEC rm -R /var/lib/rpimonitor
[[ -f '/var/log/rpimonitor.log' ]] && G_EXEC rm /var/log/rpimonitor.log
fi
if To_Uninstall 57 # Baïkal
then
# Remove webserver configs
# - Lighttpd
if [[ -f '/etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf' ]]
then
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-dav_redirect
G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-dav_redirect.conf
fi
if [[ -f '/etc/lighttpd/conf-available/99-dietpi-baikal.conf' ]]
then
command -v lighty-disable-mod > /dev/null && lighty-disable-mod dietpi-baikal
G_EXEC rm /etc/lighttpd/conf-available/99-dietpi-baikal.conf
fi
# - Apache
if [[ -f '/etc/apache2/conf-available/dietpi-dav_redirect.conf' ]]
then
command -v a2disconf > /dev/null && a2disconf dietpi-dav_redirect
G_EXEC rm /etc/apache2/conf-available/dietpi-dav_redirect.conf
fi
if [[ -f '/etc/apache2/sites-available/dietpi-baikal.conf' ]]
then
command -v a2dissite > /dev/null && a2dissite dietpi-baikal
G_EXEC rm /etc/apache2/sites-available/dietpi-baikal.conf
fi
# - Nginx
[[ -f '/etc/nginx/sites-dietpi/dietpi-dav_redirect.conf' ]] && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-dav_redirect.conf
[[ -f '/etc/nginx/sites-dietpi/dietpi-baikal.conf' ]] && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-baikal.conf
[[ -d '/var/www/baikal' ]] && G_EXEC rm -R /var/www/baikal
Remove_Database baikal
fi
if To_Uninstall 65 # Netdata
then
G_AGP netdata
fi
if To_Uninstall 43 # Mumble Server
then
G_AGP mumble-server
fi
if To_Uninstall 41 # Emby
then
G_AGP emby-server
fi
if To_Uninstall 42 # Plex Media Server
then
# Force deletion of plex group which is not removed automatically because it is not the primary group of user plex
Remove_Service plexmediaserver plex plex
# Remove systemd unit which currently survives the package purging: https://github.com/MichaIng/DietPi/issues/3551
[[ -f '/lib/systemd/system/plexmediaserver.service' ]] && G_EXEC rm /lib/systemd/system/plexmediaserver.service
G_AGP plexmediaserver
[[ -d '/var/lib/plexmediaserver' ]] && G_EXEC rm -R /var/lib/plexmediaserver # Left over from purging package, still...
[[ -f '/etc/unbound/unbound.conf.d/dietpi-plex.conf' ]] && G_EXEC rm /etc/unbound/unbound.conf.d/dietpi-plex.conf
fi
if To_Uninstall 52 # Cuberite
then
Remove_Service cuberite 1 1
[[ -d '/mnt/dietpi_userdata/cuberite' ]] && G_EXEC rm -R /mnt/dietpi_userdata/cuberite
fi
if To_Uninstall 53 # MineOS
then
Remove_Service mineos 1 1
[[ -d '/mnt/dietpi_userdata/mineos' ]] && G_EXEC rm -R /mnt/dietpi_userdata/mineos
[[ -f '/etc/mineos.conf' ]] && G_EXEC rm /etc/mineos.conf
G_EXEC rm -Rf /var/games/minecraft /usr/local/bin/mineos /etc/ssl/certs/mineos.* # Symlinks and SSL cert
[[ -d '/home/mineos' ]] && G_EXEC rm -R /home/mineos # pre-v6.34
fi
if To_Uninstall 49 # Gogs
then
Remove_Service gogs 1 1
# Data
[[ -d '/etc/gogs' ]] && G_EXEC rm -R /etc/gogs
[[ -d '/var/log/gogs' ]] && G_EXEC rm -R /var/log/gogs
Remove_Database gogs
fi
if To_Uninstall 46 # qBittorrent
then
Remove_Service qbittorrent 1 1 # group for pre-v6.29
G_AGP qbittorrent-nox
[[ -d '/home/qbittorrent' ]] && G_EXEC rm -R /home/qbittorrent
[[ -d '/var/log/qbittorrent' ]] && G_EXEC rm -R /var/log/qbittorrent
fi
if To_Uninstall 50 # Syncthing
then
Remove_Service syncthing 1
[[ -d '/opt/syncthing' ]] && G_EXEC rm -R /opt/syncthing
[[ -d '/mnt/dietpi_userdata/syncthing' ]] && G_EXEC rm -R /mnt/dietpi_userdata/syncthing
#[[ -d '/mnt/dietpi_userdata/syncthing_data' ]] && G_EXEC rm -R /mnt/dietpi_userdata/syncthing_data # Leave data in place
[[ -f '/etc/sysctl.d/dietpi-syncthing.conf' ]] && G_EXEC rm /etc/sysctl.d/dietpi-syncthing.conf
fi
if To_Uninstall 116 # Medusa
then
Remove_Service medusa 1 1 # group for pre-v6.33
[[ -d '/mnt/dietpi_userdata/medusa' ]] && G_EXEC rm -R /mnt/dietpi_userdata/medusa
fi
if To_Uninstall 107 # rTorrent
then
# ruTorrent
[[ -d '/var/www/rutorrent' ]] && G_EXEC rm -R /var/www/rutorrent
# - Authentication
[[ -f '/etc/.rutorrent-htaccess' ]] && G_EXEC rm /etc/.rutorrent-htaccess
# - Lighttpd
# Pre-v7.2: Remove from #RUTORRENT_DIETPI to #RUTORRENT_DIETPI in /etc/lighttpd/lighttpd.conf if exist
[[ -f '/etc/lighttpd/lighttpd.conf' ]] && grep -q '^#RUTORRENT_DIETPI' /etc/lighttpd/lighttpd.conf && G_EXEC sed --follow-symlinks -i '/#RUTORRENT_DIETPI/,/#RUTORRENT_DIETPI/d' /etc/lighttpd/lighttpd.conf
# Remove rTorrent config from Lighttpd
[[ -f '/etc/lighttpd/conf-enabled/98-dietpi-rtorrent.conf' ]] && G_EXEC lighty-disable-mod dietpi-rtorrent
[[ -f '/etc/lighttpd/conf-available/98-dietpi-rtorrent.conf' ]] && G_EXEC rm /etc/lighttpd/conf-available/98-dietpi-rtorrent.conf
# - Nginx
[[ -f '/etc/nginx/sites-dietpi/dietpi-rutorrent.conf' ]] && G_EXEC rm /etc/nginx/sites-dietpi/dietpi-rutorrent.conf
# - Apache
command -v a2dissite &> /dev/null && a2dissite dietpi-rutorrent
[[ -f '/etc/apache2/sites-available/dietpi-rutorrent.conf' ]] && G_EXEC rm /etc/apache2/sites-available/dietpi-rutorrent.conf
# rTorrent
Remove_Service rtorrent 1 1 # group for pre-v6.29
G_AGP rtorrent
[[ -d '/mnt/dietpi_userdata/rtorrent' ]] && G_EXEC rm -R /mnt/dietpi_userdata/rtorrent
[[ -d '/mnt/dietpi_userdata/downloads/.session' ]] && G_EXEC rm -R /mnt/dietpi_userdata/downloads/.session
fi
if To_Uninstall 108 # Amiberry
then
G_AGP amiberry
Remove_Service amiberry # pre-v8.5
# Files
[[ -d '/mnt/dietpi_userdata/amiberry' ]] && G_EXEC rm -R /mnt/dietpi_userdata/amiberry
# Autostart index: If currently Amiberry, revert to console login
[[ -f '/boot/dietpi/.dietpi-autostart_index' && $(</boot/dietpi/.dietpi-autostart_index) == [68] ]] && /boot/dietpi/dietpi-autostart 0
fi
if To_Uninstall 10 # Amiberry-Lite
then
G_AGP amiberry-lite
# Files
[[ -d '/mnt/dietpi_userdata/amiberry-lite' ]] && G_EXEC rm -R /mnt/dietpi_userdata/amiberry-lite
fi
if To_Uninstall 112 # DXX-Rebirth
then
[[ -d '/mnt/dietpi_userdata/dxx-rebirth' ]] && G_EXEC rm -R /mnt/dietpi_userdata/dxx-rebirth
# Remove symlinks
[[ -f '/root/.d1x-rebirth' ]] && G_EXEC rm /root/.d1x-rebirth
[[ -f '/root/.d2x-rebirth' ]] && G_EXEC rm /root/.d2x-rebirth
G_EXEC rm -f /{root,home/*}/Desktop/dxx-rebirth.desktop
[[ -f '/usr/share/applications/dxx-rebirth.desktop' ]] && G_EXEC rm /usr/share/applications/dxx-rebirth.desktop
# shellcheck disable=SC2046
apt-mark auto $(dpkg --get-selections 'libsdl-mixer1.2' 'libsdl1.2debian' 'libphysfs1' 'libgl1' 'libglu1-mesa' 2> /dev/null | mawk '{print $1}') 2> /dev/null
fi
if To_Uninstall 113 # Chromium
then
# Package
G_AGP 'chromium*'
# Files
[[ -d '/etc/chromium.d' ]] && G_EXEC rm -R /etc/chromium.d
[[ -d '/usr/lib/chromium' ]] && G_EXEC rm -R /usr/lib/chromium
[[ -d '/usr/lib/chromium-browser' ]] && G_EXEC rm -R /usr/lib/chromium-browser
G_EXEC rm -Rf /{root,home/*}/{.chromium-browser.init,.{cache,config}/chromium,Desktop/chromium{,-browser}.desktop}
[[ -f '/var/lib/dietpi/dietpi-software/installed/chromium-autostart.sh' ]] && G_EXEC rm /var/lib/dietpi/dietpi-software/installed/chromium-autostart.sh
# Autostart index: If currently Chromium, revert to console login
[[ -f '/boot/dietpi/.dietpi-autostart_index' && $(</boot/dietpi/.dietpi-autostart_index) == 11 ]] && /boot/dietpi/dietpi-autostart 0
fi
if To_Uninstall 190 # Beets
then
G_AGP beets
[[ -f '/etc/bashrc.d/dietpi-beets.sh' ]] && G_EXEC rm /etc/bashrc.d/dietpi-beets.sh
[[ -d '/mnt/dietpi_userdata/beets' ]] && G_EXEC rm -R /mnt/dietpi_userdata/beets
fi
if To_Uninstall 157 # Home Assistant
then
Remove_Service home-assistant homeassistant homeassistant
[[ -d '/home/homeassistant' ]] && G_EXEC rm -R /home/homeassistant
[[ -d '/srv/homeassistant' ]] && G_EXEC rm -R /srv/homeassistant # pre-v6-27
G_DIETPI-NOTIFY 2 'Home Assistant data and settings are not removed.\n - You may want to do this manually: rm -R /mnt/dietpi_userdata/homeassistant'
fi
if To_Uninstall 140 # Domoticz
then
G_AGP domoticz
fi
if To_Uninstall 165 # Gitea
then
Remove_Service gitea 1 1
# Data
[[ -d '/mnt/dietpi_userdata/gitea' ]] && G_EXEC rm -R /mnt/dietpi_userdata/gitea
[[ -d '/var/log/gitea' ]] && G_EXEC rm -R /var/log/gitea
Remove_Database gitea
fi
if To_Uninstall 177 # Forgejo
then
Remove_Service forgejo 1 1
# Data
[[ -d '/mnt/dietpi_userdata/forgejo' ]] && G_EXEC rm -R /mnt/dietpi_userdata/forgejo
[[ -d '/var/log/forgejo' ]] && G_EXEC rm -R /var/log/forgejo
Remove_Database forgejo
fi
if To_Uninstall 166 # Audiophonics PI-SPC
then
Remove_Service pi-spc
[[ -d '/var/lib/dietpi/dietpi-software/installed/pi-spc' ]] && G_EXEC rm -R /var/lib/dietpi/dietpi-software/installed/pi-spc
G_EXEC sed --follow-symlinks -Ei '/^[[:blank:]]*dtoverlay=gpio-(shutdown|poweroff)/d' /boot/config.txt
fi
if To_Uninstall 167 # Raspotify
then
G_AGP raspotify
[[ -f '/etc/apt/sources.list.d/dietpi-raspotify.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-raspotify.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-raspotify.asc' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-raspotify.asc
fi
if To_Uninstall 100 # PiJuice
then
G_AGP pijuice-base
[[ -d '/var/lib/dietpi/dietpi-software/installed/pijuice' ]] && G_EXEC rm -R /var/lib/dietpi/dietpi-software/installed/pijuice
[[ -d '/var/lib/pijuice' ]] && G_EXEC rm -R /var/lib/pijuice
fi
if To_Uninstall 205 # Homer
then
[[ -d '/var/www/homer' ]] && G_EXEC rm -R /var/www/homer
if [[ -f '/mnt/dietpi_userdata/homer_backup' ]]
then
if G_WHIP_BUTTON_CANCEL_TEXT='Keep it' G_WHIP_YESNO 'Do you want to remove the Homer backup at /mnt/dietpi_userdata/homer_backup as well?'
then
G_EXEC rm -R /mnt/dietpi_userdata/homer_backup
else
G_DIETPI-NOTIFY 2 'Leaving Homer backup at /mnt/dietpi_userdata/homer_backup in place'
fi
fi
fi
if To_Uninstall 158 # MinIO
then
Remove_Service minio minio-user minio-user
# Files
[[ -f '/usr/local/bin/minio' ]] && G_EXEC rm /usr/local/bin/minio
[[ -f '/etc/default/minio' ]] && G_EXEC rm /etc/default/minio
# Certbot hook
[[ -f '/etc/letsencrypt/renewal-hooks/deploy/dietpi-minio.sh' ]] && G_EXEC rm /etc/letsencrypt/renewal-hooks/deploy/dietpi-minio.sh
# - Pre-v8.0
[[ -f '/etc/systemd/system/certbot.service.d/dietpi-minio.conf' ]] && G_EXEC rm /etc/systemd/system/certbot.service.d/dietpi-minio.conf
[[ -d '/etc/systemd/system/certbot.service.d' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /etc/systemd/system/certbot.service.d
fi
if To_Uninstall 161 # FuguHub
then
Remove_Service bdd bd bd
# Files
[[ -d '/home/bd' ]] && G_EXEC rm -R /home/bd
G_DIETPI-NOTIFY 2 'FuguHub file server data at /mnt/dietpi_userdata/fuguhub-data are not removed. You may do this manually:\n - rm -R /mnt/dietpi_userdata/fuguhub-data'
fi
if To_Uninstall 189 # VSCodium
then
G_AGP codium
[[ -f '/etc/apt/sources.list.d/dietpi-vscodium.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-vscodium.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-vscodium.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-vscodium.gpg
G_EXEC rm -f /{usr/share/applications,{root,home/*}/Desktop}/codium.desktop
G_EXEC rm -Rf /{root,home/*}/{.config/VSCodium,.vscode-oss}
fi
if To_Uninstall 185 # Portainer
then
# Check if Docker is still installed
if [[ -d '/mnt/dietpi_userdata/docker-data' ]]
then
local container=$(docker container ls -aqf 'ancestor=portainer/portainer' -f 'ancestor=portainer/portainer-ce' -f 'ancestor=portainer/portainer-ee')
[[ $container ]] && G_EXEC docker container rm -f "$container"
local image=$(docker image ls -aqf 'reference=portainer/portainer' -f 'reference=portainer/portainer-ce' -f 'reference=portainer/portainer-ee')
[[ $image ]] && G_EXEC docker image rm "$image"
[[ $(docker volume ls -qf 'name=^portainer_data$') ]] && G_EXEC docker volume rm 'portainer_data'
fi
fi
if To_Uninstall 134 # Docker Compose
then
G_AGP docker-compose-plugin docker-compose
[[ -f '/usr/local/bin/docker-compose' ]] && G_EXEC rm /usr/local/bin/docker-compose # Pre-v8.14
command -v docker-compose > /dev/null && command -v pip3 > /dev/null && G_EXEC_OUTPUT=1 G_EXEC pip3 uninstall -y docker-compose # Pre-v8.2
fi
if To_Uninstall 162 # Docker
then
Remove_Service docker
# Packages, repo and key
G_AGP docker-ce docker-ce-cli docker.io docker-cli
[[ -f '/etc/apt/sources.list.d/docker.list' ]] && G_EXEC rm /etc/apt/sources.list.d/docker.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-docker.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-docker.gpg
# DietPi data dir
[[ -d '/mnt/dietpi_userdata/docker-data' ]] && G_EXEC rm -R /mnt/dietpi_userdata/docker-data
# Default data dir
[[ -d '/var/lib/docker' ]] && G_EXEC rm -R /var/lib/docker
# Config dir
[[ -d '/etc/docker' ]] && G_EXEC rm -R /etc/docker
# Set Portainer as not installed
aSOFTWARE_INSTALL_STATE[185]=0
fi
if To_Uninstall 164 # Nukkit
then
Remove_Service nukkit
[[ -d '/usr/local/bin/nukkit' ]] && G_EXEC rm -R /usr/local/bin/nukkit
fi
if To_Uninstall 163 # GMediaRender
then
G_AGP gmediarender
fi
if To_Uninstall 67 # Firefox
then
G_AGP firefox-esr
G_EXEC rm -Rf /{root,home/*}/.mozilla/firefox
for i in /{root,home/*}/.mozilla; do [[ -d $i ]] && G_EXEC rmdir --ignore-fail-on-non-empty "$i"; done
fi
if To_Uninstall 159 # Allo GUI full
then
# Mark standalone version for uninstall
aSOFTWARE_INSTALL_STATE[160]=-1
fi
if To_Uninstall 160 # Allo GUI
then
# Files
[[ -f '/var/www/index.php' ]] && G_EXEC rm /var/www/index.php
G_EXEC rm -Rf /var/www/allo
[[ -d '/opt/allo' ]] && G_EXEC rm -R /opt/allo
[[ -f '/etc/sudoers.d/allo' ]] && G_EXEC rm /etc/sudoers.d/allo
# Database
Remove_Database allo
# Mark full version as uninstalled
aSOFTWARE_INSTALL_STATE[159]=0
fi
if To_Uninstall 88 # MariaDB
then
# Do a full database backup if binary is still available: https://github.com/MichaIng/DietPi/issues/3257#issuecomment-568764107
if command -v mysqldump > /dev/null
then
G_WHIP_MSG "Creating MariaDB database backup before uninstallation:\n\nIn case of accident, we create a database backup for you. You can remove it manually if you are sure that you don't need it any more.\n\n/mnt/dietpi_userdata/mariadb-database-backup.sql"
G_EXEC systemctl start mariadb
mysqldump --all-databases > /mnt/dietpi_userdata/mariadb-database-backup.sql
systemctl stop mariadb
fi
G_AGP mariadb-server 'php*-mysql'
G_EXEC rm -Rf /{mnt/dietpi_userdata,var/lib,var/log,etc}/mysql /{root,home/*}/.mysql_history
fi
if To_Uninstall 77 # Grafana
then
Remove_Service grafana-server grafana grafana
G_AGP grafana
[[ -f '/etc/apt/sources.list.d/grafana.list' ]] && G_EXEC rm /etc/apt/sources.list.d/grafana.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-grafana.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-grafana.gpg
G_EXEC rm -Rf /{mnt/dietpi_userdata,var/{lib,log},etc}/grafana
fi
if To_Uninstall 74 # InfluxDB
then
Remove_Service influxdb 1 1
G_AGP influxdb
[[ -f '/etc/apt/sources.list.d/dietpi-influxdb.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-influxdb.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-influxdb.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-influxdb.gpg
G_EXEC rm -Rf /{mnt/dietpi_userdata,var/{lib,log},etc}/influxdb
fi
if To_Uninstall 80 # Ubooquity
then
Remove_Service ubooquity 1 1
[[ -d '/mnt/dietpi_userdata/ubooquity' ]] && G_EXEC rm -R /mnt/dietpi_userdata/ubooquity
fi
if To_Uninstall 179 # Komga
then
Remove_Service komga 1 1
[[ -d '/mnt/dietpi_userdata/komga' ]] && G_EXEC rm -R /mnt/dietpi_userdata/komga
fi
if To_Uninstall 86 # Roon Extension Manager
then
local installer='/mnt/dietpi_userdata/roon-extension-manager/rem-setup.sh' user='roon-extension-manager'
# Pre-v8.2
if [[ ! -f $installer ]]
then
G_EXEC curl -sSfL 'https://raw.githubusercontent.com/TheAppgineer/roon-extension-manager/v1.x/rem-setup.sh' -o rem-setup.sh
G_EXEC chmod +x rem-setup.sh
installer='./rem-setup.sh' user='root'
fi
SUDO_USER=$user G_EXEC_OUTPUT=1 G_EXEC "$installer" --uninstall
G_EXEC rm "$installer"
getent passwd roon-extension-manager > /dev/null && G_EXEC userdel roon-extension-manager
getent group roon-extension-manager > /dev/null && G_EXEC groupdel roon-extension-manager
[[ -d '/mnt/dietpi_userdata/roon-extension-manager' ]] && G_EXEC rm -R /mnt/dietpi_userdata/roon-extension-manager
fi
if To_Uninstall 178 # Jellyfin
then
G_AGP jellyfin 'jellyfin-*'
[[ -f '/etc/apt/sources.list.d/dietpi-jellyfin.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-jellyfin.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-jellyfin.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-jellyfin.gpg
[[ -d '/etc/systemd/system/jellyfin.service.d' ]] && G_EXEC rm -R /etc/systemd/system/jellyfin.service.d
[[ -d '/mnt/dietpi_userdata/jellyfin' ]] && G_EXEC rm -R /mnt/dietpi_userdata/jellyfin
fi
if To_Uninstall 3 # Midnight Commander
then
G_AGP mc
fi
if To_Uninstall 0 # OpenSSH Client
then
# The OpenSSH server depends on the OpenSSH client, hence only mark it for autoremoval only.
dpkg-query -s 'openssh-client' &> /dev/null && G_EXEC apt-mark auto openssh-client
fi
if To_Uninstall 1 # Samba Client
then
# Unmount default mount point
findmnt /mnt/samba > /dev/null && G_EXEC umount -Rfl /mnt/samba
# Remove from fstab
G_EXEC sed --follow-symlinks -i '\|/mnt/samba|d' /etc/fstab
G_AGP cifs-utils smbclient
[[ -d '/mnt/samba' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /mnt/samba
fi
if To_Uninstall 110 # NFS Client
then
# Unmount default mount point
findmnt /mnt/nfs_client > /dev/null && G_EXEC umount -Rfl /mnt/nfs_client
# Remove from fstab
G_EXEC sed --follow-symlinks -i '\|/mnt/nfs_client|d' /etc/fstab
# nfs-kernel-server depends on nfs-common
dpkg-query -s 'nfs-common' &> /dev/null && G_EXEC apt-mark auto nfs-common
[[ -d '/mnt/nfs_client' ]] && G_EXEC rmdir --ignore-fail-on-non-empty /mnt/nfs_client
fi
if To_Uninstall 111 # UrBackup Server
then
# Pre-v6.29: ARMv8 source build and the package attempts to remove the non-existent "/etc/init.d/urbackup_srv" ...
Remove_Service urbackupsrv urbackup urbackup
if [[ -f '/var/urbackup/backup_server_settings.db' ]] && command -v sqlite3 > /dev/null
then
local backuppath=$(sqlite3 /var/urbackup/backup_server_settings.db 'select value from settings where key = "backupfolder"')
else
local backuppath=$(sed -n '/^[[:blank:]]*SOFTWARE_URBACKUP_BACKUPPATH=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
fi
G_AGP urbackup-server
G_EXEC rm -Rf /var/log/urbackup.log "${backuppath:-/mnt/dietpi_userdata/urbackup}"
unset -v backuppath
G_EXEC rm -f /etc/apt/sources.list.d/dietpi-urbackup.sources
G_EXEC rm -f /etc/apt/keyrings/dietpi-urbackup.asc
# Pre-v6.29: ARMv8 source build
command -v urbackupsrv > /dev/null && G_EXEC rm "$(command -v urbackupsrv)"
command -v urbackup_mount_helper > /dev/null && G_EXEC rm "$(command -v urbackup_mount_helper)"
command -v urbackup_snapshot_helper > /dev/null && G_EXEC rm "$(command -v urbackup_snapshot_helper)"
[[ -d '/usr/share/urbackup' ]] && G_EXEC rm -R /usr/share/urbackup
[[ -d '/usr/local/share/urbackup' ]] && G_EXEC rm -R /usr/local/share/urbackup
[[ -d '/var/urbackup' ]] && G_EXEC rm -R /var/urbackup
[[ -f '/etc/default/urbackupsrv' ]] && G_EXEC rm /etc/default/urbackupsrv
[[ -f '/etc/logrotate.d/urbackupsrv' ]] && G_EXEC rm /etc/logrotate.d/urbackupsrv
fi
if To_Uninstall 171 # frp
then
Remove_Service frpc
Remove_Service frps frp frp
# Binaries and configs
[[ -f '/usr/local/bin/frpc' ]] && G_EXEC rm /usr/local/bin/frpc
[[ -f '/usr/local/bin/frps' ]] && G_EXEC rm /usr/local/bin/frps
[[ -d '/etc/frp' ]] && G_EXEC rm -R /etc/frp
fi
if To_Uninstall 17 # Git
then
G_AGP git
fi
if To_Uninstall 4 # fish
then
getent passwd | while read -r line
do
[[ $line == *':/usr/bin/fish' ]] || continue
local user=${line%%:*}
G_EXEC chsh "$user" -s /bin/bash
done
G_AGP fish
[[ -d '/etc/fish/conf.d' ]] && G_EXEC rm -R /etc/fish/conf.d
fi
if To_Uninstall 5 # ALSA
then
/boot/dietpi/func/dietpi-set_hardware soundcard none
G_AGP alsa-utils libasound2-plugin-equal firmware-intel-sound
[[ -d '/etc/systemd/system/alsa-state.service.d' ]] && G_EXEC rm -R /etc/systemd/system/alsa-state.service.d
fi
if To_Uninstall 6 # X11
then
# shellcheck disable=SC2046
apt-mark auto $(dpkg --get-selections 'x11-*' dbus-x11 dbus-user-session 'libegl1*' 'libgles2*' libgl1-mesa-dri 'mesa-*' libdrm-rockchip1 libmali-rk-utgard-450-r7p0 'xf86-video-*' malit628-odroid mali450-odroid aml-libs-odroid 'libump*' firmware-samsung 2> /dev/null | mawk '{print $1}') 2> /dev/null
G_AGP 'xorg*' 'xserver-xorg*' xinit xcompmgr xterm xfonts-base
G_EXEC rm -f /etc/xdg/autostart/xcompmgr.desktop /etc/X11/xorg.conf /etc/X11/xorg.conf.d/98-dietpi-disable_dpms.conf
fi
if To_Uninstall 7 # FFmpeg
then
G_AGP ffmpeg
fi
if To_Uninstall 8 # Java JDK
then
# shellcheck disable=SC2046
apt-mark auto $(dpkg --get-selections 'temurin-*-jdk' 'default-jdk*' 'openjdk-*-jdk*' 'openjdk-*-doc' 'openjdk-*-demo' 'openjdk-*-source' 'openjdk-*-testsupport' 2> /dev/null | mawk '{print $1}') 2> /dev/null
fi
if To_Uninstall 196 # Java JRE
then
G_EXEC rm -f /etc/apt/sources.list.d/dietpi-adoptium.sources
G_EXEC rm -f /etc/apt/keyrings/dietpi-adoptium.asc
# shellcheck disable=SC2046
apt-mark auto $(dpkg --get-selections 'temurin-*-jre' 'default-jre*' 'openjdk-*-jre*' 'openjdk-*-dbg' ca-certificates-java 2> /dev/null | mawk '{print $1}') 2> /dev/null
fi
if To_Uninstall 104 # Dropbear
then
G_AGP 'dropbear*'
fi
if To_Uninstall 105 # OpenSSH Server
then
G_AGP openssh-{,sftp-}server
fi
if To_Uninstall 181 # PaperMC
then
Remove_Service papermc 1 1
# Files
[[ -d '/opt/papermc' ]] && G_EXEC rm -R /opt/papermc
[[ -d '/var/log/papermc' ]] && G_EXEC rm -R /var/log/papermc
[[ -d '/mnt/dietpi_userdata/papermc' ]] && G_EXEC rm -R /mnt/dietpi_userdata/papermc
[[ -f '/usr/local/bin/mcrcon' ]] && G_EXEC rm /usr/local/bin/mcrcon
fi
if To_Uninstall 101 # Logrotate
then
G_AGP logrotate
fi
if To_Uninstall 102 # Rsyslog
then
dpkg-query -s 'rsyslog' &> /dev/null && G_EXEC apt-mark auto rsyslog # https://github.com/MichaIng/DietPi/issues/2454
# Update logging choice index
if [[ $INDEX_LOGGING == -3 ]]
then
grep -q '[[:blank:]]/var/log[[:blank:]]' /etc/fstab || findmnt -t tmpfs -M /var/log > /dev/null && INDEX_LOGGING=-1 || INDEX_LOGGING=0
fi
fi
if To_Uninstall 103 # DietPi-RAMlog
then
G_EXEC sed --follow-symlinks -i '/[[:blank:]]\/var\/log[[:blank:]]/d' /etc/fstab
G_EXEC mkdir -p /var/lib/dietpi/dietpi-ramlog
cat << '_EOF_' > /var/lib/dietpi/dietpi-ramlog/disable.sh
#!/bin/dash
{
systemctl --no-reload disable dietpi-ramlog
systemctl stop dietpi-ramlog
rm -R /var/tmp/dietpi/logs/dietpi-ramlog_store
if systemctl -q is-enabled rsyslog
then
sed --follow-symlinks -i '/^[[:blank:]]*INDEX_LOGGING=/c\INDEX_LOGGING=-3' /boot/dietpi/.installed
else
sed --follow-symlinks -i '/^[[:blank:]]*INDEX_LOGGING=/c\INDEX_LOGGING=0' /boot/dietpi/.installed
fi
systemctl --no-reload disable dietpi-ramlog_disable
rm /etc/systemd/system/dietpi-ramlog_disable.service
rm /var/lib/dietpi/dietpi-ramlog/disable.sh
}
_EOF_
G_EXEC chmod +x /var/lib/dietpi/dietpi-ramlog/disable.sh
cat << '_EOF_' > /etc/systemd/system/dietpi-ramlog_disable.service
[Unit]
Description=DietPi-RAMlog_disable
After=dietpi-ramlog.service
Before=dietpi-preboot.service rsyslog.service syslog.service
[Service]
Type=oneshot
StandardOutput=tty
ExecStart=/bin/dash -c '/var/lib/dietpi/dietpi-ramlog/disable.sh > /var/tmp/dietpi/logs/dietpi-ramlog_disable_debug.log 2>&1'
[Install]
WantedBy=local-fs.target
_EOF_
G_EXEC systemctl daemon-reload
G_EXEC systemctl --no-reload enable dietpi-ramlog_disable
fi
if To_Uninstall 9 # Node.js
then
command -v npm > /dev/null && npm r -g n yarn npm
# Old install via repo
G_AGP nodejs
[[ -f '/etc/apt/sources.list.d/nodesource_nodejs.list' ]] && G_EXEC rm /etc/apt/sources.list.d/nodesource_nodejs.list
[[ -f '/usr/local/bin/node' ]] && G_EXEC rm /usr/local/bin/node
[[ -d '/usr/local/n' ]] && G_EXEC rm -R /usr/local/n
[[ -d '/usr/local/yarn' ]] && G_EXEC rm -R /usr/local/yarn
[[ -d '/usr/local/include/node' ]] && G_EXEC rm -R /usr/local/include/node
[[ -d '/usr/local/lib/node_modules' ]] && G_EXEC rm -R /usr/local/lib/node_modules
[[ -d '/usr/local/share/doc/node' ]] && G_EXEC rm -R /usr/local/share/doc/node
[[ -f '/usr/local/man/man1/node.1' ]] && G_EXEC rm /usr/local/man/man1/node.1
[[ -f '/usr/local/share/man/man1/node.1' ]] && G_EXEC rm /usr/local/share/man/man1/node.1
[[ -f '/usr/local/README.md' ]] && G_EXEC rm /usr/local/README.md
[[ -f '/usr/local/CHANGELOG.md' ]] && G_EXEC rm /usr/local/CHANGELOG.md
[[ -f '/usr/local/LICENSE' ]] && G_EXEC rm /usr/local/LICENSE
[[ -f '/usr/local/share/systemtap/tapset/node.stp' ]] && G_EXEC rm /usr/local/share/systemtap/tapset/node.stp
[[ -d '/root/.npm' ]] && G_EXEC rm -R /root/.npm
[[ -f '/root/.config/configstore/update-notifier-npm.json' ]] && G_EXEC rm /root/.config/configstore/update-notifier-npm.json
fi
if To_Uninstall 170 # UnRAR
then
G_AGP unrar
fi
if To_Uninstall 130 # Python 3
then
command -v pip3 > /dev/null && G_EXEC_OUTPUT=1 G_EXEC pip3 uninstall -y pip setuptools wheel
G_AGP python3-dev python3-pip # python3-pip: Pre-v6.32
[[ -f '/etc/pip.conf' ]] && G_EXEC rm /etc/pip.conf
G_EXEC rm -Rf /{root,home/*}/.cache/pip
fi
if To_Uninstall 188 # Go
then
G_AGP golang-go
[[ -f '/etc/bashrc.d/go.sh' ]] && G_EXEC rm /etc/bashrc.d/go.sh
[[ -d '/usr/local/go' ]] && G_EXEC rm -R /usr/local/go
fi
if To_Uninstall 191 # Snapcast Server
then
G_AGP snapserver
# Remove users from Debian's and snapcast's packages, to cleanly cover upgrades from one to the other.
getent passwd snapserver > /dev/null && G_EXEC userdel snapserver
getent passwd _snapserver > /dev/null && G_EXEC userdel _snapserver
fi
if To_Uninstall 192 # Snapcast Client
then
G_AGP snapclient
fi
if To_Uninstall 194 # PostgreSQL
then
G_AGP postgresql 'postgresql-*'
G_EXEC rm -Rf /etc/postgresql*
[[ -d '/mnt/dietpi_userdata/postgresql' ]] && G_EXEC rm -R /mnt/dietpi_userdata/postgresql
fi
if To_Uninstall 87 # SQLite
then
G_AGP sqlite3 'php*-sqlite3'
fi
if To_Uninstall 202 # Rclone
then
G_AGP rclone
G_EXEC rm -Rf /{root,home/*}/.config/rclone
fi
if To_Uninstall 209 # Restic
then
[[ -f '/usr/local/bin/restic' ]] && G_EXEC rm /usr/local/bin/restic
fi
if To_Uninstall 210 # MediaWiki
then
[[ -d '/var/www/wiki' ]] && G_EXEC rm -R /var/www/wiki
Remove_Database mediawiki
fi
if To_Uninstall 211 # Homebridge
then
Remove_Service homebridge 1 1
G_AGP homebridge
[[ -f '/etc/apt/sources.list.d/dietpi-homebridge.list' ]] && G_EXEC rm /etc/apt/sources.list.d/dietpi-homebridge.list
[[ -f '/etc/apt/trusted.gpg.d/dietpi-homebridge.gpg' ]] && G_EXEC rm /etc/apt/trusted.gpg.d/dietpi-homebridge.gpg
fi
if To_Uninstall 198 # File Browser
then
Remove_Service filebrowser 1
[[ -d '/opt/filebrowser' ]] && G_EXEC rm -R /opt/filebrowser
[[ -d '/mnt/dietpi_userdata/filebrowser' ]] && G_EXEC rm -R /mnt/dietpi_userdata/filebrowser
fi
if To_Uninstall 199 # Spotifyd
then
Remove_Service spotifyd 1 1
[[ -d '/opt/spotifyd' ]] && G_EXEC rm -R /opt/spotifyd
[[ -d '/mnt/dietpi_userdata/spotifyd' ]] && G_EXEC rm -R /mnt/dietpi_userdata/spotifyd
fi
if To_Uninstall 213 # soju
then
Remove_Service soju 1 1
local x
local commands=('soju' 'sojuctl' 'sojudb')
local manpages=('soju.1' 'sojuctl.1')
for x in "${commands[@]}"
do
[[ -f /usr/local/bin/$x ]] && G_EXEC rm "/usr/local/bin/$x"
done
for x in "${manpages[@]}"
do
[[ -f /usr/local/share/man/man1/$x ]] && G_EXEC rm "/usr/local/share/man/man1/$x"
done
[[ -d '/usr/local/share/man/man1' ]] && G_EXEC rmdir -p --ignore-fail-on-non-empty /usr/local/share/man/man1
[[ -d '/mnt/dietpi_userdata/soju' ]] && G_EXEC rm -Rf /mnt/dietpi_userdata/soju
fi
if To_Uninstall 169 # LazyLibrarian
then
Remove_Service lazylibrarian 1 1
G_EXEC rm -Rf /opt/lazylibrarian
G_EXEC rm -Rf /var/log/lazylibrarian
G_EXEC rm -Rf /mnt/dietpi_userdata/lazylibrarian
fi
if To_Uninstall 12 # RustDesk Server
then
Remove_Service rustdeskrelay
Remove_Service rustdesksignal rustdesk rustdesk
G_EXEC rm -Rf /opt/rustdesk
G_EXEC rm -Rf /mnt/dietpi_userdata/rustdesk
fi
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Finalising uninstall'
# Uninstall finished, set all uninstalled software to state 0 (not installed)
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == -1 )) && aSOFTWARE_INSTALL_STATE[$i]=0
done
G_AGA
G_EXEC systemctl daemon-reload
#----------------------------------------------------------------------
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Uninstall completed'
#----------------------------------------------------------------------
}
Run_Installations()
{
G_NOTIFY_3_MODE='Step'
#------------------------------------------------------------
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Checking for conflicts and missing inputs'
# Unmark software which requires user input during automated installs
Unmark_Unattended
# Unmark conflicting software if not done after interactive software selection already
(( $CONFLICTS_RESOLVED )) || Unmark_Conflicts
# Abort install if no selections are left and not first run setup
if (( $G_DIETPI_INSTALL_STAGE == 2 ))
then
local abort=1
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) || continue
abort=0
break
done
(( $abort )) && { G_DIETPI-NOTIFY 1 'No software installs are done. Aborting...'; exit 1; }
fi
#------------------------------------------------------------
# Disable powersaving on main screen during installation
command -v setterm > /dev/null && setterm -blank 0 -powersave off 2> /dev/null
# Mark dependencies for install
Mark_Dependencies
# Check network connectivity, DNS resolving and network time sync: https://github.com/MichaIng/DietPi/issues/786
# - Skip on first run installs where it is done in DietPi-Automation_Pre() already
(( $G_DIETPI_INSTALL_STAGE == 2 )) || Check_Net_and_Time_sync
# Pre-create directories which are required for many software installs
Create_Required_Dirs
# Read global software password
Update_Global_Pw
# Stop all services
# shellcheck disable=SC2154
[[ $G_SERVICE_CONTROL == 0 ]] || /boot/dietpi/dietpi-services stop
# Update package cache: Skip when flag was set by first run setup
(( $SKIP_APT_UPDATE )) || G_AGUP
# Install software
Install_Software
# Uninstall software if required by e.g. DietPi choice system
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == -1 )) || continue
Uninstall_Software
break
done
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Finalising install'
# Enable installed services
if [[ ${aENABLE_SERVICES[0]}${aSTART_SERVICES[0]} ]]
then
G_DIETPI-NOTIFY 2 'Enabling installed services'
for i in "${aENABLE_SERVICES[@]}" "${aSTART_SERVICES[@]}"
do
G_EXEC_NOHALT=1 G_EXEC systemctl --no-reload enable "$i"
done
fi
# Reload systemd units
G_EXEC systemctl daemon-reload
# Unmask systemd-logind if Kodi or Chromium were installed, it's set in dietpi.txt or libpam-systemd was installed
if [[ $(readlink /etc/systemd/system/systemd-logind.service) == '/dev/null' ]] &&
{ (( ${aSOFTWARE_INSTALL_STATE[31]} == 1 || ${aSOFTWARE_INSTALL_STATE[113]} == 1 )) || grep -q '^[[:blank:]]*AUTO_UNMASK_LOGIND=1' /boot/dietpi.txt || dpkg-query -s 'libpam-systemd' &> /dev/null; }
then
G_DIETPI-NOTIFY 2 'Enabling systemd-logind'
# dbus is required for systemd-logind to start
dpkg-query -s dbus &> /dev/null || G_AGI dbus
G_EXEC systemctl unmask dbus
G_EXEC systemctl start dbus
G_EXEC systemctl unmask systemd-logind
G_EXEC systemctl start systemd-logind
fi
# Sync DietPi-RAMlog to disk: https://github.com/MichaIng/DietPi/issues/4884
systemctl -q is-enabled dietpi-ramlog && /boot/dietpi/func/dietpi-ramlog 1
# Apply GPU Memory Splits
Install_Apply_GPU_Settings
# Offer to change DietPi-AutoStart option
if (( $G_DIETPI_INSTALL_STAGE == 2 )) && ((
${aSOFTWARE_INSTALL_STATE[23]} == 1 ||
${aSOFTWARE_INSTALL_STATE[24]} == 1 ||
${aSOFTWARE_INSTALL_STATE[25]} == 1 ||
${aSOFTWARE_INSTALL_STATE[26]} == 1 ||
${aSOFTWARE_INSTALL_STATE[31]} == 1 ||
${aSOFTWARE_INSTALL_STATE[51]} == 1 ||
${aSOFTWARE_INSTALL_STATE[108]} == 1 ||
${aSOFTWARE_INSTALL_STATE[112]} == 1 ||
${aSOFTWARE_INSTALL_STATE[113]} == 1 ||
${aSOFTWARE_INSTALL_STATE[119]} == 1 ||
${aSOFTWARE_INSTALL_STATE[173]} == 1 ))
then
G_WHIP_YESNO 'Would you like to configure the DietPi-AutoStart option?
\nThis will allow you to choose which program loads automatically, after the system has booted up, e.g.:
- Console\n - Desktop\n - Kodi' && /boot/dietpi/dietpi-autostart
fi
# Final user info
# - Home Assistant: https://dietpi.com/forum/t/home-assistant-finally-integrates-python-3-11/17033/40
if (( ${aSOFTWARE_INSTALL_STATE[157]} == 1 ))
then
G_WHIP_MSG '[ INFO ] Home Assistant may require a manual restart
\nOn first service start, Home Assistant installs and in case compiles a bunch of required Python module. This can take another while.
\nThe compilation of those modules can fail on first attempt for unknown reasons, but succeeds after restarting the service:
- sudo systemctl restart home-assistant
\nAlso check our documentation about this matter:
- https://dietpi.com/docs/software/home_automation/#home-assistant'
fi
# Install finished, set all installed software to state 2 (installed)
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && aSOFTWARE_INSTALL_STATE[$i]=2
done
# Write to .installed state file
Write_InstallFileList
}
#/////////////////////////////////////////////////////////////////////////////////////
# First Run / Automation function
#/////////////////////////////////////////////////////////////////////////////////////
# Setup steps prior to software installs
DietPi-Automation_Pre()
{
G_NOTIFY_3_MODE='Step'
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Applying initial first run setup steps'
# Get settings
AUTOINSTALL_ENABLED=$(grep -cm1 '^[[:blank:]]*AUTO_SETUP_AUTOMATED=1' /boot/dietpi.txt)
AUTOINSTALL_AUTOSTARTTARGET=$(sed -n '/^[[:blank:]]*AUTO_SETUP_AUTOSTART_TARGET_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local AUTOINSTALL_SSHINDEX=$(sed -n '/^[[:blank:]]*AUTO_SETUP_SSH_SERVER_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local AUTOINSTALL_LOGGINGINDEX=$(sed -n '/^[[:blank:]]*AUTO_SETUP_LOGGING_INDEX=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
AUTOINSTALL_CUSTOMSCRIPTURL=$(sed -n '/^[[:blank:]]*AUTO_SETUP_CUSTOM_SCRIPT_EXEC=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local AUTOINSTALL_RESTORE=$(sed -n '/^[[:blank:]]*AUTO_SETUP_BACKUP_RESTORE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
local AUTOINSTALL_RAMLOG_SIZE=$(sed -n '/^[[:blank:]]*AUTO_SETUP_RAMLOG_MAXSIZE=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
AUTO_SETUP_DHCP_TO_STATIC=$(grep -cm1 '^[[:blank:]]*AUTO_SETUP_DHCP_TO_STATIC=1' /boot/dietpi.txt)
# Else set defaults
[[ $AUTOINSTALL_AUTOSTARTTARGET ]] || AUTOINSTALL_AUTOSTARTTARGET=0
[[ $AUTOINSTALL_SSHINDEX ]] || AUTOINSTALL_SSHINDEX=-1
[[ $AUTOINSTALL_LOGGINGINDEX ]] || AUTOINSTALL_LOGGINGINDEX=-1
[[ $AUTOINSTALL_CUSTOMSCRIPTURL ]] || AUTOINSTALL_CUSTOMSCRIPTURL=0
[[ $AUTOINSTALL_RESTORE ]] || AUTOINSTALL_RESTORE=0
[[ $AUTOINSTALL_RAMLOG_SIZE ]] || AUTOINSTALL_RAMLOG_SIZE=50
# Restore DietPi-Backup
if (( $AUTOINSTALL_RESTORE )); then
# Reboot only when backup restore succeeded
restore_succeeded=0
G_DIETPI-NOTIFY 2 'DietPi-Backup restore selected, scanning and mounting attached drives...'
i=0
while read -r line
do
# Mount drives to temporary mount points
mkdir -p "/mnt/dietpi-backup$i" && mount "$line" "/mnt/dietpi-backup$i"
((i++))
done < <(lsblk -rnpo NAME,UUID,MOUNTPOINT | mawk '$2 && ! $3 {print $1}')
G_DIETPI-NOTIFY 2 'Searching all drives for DietPi-Backup instances...'
mapfile -t alist < <(find /mnt -type f -name '.dietpi-backup_stats')
# Interactive restore
if [[ $AUTOINSTALL_RESTORE == 1 ]]; then
# Do we have any results?
if [[ ${alist[0]} ]]
then
# Create List for Whiptail
G_WHIP_MENU_ARRAY=()
for i in "${alist[@]}"
do
last_backup_date=$(sed -n '/ompleted/s/^.*: //p' "$i" | tail -1) # Date of last backup for this backup
backup_directory=${i%/.dietpi-backup_stats} # Backup directory (minus the backup file), that we can use for target backup directory.
G_WHIP_MENU_ARRAY+=("$backup_directory" ": $last_backup_date")
done
export G_DIETPI_SERVICES_DISABLE=1
G_WHIP_MENU 'Please select a previous backup to restore:' && /boot/dietpi/dietpi-backup -1 "$G_WHIP_RETURNED_VALUE" && restore_succeeded=1
unset -v G_DIETPI_SERVICES_DISABLE
else
G_WHIP_MSG 'No previous backups were found in /mnt/*. Install will continue like normal.'
fi
# Non-interactive restore
elif [[ $AUTOINSTALL_RESTORE == 2 ]]; then
# Do we have any results?
if [[ ${alist[0]} ]]
then
# Restore first found backup
export G_DIETPI_SERVICES_DISABLE=1
/boot/dietpi/dietpi-backup -1 "${alist[0]%/.dietpi-backup_stats}" && restore_succeeded=1
unset -v G_DIETPI_SERVICES_DISABLE
else
G_DIETPI-NOTIFY 1 'DietPi-Backup auto-restore was selected but no backup has been found in /mnt/*. Install will continue like normal.'
fi
# Downgrade dietpi.txt option
G_CONFIG_INJECT 'AUTO_SETUP_BACKUP_RESTORE=' 'AUTO_SETUP_BACKUP_RESTORE=1' /boot/dietpi.txt
fi
# Remove mounted drives and mount points
findmnt /mnt/dietpi-backup[0-9]* > /dev/null && umount /mnt/dietpi-backup[0-9]*
[[ -d '/mnt/dietpi-backup0' ]] && rmdir /mnt/dietpi-backup[0-9]*
# Reboot on successful restore
if (( $restore_succeeded ))
then
G_DIETPI-NOTIFY 2 'The system will now reboot into the restored system'
sync # Failsafe
G_SLEEP 3
reboot
exit 0
fi
fi
# Check network connectivity, DNS resolving and network time sync: https://github.com/MichaIng/DietPi/issues/786
Check_Net_and_Time_sync
# Full package upgrade on first run installs: https://github.com/MichaIng/DietPi/issues/3098
if [[ ! -f '/boot/dietpi/.skip_distro_upgrade' ]]
then
G_AGUP
G_AGDUG
G_AGA
# Create a persistent flag to not repeat G_AGDUG and rule out a reboot loop when kernel modules remain missing
G_EXEC eval '> /boot/dietpi/.skip_distro_upgrade'
# Perform a reboot if required as of missing kernel modules
G_CHECK_KERNEL || { G_DIETPI-NOTIFY 2 'A reboot is done to finalise the kernel upgrade'; sync; reboot; exit 0; }
# Do not repeat APT update in Run_Installations()
SKIP_APT_UPDATE=1
fi
# Change password if default has not been changed via dietpi.txt
# - Non-automated interactive first run setup: Show password prompts
if (( ! $AUTOINSTALL_ENABLED && $G_INTERACTIVE ))
then
# Local console: Prompt to select keyboard layout first if default is still set: https://github.com/MichaIng/DietPi/issues/5925
[[ $(tty) == '/dev/tty1' ]] && grep -q '^XKBLAYOUT="gb"' /etc/default/keyboard && /boot/dietpi/func/dietpi-set_hardware keyboard
/boot/dietpi/func/dietpi-set_software password users check
Update_Global_Pw
# - Automated or non-interactive first run setup
else
# Update_Global_Pw is always executed right before installs start and in case forces users to change the default on any next interactive execution.
# Set flag for dietpi-login to check user passwords to in case enforce a change on next interactive login.
G_EXEC touch /var/lib/dietpi/.check_user_passwords
fi
# Disable serial console?
if (( $G_HW_MODEL != 75 )) && ! grep -q '^[[:blank:]]*CONFIG_SERIAL_CONSOLE_ENABLE=0' /boot/dietpi.txt &&
[[ ! $(tty) =~ ^/dev/(serial|tty(S|AMA|SAC|AML|SC|GS|FIQ|MV)|hvc)[0-9] ]] &&
G_WHIP_BUTTON_OK_TEXT='Yes' G_WHIP_BUTTON_CANCEL_TEXT='No' G_WHIP_YESNO 'A serial/UART console is currently enabled, would you like to disable it?
\nTL;DR: If you do not know what a UART device or a serial console is, it is safe to select "Yes", which frees some MiB memory by stopping the related process(es).
\nA serial console is a way to interact with a system without any screen or network (SSH) required, but from another system physically connected. It is accessed with a UART adapter cable (often UART-to-USB), connected to a special UART port or GPIO pins. It can then be accessed via COM port from the attached system with a serial console client, e.g. PuTTY (which supports both, SSH and serial console access).
\nAnother benefit is that you can view early boot logs, before network or even screen output is up, which makes it a great way to debug issues with the bootloader or kernel. However, to allow as well common user logins via serial console, at least one additional login prompt process is running, which you may want to avoid when not using this feature at all.
\nSerial consoles can re-enabled at any time via dietpi-config > Advanced Options > Serial/UART'
then
/boot/dietpi/func/dietpi-set_hardware serialconsole disable
fi
# RPi: Convert "serial0" to its actual symlink target without breaking the possibly currently used serial connection or starting a doubled console on the same serial device.
if (( $G_HW_MODEL < 10 )) && ! grep -q '^[[:blank:]]*CONFIG_SERIAL_CONSOLE_ENABLE=0' /boot/dietpi.txt
then
if [[ -e '/dev/serial0' ]]
then
local tty=$(realpath /dev/serial0); tty=${tty#/dev/}
G_DIETPI-NOTIFY 2 "Converting serial console from symlink /dev/serial0 to native /dev/$tty"
if [[ $(</boot/cmdline.txt) == *'console=serial0'* ]]
then
if [[ $(</boot/cmdline.txt) == *"console=$tty"* ]]
then
G_EXEC sed --follow-symlinks -Ei 's/[[:blank:]]*console=serial0[^[:blank:]]*([[:blank:]]*$)?//' /boot/cmdline.txt
else
G_EXEC sed --follow-symlinks -i "s/console=serial0/console=$tty/" /boot/cmdline.txt
fi
fi
if systemctl -q is-enabled serial-getty@serial0
then
G_EXEC systemctl --no-reload disable serial-getty@serial0
G_EXEC systemctl --no-reload unmask "serial-getty@$tty"
G_EXEC systemctl --no-reload enable "serial-getty@$tty"
fi
unset -v tty
else
G_DIETPI-NOTIFY 2 'Disabling serial console on non-existing /dev/serial0 device node'
/boot/dietpi/func/dietpi-set_hardware serialconsole disable serial0
fi
fi
# Apply RAMlog size
sed --follow-symlinks -i "\|[[:blank:]]/var/log[[:blank:]]|c\tmpfs /var/log tmpfs size=${AUTOINSTALL_RAMLOG_SIZE}M,noatime,lazytime,nodev,nosuid" /etc/fstab
findmnt /var/log > /dev/null && G_EXEC mount -o remount /var/log
# Set time sync mode if no container system
(( $G_HW_MODEL == 75 )) || /boot/dietpi/func/dietpi-set_software ntpd-mode
# Apply choice and preference system settings
Apply_SSHServer_Choices "$AUTOINSTALL_SSHINDEX"
Apply_Logging_Choices "$AUTOINSTALL_LOGGINGINDEX"
G_DIETPI-NOTIFY 0 'Applied initial first run setup steps'
# Automated installs
(( $AUTOINSTALL_ENABLED )) || return 0
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Running automated install'
TARGETMENUID=-1 # Skip menu loop
GOSTARTINSTALL=1 # Set install start flag
# Find all software entries of AUTO_SETUP_INSTALL_SOFTWARE_ID= in dietpi.txt. Then set to state 1 for installation.
G_DIETPI-NOTIFY 2 'Checking AUTO_SETUP_INSTALL_SOFTWARE_ID entries'
# - Pre-v9.10: Support multiple AUTO_SETUP_INSTALL_SOFTWARE_ID lines
while read -ra software_ids
do
# v9.10: Support multiple IDs per AUTO_SETUP_INSTALL_SOFTWARE_ID line
for software_id in "${software_ids[@]}"
do
# Skip if software does not exist, is not supported on architecture, hardware model or Debian version
if [[ ! ${aSOFTWARE_NAME[$software_id]} ]]
then
G_DIETPI-NOTIFY 1 "Software title with ID $software_id does not exist. Skipping it."
elif (( ! ${aSOFTWARE_AVAIL_G_HW_ARCH[$software_id,$G_HW_ARCH]:=1} ))
then
G_DIETPI-NOTIFY 1 "Software title ${aSOFTWARE_NAME[$software_id]} is not supported on ${RPI_64KERNEL_32OS:-$G_HW_ARCH_NAME} systems. Skipping it."
elif (( ! ${aSOFTWARE_AVAIL_G_HW_MODEL[$software_id,$G_HW_MODEL]:=1} ))
then
G_DIETPI-NOTIFY 1 "Software title ${aSOFTWARE_NAME[$software_id]} is not supported on $G_HW_MODEL_NAME. Skipping it."
elif (( ! ${aSOFTWARE_AVAIL_G_DISTRO[$software_id,$G_DISTRO]:=1} )); then
G_DIETPI-NOTIFY 1 "Software title ${aSOFTWARE_NAME[$software_id]} is not supported on Debian ${G_DISTRO_NAME^}. Skipping it."
else
aSOFTWARE_INSTALL_STATE[$software_id]=1
G_DIETPI-NOTIFY 0 "Software title ${aSOFTWARE_NAME[$software_id]} flagged for installation."
fi
done
done < <(grep '^[[:blank:]]*AUTO_SETUP_INSTALL_SOFTWARE_ID=' /boot/dietpi.txt | sed -e 's/^[^=]*=//' -e 's/#.*$//')
}
# Setup steps after software installs
DietPi-Automation_Post()
{
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Applying final first run setup steps'
# Remove fake-hwclock if real hwclock is available
# REMOVED: "hwclock" succeeds if an RTC connector is available but no battery attached (or empty), hence we cannot guarantee correct RTC time on boot by only testing "hwclock".
#hwclock &> /dev/null && G_AGP fake-hwclock
# x86_64 PC: Install microcode updates
if (( $G_HW_MODEL == 21 ))
then
grep -qi 'vendor_id.*intel' /proc/cpuinfo && G_AGI intel-microcode
grep -qi 'vendor_id.*amd' /proc/cpuinfo && G_AGI amd64-microcode
# VM: Enable QEMU guest agent if detected
elif (( $G_HW_MODEL == 20 ))
then
/boot/dietpi/func/dietpi-set_hardware qga 1
# RPi 4/5 EEPROM update: https://github.com/MichaIng/DietPi/issues/3217
elif [[ $G_HW_MODEL == [45] ]]
then
/boot/dietpi/func/dietpi-set_hardware rpi-eeprom
fi
# Install GPU driver if set
local gpu_driver=$(sed -n '/^[[:blank:]]*CONFIG_GPU_DRIVER=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
[[ $gpu_driver && ${gpu_driver,,} != 'none' ]] && /boot/dietpi/func/dietpi-set_hardware gpudriver "$gpu_driver"
# Apply DHCP leased network settings as static network settings if requested
if (( $AUTO_SETUP_DHCP_TO_STATIC ))
then
G_DIETPI-NOTIFY 2 'Applying DHCP leased network settings as static network settings'
# Function to convert CIDR notation into dot-decimal notation
cidr2mask()
{
local i mask full_octets=$(( $1 / 8 )) partial_octet=$(( $1%8 ))
for i in {0..3}
do
if (( $i < $full_octets ))
then
mask+='255'
elif (( $i == $full_octets ))
then
mask+=$(( 256 - 2 ** ( 8 - $partial_octet ) ))
else
mask+='0'
fi
(( $i < 3 )) && mask+=.
done
echo "$mask"
}
# Get Ethernet index
local nameservers eth_index=$(sed -En '/^[[:blank:]]*(allow-hotplug|auto)[[:blank:]]+eth[0-9]+$/{s/^.*eth//p;q}' /etc/network/interfaces)
# - Is enabled and uses DHCP
if [[ $eth_index ]] && grep -Eq "^[[:blank:]]*iface[[:blank:]]+eth${eth_index}[[:blank:]]+inet[[:blank:]]+dhcp$" /etc/network/interfaces
then
G_DIETPI-NOTIFY 2 'Applying DHCP leased Ethernet settings as static Ethernet settings'
# Get current network info
local eth_ip=$(ip -br -f inet a s "eth$eth_index" | mawk '{print $3}' | sed 's|/.*$||')
local eth_mask=$(cidr2mask "$(ip -br -f inet a s "eth$eth_index" | mawk '{print $3}' | sed 's|^.*/||')")
local eth_gateway=$(ip r l dev "eth$eth_index" 0/0 | mawk '{print $3;exit}')
nameservers=$(mawk '/^[[:blank:]]*nameserver[[:blank:]]/{print $2}' ORS=' ' /etc/resolv.conf)
# Apply current network settings statically
G_CONFIG_INJECT "iface[[:blank:]]+eth$eth_index" "iface eth$eth_index inet static" /etc/network/interfaces
sed --follow-symlinks -i "0,\|^.*address[[:blank:]].*\$|s||address $eth_ip|" /etc/network/interfaces
sed --follow-symlinks -i "0,\|^.*netmask[[:blank:]].*\$|s||netmask $eth_mask|" /etc/network/interfaces
sed --follow-symlinks -i "0,\|^.*gateway[[:blank:]].*\$|s||gateway $eth_gateway|" /etc/network/interfaces
fi
# Get WiFi index
local wlan_index=$(sed -En '/^[[:blank:]]*(allow-hotplug|auto)[[:blank:]]+wlan[0-9]+$/{s/^.*wlan//p;q}' /etc/network/interfaces)
# - Is enabled and uses DHCP
if [[ $wlan_index ]] && grep -Eq "^[[:blank:]]*iface[[:blank:]]+wlan${wlan_index}[[:blank:]]+inet[[:blank:]]+dhcp$" /etc/network/interfaces
then
G_DIETPI-NOTIFY 2 'Applying DHCP leased WiFi settings as static WiFi settings'
# Get current network info
local wlan_ip=$(ip -br -f inet a s "wlan$wlan_index" | mawk '{print $3}' | sed 's|/.*$||')
local wlan_mask=$(cidr2mask "$(ip -br -f inet a s "wlan$wlan_index" | mawk '{print $3}' | sed 's|^.*/||')")
local wlan_gateway=$(ip r l dev "wlan$wlan_index" 0/0 | mawk '{print $3;exit}')
[[ $nameservers ]] || nameservers=$(mawk '/^[[:blank:]]*nameserver[[:blank:]]/{print $2}' ORS=' ' /etc/resolv.conf)
# Apply current network settings statically
G_CONFIG_INJECT "iface[[:blank:]]+wlan$wlan_index" "iface wlan$wlan_index inet static" /etc/network/interfaces
sed --follow-symlinks -i "\|^iface wlan|,\$s|^.*address[[:blank:]].*\$|address $wlan_ip|" /etc/network/interfaces
sed --follow-symlinks -i "\|^iface wlan|,\$s|^.*netmask[[:blank:]].*\$|netmask $wlan_mask|" /etc/network/interfaces
sed --follow-symlinks -i "\|^iface wlan|,\$s|^.*gateway[[:blank:]].*\$|gateway $wlan_gateway|" /etc/network/interfaces
fi
unset -f cidr2mask
# Apply DNS nameservers
if [[ $nameservers ]]
then
G_DIETPI-NOTIFY 2 'Applying DHCP leased DNS nameservers as static nameservers'
if command -v resolvconf > /dev/null
then
sed --follow-symlinks -i "/dns-nameservers[[:blank:]]/c\dns-nameservers ${nameservers% }" /etc/network/interfaces
else
sed --follow-symlinks -i "/dns-nameservers[[:blank:]]/c\#dns-nameservers ${nameservers% }" /etc/network/interfaces
> /etc/resolv.conf
for i in $nameservers; do echo "nameserver $i" >> /etc/resolv.conf; done
fi
fi
G_DIETPI-NOTIFY 0 'Network changes will become effective with next reboot'
fi
# RPi: Disable onboard WiFi if WiFi modules didn't get enabled until now: https://github.com/MichaIng/DietPi/issues/5391
[[ $G_HW_MODEL -gt 9 || ! -f '/etc/modprobe.d/dietpi-disable_wifi.conf' ]] || /boot/dietpi/func/dietpi-set_hardware wifimodules onboard_disable
# Apply AutoStart choice
/boot/dietpi/dietpi-autostart "$AUTOINSTALL_AUTOSTARTTARGET"
# Disable console on TTY1 for purely headless SBCs, enabled for automated first run setup
[[ $G_HW_MODEL =~ ^(47|48|55|56|57|59|60|64|65|73)$ ]] && G_EXEC systemctl --no-reload disable getty@tty1
# Images are shipped with cron disabled, hence enable it now after first run setup has finished
G_EXEC systemctl enable cron
# Set install stage to finished
G_DIETPI_INSTALL_STAGE=2
G_EXEC eval 'echo 2 > /boot/dietpi/.install_stage'
# Remove now obsolete flag
[[ -f '/boot/dietpi/.skip_distro_upgrade' ]] && G_EXEC rm /boot/dietpi/.skip_distro_upgrade
G_DIETPI-NOTIFY 0 'Applied final first run setup steps'
# Process automatic APT package installs
local packages=() package
read -ra packages < <(sed -n '/^[[:blank:]]*AUTO_SETUP_APT_INSTALLS=/{s/^[^=]*=//p;q}' /boot/dietpi.txt)
for package in "${packages[@]}"
do
G_EXEC_NOHALT=1 G_AGI "$package"
done
# Custom 1st run script
[[ $AUTOINSTALL_CUSTOMSCRIPTURL != '0' || -f '/boot/Automation_Custom_Script.sh' ]] || return 0
# Download online script
[[ -f '/boot/Automation_Custom_Script.sh' ]] || G_EXEC_NOEXIT=1 G_EXEC curl -sSfL "$AUTOINSTALL_CUSTOMSCRIPTURL" -o /boot/Automation_Custom_Script.sh || return $?
G_DIETPI-NOTIFY 2 'Running custom script, please wait...'
[[ -x '/boot/Automation_Custom_Script.sh' ]] || G_EXEC_NOEXIT=1 G_EXEC chmod +x /boot/Automation_Custom_Script.sh
/boot/Automation_Custom_Script.sh 2>&1 | tee /var/tmp/dietpi/logs/dietpi-automation_custom_script.log
G_DIETPI-NOTIFY $(( ! ! ${PIPESTATUS[0]} )) 'Custom script: /var/tmp/dietpi/logs/dietpi-automation_custom_script.log'
}
#/////////////////////////////////////////////////////////////////////////////////////
# Globals
#/////////////////////////////////////////////////////////////////////////////////////
Input_Modes()
{
# Process software and exit
if [[ $1 == 'install' || $1 == 'reinstall' || $1 == 'uninstall' ]]; then
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" "Automated $1"
# Make sure we have at least one entry
[[ $2 ]] || { G_DIETPI-NOTIFY 1 'Please enter at least one software ID to process'; return 1; }
# Process software IDs
local command=$1
shift
for i in "$@"
do
# Check if input software ID exists, install state was defined
if disable_error=1 G_CHECK_VALIDINT "$i" 0 && disable_error=1 G_CHECK_VALIDINT "${aSOFTWARE_INSTALL_STATE[$i]}"; then
if [[ $command == 'uninstall' ]]; then
if (( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 )); then
aSOFTWARE_INSTALL_STATE[$i]=-1
G_DIETPI-NOTIFY 0 "Uninstalling ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"
elif (( ${aSOFTWARE_INSTALL_STATE[$i]} != -1 )); then
G_DIETPI-NOTIFY 2 "$i: ${aSOFTWARE_NAME[$i]} is not currently installed"
G_DIETPI-NOTIFY 0 "No changes applied for: ${aSOFTWARE_NAME[$i]}"
fi
elif [[ $command == 'reinstall' ]]; then
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && continue
if (( ${aSOFTWARE_INSTALL_STATE[$i]} != 2 )); then
G_DIETPI-NOTIFY 2 "$i: ${aSOFTWARE_NAME[$i]} is not currently installed"
G_DIETPI-NOTIFY 2 "Use \"dietpi-software install $i\" to install ${aSOFTWARE_NAME[$i]}."
G_DIETPI-NOTIFY 0 "No changes applied for: ${aSOFTWARE_NAME[$i]}"
elif (( ! ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} )); then
G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported on $G_HW_ARCH_NAME systems."
elif (( ! ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} )); then
G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported for $G_HW_MODEL_NAME."
elif (( ! ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )); then
G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported on Debian ${G_DISTRO_NAME^}."
else
aSOFTWARE_INSTALL_STATE[$i]=1
GOSTARTINSTALL=1 # Set install start flag
G_DIETPI-NOTIFY 0 "Reinstalling ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"
fi
elif [[ $command == 'install' ]]; then
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && continue
if (( ! ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} )); then
G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported on $G_HW_ARCH_NAME systems."
elif (( ! ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} )); then
G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported for $G_HW_MODEL_NAME."
elif (( ! ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )); then
G_DIETPI-NOTIFY 1 "Software title (${aSOFTWARE_NAME[$i]}) is not supported on Debian ${G_DISTRO_NAME^}."
elif (( ${aSOFTWARE_INSTALL_STATE[$i]} != 2 )); then
aSOFTWARE_INSTALL_STATE[$i]=1
GOSTARTINSTALL=1 # Set install start flag
G_DIETPI-NOTIFY 0 "Installing ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"
else
G_DIETPI-NOTIFY 2 "$i: ${aSOFTWARE_NAME[$i]} is already installed"
G_DIETPI-NOTIFY 2 "Use \"dietpi-software reinstall $i\" to force rerun of installation and configuration steps for ${aSOFTWARE_NAME[$i]}."
G_DIETPI-NOTIFY 0 "No changes applied for: ${aSOFTWARE_NAME[$i]}"
fi
fi
fi
done
# Reinstall, prompt for backup
if [[ $command == 'reinstall' && $GOSTARTINSTALL == 1 ]]; then
G_PROMPT_BACKUP
CONFLICTS_RESOLVED=1
# Uninstall | Finish up and clear non-required packages
elif [[ $command == 'uninstall' ]]; then
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == -1 )) || continue
Uninstall_Software
Write_InstallFileList
break
done
fi
# List software IDs, names and additional info
elif [[ $1 == 'list' ]]; then
if [[ $MACHINE_READABLE ]]
then
for i in "${!aSOFTWARE_NAME[@]}"
do
# ID
local string=$i
# Show if disabled
(( ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} && ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} && ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )) || string+=' DISABLED'
# Install state, software name, and description
string+="|${aSOFTWARE_INSTALL_STATE[$i]}|${aSOFTWARE_NAME[$i]}|${aSOFTWARE_DESC[$i]}|"
# Append dependencies
for j in ${aSOFTWARE_DEPS[$i]}
do
# Add software name or raw meta dependencies to string
[[ $j == *[^0-9]* ]] && string+="$j," || string+="${aSOFTWARE_NAME[$j]},"
done
echo "${string%,}|${aSOFTWARE_DOCS[$i]}"
done
else
for i in "${!aSOFTWARE_NAME[@]}"
do
# ID, install state, software name and description
local string="ID $i | =${aSOFTWARE_INSTALL_STATE[$i]} | ${aSOFTWARE_NAME[$i]}:\e[0m \e[90m${aSOFTWARE_DESC[$i]}\e[0m |"
# Paint green if installed
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 )) && string="\e[32m$string"
# Append dependencies
for j in ${aSOFTWARE_DEPS[$i]}
do
# Add software name or raw meta dependencies to string
[[ $j == *[^0-9]* ]] && string+=" +$j" || string+=" +${aSOFTWARE_NAME[$j]}"
done
# Available for G_HW_ARCH?
if (( ! ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} )); then
string+=" \e[31mDISABLED for ${RPI_64KERNEL_32OS:-$G_HW_ARCH_NAME}\e[0m"
# Available for G_HW_MODEL?
elif (( ! ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} )); then
string+=" \e[31mDISABLED for $G_HW_MODEL_NAME\e[0m"
# Available for G_DISTRO?
elif (( ! ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )); then
string+=" \e[31mDISABLED for Debian $G_DISTRO_NAME\e[0m"
fi
# Append online docs if available
[[ ${aSOFTWARE_DOCS[$i]} ]] && string+=" | \e[90m${aSOFTWARE_DOCS[$i]}\e[0m"
echo -e "$string"
done
fi
elif [[ $1 == 'free' ]]; then
# Get highest software array index
local max=0
for max in "${!aSOFTWARE_NAME[@]}"; do :; done
# Check for unused indices
local free=
for (( i=0; i<=$max; i++ )); do [[ ${aSOFTWARE_NAME[$i]} ]] || free+=" $i"; done
echo "Free software ID(s):${free:- None, so use $(($max+1))!}"
else
G_DIETPI-NOTIFY 1 "Invalid input command ($1). Aborting...\n$USAGE"
exit 1
fi
}
#/////////////////////////////////////////////////////////////////////////////////////
# Whip menus
#/////////////////////////////////////////////////////////////////////////////////////
MENU_MAIN_LASTITEM='Help!'
TARGETMENUID=0
# $1=search: Show search box and show only matches in menu
Menu_CreateSoftwareList()
{
local i j selected reset=()
# Search mode
if [[ $1 == 'search' ]]
then
G_WHIP_INPUTBOX 'Please enter a software title, ID or keyword to search, e.g.: desktop/cloud/media/torrent' || return 0
G_WHIP_CHECKLIST_ARRAY=()
# Loop through all software titles
for i in "${!aSOFTWARE_NAME[@]}"
do
# Check if this software is available for hardware, arch and distro
(( ${aSOFTWARE_AVAIL_G_HW_MODEL[$i,$G_HW_MODEL]:=1} && ${aSOFTWARE_AVAIL_G_HW_ARCH[$i,$G_HW_ARCH]:=1} && ${aSOFTWARE_AVAIL_G_DISTRO[$i,$G_DISTRO]:=1} )) || continue
# Check if input matches software ID, name or description
[[ $G_WHIP_RETURNED_VALUE == "$i" || ${aSOFTWARE_NAME[$i],,} == *"${G_WHIP_RETURNED_VALUE,,}"* || ${aSOFTWARE_DESC[$i],,} == *"${G_WHIP_RETURNED_VALUE,,}"* ]] || continue
# Set checkbox based on install state, including previous selection
(( ${aSOFTWARE_INSTALL_STATE[$i]} > 0 )) && selected='on' || selected='off'
# Add this software title to whiptail menu
G_WHIP_CHECKLIST_ARRAY+=("$i" "${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}" "$selected")
# Add previously selected items to array to be unmarked if deselected when selection is confirmed.
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && reset+=("$i")
done
(( ${#G_WHIP_CHECKLIST_ARRAY[@]} )) || { G_WHIP_MSG "We couldn't find any available software title for the search term: \"$G_WHIP_RETURNED_VALUE\""; return 0; }
# Generate whiptail menu list of all software titles, sorted by category
else
G_WHIP_CHECKLIST_ARRAY=()
# Loop through software category IDs
for i in "${!aSOFTWARE_CATEGORIES[@]}"
do
# Add category to whiptail menu
G_WHIP_CHECKLIST_ARRAY+=('' "${aSOFTWARE_CATEGORIES[$i]}" 'off')
# Loop through software title IDs
for j in "${!aSOFTWARE_CATX[@]}"
do
# Check if this software's category matches the current category
(( ${aSOFTWARE_CATX[$j]} == $i )) || continue
# Check if this software is available for hardware, arch and distro
(( ${aSOFTWARE_AVAIL_G_HW_MODEL[$j,$G_HW_MODEL]:=1} && ${aSOFTWARE_AVAIL_G_HW_ARCH[$j,$G_HW_ARCH]:=1} && ${aSOFTWARE_AVAIL_G_DISTRO[$j,$G_DISTRO]:=1} )) || continue
# Set checkbox based on install state, including previous selection
(( ${aSOFTWARE_INSTALL_STATE[$j]} > 0 )) && selected='on' || selected='off'
# Add this software title to whiptail menu
G_WHIP_CHECKLIST_ARRAY+=("$j" "${aSOFTWARE_NAME[$j]}: ${aSOFTWARE_DESC[$j]}" "$selected")
# Add previously selected items to array to be unmarked if deselected when selection is confirmed.
(( ${aSOFTWARE_INSTALL_STATE[$j]} == 1 )) && reset+=("$j")
done
done
fi
G_WHIP_SIZE_X_MAX=96 # Assure this is enough to show full software descriptions + scroll bar
G_WHIP_BUTTON_OK_TEXT='Confirm'
G_WHIP_CHECKLIST 'Please use the spacebar to select the software you wish to install. Then press ENTER/RETURN or select <Confirm> to confirm.
- Press ESC or select <Cancel> to discard changes made.
- Software and usage details: https://dietpi.com/docs/software/' || return 0
# Unmark all listed pending state items, so deselected items are not installed.
for i in "${reset[@]}"
do
aSOFTWARE_INSTALL_STATE[$i]=0
done
# Mark selected items for install
for i in $G_WHIP_RETURNED_VALUE
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 )) || aSOFTWARE_INSTALL_STATE[$i]=1
done
# Unmark conflicting software
Unmark_Conflicts
#-----------------------------------------------------------------------------
# Install info/warnings/inputs
# DietPi-Drive_Manager can be used to setup Samba/NFS shares with ease!
(( ${aSOFTWARE_INSTALL_STATE[1]} == 1 || ${aSOFTWARE_INSTALL_STATE[110]} == 1 )) && G_WHIP_MSG "[ INFO ] Mount NFS/Samba shares via DietPi-Drive_Manager
\nDietPi-Drive_Manager is a powerful tool which vastly simplifies the mounting of NFS and Samba shares.
\nOnce $G_PROGRAM_NAME has finished installation, simply run 'dietpi-drive_manager' to setup required network mounts."
# PaperMC: Inform user about long install/startup time and possible swap file usage
if (( ${aSOFTWARE_INSTALL_STATE[181]} == 1 ))
then
local swap_info=
(( $RAM_PHYS < 924 )) && swap_info='\n\nThe server will be started with with minimal required memory usage, but a swap file will be created to assure that no out-of-memory crash can happen.
On servers with less than 1 GiB physical memory, we strongly recommend to move the swap file to an external drive if your system runs on an SD card, since during normal PaperMC operation this swap space will be heavily used.'
G_WHIP_MSG "PaperMC will be started during install to allow pre-configuring it's default configuration files. Especially on smaller SBCs, like Raspberry Pi Zero, this can take a long time.
We allow it to take up to 30 minutes, it's process can be followed, please be patient.$swap_info"
fi
# mjpg-streamer: Warn about unprotected stream and inform about additional plugins
(( ${aSOFTWARE_INSTALL_STATE[137]} == 1 )) && G_WHIP_MSG '[ WARN ] The mjpg-streamer camera stream will be accessible unprotected at port 8082 by default.
\nYou can configure a password protection, but this will break embedding the stream into other web interfaces, like OctoPrint.
\nWe hence recommend to not forward port 8082 through your NAT and/or block public access via firewall.
\nIf you require access from outside your local network to a web interface that embeds the camera stream, we recommend to setup a VPN connection for this.
\nRead more about this matter and how to configure mjpg-streamer at our online documentation: https://dietpi.com/docs/software/camera/#mjpg-streamer
\n[ INFO ] mjpg-streamer will not be compiled with all available plugins by default.
\nIf you require other input or output plugins, simply install the required dependencies. Plugins will be compiled automatically if dependencies are met.
\nFor available plugins and their dependencies, watch the info printed during the build and check out the official GitHub repository: https://github.com/jacksonliam/mjpg-streamer'
# RPi Cam Web Interface: Warn user of locking out camera: https://github.com/MichaIng/DietPi/issues/249
(( ${aSOFTWARE_INSTALL_STATE[59]} == 1 )) && G_WHIP_MSG 'RPi Cam Web Interface will automatically start and activate the camera during boot. This will prevent other programs (like raspistill) from using the camera.
\nYou can free up the camera by selecting "Stop Camera" from the web interface:\n - http://myip/rpicam'
# Offer to install Unbound along with AdGuard Home and Pi-hole
if (( ${aSOFTWARE_INSTALL_STATE[93]} == 1 || ${aSOFTWARE_INSTALL_STATE[126]} == 1 ))
then
# Add option to use Unbound as upstream DNS server
if (( ${aSOFTWARE_INSTALL_STATE[182]} == 0 ))
then
G_WHIP_BUTTON_CANCEL_TEXT='Skip' G_WHIP_YESNO 'Would you like to use Unbound, a tiny recursive DNS server hosted on your device, as your upstream DNS server?
\nThis will increase privacy, because you will not be sending data to Google etc.
\nHowever, the downside is that some websites may load slower the first time you visit them.' && aSOFTWARE_INSTALL_STATE[182]=1
fi
# Prompt for static IP
if G_WHIP_BUTTON_CANCEL_TEXT='Skip' G_WHIP_YESNO 'A static IP address is essential for a DNS server installation. DietPi-Config can be used to quickly setup your static IP address.
\nIf you have already setup your static IP, please ignore this message.\n\nWould you like to setup your static IP address now?'
then
G_WHIP_MSG 'DietPi-Config will now be launched. Simply select your Ethernet or Wifi connection from the menu to access the IP address settings.
\nThe "copy current address to STATIC" menu option can be used to quickly setup your static IP. Please ensure you change the mode "DHCP" to "STATIC".
\nOnce completed, select "Apply Save Changes", then exit DietPi-Config to resume setup.'
/boot/dietpi/dietpi-config 8
fi
fi
# WiFi Hotspot Criteria
if (( ${aSOFTWARE_INSTALL_STATE[60]} == 1 || ${aSOFTWARE_INSTALL_STATE[61]} == 1 ))
then
# Enable WiFi modules
/boot/dietpi/func/dietpi-set_hardware wifimodules enable
while :
do
local criteria_passed=1
local output_string='The following criteria must be met for the installation of WiFi Hotspot to succeed:'
if [[ $(G_GET_NET -q -t eth ip) ]]
then
output_string+='\n\n - Ethernet online: PASSED'
else
criteria_passed=0
output_string+='\n\n - Ethernet online: FAILED.\nUse dietpi-config to connect and configure Ethernet.'
fi
if [[ $(G_GET_NET -q -t wlan iface) ]]
then
output_string+='\n\n - WiFi adapter detected: PASSED'
else
criteria_passed=0
output_string+='\n\n - WiFi adapter detected: FAILED.\nPlease connect a WiFi adapter and try again.'
fi
# Passed
if (( $criteria_passed ))
then
output_string+='\n\nPASSED: Criteria met. Good to go.'
G_WHIP_MSG "$output_string"
break
# Failed, retry?
else
output_string+='\n\nFAILED: Criteria not met. Would you like to check again?'
G_WHIP_YESNO "$output_string" && continue
(( ${aSOFTWARE_INSTALL_STATE[60]} == 1 )) && aSOFTWARE_INSTALL_STATE[60]=0
(( ${aSOFTWARE_INSTALL_STATE[61]} == 1 )) && aSOFTWARE_INSTALL_STATE[61]=0
G_WHIP_MSG 'WiFi Hotspot criteria were not met. The software will not be installed.'
break
fi
done
fi
# Let's Encrypt
(( ${aSOFTWARE_INSTALL_STATE[92]} == 1 )) && G_WHIP_MSG 'The DietPi installation of Certbot supports all offered web servers.\n\nOnce the installation has finished, you can setup your free SSL cert with:
- DietPi-LetsEncrypt\n\nThis is an easy to use frontend for Certbot and allows integration into DietPi systems.\n\nMore information:\n - https://dietpi.com/docs/software/system_security/#lets-encrypt'
# Steam on ARMv7 via Box86 warning
(( ${aSOFTWARE_INSTALL_STATE[156]} == 1 && $G_HW_ARCH == 2 )) && G_WHIP_MSG '[WARNING] Steam natively only runs on the x86 systems.\n\nBox86 will be used to run it on ARM, however there may be performance and compatibility issues.'
# Home Assistant: Inform about long install/build time: https://github.com/MichaIng/DietPi/issues/2897
(( ${aSOFTWARE_INSTALL_STATE[157]} == 1 )) && G_WHIP_MSG '[ INFO ] Home Assistant: Grab yourself a coffee
\nThe install process of Home Assistant within the virtual environment, especially the Python build, can take more than one hour, especially on slower SBCs like RPi Zero and similar.
\nPlease be patient. In the meantime you may study the documentation:
- https://dietpi.com/docs/software/home_automation/#home-assistant'
}
Menu_Main()
{
# Selected SSH server choice
local sshserver_text='None'
if (( ${aSOFTWARE_INSTALL_STATE[104]} > 0 )); then
sshserver_text=${aSOFTWARE_NAME[104]} # Dropbear
elif (( ${aSOFTWARE_INSTALL_STATE[105]} > 0 )); then
sshserver_text=${aSOFTWARE_NAME[105]} # OpenSSH
fi
# Selected logging system choice
local index_logging_text='None'
if (( $INDEX_LOGGING == -1 )); then
index_logging_text='DietPi-RAMlog #1'
elif (( $INDEX_LOGGING == -2 )); then
index_logging_text='DietPi-RAMlog #2'
elif (( $INDEX_LOGGING == -3 )); then
index_logging_text='Full'
fi
# Get real userdata location
local user_data_location_current=$(readlink -f /mnt/dietpi_userdata)
local user_data_location_description="Custom | $user_data_location_current"
if [[ $user_data_location_current == '/mnt/dietpi_userdata' ]]; then
user_data_location_description="SD/eMMC | $user_data_location_current"
elif [[ $user_data_location_current == "$(findmnt -Ufnro TARGET -S /dev/sda1)" ]]; then
user_data_location_description="USB Drive | $user_data_location_current"
fi
# Software to be installed or removed based on choice system
local tobeinstalled_text toberemoved_text
G_WHIP_MENU_ARRAY=(
'Help!' ': Links to online guides, docs and information'
'DietPi-Config' ': Feature-rich configuration tool for your device'
'' '●─ Select Software '
'Search Software' ': Find software to install via search box'
'Browse Software' ': Select software from the full list'
'SSH Server' ": [$sshserver_text]"
'Log System' ": [$index_logging_text]"
'User Data Location' ": [$user_data_location_description]"
'' '●─ Install or Remove Software '
'Uninstall' ': Select installed software for removal'
'Install' ': Go >> Start installation for selected software'
)
G_WHIP_DEFAULT_ITEM=$MENU_MAIN_LASTITEM
G_WHIP_BUTTON_CANCEL_TEXT='Exit'
G_WHIP_SIZE_X_MAX=80
if G_WHIP_MENU; then
MENU_MAIN_LASTITEM=$G_WHIP_RETURNED_VALUE
case "$G_WHIP_RETURNED_VALUE" in
'Uninstall') Menu_Uninstall_Software;;
'Search Software') Menu_CreateSoftwareList search;;
'Browse Software') Menu_CreateSoftwareList;;
'SSH Server')
G_WHIP_MENU_ARRAY=(
'None' ': Not required / manual setup'
"${aSOFTWARE_NAME[104]}" ": ${aSOFTWARE_DESC[104]} (recommended)"
"${aSOFTWARE_NAME[105]}" ": ${aSOFTWARE_DESC[105]}"
)
G_WHIP_DEFAULT_ITEM=$sshserver_text
G_WHIP_BUTTON_CANCEL_TEXT='Back'
G_WHIP_MENU 'Please select desired SSH server:
\n- None: Selecting this option will uninstall all SSH servers. This reduces system resources and improves performance. Useful for users who do NOT require networked/remote terminal access.
\n- Dropbear (recommended): Lightweight SSH server, installed by default on DietPi systems.
\n- OpenSSH: A feature-rich SSH server with SFTP/SCP support, at the cost of increased resource usage.' || return 0
# Apply selection
case "$G_WHIP_RETURNED_VALUE" in
'None') Apply_SSHServer_Choices 0;;
"${aSOFTWARE_NAME[105]}") Apply_SSHServer_Choices -2;;
*) Apply_SSHServer_Choices -1;;
esac
# Check for changes
for i in 104 105
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && tobeinstalled_text+="\n - ${aSOFTWARE_NAME[$i]}"
(( ${aSOFTWARE_INSTALL_STATE[$i]} == -1 )) && toberemoved_text+="\n - ${aSOFTWARE_NAME[$i]}"
done
[[ $tobeinstalled_text || $toberemoved_text ]] || return 0
[[ $tobeinstalled_text ]] && tobeinstalled_text="\n\nThe following software will be installed:$tobeinstalled_text"
[[ $toberemoved_text ]] && toberemoved_text="\n\nThe following software will be uninstalled:$toberemoved_text"
G_WHIP_MSG "$G_WHIP_RETURNED_VALUE has been selected:\n- Your choice will be applied when 'Install Go >> Start installation' is selected.$tobeinstalled_text$toberemoved_text"
;;
'Log System')
G_WHIP_MENU_ARRAY=(
'None' ': Not required / manual setup'
'DietPi-RAMlog #1' ': Hourly clear (recommended)'
'DietPi-RAMlog #2' ': Hourly save, then clear'
'Full' ': Logrotate and Rsyslog'
)
G_WHIP_DEFAULT_ITEM=$index_logging_text
G_WHIP_BUTTON_CANCEL_TEXT='Back'
G_WHIP_MENU 'Please select desired logging system:
\n- None: Selecting this option will uninstall DietPi-RAMlog, Logrotate and Rsyslog.
\n- DietPi-RAMlog #1 (Max performance): Mounts /var/log to RAM, reducing filesystem I/O. Logfiles are cleared every hour. Does NOT save logfiles to disk.
\n- DietPi-RAMlog #2: Same as #1, with the added feature of appending logfile contents to disk at /root/logfile_storage, before being cleared.
\n- Full (Reduces performance): Leaves /var/log on DISK, reduces SD card lifespan. Full logging system with Logrotate and Rsyslog.' || return 0
# Apply selection
case "$G_WHIP_RETURNED_VALUE" in
'None') Apply_Logging_Choices 0;;
'DietPi-RAMlog #2') Apply_Logging_Choices -2;;
'Full') Apply_Logging_Choices -3;;
*) Apply_Logging_Choices -1;;
esac
# Check for changes
for i in 101 102 103
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && tobeinstalled_text+="\n - ${aSOFTWARE_NAME[$i]}"
(( ${aSOFTWARE_INSTALL_STATE[$i]} == -1 )) && toberemoved_text+="\n - ${aSOFTWARE_NAME[$i]}"
done
[[ $tobeinstalled_text || $toberemoved_text ]] || return 0
[[ $tobeinstalled_text ]] && tobeinstalled_text="\n\nThe following software will be installed:$tobeinstalled_text"
[[ $toberemoved_text ]] && toberemoved_text="\n\nThe following software will be uninstalled:$toberemoved_text"
G_WHIP_MSG "$G_WHIP_RETURNED_VALUE has been selected:\n- Your choice will be applied when 'Install : Go >> Start installation' is selected.$tobeinstalled_text$toberemoved_text"
;;
'User Data Location')
# - Vars if we need to move data.
local move_data_target=$user_data_location_current
G_WHIP_MENU_ARRAY=(
'List' ': Select from a list of available drives to move user data.'
'Custom' ': Manually enter a location to move user data.'
'Drive' ': Launch DietPi-Drive_Manager.'
)
G_WHIP_BUTTON_CANCEL_TEXT='Back'
G_WHIP_MENU 'Choose where to store your user data. User data includes software such as ownCloud data store, BitTorrent downloads etc.
\nMore information on user data in DietPi:\n- https://dietpi.com/docs/dietpi_tools/#quick-selections
\n- DietPi-Drive_Manager: Launch DietPi-Drive_Manager to setup external drives, and, move user data to different locations.' || return 0
# Launch DietPi-Drive_Manager
if [[ $G_WHIP_RETURNED_VALUE == 'Drive' ]]; then
/boot/dietpi/dietpi-drive_manager
return 0
# List
elif [[ $G_WHIP_RETURNED_VALUE == 'List' ]]; then
/boot/dietpi/dietpi-drive_manager 1 || return 1
local return_value=$(</tmp/dietpi-drive_manager_selmnt)
[[ $return_value ]] || return 1
[[ $return_value == '/' ]] && return_value='/mnt'
move_data_target=$return_value
move_data_target+='/dietpi_userdata'
# Manual file path entry
elif [[ $G_WHIP_RETURNED_VALUE == 'Custom' ]]; then
G_WHIP_INPUTBOX 'Please input a location. Your user data will be stored inside this location.\n - eg: /mnt/MyDrive/MyData' || return 1
move_data_target=$G_WHIP_RETURNED_VALUE
fi
# Move data if the new entry has changed
[[ $user_data_location_current != "$move_data_target" ]] || return 0
# Ask before we begin
G_WHIP_YESNO "DietPi will now attempt to transfer your existing user data to the new location:
\n- From: $user_data_location_current\n- To: $move_data_target\n\nWould you like to begin?" || return 0
# Move data, setup symlinks
if /boot/dietpi/func/dietpi-set_userdata "$user_data_location_current" "$move_data_target"; then
G_WHIP_MSG "User data transfer: Completed\n\nYour user data has been successfully moved:\n\n- From: $user_data_location_current\n- To: $move_data_target"
else
G_WHIP_MSG "User data transfer: Failed\n\n$(</var/log/dietpi-move_userdata.log)\n\nNo changes have been applied."
fi
;;
'DietPi-Config') /boot/dietpi/dietpi-config;;
'Help!')
local string='───────────────────────────────────────────────────────────────
Welcome to DietPi:
───────────────────────────────────────────────────────────────
Use PageUp/Down or Arrow Up/Down to scroll this help screen.
Press ESC, or TAB then ENTER to exit this help screen.\n
Easy to follow, step by step guides for installing DietPi:
https://dietpi.com/docs/install/\n
For a list of all installation options and their details:
https://dietpi.com/docs/software/\n
───────────────────────────────────────────────────────────────
List of installed software and their online documentation URLs:
───────────────────────────────────────────────────────────────\n'
# Installed software
for i in "${!aSOFTWARE_NAME[@]}"
do
[[ ${aSOFTWARE_INSTALL_STATE[i]} -gt 0 && ${aSOFTWARE_DOCS[$i]} ]] || continue
string+="\n${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}\n${aSOFTWARE_DOCS[$i]}\n"
done
G_WHIP_SIZE_X_MAX=70
G_WHIP_MSG "$string"
;;
'Install') Menu_StartInstall;;
*) :;;
esac
# Exit/Abort Setup
else
Menu_Exit
fi
}
Menu_Exit()
{
TARGETMENUID=0 # Return to Main Menu
# Standard exit
if (( $G_DIETPI_INSTALL_STAGE == 2 ))
then
G_WHIP_DEFAULT_ITEM='ok'
G_WHIP_YESNO 'Do you wish to exit DietPi-Software?\n\nAll changes to software selections will be cleared.' || return 0
exit 0
fi
# Prevent exit on 1st run setup
G_WHIP_MSG 'DietPi has not fully been installed.\nThis must be completed prior to using DietPi by selecting:\n - Install : Go >> Start installation'
}
Menu_StartInstall()
{
local tobeinstalled_text toberemoved_text summary_text
# Obtain list of pending software installs and uninstalls
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 1 )) && tobeinstalled_text+="\n - ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"
(( ${aSOFTWARE_INSTALL_STATE[$i]} == -1 )) && toberemoved_text+="\n - ${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}"
done
# Check if user made/changed software selections
if [[ $tobeinstalled_text || $toberemoved_text ]]
then
# List selections and ask for confirmation
[[ $tobeinstalled_text ]] && tobeinstalled_text="\n\nThe following software will be installed:$tobeinstalled_text"
[[ $toberemoved_text ]] && toberemoved_text="\n\nThe following software will be uninstalled:$toberemoved_text"
[[ $G_SERVICE_CONTROL == 0 ]] || summary_text='\n\nNB: Software services will be temporarily controlled (stopped) by DietPi during this process. Please inform connected users, before continuing. SSH and VNC are not affected.'
G_WHIP_DEFAULT_ITEM='ok'
G_WHIP_YESNO "DietPi is now ready to apply your software choices:$tobeinstalled_text$toberemoved_text$summary_text
\nSoftware details, usernames, passwords etc:\n - https://dietpi.com/docs/software/\n\nWould you like to begin?" || return 0
# If due to choice changes only uninstalls are done and it is not the first run setup, skip the install function and call the uninstall function directly.
if [[ $G_DIETPI_INSTALL_STAGE == 2 && ! $tobeinstalled_text ]]
then
Uninstall_Software
Write_InstallFileList
else
GOSTARTINSTALL=1 # Set install start flag
fi
TARGETMENUID=-1 # Exit menu loop
# After first run setup has finished, abort install without any selections
elif (( $G_DIETPI_INSTALL_STAGE == 2 ))
then
G_WHIP_MSG 'No changes have been detected. Unable to start installation.'
# Allow to finish first run setup without any selections
else
G_WHIP_DEFAULT_ITEM='ok'
G_WHIP_YESNO 'DietPi was unable to detect any additional software selections for install.
\nNB: You can use dietpi-software at a later date, to install optimised software from our catalogue as required.
\nDo you wish to continue with DietPi as a pure minimal image?' || return 0
TARGETMENUID=-1 # Exit menu loop
GOSTARTINSTALL=1 # Set install start flag
fi
}
Menu_Uninstall_Software()
{
# Array which will hold all software IDs to be removed.
G_WHIP_CHECKLIST_ARRAY=()
# Obtain list of installed software
local i
for i in "${!aSOFTWARE_NAME[@]}"
do
(( ${aSOFTWARE_INSTALL_STATE[$i]} == 2 )) || continue
# Skip webserver stacks: Their install states will be aligned with webserver/database install states automatically.
[[ $i =~ ^(75|76|78|79|81|82)$ ]] || G_WHIP_CHECKLIST_ARRAY+=("$i" "${aSOFTWARE_NAME[$i]}: ${aSOFTWARE_DESC[$i]}" 'off')
done
# No software installed
if (( ! ${#G_WHIP_CHECKLIST_ARRAY[@]} ))
then
G_WHIP_MSG 'No software is currently installed.'
# Run menu
else
G_WHIP_DEFAULT_ITEM='ok' G_WHIP_BUTTON_CANCEL_TEXT='Back'
G_WHIP_CHECKLIST 'Use the spacebar to select the software you would like to remove:' && [[ $G_WHIP_RETURNED_VALUE ]] || return 0
# Create list for user to review before removal
local output_string='The following software will be REMOVED from your system:'
for i in $G_WHIP_RETURNED_VALUE
do
output_string+="\n - ${aSOFTWARE_NAME[$i]} (ID=$i): ${aSOFTWARE_DESC[$i]}"
done
G_WHIP_YESNO "$output_string
\nNB: Uninstalling usually PURGES any related userdata and configs. If you only need to repair or update software, please use \"dietpi-software reinstall <ID>\" instead.
\nDo you wish to continue?" || return 0
# Mark for uninstall
for i in $G_WHIP_RETURNED_VALUE
do
aSOFTWARE_INSTALL_STATE[$i]=-1
done
# Run uninstall
Uninstall_Software
# Save install states
Write_InstallFileList
G_WHIP_MSG 'Uninstall completed'
fi
}
#/////////////////////////////////////////////////////////////////////////////////////
# Main Loop
#/////////////////////////////////////////////////////////////////////////////////////
# Abort if a reboot is required as of missing kernel modules
if [[ $1 != 'list' && $G_DIETPI_INSTALL_STAGE == 2 ]] && ! G_CHECK_KERNEL
then
G_WHIP_BUTTON_CANCEL_TEXT='Abort' G_WHIP_YESNO "[ INFO ] A reboot is required
\nKernel modules for the loaded kernel at /lib/modules/$(uname -r) are missing. This is most likely the case as of a recently applied kernel upgrade where a reboot is required to load the new kernel.
\nTo assure that $G_PROGRAM_NAME can run successfully, especially when performing installs, it is required that you perform a reboot so that kernel modules can be loaded ondemand.
\nThere may be rare cases where no dedicated kernel modules are used but all require modules are builtin. If this is the case, please create the mentioned directory manually to proceed.
\nDo you want to reboot now?" && reboot || exit 1
exit 0
fi
# Init software arrays
Software_Arrays_Init
#-------------------------------------------------------------------------------------
# Load .installed file, update vars if it exists
Read_InstallFileList
#-------------------------------------------------------------------------------------
# CLI input mode: Force menu mode on first run
if [[ $1 && $G_DIETPI_INSTALL_STAGE == 2 ]]
then
Input_Modes "$@"
#-------------------------------------------------------------------------------------
# Standard launch
else
# DietPi-Automation pre install steps
(( $G_DIETPI_INSTALL_STAGE == 2 )) || DietPi-Automation_Pre
# Start DietPi Menu
until (( $TARGETMENUID < 0 ))
do
G_TERM_CLEAR
Menu_Main
done
fi
#-------------------------------------------------------------------------------------
# Start DietPi-Software installs
(( $GOSTARTINSTALL )) || exit 0
# Userdata location verify
G_CHECK_USERDATA
# Start software installs
Run_Installations
# DietPi-Automation post install steps
(( $G_DIETPI_INSTALL_STAGE == 2 )) || DietPi-Automation_Post
G_DIETPI-NOTIFY 3 "$G_PROGRAM_NAME" 'Install completed'
# Upload DietPi-Survey data if opted in, prompt user choice if no settings file exists
# - Skip if G_SERVICE_CONTROL == 0, exported by "patches" (DietPi-Update) which sends survey already
# Start services, restart to reload configs of possibly running services
if [[ $G_SERVICE_CONTROL != 0 ]]
then
/boot/dietpi/dietpi-survey 1
/boot/dietpi/dietpi-services restart
(( $RESTART_DELUGE_WEB )) && { G_SLEEP 1; G_EXEC_NOHALT=1 G_EXEC systemctl restart deluge-web; }
fi
# Start installed services, not controlled by DietPi-Services
[[ ${aSTART_SERVICES[0]} ]] || exit 0
G_DIETPI-NOTIFY 2 'Starting installed services not controlled by DietPi-Services'
for i in "${aSTART_SERVICES[@]}"
do
G_EXEC_NOHALT=1 G_EXEC systemctl start "$i"
done
#-------------------------------------------------------------------------------------
exit 0
#-------------------------------------------------------------------------------------
}