Add FreeBSD support to install.sh and update docs (#3216)

This commit is contained in:
Martin
2026-07-11 12:48:10 +02:00
committed by GitHub
parent 9a5f5c11f7
commit 520dbe9d71
4 changed files with 92 additions and 8 deletions

View File

@@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file. This projec
If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If you are upgrading from v0.15.x and below, please read the [upgrading documentation](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md) for more information on how to upgrade from previous versions. If you are upgrading from v0.16.x, replace the binary (or run `docker pull`). If you are upgrading from v0.15.x and below, please read the [upgrading documentation](https://github.com/stalwartlabs/stalwart/blob/main/UPGRADING/v0_16.md) for more information on how to upgrade from previous versions.
## Added ## Added
- FreeBSD support (x86_64 only for now): release binaries, installer support with rc.d service, and hier(7)-compliant default paths (`/usr/local/etc/stalwart`, `/var/db/stalwart`).
## Changed ## Changed

View File

@@ -134,7 +134,7 @@ Key features:
Install Stalwart on your server by following the instructions for your platform: Install Stalwart on your server by following the instructions for your platform:
- [Linux / MacOS](https://stalw.art/docs/install/platform/linux) - [Linux / MacOS / FreeBSD](https://stalw.art/docs/install/platform/linux)
- [Windows](https://stalw.art/docs/install/platform/windows) - [Windows](https://stalw.art/docs/install/platform/windows)
- [Docker](https://stalw.art/docs/install/platform/docker) - [Docker](https://stalw.art/docs/install/platform/docker)

View File

@@ -651,6 +651,14 @@ fn map_dns_server(dns_server: &DnsServerBootstrap) -> Option<registry::schema::s
} }
} }
// FreeBSD keeps variable application data under /var/db (hier(7))
// rather than FHS /var/lib.
const DEFAULT_DATA_PATH: &str = if cfg!(target_os = "freebsd") {
"/var/db/stalwart/"
} else {
"/var/lib/stalwart/"
};
fn build_default_bootstrap(server: &Server) -> Bootstrap { fn build_default_bootstrap(server: &Server) -> Bootstrap {
let server_hostname = server.registry().local_hostname().to_string(); let server_hostname = server.registry().local_hostname().to_string();
let default_domain = psl::domain_str(&server_hostname) let default_domain = psl::domain_str(&server_hostname)
@@ -659,7 +667,7 @@ fn build_default_bootstrap(server: &Server) -> Bootstrap {
Bootstrap { Bootstrap {
data_store: DataStore::RocksDb(RocksDbStore { data_store: DataStore::RocksDb(RocksDbStore {
path: "/var/lib/stalwart/".to_string(), path: DEFAULT_DATA_PATH.to_string(),
..Default::default() ..Default::default()
}), }),
blob_store: BlobStore::Default, blob_store: BlobStore::Default,

View File

@@ -34,11 +34,11 @@ main() {
# Detect OS # Detect OS
local _os _uname _account local _os _uname _account
_uname="$(uname)" _uname="$(uname)"
_account="stalwart"
case "$_uname" in case "$_uname" in
Linux) _os="linux" ;; Linux) _os="linux"; _account="stalwart" ;;
Darwin) _os="macos"; _account="_stalwart" ;; Darwin) _os="macos"; _account="_stalwart" ;;
*) err "❌ Install failed: Unsupported OS: $_uname" ;; FreeBSD) _os="freebsd"; _account="stalwart" ;;
*) err "❌ Install failed: Unsupported OS: $_uname" ;;
esac esac
# Parse arguments # Parse arguments
@@ -70,9 +70,16 @@ main() {
local _bin_dir _bin_file _conf_dir _log_dir _data_dir _env_file _config_file local _bin_dir _bin_file _conf_dir _log_dir _data_dir _env_file _config_file
if [ -z "$_prefix" ]; then if [ -z "$_prefix" ]; then
_bin_dir="/usr/local/bin" _bin_dir="/usr/local/bin"
_conf_dir="/etc/stalwart"
_log_dir="/var/log/stalwart" _log_dir="/var/log/stalwart"
_data_dir="/var/lib/stalwart" if [ "$_os" = "freebsd" ]; then
# hier(7): third-party config lives under /usr/local/etc,
# variable data under /var/db
_conf_dir="/usr/local/etc/stalwart"
_data_dir="/var/db/stalwart"
else
_conf_dir="/etc/stalwart"
_data_dir="/var/lib/stalwart"
fi
else else
_bin_dir="${_prefix}/bin" _bin_dir="${_prefix}/bin"
_conf_dir="${_prefix}/etc" _conf_dir="${_prefix}/etc"
@@ -139,6 +146,10 @@ main() {
create_service_macos "$_bin_file" "$_config_file" "$_env_file" "$_account" create_service_macos "$_bin_file" "$_config_file" "$_env_file" "$_account"
_service_type="launchd" _service_type="launchd"
;; ;;
freebsd)
create_service_freebsd "$_bin_file" "$_config_file" "$_env_file" "$_account" "$_log_dir"
_service_type="rcd"
;;
esac esac
# Completion message # Completion message
@@ -162,6 +173,9 @@ main() {
launchd) launchd)
say " sudo log show --predicate 'process == \"stalwart\"' --last 5m" say " sudo log show --predicate 'process == \"stalwart\"' --last 5m"
;; ;;
rcd)
say " grep -A8 'bootstrap mode' ${_log_dir}/stalwart.log"
;;
esac esac
say "" say ""
say " Or set STALWART_RECOVERY_ADMIN=admin:<password> in" say " Or set STALWART_RECOVERY_ADMIN=admin:<password> in"
@@ -190,6 +204,11 @@ With no PREFIX, Stalwart is installed under standard FHS paths:
logs /var/log/stalwart/ logs /var/log/stalwart/
data /var/lib/stalwart/ data /var/lib/stalwart/
On FreeBSD, config and data follow hier(7) instead:
config /usr/local/etc/stalwart/config.json
env /usr/local/etc/stalwart/stalwart.env
data /var/db/stalwart/
When PREFIX is provided, a self-contained layout is used instead: When PREFIX is provided, a self-contained layout is used instead:
binary $PREFIX/bin/stalwart binary $PREFIX/bin/stalwart
config $PREFIX/etc/config.json config $PREFIX/etc/config.json
@@ -265,6 +284,8 @@ create_account() {
ensure dscl /Local/Default -delete /Users/_stalwart AuthenticationAuthority ensure dscl /Local/Default -delete /Users/_stalwart AuthenticationAuthority
ensure dscl /Local/Default -delete /Users/_stalwart PasswordPolicyOptions ensure dscl /Local/Default -delete /Users/_stalwart PasswordPolicyOptions
elif [ "$_os" = "freebsd" ]; then
ensure pw useradd -n "$_account" -c "Stalwart service" -d /nonexistent -s /usr/sbin/nologin -w no
else else
ensure useradd "$_account" -s /usr/sbin/nologin -M -r -U ensure useradd "$_account" -s /usr/sbin/nologin -M -r -U
fi fi
@@ -451,6 +472,60 @@ EOF
launchctl enable system/stalwart launchctl enable system/stalwart
} }
create_service_freebsd() {
local _bin="$1" _config="$2" _env="$3" _user="$4" _log_dir="$5"
ensure mkdir -p /usr/local/etc/rc.d
cat > /usr/local/etc/rc.d/stalwart <<EOF
#!/bin/sh
# PROVIDE: stalwart
# REQUIRE: NETWORKING LOGIN
# KEYWORD: shutdown
. /etc/rc.subr
name="stalwart"
rcvar="stalwart_enable"
desc="Stalwart Server"
load_rc_config \$name
: \${stalwart_enable:="NO"}
: \${stalwart_user:="${_user}"}
: \${stalwart_config:="${_config}"}
: \${stalwart_envfile:="${_env}"}
: \${stalwart_logfile:="${_log_dir}/stalwart.log"}
: \${stalwart_fdlimit:="65536"}
pidfile="/var/run/stalwart.pid"
procname="${_bin}"
command="/usr/sbin/daemon"
command_args="-f -o \${stalwart_logfile} -p \${pidfile} -u \${stalwart_user} ${_bin} --config=\${stalwart_config}"
sig_stop="INT"
start_precmd="stalwart_precmd"
stalwart_precmd()
{
# daemon(8) passes its environment through to the service
if [ -r "\${stalwart_envfile}" ]; then
set -a
. "\${stalwart_envfile}"
set +a
fi
ulimit -n "\${stalwart_fdlimit}"
# Binding privileged ports (25/443/465/993/...) as a non-root user
# requires: sysctl net.inet.ip.portrange.reservedhigh=0
}
run_rc_command "\$1"
EOF
chmod +x /usr/local/etc/rc.d/stalwart
sysrc stalwart_enable=YES
service stalwart stop > /dev/null 2>&1 || true
service stalwart start
}
get_architecture() { get_architecture() {
local _ostype _cputype _bitness _arch _clibtype local _ostype _cputype _bitness _arch _clibtype