File: //var/packages/SMBService/conf/systemd/samba-util.sh
#! /bin/bash
#
# util functions for samba's usage
# source guard
[[ "${_SOURCE_GUARD_SYNOSAMBA_SAMBA_UTIL}" != "yes" ]] || return
_SOURCE_GUARD_SYNOSAMBA_SAMBA_UTIL="yes"
DOMAIN_DB_VERSION=11
SAMBA_ROOT="/var/packages/SMBService"
SAMBA_BIN="$(/bin/realpath ${SAMBA_ROOT}/target/usr/bin)"
SAMBA_SBIN="$(/bin/realpath ${SAMBA_ROOT}/target/usr/sbin)"
SYNOSYSTEMCTL="/usr/syno/bin/synosystemctl"
SYNOACLTOOL="/usr/syno/bin/synoacltool"
SYNOGETKEYVALUE="/usr/syno/bin/synogetkeyvalue"
# basic util functions
syslog() { # logger args
local ret=$?
/bin/logger -p user.err -t "$(basename "$0")($$)" "$@"
return $ret
}
message() { # echo args
local ret=$?
echo "$@"
return $ret
} >&2
warn() { # echo args
local ret=$?
local H="[33m" E="[0m"
echo -en "$H$*" ; echo "$E"
return $ret
} >&2
is_any_running() {
local i=
for i in "$@"; do
if /bin/pidof "$i" >/dev/null; then
return 0
fi
done
return 1
}
is_ad_mode() {
if [ -f "/run/samba/synoadserver" ]; then
return 0;
fi
return 1;
}
is_domain_mode()
{
local SECURITY
SECURITY=$(${SYNOGETKEYVALUE} /etc/samba/smb.conf "security")
if [ "ads" == "$SECURITY" ] || [ "domain" == "$SECURITY" ]; then
exit 0;
fi
if is_ad_mode; then
exit 0;
fi
exit 1;
}
is_enabled() { # <pkg-synosamba-smbd | pkg-synosamba-nmbd | pkg-synosamba-winbindd>
[[ "enabled" == $(${SYNOSYSTEMCTL} get-enable-status "$1") ]]
}
is_active() { # <pkg-synosamba-smbd | pkg-synosamba-nmbd | pkg-synosamba-winbindd>
[[ "active" == $(${SYNOSYSTEMCTL} get-active-status "$1") ]]
}
safe_restart() { # <pkg-synosamba-smbd | pkg-synosamba-nmbd | pkg-synosamba-winbindd>
${SYNOSYSTEMCTL} try-restart "$@" >/dev/null 2>&1
}
safe_restart_noblock() { # <pkg-synosamba-smbd | pkg-synosamba-nmbd | pkg-synosamba-winbindd>
${SYNOSYSTEMCTL} try-restart --no-block "$@" >/dev/null 2>&1
}
safe_reload() { # <pkg-synosamba-smbd | pkg-synosamba-nmbd | pkg-synosamba-winbindd>
for process_name in "$@"; do
if is_active "${process_name}"; then
${SYNOSYSTEMCTL} reload "${process_name}" >/dev/null 2>&1
fi
done
}
safe_reload_noblock() { # <pkg-synosamba-smbd | pkg-synosamba-nmbd | pkg-synosamba-winbindd>
for process_name in "$@"; do
if is_active "${process_name}"; then
${SYNOSYSTEMCTL} reload --no-block "${process_name}" >/dev/null 2>&1
fi
done
}
smb_status() { # <pkg-synosamba-smbd | pkg-synosamba-nmbd | pkg-synosamba-winbindd>
/bin/systemctl status "$@" 2>&1
}
# smb util functions
file_size() {
local File="$1"
local size=0
if [ -f "$1" ]; then
size=$(${SYNOACLTOOL} -stat "$File" | /bin/grep "File size:" | /bin/awk '{ print $3;}')
fi
echo "$size"
}
ARRAY_INSTALL_PATH=(
"/var/cache/samba" "755" #no this directory will let smbd start fail
"/var/log/samba" "755" #no this directory will let smbd log to stdout
"/etc/samba/private" "755" #no this directory will open/create secrets.tdb fail and force smb_panic
)
install_dir_recover() {
local count=0
while [ -n "${ARRAY_INSTALL_PATH[$count]}" ];
do
local dir_path="${ARRAY_INSTALL_PATH[$count]}"
local mode="${ARRAY_INSTALL_PATH[$((count+1))]}"
if [ ! -d "$dir_path" ]; then
syslog "$dir_path not exist. create it."
/bin/install -d -m "$mode" "$dir_path"
fi
count=$((count+2))
done
}
# below path will create by smbd/nmbd/winbindd. But if dir exist with wrong perm, the daemon will start fail.
ARRAY_PERM_PATH=(
"/run/samba/ncalrpc/np" "700"
"/run/samba/ncalrpc" "755"
"/run/samba/msg.lock" "755"
"/run/samba/msg.sock" "700"
"/run/samba/winbindd" "755"
"/run/samba/nmbd" "755"
"/var/lib/samba/winbindd_privileged" "750"
)
perm_recover() {
local count=0
local st_mode
while [ -n "${ARRAY_PERM_PATH[$count]}" ];
do
local target_path="${ARRAY_PERM_PATH[$count]}"
local mode="${ARRAY_PERM_PATH[$((count+1))]}"
if [ -e "$target_path" ]; then
st_mode=$(/bin/stat -c "%a" "$target_path")
if [ "$st_mode" != "$mode" ]; then
syslog "$dir_path wrong mode $st_mode. correct to $mode."
/bin/chmod "$mode" "$target_path"
fi
fi
count=$((count+2))
done
}
samba_file_dir_check() {
# install path check and recover
install_dir_recover
# samba create path check permission
perm_recover
#check smb.conf
local smbconfsize
smbconfsize=$(file_size /etc/samba/smb.conf)
if [ "$smbconfsize" = 0 ]; then
syslog "/etc/samba/smb.conf is empty or not exist. Recover by /etc.defaults/samba/smb.conf"
/bin/mv /etc/samba/smb.conf /etc/samba/smb.conf.bad > /dev/null 2>&1
/bin/cp -f /etc.defaults/samba/smb.conf /etc/samba/smb.conf
fi
}
smb_remove_share_tdbs() {
# those tdb will be used by nmbd, smbd, winbindd
/bin/rm -f /run/samba/messages.tdb /run/samba/serverid.tdb
}
smb_remove_smbd_temp_tdbs() {
/bin/rm -f /run/samba/brlock.tdb /run/samba/connections.tdb \
/run/samba/login_cache.tdb "/var/cache/samba/printing/*.tdb" \
/run/samba/sessionid.tdb /run/samba/locking.tdb \
/run/samba/unexpected.tdb /run/samba/deferred_open.tdb \
/var/cache/samba/notify_onelevel.tdb
}
smb_remove_winbindd_temp_tdbs()
{
#We default enable winbindd offline logon
#so we cannot remove winbindd_cache.tdb when restart winbindd.
#The netsamlogon_cache.tdb may be cannot be delete for auth.
#But netsamlogon_cache.tdb has too much cache issue.
local cache_size=0
if [ ! -f /var/lib/samba/winbindd_cache.tdb ]; then
return
fi
#winbind_cache.tdb too large will cause the winbindd check very long time
#If size > 200MB(200*1024*1024 = 209715200 Byte), we need delete it to keep performance
cache_size=$(file_size /var/lib/samba/winbindd_cache.tdb)
if [ "$cache_size" != "" ] && [ "$cache_size" -gt 209715200 ]; then
#when cache size too large, we should clear it before starting winbindd
/bin/rm -f "$(/usr/bin/realpath /var/lib/samba/winbindd_cache.tdb)"
fi
/bin/rm -f /var/cache/samba/winbindd_cache.tdb* /var/lib/samba/winbindd_cache.tdb*
}
smb_remove_smbd_winbindd_share_tdbs() {
if is_any_running smbd winbindd; then
return 1
fi
/bin/rm -f /var/cache/samba/netsamlogon_cache.tdb /run/samba/gencache_notrans.tdb \
/run/samba/gencache.tdb /var/lib/samba/group_mapping.tdb
}
smb_remove_temp_tdbs() { # <void>
if is_any_running smbd nmbd winbindd; then
return 1
fi
message "remove temp tdbs"
smb_remove_smbd_temp_tdbs
smb_remove_winbindd_temp_tdbs
smb_remove_smbd_winbindd_share_tdbs
smb_remove_share_tdbs
}
smb_check_tdb() { # <tdb file>
local tdbfile=${1:?"error param"}
local backup=${SAMBA_BIN}/tdbbackup
message -n "check tdb: $tdbfile ... "
if [ -f "$tdbfile" ]; then
if $backup -v "$tdbfile" >/dev/null 2>&1; then
# tdb is good make another backup
/bin/mv -f "$tdbfile.bkp" "$tdbfile.bkp.old"
if $backup -s ".bkp" "$tdbfile" >/dev/null 2>&1; then
message "done"
/bin/rm -f "$tdbfile.bkp.old"
else
warn "backup failed"
/bin/mv -f "$tdbfile.bkp.old" "$tdbfile.bkp"
fi
elif [ -f "$tdbfile.bkp" ]; then
warn "corrupt, restore"
if ! $backup -v -s ".bkp" "$tdbfile" >/dev/null; then
warn "restore failed, remove it"
/bin/rm -f "$tdbfile"
fi
else
warn "corrupt, remove it"
/bin/rm -f "$tdbfile"
fi
elif [ -f "$tdbfile.bkp" ];then
warn "lost, use backup tdb"
if ! $backup -v -s ".bkp" "$tdbfile" > /dev/null; then
warn "restore failed"
fi
else
message "not exist"
fi
return 0
}
# first param is /volumeX
is_winbindd_db_in_volume() {
local -r volume=$1
local -r winbindd_db_path=(
/var/lib/samba/winbindd_cache.tdb
/usr/syno/etc/private/.db.domain_user_full
/usr/syno/etc/private/.db.domain_group_full
)
if [[ "$volume" != "/volume"* ]]; then
return 1
fi
for path in ${winbindd_db_path[*]}; do
if [[ $(readlink "$path") == "$volume/"* ]]; then
return 0
fi
done
return 1
}
is_need_reload_smbd() {
local -r enable_multichannel=$("${SAMBA_BIN}"/testparm -s \
--parameter-name="server multi channel support" 2>/dev/null)
local -r enable_smb_autoblock=$("${SAMBA_BIN}"/testparm -s \
--parameter-name="syno autoblock support" 2>/dev/null)
if [[ "${enable_multichannel,,}" == "yes" ]]; then
return 0
fi
if [[ "${enable_smb_autoblock,,}" == "yes" ]]; then
return 0
fi
return 1
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
cmd="$1"
arg_type=$(type -t "$cmd")
shift
if [[ "${arg_type}" != "function" ]] || [[ "${cmd}" == _* ]]; then
message "$0: Invalid argument: $1"
exit 1
fi
${cmd} "$@"
fi
# vim:set noexpandtab: