#!/bin/bash
# shellcheck disable=SC2046,SC2006,SC2268,SC2009
# SC2046: Quote this to prevent word splitting.
# SC2006: Use $(...) notation instead of legacy backticks `...`.
# SC2268: Avoid x-prefix in comparisons as it no longer serves a purpose.
# SC2009: Consider using pgrep instead of grepping ps output.
GETCFG=/sbin/getcfg
SETCFG=/sbin/setcfg
AVAHI_DAEMON=/usr/sbin/avahi-daemon
AVAHI_CONF_DIR=/etc/avahi/services
/sbin/test -f ${AVAHI_DAEMON} || exit 0
AVAHI_PID_FILE="/var/run/avahi-daemon/pid"
AVAHI_SET_LOCK_FILE="/var/lock/avahi_set_conf.lock"
HBS_INSTALL_PATH=$(${GETCFG} HybridBackup Install_Path -d FALSE -f /etc/config/qpkg.conf)
HBS_TM_CONF="${HBS_INSTALL_PATH}/timemachine.conf"
HSB_TM_LOG_FILE="${HBS_INSTALL_PATH}/logs/tm.log"
LOG_DUP_STDOUT=y # flag for app_log() duplicate to stdout
function log_info()
{
# log information to app's log file
/bin/echo "[$(date '+%Y/%m/%d %H:%M:%S')] [$$] $*" >> "$HSB_TM_LOG_FILE"
[ -n "${LOG_DUP_STDOUT}" ] && /bin/echo "[$(date '+%Y/%m/%d %H:%M:%S')] [$$] $*"
}
function log_err()
{
# log error to app's log file
/bin/echo "[$(date '+%Y/%m/%d %H:%M:%S')] [$$] [E] $*" >> "$HSB_TM_LOG_FILE"
[ -n "${LOG_DUP_STDOUT}" ] && /bin/echo "[$(date '+%Y/%m/%d %H:%M:%S')] [$$] [E] $*"
}
function print_caller()
{
if [ -d "/proc/$PPID" ] && [ -f "/proc/$PPID/cmdline" ]; then
log_info "caller:$PPID ($(/bin/cat /proc/$PPID/cmdline) $*)"
else
log_info "caller:$PPID"
fi
}
function is_enabled_ulinux_tm() {
if [ "$(${GETCFG} "TimeMachine" Enabled -d FALSE)" = "TRUE" ]; then
return 0 # true
else
return 1 # false
fi
}
function is_enabled_hbs_tm() {
if [ "$(${GETCFG} "Global" Enable -d FALSE -f "${HBS_TM_CONF}")" = "TRUE" ]; then
return 0 # true
else
return 1 # false
fi
}
function is_bonjour_tm_smb() {
if [ "$(${GETCFG} "Global" BonjourSMB -d FALSE -f "${HBS_TM_CONF}")" = "TRUE" ]; then
return 0 # true
else
return 1 # false
fi
}
function is_bonjour_tm_afp() {
if [ "$(${GETCFG} "Global" BonjourAFP -d FALSE -f "${HBS_TM_CONF}")" = "TRUE" ]; then
return 0 # true
else
return 1 # false
fi
}
function is_enabled_shared_account() {
if [ "$(${GETCFG} "SharedAccount" Enable -d FALSE -f "${HBS_TM_CONF}")" = "TRUE" ]; then
return 0 # true
else
return 1 # false
fi
}
function is_capacity_limited(){
cap="$(${GETCFG} "SharedAccount" CapacityGB -d 0 -f "${HBS_TM_CONF}")"
if [ "$cap" -gt 0 ]; then
return 0 # true
else
return 1 # false
fi
}
_renew_timemachine_share_folder()
{
_type=$1 # for logging
_bonjour_hostname=$2
_tm_service_file=$3
# parse advf (samba)
AD_CHECK=`${GETCFG} global "server role" -d "x" -f /etc/config/smb.conf`
if [ "$AD_CHECK" == "active directory domain controller" ]; then
_tm_advf=0xa1
else
LDAP_ENABLE=`${GETCFG} "LDAP" "Enable" -d "FALSE"`
SAMBA_AUTH=`${GETCFG} global "passdb backend" -d "x" -f /etc/config/smb.conf`
if [ "x$LDAP_ENABLE" == "xTRUE" ] && [ "$SAMBA_AUTH" != "smbpasswd" ]; then
_tm_advf=0xa1
else
_tm_advf=0xa3
fi
fi
# service _adisk._tcp start
/bin/cat >> "${_tm_service_file}" <<__EOF__
${_bonjour_hostname}
_adisk._tcp
9
__EOF__
_txt_rec_afp_param=""
# shared account's shared folder (dk index 0)
if is_enabled_shared_account; then
log_info " ${_type} add folder 'TMBackup'"
if is_bonjour_tm_afp; then
# shellcheck disable=SC2086
uuid=`/bin/grep \"TMBackup\" /var/netatalk/afp_voluuid.conf 2>/dev/null | /bin/cut -f 2 -d$'\t'`
if [ "x${uuid}" != "x" ]; then
_txt_rec_afp_param=",adVU=${uuid}"
else
log_err "'TMBackup' no uuid"
fi
fi
/bin/cat >> "${_tm_service_file}" <<__EOF__
dk0=adVN=TMBackup,adVF=${_tm_advf}${_txt_rec_afp_param}
__EOF__
dk_index=1
else
dk_index=0
fi
# other shared folder
# shellcheck disable=SC2162
/sbin/user_cmd -s | while read _share_name
do
tm=`${GETCFG} "${_share_name}" timemachine -d "no" -f /etc/config/smb.conf`
if [ "${tm}" == "yes" ]; then
log_info " ${_type} add folder '${_share_name}'"
_txt_rec_afp_param=""
if is_bonjour_tm_afp; then
# shellcheck disable=SC2086
uuid=`/bin/grep \"${_share_name}\" /var/netatalk/afp_voluuid.conf 2>/dev/null | /bin/cut -f 2 -d$'\t'`
if [ "x${uuid}" != "x" ]; then
_txt_rec_afp_param=",adVU=${uuid}"
else
log_err "'${_share_name}' no uuid"
fi
fi
# replace "homes" as "home" as backup folder by nas account
if [ "${_share_name}" == "homes" ]; then
_share_name="home"
fi
/bin/cat >> "${_tm_service_file}" <<__EOF__
dk$dk_index=adVN=${_share_name},adVF=${_tm_advf}${_txt_rec_afp_param}
__EOF__
# shellcheck disable=SC2004
dk_index=$(($dk_index+1))
fi
done
# service _adisk._tcp end
/bin/cat >> "${_tm_service_file}" <<__EOF__
__EOF__
}
# version_ge: Check if version $1 >= $2
# Returns 0 (true) if v1 >= v2, 1 (false) otherwise
# Compatible with POSIX sh and older systems without sort -V
version_ge() {
_vge_v1=$1
_vge_v2=$2
# Handle empty versions
[ -z "$_vge_v1" ] && _vge_v1="0"
[ -z "$_vge_v2" ] && _vge_v2="0"
# Compare each segment using parameter expansion (POSIX compatible)
_vge_remain1=$_vge_v1
_vge_remain2=$_vge_v2
while :; do
# Extract first segment before '.'
_vge_seg1=${_vge_remain1%%.*}
_vge_seg2=${_vge_remain2%%.*}
# Treat empty or non-numeric as 0
case "$_vge_seg1" in
''|*[!0-9]*) _vge_seg1=0 ;;
esac
case "$_vge_seg2" in
''|*[!0-9]*) _vge_seg2=0 ;;
esac
# Numeric comparison using arithmetic expansion for safety
if [ $((_vge_seg1)) -gt $((_vge_seg2)) ]; then
return 0
elif [ $((_vge_seg1)) -lt $((_vge_seg2)) ]; then
return 1
fi
# Check if we've processed all segments
case "$_vge_remain1" in
*.*) _vge_remain1=${_vge_remain1#*.} ;;
*) _vge_remain1="" ;;
esac
case "$_vge_remain2" in
*.*) _vge_remain2=${_vge_remain2#*.} ;;
*) _vge_remain2="" ;;
esac
# Handle different segment counts explicitly
# v1 exhausted but v2 has more -> v1 < v2
[ -z "$_vge_remain1" ] && [ -n "$_vge_remain2" ] && return 1
# v1 has more but v2 exhausted -> v1 > v2
[ -n "$_vge_remain1" ] && [ -z "$_vge_remain2" ] && return 0
# Both exhausted means equal, so v1 >= v2 is true
[ -z "$_vge_remain1" ] && [ -z "$_vge_remain2" ] && return 0
done
}
_generate_avahi_tm_conf()
{
_type=$1
# prepare local var
# After apply, use `avahi-browse -rt _adisk._tcp` to query mDNS for test
_version=$(${GETCFG} System Version) # Get system version
if version_ge "$_version" "5.3.0"; then # if fw >= 5.3.0
_hostname=$(${GETCFG} -f /etc/config.local/uLinux.conf System "Server Name")
log_info " fw version: $_version. read hostname from /etc/config.local/uLinux.conf: $_hostname"
else
_hostname=$(${GETCFG} System "Server Name")
fi
if [ -z "$_hostname" ]; then
/sbin/gen_hostname
/bin/sync
_hostname=`${GETCFG} System "Server Name"`
fi
if [ "$_type" = "basic" ]; then
_bonjour_hostname="${_hostname}.local"
_bonjour_name="${_hostname}(TimeMachine)"
_tm_service_file="${AVAHI_CONF_DIR}/timemachine.service"
elif [ "$_type" = "tbt" ]; then
_bonjour_hostname="${_hostname}tbt.local"
_bonjour_name="${_hostname}(TimeMachine)(Thunderbolt)"
_tm_service_file="${AVAHI_CONF_DIR}/tbt.timemachine.service"
fi
# generate bonjour tm service file depend on hbs time machine setting
/bin/cat > "${_tm_service_file}" <<__EOF__
${_bonjour_name}
__EOF__
_renew_timemachine_share_folder "$_type" "${_bonjour_hostname}" "${_tm_service_file}"
if is_bonjour_tm_smb; then
/bin/cat >> "${_tm_service_file}" <<__EOF__
${_bonjour_hostname}
_smb._tcp
445
__EOF__
fi
if is_bonjour_tm_afp; then
/bin/cat >> "${_tm_service_file}" <<__EOF__
${_bonjour_hostname}
_afpovertcp._tcp
548
__EOF__
fi
/bin/cat >> "${_tm_service_file}" <<__EOF__
${_bonjour_hostname}
_device-info._tcp
0
model=Xserve
__EOF__
}
_clean_avahi_tm_conf()
{
/bin/rm -rf ${AVAHI_CONF_DIR}/timemachine.service >&/dev/null
/bin/rm -rf ${AVAHI_CONF_DIR}/tbt.timemachine.service >&/dev/null
}
_set_avahi_tm_conf()
{
_clean_avahi_tm_conf
if is_enabled_hbs_tm; then
_generate_avahi_tm_conf "basic"
if [ -x /sbin/hal_app ]; then
if [ "x$(/sbin/hal_app --check_tbt_support)" = "xyes" ]; then
if [ x`/bin/ls /sys/class/net/ | /bin/grep tbtbr` != "x" ]; then
_generate_avahi_tm_conf "tbt"
fi
fi
fi
else
log_info " timemachine bonjour setting is disabled"
fi
}
# Compatible with smb.sh / atalk.sh, it read uLinux.conf [TimeMachine].
# Save hbs config to uLinux.conf before let smbd/afpd start script know
# hbs's settings about shared account.
_enable_ulinux_tm_conf()
{
log_info " [${FUNCNAME[0]}] begin"
$SETCFG "TimeMachine" "Enabled" "TRUE"
$SETCFG "TimeMachine" "Storage Type" "1"
$SETCFG "TimeMachine" "User Name" "TimeMachine"
if is_enabled_shared_account; then
capGB="$(${GETCFG} "SharedAccount" CapacityGB -d 0 -f "${HBS_TM_CONF}")"
$SETCFG "TimeMachine" "Capacity" "${capGB}"
else
$SETCFG "TimeMachine" "Capacity" "0"
fi
log_info " [${FUNCNAME[0]}] done"
}
_disable_ulinux_tm_conf()
{
log_info " [${FUNCNAME[0]}] begin"
if is_enabled_ulinux_tm; then
$SETCFG "TimeMachine" "Enabled" "FALSE"
fi
log_info " [${FUNCNAME[0]}] done"
}
avahi_tm_start()
{
log_info " [${FUNCNAME[0]}] begin"
_set_avahi_tm_conf
_avahi_daemon_reload
log_info " [${FUNCNAME[0]}] done"
}
avahi_tm_stop()
{
log_info " [${FUNCNAME[0]}] begin"
_clean_avahi_tm_conf
_avahi_daemon_reload
log_info " [${FUNCNAME[0]}] done"
}
_avahi_daemon_reload()
{
log_info " [${FUNCNAME[0]}] begin"
CURRENT_AVAHI_PID=`/bin/cat ${AVAHI_PID_FILE} 2>/dev/null`
if [ -n "$CURRENT_AVAHI_PID" ]; then
if [ -f ${AVAHI_SET_LOCK_FILE} ]; then
log_err " unexpect avahi lock file existed"
return
fi
touch ${AVAHI_SET_LOCK_FILE}
/bin/kill -HUP "$CURRENT_AVAHI_PID" 2>/dev/null 1>/dev/null
rm -f ${AVAHI_SET_LOCK_FILE}
else
log_err " avahi not runing"
fi
log_info " [${FUNCNAME[0]}] done"
}
apply_hbs_tm_settings()
{
log_info "[${FUNCNAME[0]}] begin"
case "$1" in
enable)
log_info " enable tm service"
_enable_ulinux_tm_conf
if is_enabled_shared_account; then
if is_bonjour_tm_smb || is_capacity_limited; then
log_info " smb restarting"
/etc/init.d/smb.sh restart >/dev/null 2>/dev/null
log_info " smb started"
fi
fi
if is_bonjour_tm_afp; then
log_info " atalk restarting"
/etc/init.d/atalk.sh restart 1>/dev/null 2>/dev/null
log_info " atalk started"
fi
# perform bonjour of timemachine service at least
# to avoid avahi.sh setting the old design
avahi_tm_start
;;
disable)
log_info " disable tm service"
# QTSHBS00-3914 alway disable bonjour and uLinux.conf [TimeMachine]
# when HBS qpkg disabled or timemachine global setting is off
_disable_ulinux_tm_conf
avahi_tm_stop
;;
esac
log_info "[${FUNCNAME[0]}] done"
}
cd "$HBS_INSTALL_PATH" &>/dev/null || (echo "install path not exist" && exit 1)
print_caller "$@"
if [ "$(${GETCFG} "Bonjour Service" Enable -d FALSE)" = "FALSE" ]; then
log_err "Bonjour service disabled"
fi
case "$1" in
apply)
if is_enabled_hbs_tm; then
apply_hbs_tm_settings "enable"
else
apply_hbs_tm_settings "disable"
fi
;;
stop)
apply_hbs_tm_settings "disable"
;;
restart)
if is_enabled_hbs_tm; then
apply_hbs_tm_settings "disable"
apply_hbs_tm_settings "enable"
else
apply_hbs_tm_settings "disable"
fi
;;
*)
if [ "$1" != "" ]; then
$1 "$2" "$3" "$4" "$5";
else
echo ""
/bin/echo "Usage: $0 {apply|restart}"
echo ""
exit 1
fi
esac
exit 0