File: //var/packages/SMBService/scripts/util.sh
#!/bin/bash
# source guard
[[ "${_SOURCE_GUARD_SYNOSAMBA_UTIL}" != "yes" ]] || return
_SOURCE_GUARD_SYNOSAMBA_UTIL="yes"
###################################
# Normalize Package Variables
###################################
: "${SYNOPKG_PKGNAME:="SMBService"}"
: "${SYNOPKG_PKGDIR:=$(/bin/realpath "/var/packages/${SYNOPKG_PKGNAME}")}"
: "${SYNOPKG_PKGDEST:=$(/bin/realpath "${SYNOPKG_PKGDIR}/target")}"
SYNOSETKEYVALUE="/usr/syno/bin/synosetkeyvalue"
SYNOGETKEYVALUE="/usr/syno/bin/synogetkeyvalue"
SYNOSYSTEMCTL="/usr/syno/bin/synosystemctl"
SYNOSMBTOOL="${SYNOPKG_PKGDEST}/tools/synosmbtool"
###################################
# Help Functions
###################################
exit_with_ui_error()
{
(>&2 echo "$@")
if [[ -n "${SYNOPKG_TEMP_LOGFILE}" ]]; then
echo "$@" > "${SYNOPKG_TEMP_LOGFILE}"
fi
exit 1
}
exit_with_error()
{
(>&2 echo "$@")
exit 1
}
logger_err()
{
/bin/logger -s -t SMBService -p user.err "$@"
}
logger_info()
{
/bin/logger -s -t SMBService -p user.info "$@"
}
is_smb_option_enabled()
{
local -r opt_key=${1? "lack of smb option"}
local -r TESTPARM="${SYNOPKG_PKGDEST}/usr/bin/testparm"
local -r smbinfo_conf="/etc/samba/smbinfo.conf"
local -r enable_status=$(${TESTPARM} -s --parameter-name="${opt_key}" 2>/dev/null)
if [[ "${enable_status,,}" == "yes" ]]; then
return 0
fi
return 1
}
##################################
# Manual install/uninstall
##
# - pam: TODO
##################################
samba_pam()
{
local -r -A install_map=(
["${SYNOPKG_PKGDEST}/resource/pam-config/samba.pam"]="/etc/pam.d/samba"
["${SYNOPKG_PKGDEST}/usr/lib/security/pam_winbind.so"]="/usr/lib/security/pam_winbind.so"
["${SYNOPKG_PKGDEST}/usr/etc/security/pam_winbind.conf"]="/etc/security/pam_winbind.conf"
)
case ${1,,} in
install)
for key in "${!install_map[@]}"; do
if [ -e "${install_map[$key]}" ]; then
continue
fi
echo "install ${key} --> ${install_map[$key]}"
/bin/install -D "${key}" "${install_map[$key]}"
done
;;
uninstall)
for key in "${!install_map[@]}"; do
/bin/rm -f "${install_map[$key]}"
done
;;
*)
exit_with_ui_error "Interanl Error: invalid argument '${1}' for ${FUNCNAME[0]} function"
;;
esac
}
samba_nss()
{
local -r -A install_map=(
["${SYNOPKG_PKGDEST}/usr/lib/libnss_wins.so.2"]="/usr/lib/libnss_wins.so.2"
["${SYNOPKG_PKGDEST}/usr/lib/libnss_winbind.so.2"]="/usr/lib/libnss_winbind.so.2"
)
local -r -A link_map=(
["/usr/lib/libnss_wins.so.2"]="/usr/lib/libnss_wins.so"
["/usr/lib/libnss_winbind.so.2"]="/usr/lib/libnss_winbind.so"
)
case ${1,,} in
install)
for key in "${!install_map[@]}"; do
if [ -e "${install_map[$key]}" ]; then
continue
fi
echo "install ${key} --> ${install_map[$key]}"
/bin/install -D "${key}" "${install_map[$key]}"
done
for key in "${!link_map[@]}"; do
if [ -e "${link_map[$key]}" ]; then
continue
fi
echo "link ${key} --> ${link_map[$key]}"
/bin/ln -s "${key}" "${link_map[$key]}"
done
;;
uninstall)
for key in "${!install_map[@]}"; do
/bin/rm -f "${install_map[$key]}"
done
for key in "${!link_map[@]}"; do
/bin/rm -f "${link_map[$key]}"
done
;;
*)
exit_with_ui_error "Interanl Error: invalid argument '${1}' for ${FUNCNAME[0]} function"
;;
esac
}
samba_services()
{
local -r backup_file="${SYNOPKG_PKGDIR}/etc/service-status.conf"
local -r services=(
pkg-DirectoryServerForWindowsDomain
pkg-synosamba-nmbd
pkg-synosamba-smbd
pkg-synosamba-winbindd
pkg-synosamba-wstransfer-genconf
)
logger_info "samba_service: ${1,,}, status: ${SYNOPKG_PKG_STATUS}"
case ${1,,} in
backup_status)
/bin/rm -f "${backup_file}"
for service in ${services[*]}; do
if enable_status=$(${SYNOSYSTEMCTL} get-enable-status "${service}"); then
${SYNOSETKEYVALUE} "${backup_file}" "${service}" "${enable_status}"
fi
done
${SYNOSYSTEMCTL} disable "${services[@]}"
${SYNOSYSTEMCTL} stop "${services[@]}"
;;
restore_status)
local enabled_services=()
if [[ -f ${backup_file} ]]; then
for service in ${services[*]}; do
if [[ "$(${SYNOGETKEYVALUE} "${backup_file}" "${service}")" = 'enabled' ]]; then
enabled_services+=("${service}")
fi
done
else
enabled_services+=("pkg-synosamba-nmbd")
enabled_services+=("pkg-synosamba-wstransfer-genconf")
enabled_services+=("pkg-synosamba-smbd")
logger_info "no previous status of samba services, start default enabled services."
fi
logger_info "enabled_service: ${enabled_services[*]}"
if [[ ${#enabled_services[@]} -ne 0 ]]; then
${SYNOSYSTEMCTL} enable "${enabled_services[@]}"
${SYNOSYSTEMCTL} start "${enabled_services[@]}"
fi
;;
*)
;;
esac
}
ctdb_samba_services()
{
local -r backup_file="${SYNOPKG_PKGDIR}/etc/service-status.conf"
local -r all_services=(
pkg-DirectoryServerForWindowsDomain
pkg-synosamba-nmbd
pkg-synosamba-wstransfer-genconf
pkg-synosamba-ctdbd
pkg-synosamba-smbd
pkg-synosamba-winbindd
)
local -r ctdb_services=(
pkg-synosamba-smbd
pkg-synosamba-winbindd
)
logger_info "samba_service: ${1,,}, status: ${SYNOPKG_PKG_STATUS}"
case ${1,,} in
backup_status)
/bin/rm -f "${backup_file}"
for service in ${all_services[*]}; do
if enable_status=$(${SYNOSYSTEMCTL} get-enable-status "${service}"); then
${SYNOSETKEYVALUE} "${backup_file}" "${service}" "${enable_status}"
fi
done
${SYNOSYSTEMCTL} disable "${all_services[@]}"
${SYNOSYSTEMCTL} stop "${all_services[@]}"
;;
restore_status)
local enabled_services=()
if [[ -f ${backup_file} ]]; then
for service in ${all_services[*]}; do
if [[ "$(${SYNOGETKEYVALUE} "${backup_file}" "${service}")" = 'enabled' ]]; then
enabled_services+=("${service}")
fi
done
else
enabled_services+=("pkg-synosamba-nmbd")
enabled_services+=("pkg-synosamba-wstransfer-genconf")
enabled_services+=("pkg-synosamba-ctdbd")
enabled_services+=("pkg-synosamba-smbd")
logger_info "no previous status of samba services, start default enabled services."
fi
logger_info "enabled_service: ${enabled_services[*]}"
if [[ ${#enabled_services[@]} -ne 0 ]]; then
${SYNOSYSTEMCTL} enable "${enabled_services[@]}"
# remove ctdb_services, ctdb_services will startup via ctdb
for service in ${ctdb_services[*]}; do
enabled_services=("${enabled_services[@]/$service}")
done
${SYNOSYSTEMCTL} start "${enabled_services[@]}"
fi
;;
*)
;;
esac
}
is_path_in_btrfs()
{
local -r target_path=${1? "lack of target_path"}
local -r dev=$(df "${target_path}" --output=source | tail -1)
local -r fstype=$(blkid -ovalue -sTYPE "${dev}")
if [ "${fstype,,}" != "btrfs" ]; then
return 1
fi
return 0
}
is_path_CoW_on()
{
local -r target_path=${1? "lack of target_path"}
if /bin/lsattr -ld "${target_path}" | grep -q "No_COW"; then
return 1
fi
return 0
}
disable_CoW_on_path_if_btrfs()
{
local -r target_path=${1? "lack of target_path"}
if ! is_path_in_btrfs "${target_path}"; then
return
fi
if ! is_path_CoW_on "${target_path}"; then
return
fi
if ! /bin/chattr +C "${target_path}"; then
echo "Unable to disable CoW on [${target_path}]"
return
fi
echo "Disable CoW on [${target_path}] successfully"
}
prepare_samba_folders()
{
# XXX: should be rewritten to systemd options if our systemd is upgraded.
# see the comment in pkg-synosamba-nmbd.service
local -r state_directory="/var/lib/samba"
local -r cache_directory="/var/cache/samba"
local -r logs_directory="/var/log/samba"
local -r configuration_directory="/etc/samba"
local -r -a directory_list=(
"${state_directory}"
"${cache_directory}"
"${logs_directory}"
"${configuration_directory}"
)
/bin/install -d "${directory_list[@]}"
disable_CoW_on_path_if_btrfs "${cache_directory}"
}
is_new_installation()
{
/bin/diff "/etc/VERSION" "/etc.defaults/VERSION" > /dev/null 2>&1
}
_setkeyvalue_noquote()
{
local -r file="${1?"file should be specified"}"
local -r key="${2?"key should be specified"}"
local -r value="${3?"value should be specified"}"
local -r tmpfile=$(/bin/mktemp "${file}.XXXXXXXX")
/bin/rm -f "${tmpfile}"
while IFS= read -r line; do
local trimkey
local trimline
trimkey=$(echo "$key" | tr -d '[:space:]')
trimline=$(echo "$line" | tr -d '[:space:]')
if ! [[ ${trimline,,} =~ ^${trimkey,,}= ]]; then
printf '%s\n' "${line}" >> "${tmpfile}"
continue
fi
done < "${file}"
if [[ -n "${value}" ]]; then
printf '\t%s = %s\n' "${key}" "${value}" >> "${tmpfile}"
fi
/bin/mv "${tmpfile}" "${file}"
}
_smb_set_directory_leasing_home_only_conf()
{
local -r conf_file="${1? "lack of conf_file"}"
_setkeyvalue_noquote "${conf_file}" \
"syno directory lease grant" "yes"
_setkeyvalue_noquote "${conf_file}" \
"syno directory lease grant restrict home only" "yes"
}
smb_enable_directory_leasing_home_only()
{
local -r smbinfo_conf="/etc/samba/smbinfo.conf"
init_smbinfo_conf
_smb_set_directory_leasing_home_only_conf "${smbinfo_conf}"
}
init_smbinfo_conf()
{
local -r smbinfo_conf="/etc/samba/smbinfo.conf"
local -r synoinfo_conf="/etc/synoinfo.conf"
local -r smbinfo_default_conf="${SYNOPKG_PKGDIR}/conf/smbinfo_default.conf"
if [[ ! -e "${smbinfo_conf}" ]]; then
echo "install ${smbinfo_default_conf}"
/bin/install -D -m 644 "${smbinfo_default_conf}" "${smbinfo_conf}"
# if this DS is newly install in DSM7.0, default on catia
if is_new_installation; then
_setkeyvalue_noquote "${smbinfo_conf}" "syno catia" "yes"
_setkeyvalue_noquote "${smbinfo_conf}" "follow symlinks" "yes"
_setkeyvalue_noquote "${smbinfo_conf}" "smb2 leases" "yes"
_smb_set_directory_leasing_home_only_conf "${smbinfo_conf}"
fi
fi
# move "oplock_disabled" key from synoinfo.conf to smbinfo.conf
# and change name to "oplocks"
oplock_disabled=$(${SYNOGETKEYVALUE} "${synoinfo_conf}" "oplock_disabled")
if [[ -n "${oplock_disabled}" ]]; then
oplocks="yes"
if [[ "yes" == "${oplock_disabled,,}" ]]; then
oplocks="no"
fi
${SYNOSETKEYVALUE} "${synoinfo_conf}" "oplock_disabled" ""
_setkeyvalue_noquote "${smbinfo_conf}" "oplocks" "${oplocks}"
fi
}
smbinfo_conf_check() {
# we will check empty value and remove it
# this can prevent include /etc/samba/smbinfo.conf which has key but no value
local -r smbinfo_conf="/etc/samba/smbinfo.conf"
local -r smbinfo_conf_temp="/etc/samba/smbinfo.conf.tmp"
# clean tmp file
/bin/rm -f "${smbinfo_conf_temp}"
while IFS=$' \t\n' read -r line; do
if [[ -z "${line}" ]] || [[ "[" == "${line:0:1}" ]] || [[ ";" == "${line:0:1}" ]] || [[ "#" == "${line:0:1}" ]]; then
echo "$line" >> "${smbinfo_conf_temp}"
continue
fi
# remove deprecated key
local key=${line%=*}
case "$key" in
"advanced_domain_option")
# "advanced_domain_option" deprecate from DSM7.0
continue;;
"register nic")
# "register nic" is removed from DSM7.0
continue;;
"btrfs clone")
# "btrfs clone" deprecate from DSM#98173
continue;;
esac
if [[ ${line} != *"=" ]]; then
echo -e "\t${line}" >> "${smbinfo_conf_temp}"
fi
done < "${smbinfo_conf}"
if ! /bin/diff "${smbinfo_conf_temp}" "${smbinfo_conf}" > /dev/null 2>&1; then
/bin/mv "${smbinfo_conf_temp}" "${smbinfo_conf}"
else
/bin/rm -f "${smbinfo_conf_temp}"
fi
}
remove_old_config()
{
local -r old_config=(
"/etc/logrotate.d/synowstransferd"
"/etc/syslog-ng/patterndb.d/synowstransferd.conf"
"/etc/syslog-ng/patterndb.d/include/not2msg/synowstransfer.conf"
"/usr/syno/etc/services.d/synowstrasnfer.sc"
"/usr/syno/etc/synowstransfer_nginx.conf"
)
/bin/rm -f "${old_config[@]}"
${SYNOSYSTEMCTL} reload syslog-ng
}
smb_transfer_log_check()
{
#init when smbxferlog in /etc/synoinfo.conf is not exist
smb_transfer_log=$(/usr/syno/bin/synogetkeyvalue "/etc/synoinfo.conf" "smbxferlog")
if [[ -z "${smb_transfer_log}" ]]; then
/bin/cat << XFERLOG_LEVEL >> "/usr/syno/etc/xferloglevel.conf"
[WinFileService]
create=0
write=0
delete=1
rename=1
read=0
move=0
permission change=0
XFERLOG_LEVEL
/usr/syno/bin/synosetkeyvalue "/etc/synoinfo.conf" "smbxferlog" "yes"
fi
}
has_smb_connection()
{
share_name=$1
/var/packages/SMBService/target/usr/bin/smbstatus --shares-syno | awk -F '\t' -v share="${share_name}" '{
if(tolower($5) == tolower(share)) {
_has_connection = 1
exit
}
} END {
if (_has_connection) {
exit 0
} else {
exit 1
}
}'
}
smb_checkconf()
{
if ! ${SYNOSMBTOOL} --checkconf; then
logger_info "smb check conf failed"
fi
}
remove_option_from_file()
{
local -r target_file=${1?"lack of target file"}
local -r target_option=${2?"lack of target option"}
if ! grep -qi "${target_option}" "${target_file}"; then
return
fi
echo "remove '${target_option}' from ${target_file}"
sed -i "/^\s*${target_option}\s*=/Id" "${target_file}"
}
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
cmd="$1"
arg_type=$(type -t "$cmd")
shift
if [[ "${arg_type}" != "function" ]] || [[ "${cmd}" == _* ]]; then
logger_info "$0: Invalid argument: $1"
exit 1
fi
${cmd} "$@"
fi
remove_deprecated_settings_from_smbconf()
{
local -r smbconf="/etc/samba/smb.conf"
local -r deprecated_keys=(
"client ntlmv2 auth"
"syno sync dctime"
)
# `client ntlmv2 auth = no` was set when DSM joined to a domain in the NT4
# way. since DSM7.0, ntlmv1 is disabled by default and we don't support
# joining NT4 domain anymore. this setting is considered to be remove to
# prevent access denied due to no acceptable NTLM version between SMB
# clients and the SMB server.
for KEY in "${deprecated_keys[@]}"
do
if grep -i "${KEY}" "${smbconf}"; then
logger_info "remove '${KEY}' from ${smbconf}"
_setkeyvalue_noquote "${smbconf}" "${KEY}" ""
fi
done
}
remove_smbd_volatile_db()
{
local -r db_path=(
"/var/cache/samba/autoblock_tolerance.tdb"
)
for path in "${db_path[@]}"; do
/bin/rm -f "${path}"
done
}
is_HCI()
{
local -r synoinfo_conf="/etc/synoinfo.conf"
local -r hci_key="support_hyper_converged"
if [[ "$(${SYNOGETKEYVALUE} "${synoinfo_conf}" "${hci_key}")" == "yes" ]]; then
return 0
fi
return 1
}
is_cluster_controller() {
local role
role=$(/usr/syno/bin/hcm-sentinel-tool get-role)
if [[ "controller" == "$role" ]]; then
return 0
fi
return 1
}
# vim:set ts=4 sw=4 sts=4 expandtab: