HEX
Server: Apache/2.4.63 (Unix)
System: Linux Synopilou92 4.4.302+ #72806 SMP Mon Jul 21 23:16:00 CST 2025 x86_64
User: pilou92 (1026)
PHP: 8.0.30
Disabled: NONE
Upload Files
File: //var/packages/Spreadsheet/scripts/pkg_utils
#!/bin/bash

SYNOPKG_PKGNAME="${SYNOPKG_PKGNAME:-Spreadsheet}"
PACKAGE_DIR="/var/packages/${SYNOPKG_PKGNAME}"
PKG_APP_PATH="${PACKAGE_DIR}/target/ui"
TOOL_PATH="${PACKAGE_DIR}/target/tools"

HOOK_LIB_PATH="${PACKAGE_DIR}/target/hook/office.lib"
SYNODRIVE_HOOK_PATH="/usr/local/libexec/synodrive/"
SYNODRIVE_HOOK_LIB_PATH="${SYNODRIVE_HOOK_PATH}/office.lib"
SYNODRIVE_HOOK_OLD_LIB_PATH="${SYNODRIVE_HOOK_PATH}/synoffice.lib"

LN_BIN="/bin/ln"
RM_BIN="/bin/rm"
MV_BIN="/bin/mv"
SU_BIN="/bin/su"
KILLALL_BIN="/usr/bin/killall"
CHOWN_BIN="/bin/chown"
STAT_BIN="/bin/stat"
CHMOD_BIN="/bin/chmod"
MKDIR_BIN="/bin/mkdir"

TOOL_PATH="${PACKAGE_DIR}/target/tools"
OFFICE_INDEX_BIN="${TOOL_PATH}/synofficeindex"
SYNODRIVE_INDEX_BIN="${TOOL_PATH}/synodriveindex"
SYNODRIVE_SETTING_BIN="${TOOL_PATH}/synodrivesettings"
SYNODRIVE_VOLUME_BIN="${TOOL_PATH}/synodrivevolume"
FEASIBLILITY_BIN="${TOOL_PATH}/synofficefeasibility"
COMMON_BIN="${TOOL_PATH}/synofficecommon"
REPO_BIN="${TOOL_PATH}/synofficerepo"
DB_BACKUP_BIN="${TOOL_PATH}/synofficedbbackup"
SETTING_BIN="${TOOL_PATH}/synofficesetting"
NODE_BIN="${TOOL_PATH}/synofficenode"
TEMPLATE_BIN="${TOOL_PATH}/synofficetemplate"
CLOUDSTAION_DRIVE_BIN="${TOOL_PATH}/synofficedrive"
WEB_BIN="${TOOL_PATH}/synofficeweb"
CLEANER_BIN="${TOOL_PATH}/synofficecleaner"

# pgsql related
PSQL="/usr/bin/psql"
DB_NAME="synoffice"
DB_USER="office"
DB_CREATE_SCRIPT="${PACKAGE_DIR}/scripts/create_table.sql"
TEMPLATE_CREATE_SCRIPT="${PACKAGE_DIR}/scripts/create_template.sql"
TEMPLATE_FUNC_CREATE_SCRIPT="${PACKAGE_DIR}/scripts/create_template_func.sql"
VIEW_CREATE_SCRIPT="${PACKAGE_DIR}/scripts/create_view.sql"
CLEANER_CREATE_SCRIPT="${PACKAGE_DIR}/scripts/create_cleaner.sql"
USER_STATIST_CREATE_SCRIPT="${PACKAGE_DIR}/scripts/create_user_statistic.sql"

QUEUE_SNAPSHOT_DIR="${PACKAGE_DIR}/tmp/quesnapshot"

NGINX_RULE="${PACKAGE_DIR}/target/etc/Spreadsheet.mustache"
NGINX_ORI_RULE="${PACKAGE_DIR}/target/etc/Spreadsheet.ori.mustache"
NGINX_MAIN_CONF="main.pkg-static.Spreadsheet.conf"
NGINX_HTTP_CONF="http.pkg-static.Spreadsheet.conf"

FONT_SETTING="font.json"

Rebuild()
{
	# clear old db and table
	DropTable

	# create table
	$SU_BIN "$DB_USER" -s /bin/sh -c "$PSQL $DB_NAME < $DB_CREATE_SCRIPT"

	CreateTables true
	CreateHookLink
	EnableSynoDrive

	# reindex synodrive
	$SYNODRIVE_INDEX_BIN --reindex ns=office

	$OFFICE_INDEX_BIN --reindex

	$DB_BACKUP_BIN --import
}

CreateTable()
{
	# Check database schema match or not
	${COMMON_BIN} --check_schema
	RESULT=$?
	if [ "$RESULT" = "0" ]; then
		return
	elif [ "$RESULT" = "254" ]; then
		echo "DB is not ready in CreateTable()"
		exit 1
	fi

	if [ "UPGRADE" == "$SYNOPKG_PKG_STATUS" ]; then
		$DB_BACKUP_BIN --backup
	fi

	Rebuild
}

CreateTemplateTable()
{
	if [ "$force" = "false" ];then
		# Check database schema match or not
		${TEMPLATE_BIN} --check_schema
		RESULT=$?
		if [ "$RESULT" = "0" ]; then
			TemplateInit
			return
		elif [ "$RESULT" = "254" ]; then
			echo "DB is not ready in CreateTemplateTable()"
			exit 1
		fi
		if [ "UPGRADE" == "$SYNOPKG_PKG_STATUS" ]; then
			$DB_BACKUP_BIN --template_backup
		fi
	fi
	$SU_BIN "$DB_USER" -s /bin/sh -c "$PSQL $DB_NAME < $TEMPLATE_CREATE_SCRIPT"
	${TEMPLATE_BIN} --index
	TemplateInit
	$DB_BACKUP_BIN --template_import
}

TemplateInit()
{
	CreateTemplateFunc
	local rebuild_file="/var/packages/Spreadsheet/tmp/template_need_rebuild"
	if [ -f "$rebuild_file" ];then
		${TEMPLATE_BIN} --init clean=1
		$RM_BIN -f "$rebuild_file"
	else
		${TEMPLATE_BIN} --init
	fi
}

CreateView()
{
	$SU_BIN "$DB_USER" -s /bin/sh -c "$PSQL $DB_NAME < $VIEW_CREATE_SCRIPT"
}

CreateTemplateFunc()
{
	$SU_BIN "$DB_USER" -s /bin/sh -c "$PSQL $DB_NAME < $TEMPLATE_FUNC_CREATE_SCRIPT"
}

CreateCleanerTable()
{
	if [ "$force" = "false" ];then
		# Check database schema match or not
		${CLEANER_BIN} --check_schema
		RESULT=$?
		if [ "$RESULT" = "0" ]; then
			return
		elif [ "$RESULT" = "254" ]; then
			echo "DB is not ready in CreateCleanerTable()"
			exit 1
		fi
	fi
	$SU_BIN "$DB_USER" -s /bin/sh -c "$PSQL $DB_NAME < $CLEANER_CREATE_SCRIPT"
}

CreateUserStatisticTable()
{
	if [ "$force" = "false" ];then
		# Check database schema match or not
		${COMMON_BIN} --check_user_statistic_schema
		RESULT=$?
		if [ "$RESULT" = "0" ]; then
			return
		elif [ "$RESULT" = "254" ]; then
			echo "DB is not ready in CreateUserStatisticTable()"
			exit 1
		fi
	fi
	$SU_BIN "$DB_USER" -s /bin/sh -c "$PSQL $DB_NAME < $USER_STATIST_CREATE_SCRIPT"
}

CreateTables()
{
	if ! IsController ;then
		return
	fi
	force=$1
	if [ "$force" = "false" ];then
		CreateTable
	fi
	CreateTemplateTable "$force"
	CreateCleanerTable "$force"
	CreateUserStatisticTable "$force"
	CreateView "$force"
}

KillProcess()
{
	$KILLALL_BIN -9 "$1"
	local count=0
	while [ -n "$(pidof "$1")" ]; do
		sleep 1
		count=$((count+1))
		if [ ${count} -ge 30 ]; then
			$KILLALL_BIN -9 "$1"
			break
		fi
	done
}

DropTable()
{
	KillProcess "synofficedbbackup"
	$PSQL -U postgres -d $DB_NAME -c "drop schema public cascade;"
	$SU_BIN "$DB_USER" -s /bin/sh -c "$PSQL $DB_NAME -c \"create schema public;\""
}

CreateHookLink()
{
	if [ ! -d ${SYNODRIVE_HOOK_PATH} ]; then
		$MKDIR_BIN -p ${SYNODRIVE_HOOK_PATH}
	fi

	${LN_BIN} -sf "${HOOK_LIB_PATH}" ${SYNODRIVE_HOOK_LIB_PATH}
	${RM_BIN} -f ${SYNODRIVE_HOOK_OLD_LIB_PATH}
}

RemoveHookLink()
{
	${RM_BIN} -f ${SYNODRIVE_HOOK_LIB_PATH}
}

GenNginxMustacheRule()
{
	local tmp_file
	tmp_file="$(/bin/mktemp "$NGINX_ORI_RULE.XXXXXX")"
	/bin/cp "$NGINX_ORI_RULE" "$tmp_file"
	if ClusterIsNormal ;then
		/bin/sed -i -e "/__MUSTACHE_BLOCK__/{r $PACKAGE_DIR/target/etc/Spreadsheet-block-cluster-active.conf
r $PACKAGE_DIR/target/etc/Spreadsheet-block-cluster-passive.conf" -e "d}" "$tmp_file"
	else
		/bin/sed -i -e "/__MUSTACHE_BLOCK__/{r $PACKAGE_DIR/target/etc/Spreadsheet-block-dsm.conf" -e "d}" "$tmp_file"
	fi
	$CHMOD_BIN 644 "$tmp_file"
	$MV_BIN -f "$tmp_file" "$NGINX_RULE"
}

GenNginxStaticRule()
{
	if ! ClusterIsSupport ;then
		return
	fi
	if ClusterIsNormal ;then
		local dsm_port
		local uuid
		local host_conf="upstream synoffice_cluster_nodes {synoffice_load_balancer;"
		if SOFSIsSupport ;then
			dsm_port=$(/usr/syno/bin/synow3 --get-dsm-port)
			for uuid in $(/usr/syno/bin/hcm-sentinel-tool get-all-hosts);do
				host_conf+="server $(/usr/syno/bin/hcm-sentinel-tool get-cluster-network-fixed-ip --host "$uuid"):$dsm_port;"
			done
		elif FSDNIsSupport ;then
			dsm_port=6666
			host_conf+="server 169.254.4.1:$dsm_port;server 169.254.4.2:$dsm_port;"
		fi
		host_conf+="}"
		local tmp_file
		tmp_file="$(/bin/mktemp "$PACKAGE_DIR/target/etc/$NGINX_HTTP_CONF.XXXXXX")"
		echo "$host_conf" > "$tmp_file"
		$CHMOD_BIN 644 "$tmp_file"
		$MV_BIN -f "$tmp_file" "$PACKAGE_DIR/target/etc/$NGINX_HTTP_CONF"
		/usr/syno/bin/synow3tool --reg-nginx-confd="$PACKAGE_DIR/target/etc/$NGINX_MAIN_CONF,$PACKAGE_DIR/target/etc/$NGINX_HTTP_CONF"
		/usr/syno/bin/synow3tool --enable-nginx-confd="$NGINX_MAIN_CONF,$NGINX_HTTP_CONF"
	else
		/usr/syno/bin/synow3tool --force-unreg-nginx-confd="$NGINX_MAIN_CONF,$NGINX_HTTP_CONF"
	fi
}

InstallNginxModule()
{
	if ! ClusterIsNormal ;then
		return
	fi
	RemoveNginxModule
	/bin/cp -f /var/packages/Spreadsheet/target/lib/ngx_synoffice_load_balancer_module.so /usr/local/lib/
	$CHMOD_BIN 644 /usr/local/lib/ngx_synoffice_load_balancer_module.so
}

RemoveNginxModule()
{
	if ! ClusterIsSupport ;then
		return
	fi
	$RM_BIN -f /usr/local/lib/ngx_synoffice_load_balancer_module.so
}

CreateNginxConf()
{
	RegenNginxConf
	/usr/syno/bin/synow3tool --nginx=upgrade
	/usr/syno/bin/synosystemctl reload nginx
}

RegenNginxConf()
{
	GenNginxStaticRule
	GenNginxMustacheRule
	RegenNginxDriveRule
}

RegenNginxStaticConf()
{
	GenNginxStaticRule
	/usr/syno/bin/synosystemctl reload nginx
}

RemoveNginxMustacheConf()
{
	$RM_BIN -f "$NGINX_RULE"
}

RemoveNginxStaticRule()
{
	if ! ClusterIsSupport ;then
		return
	fi
	/usr/syno/bin/synow3tool --force-unreg-nginx-confd="$NGINX_MAIN_CONF,$NGINX_HTTP_CONF"
	/usr/syno/bin/synosystemctl reload nginx
}

RemoveNginxConf()
{
	RemoveNginxMustacheConf
	RemoveNginxStaticRule
	RegenNginxDriveRule
}

CreateSynologyDriveHookLink()
{
	${CLOUDSTAION_DRIVE_BIN} --hook_create
}

RemoveSynologyDriveHookLink()
{
	${CLOUDSTAION_DRIVE_BIN} --hook_delete
}

GetQueueDir()
{
	/bin/echo "$(GetPkgVarDir)/queue"
}

CreateAppDir()
{
	local dirs
	local queue_dir
	queue_dir=$(GetQueueDir)
	dirs="$(GetPkgEtcDir) $(GetPkgVarDir) $(GetPkgLockDir) $queue_dir $queue_dir/sheet $queue_dir/doc $queue_dir/presentation ${QUEUE_SNAPSHOT_DIR}"
	for dir in ${dirs} ;do
		[ -d "$dir" ] || $MKDIR_BIN -m 755 "$dir"
		$CHOWN_BIN office:office "$dir"
	done
}

CheckSynoDrivePath()
{
	/var/packages/Spreadsheet/target/tools/migration_tool -c
}

EnableSynoDrive()
{
	local vol
	local port=0
	if SOFSIsSupport ;then
		vol=$(/usr/syno/bin/etcdctl get /syno/live_cluster/SynologyDrive/sync-config//var/packages/SynologyDrive/etc/db-path.conf/config | /bin/grep "^db-vol=" | cut -d"=" -f2 | /bin/sed -e 's/^"//' -e 's/"$//')
		port=6432
	else
		vol=$(/usr/syno/bin/synogetkeyvalue /var/packages/SynologyDrive/etc/db-path.conf db-vol)
	fi
	${SYNODRIVE_SETTING_BIN} --access enable=1
	${SYNODRIVE_SETTING_BIN} --access enable=1 ns=office pkg_id=Spreadsheet "vol=$vol" git="{\"pack_no_delta\":{\"metatext\":{\"encrypt\":true}}}" db="{\"socket_path\":\"/run/synoffice\",\"socket_port\":$port,\"vacuum\":false}"
}

RemoveSynoDrive()
{
	${SYNODRIVE_SETTING_BIN} --remove ns=office
}

InstallSynoDriveSetting()
{
	$COMMON_BIN --setup_ns
}

CorrectRepoPerm()
{
	local path
	path=$($REPO_BIN --get)
	local owner
	owner=$($STAT_BIN -c %u "$path")
	local access
	access=$($STAT_BIN -c %a "$path")

	if [ "0" != "$owner" ]; then
		$CHOWN_BIN root "$path"
	fi

	if [ "700" != "$access" ] ; then
		$CHMOD_BIN -R 700 "$path"
	fi
}

CreateLogDir()
{
	local PKG_VAR_DIR="/var/packages/Spreadsheet/var"
	if [ ! -d "${PKG_VAR_DIR}" ];then
		echo "Failed to find ${PKG_VAR_DIR}"
		exit 1
	fi
	local PKG_LOG_DIR="${PKG_VAR_DIR}/log"
	mkdir -p "${PKG_LOG_DIR}"
	$CHOWN_BIN system:log "${PKG_LOG_DIR}"
	$CHMOD_BIN 755 "${PKG_LOG_DIR}"

	local OLD_LOG_FILE="/var/log/synoffice.log"
	if [ -f "${OLD_LOG_FILE}" ] && [ ! -f "${PKG_LOG_DIR}/synoffice.log" ];then
		$MV_BIN "/var/log/synoffice"* "${PKG_LOG_DIR}"
		local OLD_LOG_LINK="/var/log/office"
		if [ -d "$OLD_LOG_LINK" ];then
			$MV_BIN "${OLD_LOG_LINK}"/* "${PKG_LOG_DIR}"
			local OLD_LOG_DIR
			OLD_LOG_DIR=$(readlink -f "${OLD_LOG_LINK}")
			$RM_BIN -f "${OLD_LOG_LINK}"
			$RM_BIN -fd "${OLD_LOG_DIR}"
		fi
	fi
}

FlushFull()
{
	$NODE_BIN --flush_full
}

InstallSUSParser()
{
	/usr/syno/bin/synocontentextract -a install_ext -n synoffice -t meta -c /var/packages/Spreadsheet/target/parser/synoffice_parser.conf -p /var/packages/Spreadsheet/target/parser/synoffice_parser.so
}

UninstallSUSParser()
{
	/usr/syno/bin/synocontentextract -a uninstall_ext -n synoffice -t meta
}

RegisterVolume()
{
	$MKDIR_BIN -p "${PACKAGE_DIR}/etc/synopkg_conf"
	"$TOOL_PATH/synofficepkg" --update_reg_volume
}

CreateWeb()
{
	${WEB_BIN} --create
}

RegenNginxDriveRule()
{
	/usr/syno/bin/synow3tool --gen-app=SynologyDrive
}

CleanLockDir()
{
	if ! ClusterIsController ;then
		return
	fi
	local dir
	dir="$(GetPkgLockDir)"
	if [ -e "$dir" ];then
		$MV_BIN -f "$dir" "$dir".tmp
		$RM_BIN -rf "$dir".tmp
	fi
}

CreateDefaultFont()
{
	local FONT_SETTING_PATH
	FONT_SETTING_PATH="$(GetPkgEtcDir)/${FONT_SETTING}"

	if [ ! -f "$FONT_SETTING_PATH" ]; then
		jq -c . <<EOF >"$FONT_SETTING_PATH"
{
  "Andale mono": {},
  "Arial": {},
  "Arial Black": {},
  "Book Antiqua": {},
  "Calibri": {},
  "Cambria": {},
  "Comic Sans MS": {},
  "Consolas": {},
  "Courier": {},
  "Courier New": {},
  "Georgia": {},
  "Helvetica": {},
  "Helvetica     Neue": {},
  "Impact": {},
  "Lucida Console": {},
  "Lucida Sans Unicode": {},
  "Roman": {},
  "Tahoma": {},
  "Terminal": {},
  "Times New Roman": {},
  "Trebuchet MS": {},
  "Verdana": {},
  "PMingLiu": { "display": "新細明體" },
  "MingLiu": { "display": "細明體" },
  "標楷體": {},
  "Microsoft JhengHei": { "display": "微軟正黑體" },
  "Microsoft Yahei": { "display": "微软雅黑" },
  "SimSun": { "display": "宋体" },
  "NSimSun": { "display": "新宋体" },
  "KaiTi": { "display": "楷体" },
  "SimHei": { "display": "黑体" },
  "Gulim": { "display": "굴림" },
  "Gulimche": { "display": "굴림체" },
  "궁서": {},
  "궁서체": {},
  "Dotum": { "display": "돋움" },
  "Dotumche": { "display": "돋움체" },
  "Malgun Gothic": { "display": "맑은 고딕" },
  "Yu Mincho": { "display": "游明朝" },
  "Yu Gothic": { "display": "游ゴシック" },
  "YuKyokasho": { "display": "游教科書体" },
  "Meiryo": { "display": "メイリオ" },
  "Angsana New": {},
  "Cordia New": {},
  "Browallia New": {},
  "TH Sarabun New": {}
}
EOF
	else
		jq -c '
			if ."DFKai-sb" != null and ."DFKai-sb"."system" != false then ."標楷體" = ."DFKai-sb" | del (."DFKai-sb") else . end |
			if .YuKyokasho and .YuKyokasho.display == "ヒラギノ明朝体" then .YuKyokasho.display = "游教科書体" else . end |
			if .Gulim and .Gulim.display == "굴림체" then .Gulim.display = "굴림" else . end |
			if .Gulimche == null then .Gulimche = { "display": "굴림체" } else . end |
			if .Dotum and .Dotum.display == "돋움체" then .Dotum.display = "돋움" else . end |
			if .Dotumche == null then .Dotumche = { "display": "돋움체" } else . end |
			if ."궁서" == null then ."궁서" = {} else . end
		' "$FONT_SETTING_PATH" > "$FONT_SETTING_PATH.tmp"
		[ -f "$FONT_SETTING_PATH.tmp" ] && mv -f "$FONT_SETTING_PATH.tmp" "$FONT_SETTING_PATH"
	fi
	# new default font should be handle here
}

GetMemorySize()
{
	awk '/MemTotal/ { print $2 }' /proc/meminfo
}

Control()
{
	if [ "failed" == "$(/bin/systemctl is-failed "${!#}")" ];then
		/bin/systemctl reset-failed "${!#}"
	fi
	/usr/bin/time -f "%C %E" /usr/syno/bin/synosystemctl $*
}

Status()
{
	/usr/bin/systemctl status "$1" | /bin/grep running > /dev/null
	return $?
}

ControlWait() {
	if (( $# < 2 )); then
		return 2
	fi

	Control "$1" "$2"
	sleep 1

	if (( $# > 2 )); then
		Status "$2"
		ret=$?
		if (( $1 == 'stop' )); then
			ret=$(( 1 ^ ret ))
		fi
		max_count=30
		count=1
		while (( ret == 0 )); do
			result=true
			for (( idx = 3 ; idx <= $# ; ++idx )); do
				if ! ${!idx}; then
					result=false
					break;
				fi
			done
			if $result; then
				return 0
			fi
			sleep 0.5
			Status "$2"
			ret=$?
			if (( $1 == 'stop' )); then
				ret=$(( 1 ^ ret ))
			fi
			if (( count > max_count )); then
				return 1
			fi
			((count++))
		done
		return 1
	fi
	return 0
}

DriveStatus()
{
	/var/packages/SynologyDrive/target/bin/cloud-control service --status
	return $?
}

GetString()
{
	$COMMON_BIN --string session="$1" key="$2"
}

CreateSQLiteTmpDir()
{
	[ -d /var/services/tmp/synoffice_db ] || $MKDIR_BIN -m 777 /var/services/tmp/synoffice_db
}

RemoveSQLiteTmpDir()
{
	$RM_BIN -rf /var/services/tmp/synoffice_db
}

ClusterIsSupport() {
	FSDNIsSupport || SOFSIsSupport
}

ClusterIsController() {
	if FSDNIsSupport ;then
		FSDNIsController
	elif SOFSIsSupport ;then
		SOFSIsController
	else
		return 1
	fi
}

ClusterIsAgent() {
	if FSDNIsSupport ;then
		FSDNIsAgent
	elif SOFSIsSupport ;then
		SOFSIsAgent
	else
		return 1
	fi
}

ClusterIsNormal() {
	if FSDNIsSupport ;then
		return 0
	elif SOFSIsSupport ;then
		SOFSIsController || SOFSIsAgent
	else
		return 1
	fi
}

ClusterGetControllerIP() {
	if FSDNIsSupport ;then
		FSDNGetControllerIP
	elif SOFSIsSupport ;then
		SOFSGetControllerIP
	fi
}

FSDNIsSupport() {
	if [ -z "$G_FSDN_IS_SUPPORT" ];then
		if [ -f /usr/syno/sbin/syno_feature_check.sh ] && /usr/syno/sbin/syno_feature_check.sh SYNO_PRODUCT_FSDN ;then
			G_FSDN_IS_SUPPORT=0
		else
			G_FSDN_IS_SUPPORT=1
		fi
	fi
	return $G_FSDN_IS_SUPPORT
}

FSDNInitRole() {
	if [ -z "$G_FSDN_ROLE" ];then
		if /usr/syno/bin/aacore is-admin-node > /dev/null 2>&1 ;then
			G_FSDN_ROLE=0
		else
			G_FSDN_ROLE=1
		fi
	fi
}

FSDNIsController() {
	FSDNInitRole
	return $G_FSDN_ROLE
}

FSDNIsAgent() {
	FSDNInitRole
	[ "$G_FSDN_ROLE" -eq 1 ] && return 0 || return 1
}

FSDNGetControllerIP() {
	if FSDNIsController ;then
		/usr/syno/bin/aacore get-ntb-addr local
	else
		/usr/syno/bin/aacore get-ntb-addr remote
	fi
}

SOFSIsSupport() {
	if [ -z "$G_SOFS_IS_SUPPORT" ];then
		if [ "$(/bin/get_key_value /etc.defaults/synoinfo.conf support_hyper_converged)" = "yes" ];then
			G_SOFS_IS_SUPPORT=0
		else
			G_SOFS_IS_SUPPORT=1
		fi
	fi
	return $G_SOFS_IS_SUPPORT
}

SOFSInitRole() {
	if [ -z "$G_SOFS_ROLE" ];then
		G_SOFS_ROLE="$(/usr/syno/bin/hcm-sentinel-tool get-role)"
	fi
}

SOFSIsController() {
	SOFSInitRole
	if [ "$G_SOFS_ROLE" = "controller" ];then
		return 0
	fi
	return 1
}

SOFSIsAgent() {
	SOFSInitRole
	if [ "$G_SOFS_ROLE" = "agent" ];then
		return 0
	fi
	return 1
}

SOFSGetControllerIP() {
	/usr/syno/bin/hcm-sentinel-tool get-controller-floating-ip
}

IsController() {
	if ClusterIsSupport ;then
		ClusterIsController
		return $?
	fi
	return 0
}

GetPkgEtcDir() {
	if ClusterIsNormal ;then
		echo "${PACKAGE_DIR}/share/etc"
	else
		echo "${PACKAGE_DIR}/etc"
	fi
}

GetPkgVarDir() {
	if ClusterIsNormal ;then
		echo "${PACKAGE_DIR}/share/var"
	else
		echo "${PACKAGE_DIR}/var"
	fi
}

GetPkgLockDir() {
	if ClusterIsNormal ;then
		echo "${PACKAGE_DIR}/share/var/lock"
	else
		echo "${PACKAGE_DIR}/var/lock"
	fi
}

SOFSGetControllerIP() {
	/usr/syno/bin/hcm-sentinel-tool get-controller-floating-ip
}

UpdatePGBouncerConf()
{
	local host
	local port
	local listen_addr
	local listen_port
	local cpu_count
	local max_connections

	cpu_count=$(( $(/usr/bin/nproc --all) * 20 ))
	max_connections=$(( $(/bin/get_key_value /etc/postgresql/postgresql.conf max_connections) / 5 ))
	max_connections=$(( max_connections > cpu_count ? cpu_count : max_connections ))

	host="\/var\/run\/postgresql"
	port="5432"
	listen_port=5432
	if SOFSIsSupport ;then
		local controller_ip
		controller_ip="$(ClusterGetControllerIP)"
		listen_port=6432
		if ClusterIsController ;then
			listen_addr="$controller_ip"
		elif ClusterIsAgent ;then
			host="$controller_ip"
			port="6432"
		fi
	elif FSDNIsSupport ;then
		if ClusterIsAgent ;then
			host="$(ClusterGetControllerIP)"
		fi
	fi

	/bin/sed -e "s/^synoffice =.*/synoffice = host=$host port=$port user=office/" \
			-e "s/^synodrive =.*/synodrive = host=$host port=$port user=office/" \
			-e "s/^default_pool_size =.*/default_pool_size = $max_connections/" \
			-e "s/^max_db_connections =.*/max_db_connections = $max_connections/" \
			-e "s/^#listen_addr =/listen_addr =/" \
			-e "s/^#listen_port =/listen_port =/" \
			-e "/^listen_addr =/ {s/.*/listen_addr = $listen_addr/;}" \
			-e "/^listen_port =/ {s/.*/listen_port = $listen_port/;}" \
			-e "/^listen_addr = $/s/.*/#listen_addr =/" \
			-e "/^listen_port = 5432$/s/.*/#listen_port =/" \
			/var/packages/Spreadsheet/target/etc/pgbouncer.ini.template > /var/packages/Spreadsheet/target/etc/pgbouncer.ini
}

StartClusterDaemon()
{
	if ! ClusterIsNormal ;then
		return
	fi
	Control start pkg-synoffice-clusterd
}

StopClusterDaemon()
{
	if ! ClusterIsSupport ;then
		return
	fi
	Control stop pkg-synoffice-clusterd
}