From bf45bca0a98ad1cf0a0cc415f32b50ce0b7d8a9b Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Mon, 11 May 2020 23:43:39 +0200 Subject: [PATCH] committing changes in /etc after apt run Package changes: +ctdb 2:4.7.6+dfsg~ubuntu-0ubuntu2.16 amd64 +libcephfs2 12.2.12-0ubuntu0.18.04.5 amd64 +librpm8 4.14.1+dfsg1-2 amd64 +librpmio8 4.14.1+dfsg1-2 amd64 +python-dnspython 1.15.0-1 all +qasmixer 0.21.0-1.1 amd64 +qastools-common 0.21.0-1.1 all +rpm-common 4.14.1+dfsg1-2 amd64 +rpm-i18n 4.14.1+dfsg1-2 all +rpm2cpio 4.14.1+dfsg1-2 amd64 +samba 2:4.7.6+dfsg~ubuntu-0ubuntu2.16 amd64 +samba-dsdb-modules 2:4.7.6+dfsg~ubuntu-0ubuntu2.16 amd64 +samba-vfs-modules 2:4.7.6+dfsg~ubuntu-0ubuntu2.16 amd64 +tdb-tools 1.3.15-2 amd64 +winbind 2:4.7.6+dfsg~ubuntu-0ubuntu2.16 amd64 --- .etckeeper | 53 + alternatives/tdbbackup | 1 + alternatives/tdbbackup.8.gz | 1 + cron.daily/samba | 16 + ctdb/ctdb-crash-cleanup.sh | 29 + ctdb/ctdbd.conf | 32 + ctdb/debug-hung-script.sh | 59 + ctdb/debug_locks.sh | 87 ++ ctdb/events.d/00.ctdb | 151 +++ ctdb/events.d/01.reclock | 54 + ctdb/events.d/05.system | 175 +++ ctdb/events.d/06.nfs | 40 + ctdb/events.d/10.external | 51 + ctdb/events.d/10.interface | 262 +++++ ctdb/events.d/11.natgw | 246 ++++ ctdb/events.d/11.routing | 49 + ctdb/events.d/13.per_ip_routing | 442 +++++++ ctdb/events.d/20.multipathd | 83 ++ ctdb/events.d/31.clamd | 45 + ctdb/events.d/40.vsftpd | 56 + ctdb/events.d/41.httpd | 84 ++ ctdb/events.d/49.winbind | 59 + ctdb/events.d/50.samba | 188 +++ ctdb/events.d/60.nfs | 300 +++++ ctdb/events.d/70.iscsi | 89 ++ ctdb/events.d/91.lvs | 128 +++ ctdb/events.d/99.timeout | 25 + ctdb/events.d/README | 193 ++++ ctdb/functions | 1020 +++++++++++++++++ ctdb/gcore_trace.sh | 3 + ctdb/nfs-checks.d/00.portmapper.check | 2 + ctdb/nfs-checks.d/10.status.check | 7 + ctdb/nfs-checks.d/20.nfs.check | 7 + ctdb/nfs-checks.d/30.nlockmgr.check | 6 + ctdb/nfs-checks.d/40.mountd.check | 7 + ctdb/nfs-checks.d/50.rquotad.check | 7 + ctdb/nfs-checks.d/README | 31 + ctdb/nfs-linux-kernel-callout | 253 ++++ ctdb/notify.d/README | 44 + ctdb/notify.sh | 27 + ctdb/statd-callout | 221 ++++ default/winbind | 11 + group | 1 + group- | 1 + gshadow | 1 + gshadow- | 1 + init.d/ctdb | 175 +++ init.d/nmbd | 84 ++ init.d/samba-ad-dc | 93 ++ init.d/smbd | 84 ++ init.d/winbind | 62 + logrotate.d/ctdb | 9 + logrotate.d/samba | 40 + logrotate.d/winbind | 15 + rc0.d/K01ctdb | 1 + rc0.d/K01nmbd | 1 + rc0.d/K01samba-ad-dc | 1 + rc0.d/K01smbd | 1 + rc0.d/K01winbind | 1 + rc1.d/K01ctdb | 1 + rc1.d/K01nmbd | 1 + rc1.d/K01samba-ad-dc | 1 + rc1.d/K01smbd | 1 + rc1.d/K01winbind | 1 + rc2.d/S01ctdb | 1 + rc2.d/S01nmbd | 1 + rc2.d/S01samba-ad-dc | 1 + rc2.d/S01smbd | 1 + rc2.d/S01winbind | 1 + rc3.d/S01ctdb | 1 + rc3.d/S01nmbd | 1 + rc3.d/S01samba-ad-dc | 1 + rc3.d/S01smbd | 1 + rc3.d/S01winbind | 1 + rc4.d/S01ctdb | 1 + rc4.d/S01nmbd | 1 + rc4.d/S01samba-ad-dc | 1 + rc4.d/S01smbd | 1 + rc4.d/S01winbind | 1 + rc5.d/S01ctdb | 1 + rc5.d/S01nmbd | 1 + rc5.d/S01samba-ad-dc | 1 + rc5.d/S01smbd | 1 + rc5.d/S01winbind | 1 + rc6.d/K01ctdb | 1 + rc6.d/K01nmbd | 1 + rc6.d/K01samba-ad-dc | 1 + rc6.d/K01smbd | 1 + rc6.d/K01winbind | 1 + sudoers.d/ctdb | 3 + .../multi-user.target.wants/ctdb.service | 1 + .../multi-user.target.wants/nmbd.service | 1 + .../multi-user.target.wants/smbd.service | 1 + .../multi-user.target.wants/winbind.service | 1 + systemd/system/samba-ad-dc.service | 1 + ufw/applications.d/samba | 4 + 96 files changed, 5257 insertions(+) create mode 120000 alternatives/tdbbackup create mode 120000 alternatives/tdbbackup.8.gz create mode 100755 cron.daily/samba create mode 100755 ctdb/ctdb-crash-cleanup.sh create mode 100644 ctdb/ctdbd.conf create mode 100755 ctdb/debug-hung-script.sh create mode 100755 ctdb/debug_locks.sh create mode 100755 ctdb/events.d/00.ctdb create mode 100755 ctdb/events.d/01.reclock create mode 100755 ctdb/events.d/05.system create mode 100755 ctdb/events.d/06.nfs create mode 100644 ctdb/events.d/10.external create mode 100755 ctdb/events.d/10.interface create mode 100755 ctdb/events.d/11.natgw create mode 100755 ctdb/events.d/11.routing create mode 100755 ctdb/events.d/13.per_ip_routing create mode 100755 ctdb/events.d/20.multipathd create mode 100755 ctdb/events.d/31.clamd create mode 100755 ctdb/events.d/40.vsftpd create mode 100755 ctdb/events.d/41.httpd create mode 100755 ctdb/events.d/49.winbind create mode 100755 ctdb/events.d/50.samba create mode 100755 ctdb/events.d/60.nfs create mode 100755 ctdb/events.d/70.iscsi create mode 100755 ctdb/events.d/91.lvs create mode 100755 ctdb/events.d/99.timeout create mode 100644 ctdb/events.d/README create mode 100644 ctdb/functions create mode 100755 ctdb/gcore_trace.sh create mode 100644 ctdb/nfs-checks.d/00.portmapper.check create mode 100644 ctdb/nfs-checks.d/10.status.check create mode 100644 ctdb/nfs-checks.d/20.nfs.check create mode 100644 ctdb/nfs-checks.d/30.nlockmgr.check create mode 100644 ctdb/nfs-checks.d/40.mountd.check create mode 100644 ctdb/nfs-checks.d/50.rquotad.check create mode 100644 ctdb/nfs-checks.d/README create mode 100755 ctdb/nfs-linux-kernel-callout create mode 100644 ctdb/notify.d/README create mode 100755 ctdb/notify.sh create mode 100755 ctdb/statd-callout create mode 100644 default/winbind create mode 100755 init.d/ctdb create mode 100755 init.d/nmbd create mode 100755 init.d/samba-ad-dc create mode 100755 init.d/smbd create mode 100755 init.d/winbind create mode 100644 logrotate.d/ctdb create mode 100644 logrotate.d/samba create mode 100644 logrotate.d/winbind create mode 120000 rc0.d/K01ctdb create mode 120000 rc0.d/K01nmbd create mode 120000 rc0.d/K01samba-ad-dc create mode 120000 rc0.d/K01smbd create mode 120000 rc0.d/K01winbind create mode 120000 rc1.d/K01ctdb create mode 120000 rc1.d/K01nmbd create mode 120000 rc1.d/K01samba-ad-dc create mode 120000 rc1.d/K01smbd create mode 120000 rc1.d/K01winbind create mode 120000 rc2.d/S01ctdb create mode 120000 rc2.d/S01nmbd create mode 120000 rc2.d/S01samba-ad-dc create mode 120000 rc2.d/S01smbd create mode 120000 rc2.d/S01winbind create mode 120000 rc3.d/S01ctdb create mode 120000 rc3.d/S01nmbd create mode 120000 rc3.d/S01samba-ad-dc create mode 120000 rc3.d/S01smbd create mode 120000 rc3.d/S01winbind create mode 120000 rc4.d/S01ctdb create mode 120000 rc4.d/S01nmbd create mode 120000 rc4.d/S01samba-ad-dc create mode 120000 rc4.d/S01smbd create mode 120000 rc4.d/S01winbind create mode 120000 rc5.d/S01ctdb create mode 120000 rc5.d/S01nmbd create mode 120000 rc5.d/S01samba-ad-dc create mode 120000 rc5.d/S01smbd create mode 120000 rc5.d/S01winbind create mode 120000 rc6.d/K01ctdb create mode 120000 rc6.d/K01nmbd create mode 120000 rc6.d/K01samba-ad-dc create mode 120000 rc6.d/K01smbd create mode 120000 rc6.d/K01winbind create mode 100644 sudoers.d/ctdb create mode 120000 systemd/system/multi-user.target.wants/ctdb.service create mode 120000 systemd/system/multi-user.target.wants/nmbd.service create mode 120000 systemd/system/multi-user.target.wants/smbd.service create mode 120000 systemd/system/multi-user.target.wants/winbind.service create mode 120000 systemd/system/samba-ad-dc.service create mode 100644 ufw/applications.d/samba diff --git a/.etckeeper b/.etckeeper index a1f89af..fd9d577 100755 --- a/.etckeeper +++ b/.etckeeper @@ -1113,6 +1113,7 @@ maybe chmod 0755 'cron.daily/man-db' maybe chmod 0755 'cron.daily/mdadm' maybe chmod 0755 'cron.daily/mlocate' maybe chmod 0755 'cron.daily/passwd' +maybe chmod 0755 'cron.daily/samba' maybe chmod 0755 'cron.daily/ubuntu-advantage-tools' maybe chmod 0755 'cron.daily/update-notifier-common' maybe chmod 0755 'cron.hourly' @@ -1131,6 +1132,47 @@ maybe chmod 0755 'cruft/filters-unex' maybe chmod 0644 'cruft/filters-unex/etckeeper' maybe chmod 0755 'cryptsetup-initramfs' maybe chmod 0644 'cryptsetup-initramfs/conf-hook' +maybe chmod 0755 'ctdb' +maybe chmod 0755 'ctdb/ctdb-crash-cleanup.sh' +maybe chmod 0644 'ctdb/ctdbd.conf' +maybe chmod 0755 'ctdb/debug-hung-script.sh' +maybe chmod 0755 'ctdb/debug_locks.sh' +maybe chmod 0755 'ctdb/events.d' +maybe chmod 0755 'ctdb/events.d/00.ctdb' +maybe chmod 0755 'ctdb/events.d/01.reclock' +maybe chmod 0755 'ctdb/events.d/05.system' +maybe chmod 0755 'ctdb/events.d/06.nfs' +maybe chmod 0644 'ctdb/events.d/10.external' +maybe chmod 0755 'ctdb/events.d/10.interface' +maybe chmod 0755 'ctdb/events.d/11.natgw' +maybe chmod 0755 'ctdb/events.d/11.routing' +maybe chmod 0755 'ctdb/events.d/13.per_ip_routing' +maybe chmod 0755 'ctdb/events.d/20.multipathd' +maybe chmod 0755 'ctdb/events.d/31.clamd' +maybe chmod 0755 'ctdb/events.d/40.vsftpd' +maybe chmod 0755 'ctdb/events.d/41.httpd' +maybe chmod 0755 'ctdb/events.d/49.winbind' +maybe chmod 0755 'ctdb/events.d/50.samba' +maybe chmod 0755 'ctdb/events.d/60.nfs' +maybe chmod 0755 'ctdb/events.d/70.iscsi' +maybe chmod 0755 'ctdb/events.d/91.lvs' +maybe chmod 0755 'ctdb/events.d/99.timeout' +maybe chmod 0644 'ctdb/events.d/README' +maybe chmod 0644 'ctdb/functions' +maybe chmod 0755 'ctdb/gcore_trace.sh' +maybe chmod 0755 'ctdb/nfs-checks.d' +maybe chmod 0644 'ctdb/nfs-checks.d/00.portmapper.check' +maybe chmod 0644 'ctdb/nfs-checks.d/10.status.check' +maybe chmod 0644 'ctdb/nfs-checks.d/20.nfs.check' +maybe chmod 0644 'ctdb/nfs-checks.d/30.nlockmgr.check' +maybe chmod 0644 'ctdb/nfs-checks.d/40.mountd.check' +maybe chmod 0644 'ctdb/nfs-checks.d/50.rquotad.check' +maybe chmod 0644 'ctdb/nfs-checks.d/README' +maybe chmod 0755 'ctdb/nfs-linux-kernel-callout' +maybe chmod 0755 'ctdb/notify.d' +maybe chmod 0644 'ctdb/notify.d/README' +maybe chmod 0755 'ctdb/notify.sh' +maybe chmod 0755 'ctdb/statd-callout' maybe chgrp 'lp' 'cups' maybe chmod 0755 'cups' maybe chmod 0644 'cups/cups-browsed.conf' @@ -1256,6 +1298,7 @@ maybe chmod 0644 'default/ufw' maybe chmod 0644 'default/useradd' maybe chmod 0644 'default/virtlockd' maybe chmod 0644 'default/virtlogd' +maybe chmod 0644 'default/winbind' maybe chmod 0644 'deluser.conf' maybe chmod 0755 'depmod.d' maybe chmod 0644 'depmod.d/ubuntu.conf' @@ -1873,6 +1916,7 @@ maybe chmod 0755 'init.d/console-setup.sh' maybe chmod 0755 'init.d/cron' maybe chmod 0755 'init.d/cryptdisks' maybe chmod 0755 'init.d/cryptdisks-early' +maybe chmod 0755 'init.d/ctdb' maybe chmod 0755 'init.d/cups' maybe chmod 0755 'init.d/cups-browsed' maybe chmod 0755 'init.d/dbus' @@ -1900,6 +1944,7 @@ maybe chmod 0755 'init.d/mysql' maybe chmod 0755 'init.d/netfilter-persistent' maybe chmod 0755 'init.d/network-manager' maybe chmod 0755 'init.d/networking' +maybe chmod 0755 'init.d/nmbd' maybe chmod 0755 'init.d/openvpn' maybe chmod 0755 'init.d/plymouth' maybe chmod 0755 'init.d/plymouth-log' @@ -1908,15 +1953,18 @@ maybe chmod 0755 'init.d/pppd-dns' maybe chmod 0755 'init.d/procps' maybe chmod 0755 'init.d/rsync' maybe chmod 0755 'init.d/rsyslog' +maybe chmod 0755 'init.d/samba-ad-dc' maybe chmod 0755 'init.d/saned' maybe chmod 0755 'init.d/screen-cleanup' maybe chmod 0755 'init.d/smartmontools' +maybe chmod 0755 'init.d/smbd' maybe chmod 0755 'init.d/speech-dispatcher' maybe chmod 0755 'init.d/ssh' maybe chmod 0755 'init.d/udev' maybe chmod 0755 'init.d/ufw' maybe chmod 0755 'init.d/uuidd' maybe chmod 0755 'init.d/virtlogd' +maybe chmod 0755 'init.d/winbind' maybe chmod 0755 'init.d/x11-common' maybe chmod 0644 'init/anacron.conf' maybe chmod 0644 'init/lightdm.conf' @@ -2145,6 +2193,7 @@ maybe chmod 0644 'logrotate.d/apt' maybe chmod 0644 'logrotate.d/aptitude' maybe chmod 0644 'logrotate.d/btmp' maybe chmod 0644 'logrotate.d/chrony' +maybe chmod 0644 'logrotate.d/ctdb' maybe chmod 0644 'logrotate.d/cups-daemon' maybe chmod 0644 'logrotate.d/dpkg' maybe chmod 0644 'logrotate.d/libvirtd' @@ -2158,8 +2207,10 @@ maybe chmod 0644 'logrotate.d/mysql-server' maybe chmod 0644 'logrotate.d/pm-utils' maybe chmod 0644 'logrotate.d/ppp' maybe chmod 0644 'logrotate.d/rsyslog' +maybe chmod 0644 'logrotate.d/samba' maybe chmod 0644 'logrotate.d/speech-dispatcher' maybe chmod 0644 'logrotate.d/ufw' +maybe chmod 0644 'logrotate.d/winbind' maybe chmod 0644 'logrotate.d/wtmp' maybe chmod 0644 'lsb-release' maybe chmod 0644 'ltrace.conf' @@ -2732,6 +2783,7 @@ maybe chmod 0440 'sudoers' maybe chmod 0755 'sudoers.d' maybe chmod 0440 'sudoers.d/0pwfeedback' maybe chmod 0440 'sudoers.d/README' +maybe chmod 0440 'sudoers.d/ctdb' maybe chmod 0440 'sudoers.d/mintupdate' maybe chmod 0644 'sysctl.conf' maybe chmod 0755 'sysctl.d' @@ -2810,6 +2862,7 @@ maybe chmod 0644 'ufw/applications.d/bind9' maybe chmod 0644 'ufw/applications.d/cups' maybe chmod 0644 'ufw/applications.d/openssh-server' maybe chmod 0644 'ufw/applications.d/postfix' +maybe chmod 0644 'ufw/applications.d/samba' maybe chmod 0640 'ufw/before.init' maybe chmod 0640 'ufw/before.rules' maybe chmod 0640 'ufw/before6.rules' diff --git a/alternatives/tdbbackup b/alternatives/tdbbackup new file mode 120000 index 0000000..3edf584 --- /dev/null +++ b/alternatives/tdbbackup @@ -0,0 +1 @@ +/usr/bin/tdbbackup.tdbtools \ No newline at end of file diff --git a/alternatives/tdbbackup.8.gz b/alternatives/tdbbackup.8.gz new file mode 120000 index 0000000..c910beb --- /dev/null +++ b/alternatives/tdbbackup.8.gz @@ -0,0 +1 @@ +/usr/share/man/man8/tdbbackup.tdbtools.8.gz \ No newline at end of file diff --git a/cron.daily/samba b/cron.daily/samba new file mode 100755 index 0000000..42fc98d --- /dev/null +++ b/cron.daily/samba @@ -0,0 +1,16 @@ +#!/bin/sh +# +# cron script to save a backup copy of /etc/samba/smbpasswd in /var/backups. +# +# Written by Eloy A. Paris for the Debian project. +# + +BAK=/var/backups + +umask 022 +if cd $BAK; then + # Make sure /etc/samba/smbpasswd exists + if [ -f /etc/samba/smbpasswd ]; then + cmp -s smbpasswd.bak /etc/samba/smbpasswd || cp -p /etc/samba/smbpasswd smbpasswd.bak + fi +fi diff --git a/ctdb/ctdb-crash-cleanup.sh b/ctdb/ctdb-crash-cleanup.sh new file mode 100755 index 0000000..22b0ce8 --- /dev/null +++ b/ctdb/ctdb-crash-cleanup.sh @@ -0,0 +1,29 @@ +#!/bin/sh +# +# This script can be called from a cronjob to automatically drop/release +# all public ip addresses if CTDBD has crashed or stopped running. +# + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; echo "$PWD") + +. "${CTDB_BASE}/functions" + +# If ctdb is running, just exit +if service ctdb status >/dev/null 2>&1 ; then + exit 0 +fi + +loadconfig ctdb + +[ -n "$CTDB_PUBLIC_ADDRESSES" ] || \ + CTDB_PUBLIC_ADDRESSES="$CTDB_BASE/public_addresses" + +[ -f "$CTDB_PUBLIC_ADDRESSES" ] || \ + die "No public addresses file found. Can't clean up." + +drop_all_public_ips 2>&1 | script_log "ctdb-crash-cleanup.sh" + +if [ -n "$CTDB_NATGW_PUBLIC_IP" ] ; then + drop_ip "$CTDB_NATGW_PUBLIC_IP" "ctdb-crash-cleanup.sh" +fi diff --git a/ctdb/ctdbd.conf b/ctdb/ctdbd.conf new file mode 100644 index 0000000..2d525c5 --- /dev/null +++ b/ctdb/ctdbd.conf @@ -0,0 +1,32 @@ +# Options to ctdbd, read by ctdbd_wrapper(1) +# +# See ctdbd.conf(5) for more information about CTDB configuration variables. + +# Shared recovery lock file to avoid split brain. No default. +# +# Do NOT run CTDB without a recovery lock file unless you know exactly +# what you are doing. +# CTDB_RECOVERY_LOCK=/some/place/on/shared/storage + +# List of nodes in the cluster. Default is below. +# CTDB_NODES=/etc/ctdb/nodes + +# List of public addresses for providing NAS services. No default. +# CTDB_PUBLIC_ADDRESSES=/etc/ctdb/public_addresses + +# What services should CTDB manage? Default is none. +# CTDB_MANAGES_SAMBA=yes +# CTDB_MANAGES_WINBIND=yes +# CTDB_MANAGES_NFS=yes + +# Raise the file descriptor limit for CTDB? +# CTDB_MAX_OPEN_FILES=10000 + +# Default is to use the log file below instead of syslog. +# CTDB_LOGGING=file:/var/log/log.ctdb + +# Default log level is NOTICE. Want less logging? +# CTDB_DEBUGLEVEL=ERR + +# Set some CTDB tunable variables during CTDB startup? +# CTDB_SET_TDBMutexEnabled=0 diff --git a/ctdb/debug-hung-script.sh b/ctdb/debug-hung-script.sh new file mode 100755 index 0000000..da988c2 --- /dev/null +++ b/ctdb/debug-hung-script.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +# This script only works on Linux. Please modify (and submit patches) +# for other operating systems. + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; echo "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig ctdb + +# Testing hook +if [ -n "$CTDB_DEBUG_HUNG_SCRIPT_LOGFILE" ] ; then + tmp="${CTDB_DEBUG_HUNG_SCRIPT_LOGFILE}.part" + exec >>"$tmp" 2>&1 +fi + +( + # No use running several of these in parallel if, say, "releaseip" + # event hangs for multiple IPs. In that case the output would be + # interleaved in the log and would just be confusing. + flock --wait 2 9 || exit 1 + + echo "===== Start of hung script debug for PID=\"$1\", event=\"$2\" =====" + + echo "pstree -p -a ${1}:" + out=$(pstree -p -a "$1") + echo "$out" + + # Check for processes matching a regular expression and print + # stack staces. This could help confirm that certain processes + # are stuck in certain places such as the cluster filesystem. The + # regexp must separate items with "|" and must not contain + # parentheses. The default pattern can be replaced for testing. + default_pat='exportfs|rpcinfo' + pat="${CTDB_DEBUG_HUNG_SCRIPT_STACKPAT:-${default_pat}}" + echo "$out" | + sed -r -n "s@.*-(.*(${pat}).*),([0-9]*).*@\3 \1@p" | + while read pid name ; do + trace=$(cat "/proc/${pid}/stack" 2>/dev/null) + if [ $? -eq 0 ] ; then + echo "---- Stack trace of interesting process ${pid}[${name}] ----" + echo "$trace" + fi + done + + if [ "$2" != "init" ] ; then + echo "---- ctdb scriptstatus ${2}: ----" + $CTDB scriptstatus "$2" + fi + + echo "===== End of hung script debug for PID=\"$1\", event=\"$2\" =====" + + if [ -n "$CTDB_DEBUG_HUNG_SCRIPT_LOGFILE" ] ; then + mv "$tmp" "$CTDB_DEBUG_HUNG_SCRIPT_LOGFILE" + fi + +) 9>"${CTDB_SCRIPT_VARDIR}/debug-hung-script.lock" diff --git a/ctdb/debug_locks.sh b/ctdb/debug_locks.sh new file mode 100755 index 0000000..f678724 --- /dev/null +++ b/ctdb/debug_locks.sh @@ -0,0 +1,87 @@ +#!/bin/sh + +# This script parses /proc/locks and finds the processes that are holding +# locks on CTDB databases. For all those processes the script dumps a +# stack trace. +# +# This script can be used only if Samba is configured to use fcntl locks +# rather than mutex locks. + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; echo "$PWD") + +. "${CTDB_BASE}/functions" + +# Default fallback location for database directories. +# These can be overwritten from CTDB configuration +CTDB_DBDIR="${CTDB_VARDIR}" +CTDB_DBDIR_PERSISTENT="${CTDB_VARDIR}/persistent" + +loadconfig ctdb + +( + flock -n 9 || exit 1 + + echo "===== Start of debug locks PID=$$ =====" + + # Create sed expression to convert inodes to names. + # Filenames don't contain dashes and we want basenames + # shellcheck disable=SC2035 + sed_cmd=$(cd "$CTDB_DBDIR" && + stat -c "s#[0-9a-f]*:[0-9a-f]*:%i #%n #" *.tdb.* 2>/dev/null ; + cd "$CTDB_DBDIR_PERSISTENT" && + stat -c "s#[0-9a-f]*:[0-9a-f]*:%i #%n #" *.tdb.* 2>/dev/null) + + # Parse /proc/locks and extract following information + # pid process_name tdb_name offsets [W] + out=$( grep -F "POSIX ADVISORY WRITE" /proc/locks | + awk '{ if($2 == "->") { print $6, $7, $8, $9, "W" } else { print $5, $6, $7, $8 } }' | + while read pid rest ; do + pname=$(readlink "/proc/${pid}/exe") + echo "$pid $pname $rest" + done | sed -e "$sed_cmd" | grep "\.tdb" ) + + if [ -n "$out" ]; then + # Log information about locks + echo "$out" + + # Find processes that are waiting for locks + dbs=$(echo "$out" | grep "W$" | awk '{print $3}') + all_pids="" + for db in $dbs ; do + pids=$(echo "$out" | grep -v "W$" | grep "$db" | grep -v ctdbd | awk '{print $1}') + all_pids="$all_pids $pids" + done + # Use word splitting to squash whitespace + # shellcheck disable=SC2086 + pids=$(echo $all_pids | tr " " "\n" | sort -u) + + # For each process waiting, log stack trace + for pid in $pids ; do + echo "----- Stack trace for PID=$pid -----" + # x is intentionally ignored + # shellcheck disable=SC2034 + read x x state x <"/proc/${pid}/stat" + if [ "$state" = "D" ] ; then + # Don't run gstack on a process in D state since + # gstack will hang until the process exits D state. + # Although it is possible for a process to transition + # to D state after this check, it is unlikely because + # if a process is stuck in D state then it is probably + # the reason why this script was called. Note that a + # kernel stack almost certainly won't help diagnose a + # deadlock... but it will probably give us someone to + # blame! + echo "----- Process in D state, printing kernel stack only" + cat "/proc/${pid}/stack" + else + gstack "$pid" + # gcore -o /var/log/core-deadlock-ctdb $pid + fi + done + fi + + echo "===== End of debug locks PID=$$ =====" +)9>"${CTDB_SCRIPT_VARDIR}/debug_locks.lock" | script_log "ctdbd-lock" + +exit 0 diff --git a/ctdb/events.d/00.ctdb b/ctdb/events.d/00.ctdb new file mode 100755 index 0000000..d60b9aa --- /dev/null +++ b/ctdb/events.d/00.ctdb @@ -0,0 +1,151 @@ +#!/bin/sh + +# Event script for ctdb-specific setup and other things that don't fit +# elsewhere. + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig + +############################################################ + +# type is commonly supported and more portable than which(1) +# shellcheck disable=SC2039 +select_tdb_checker () +{ + # Find the best TDB consistency check available. + use_tdb_tool_check=false + type tdbtool >/dev/null 2>&1 && found_tdbtool=true + type tdbdump >/dev/null 2>&1 && found_tdbdump=true + + if $found_tdbtool && echo "help" | tdbtool | grep -q check ; then + use_tdb_tool_check=true + elif $found_tdbtool && $found_tdbdump ; then + cat </dev/null | + grep -q "Database integrity is OK" ; then + return 0 + else + return 1 + fi + else + timeout 10 tdbdump "$_db" >/dev/null 2>/dev/null + return $? + fi +} + +check_persistent_databases () +{ + _dir="${CTDB_DBDIR_PERSISTENT:-${CTDB_DBDIR:-${CTDB_VARDIR}}/persistent}" + [ -d "$_dir" ] || return 0 + + [ "${CTDB_MAX_PERSISTENT_CHECK_ERRORS:-0}" = "0" ] || return 0 + + for _db in "$_dir/"*.tdb.*[0-9] ; do + [ -r "$_db" ] || continue + check_tdb "$_db" || \ + die "Persistent database $_db is corrupted! CTDB will not start." + done +} + +check_non_persistent_databases () +{ + _dir="${CTDB_DBDIR:-${CTDB_VARDIR}}" + [ -d "$_dir" ] || return 0 + + for _db in "${_dir}/"*.tdb.*[0-9] ; do + [ -r "$_db" ] || continue + check_tdb "$_db" || { + _backup="${_db}.$(date +'%Y%m%d.%H%M%S.%N').corrupt" + cat </dev/null 2>&1 & + + ctdb_counter_incr + num_fails=$(ctdb_counter_get) + if [ "$num_fails" -ge 200 ] ; then + echo "Reclock file \"$CTDB_RECOVERY_LOCK\" can not be accessed. Shutting down." + df + sleep 1 + $CTDB shutdown + exit 1 + elif [ "$num_fails" -ge 4 ] ; then + die "ERROR: ${num_fails} consecutive failures checking reclock" + fi + ;; +esac + +exit 0 diff --git a/ctdb/events.d/05.system b/ctdb/events.d/05.system new file mode 100755 index 0000000..3931eac --- /dev/null +++ b/ctdb/events.d/05.system @@ -0,0 +1,175 @@ +#!/bin/sh +# ctdb event script for checking local file system utilization + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig + +service_state_dir=$(ctdb_setup_service_state_dir "system-monitoring") || exit $? + +validate_percentage () +{ + case "$1" in + "") return 1 ;; # A failure that doesn't need a warning + [0-9]|[0-9][0-9]|100) return 0 ;; + *) echo "WARNING: ${1} is an invalid percentage${2:+ in \"}${2}${2:+\"} check" + return 1 + esac +} + +check_thresholds () +{ + _thing="$1" + _thresholds="$2" + _usage="$3" + _unhealthy_callout="$4" + + case "$_thresholds" in + *:*) + _warn_threshold="${_thresholds%:*}" + _unhealthy_threshold="${_thresholds#*:}" + ;; + *) + _warn_threshold="$_thresholds" + _unhealthy_threshold="" + esac + + _t=$(echo "$_thing" | sed -e 's@/@SLASH_@g' -e 's@ @_@g') + _cache="${service_state_dir}/cache_${_t}" + if validate_percentage "$_unhealthy_threshold" "$_thing" ; then + if [ "$_usage" -ge "$_unhealthy_threshold" ] ; then + echo "ERROR: ${_thing} utilization ${_usage}% >= threshold ${_unhealthy_threshold}%" + eval "$_unhealthy_callout" + echo "$_usage" >"$_cache" + exit 1 + fi + fi + + if validate_percentage "$_warn_threshold" "$_thing" ; then + if [ "$_usage" -ge "$_warn_threshold" ] ; then + if [ -r "$_cache" ] ; then + read _prev <"$_cache" + else + _prev="" + fi + if [ "$_usage" != "$_prev" ] ; then + echo "WARNING: ${_thing} utilization ${_usage}% >= threshold ${_warn_threshold}%" + echo "$_usage" >"$_cache" + fi + else + if [ -r "$_cache" ] ; then + echo "NOTICE: ${_thing} utilization ${_usage}% < threshold ${_warn_threshold}%" + fi + rm -f "$_cache" + fi + fi +} + +set_monitor_filsystem_usage_defaults () +{ + _fs_defaults_cache="${service_state_dir}/cache_monitor_filsystem_usage_defaults" + + if [ ! -r "$_fs_defaults_cache" ] ; then + # Determine filesystem for each database directory, generate + # an entry to warn at 90%, de-duplicate entries, put all items + # on 1 line (so the read below gets everything) + for _t in "${CTDB_DBDIR:-${CTDB_VARDIR}}" \ + "${CTDB_DBDIR_PERSISTENT:-${CTDB_VARDIR}/persistent}" \ + "${CTDB_DBDIR_STATE:-${CTDB_VARDIR}/state}" ; do + df -kP "$_t" | awk 'NR == 2 { printf "%s:90\n", $6 }' + done | sort -u | xargs >"$_fs_defaults_cache" + fi + + read CTDB_MONITOR_FILESYSTEM_USAGE <"$_fs_defaults_cache" +} + +monitor_filesystem_usage () +{ + if [ -z "$CTDB_MONITOR_FILESYSTEM_USAGE" ] ; then + set_monitor_filsystem_usage_defaults + fi + + # Check each specified filesystem, specified in format + # :[:fs_unhealthy_threshold] + for _fs in $CTDB_MONITOR_FILESYSTEM_USAGE ; do + _fs_mount="${_fs%%:*}" + _fs_thresholds="${_fs#*:}" + + if [ ! -d "$_fs_mount" ]; then + echo "WARNING: Directory ${_fs_mount} does not exist" + continue + fi + + # Get current utilization + _fs_usage=$(df -kP "$_fs_mount" | \ + sed -n -e 's@.*[[:space:]]\([[:digit:]]*\)%.*@\1@p') + if [ -z "$_fs_usage" ] ; then + echo "WARNING: Unable to get FS utilization for ${_fs_mount}" + continue + fi + + check_thresholds "Filesystem ${_fs_mount}" \ + "$_fs_thresholds" \ + "$_fs_usage" + done +} + +dump_memory_info () +{ + get_proc "meminfo" + ps auxfww + set_proc "sysrq-trigger" "m" +} + +monitor_memory_usage () +{ + # Defaults + if [ -z "$CTDB_MONITOR_MEMORY_USAGE" ] ; then + CTDB_MONITOR_MEMORY_USAGE=80 + fi + if [ -z "$CTDB_MONITOR_SWAP_USAGE" ] ; then + CTDB_MONITOR_SWAP_USAGE=25 + fi + + _meminfo=$(get_proc "meminfo") + # Intentional word splitting here + # shellcheck disable=SC2046 + set -- $(echo "$_meminfo" | awk ' +$1 == "MemAvailable:" { memavail += $2 } +$1 == "MemFree:" { memfree += $2 } +$1 == "Cached:" { memfree += $2 } +$1 == "Buffers:" { memfree += $2 } +$1 == "MemTotal:" { memtotal = $2 } +$1 == "SwapFree:" { swapfree = $2 } +$1 == "SwapTotal:" { swaptotal = $2 } +END { + if (memavail != 0) { memfree = memavail ; } + if (memtotal != 0) { print int((memtotal - memfree) / memtotal * 100) ; } else { print 0 ; } + if (swaptotal != 0) { print int((swaptotal - swapfree) / swaptotal * 100) ; } else { print 0 ; } +}') + _mem_usage="$1" + _swap_usage="$2" + + check_thresholds "System memory" \ + "$CTDB_MONITOR_MEMORY_USAGE" \ + "$_mem_usage" \ + dump_memory_info + + check_thresholds "System swap" \ + "$CTDB_MONITOR_SWAP_USAGE" \ + "$_swap_usage" \ + dump_memory_info +} + + +case "$1" in +monitor) + monitor_filesystem_usage + monitor_memory_usage + ;; +esac + +exit 0 diff --git a/ctdb/events.d/06.nfs b/ctdb/events.d/06.nfs new file mode 100755 index 0000000..e59f265 --- /dev/null +++ b/ctdb/events.d/06.nfs @@ -0,0 +1,40 @@ +#!/bin/sh +# script to manage nfs in a clustered environment + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +# service_name is used by various functions +# shellcheck disable=SC2034 +service_name="nfs" + +loadconfig +service_state_dir=$(ctdb_setup_service_state_dir) || exit $? + +###################################################################### + +nfs_callout_pre () +{ + _event="$1" + shift + + nfs_callout "${_event}-pre" "$@" +} + +###################################################################### + +nfs_callout_init "$service_state_dir" + +is_ctdb_managed_service || exit 0 + +case "$1" in +takeip) + nfs_callout_pre "$@" + ;; + +releaseip) + nfs_callout_pre "$@" + ;; +esac diff --git a/ctdb/events.d/10.external b/ctdb/events.d/10.external new file mode 100644 index 0000000..884357b --- /dev/null +++ b/ctdb/events.d/10.external @@ -0,0 +1,51 @@ +#!/bin/sh + +# Eventscript for CTDB to cope with externally managed public IP addresses + +# If DisableIPFailover is set: +# +# * 10.interface must be disabled. +# * For connection tracking/killing to work this script must be enabled. + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig + +if [ -z "$CTDB_PUBLIC_ADDRESSES" ] ; then + exit 0 +fi + +if [ ! -f "$CTDB_PUBLIC_ADDRESSES" ] ; then + if [ "$1" = "init" ]; then + echo "No public addresses file found. Nothing to do for 10.interfaces.external" + fi + exit 0 +fi + +takeover_assigned_ips () +{ + _pnn=$(ctdb_get_pnn) + + $CTDB -X ip | + awk -F'|' '{print $2}' | + while read ip ; do + _ip_details=$(ip_maskbits_iface "$ip") + if [ -n "$_ip_details" ] ; then + echo "Assigning $ip to this node (${_pnn})" + $CTDB moveip "$ip" "$_pnn" + fi + done +} + +ctdb_check_args "$@" + +case "$1" in + startup) + takeover_assigned_ips + ;; +esac + +exit 0 diff --git a/ctdb/events.d/10.interface b/ctdb/events.d/10.interface new file mode 100755 index 0000000..97227e8 --- /dev/null +++ b/ctdb/events.d/10.interface @@ -0,0 +1,262 @@ +#!/bin/sh + +################################# +# interface event script for ctdb +# this adds/removes IPs from your +# public interface + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig + +[ -z "$CTDB_PUBLIC_ADDRESSES" ] && { + CTDB_PUBLIC_ADDRESSES="${CTDB_BASE}/public_addresses" +} + +[ ! -f "$CTDB_PUBLIC_ADDRESSES" ] && { + if [ "$1" = "init" ]; then + echo "No public addresses file found. Nothing to do for 10.interfaces" + fi + exit 0 +} + +# This sets $all_interfaces as a side-effect. +get_all_interfaces () +{ + # Get all the interfaces listed in the public_addresses file + all_interfaces=$(sed -e "s/^[^\t ]*[\t ]*//" \ + -e "s/,/ /g" \ + -e "s/[\t ]*$//" "$CTDB_PUBLIC_ADDRESSES") + + # Add some special interfaces if they're defined + [ "$CTDB_PUBLIC_INTERFACE" ] && all_interfaces="$CTDB_PUBLIC_INTERFACE $all_interfaces" + + # Get the interfaces for which CTDB has public IPs configured. + # That is, for all but the 1st line, get the 1st field. + ctdb_ifaces=$($CTDB -X ifaces | sed -e '1d' -e 's@^|@@' -e 's@|.*@@') + + # Add $ctdb_interfaces and uniquify + # Use word splitting to squash whitespace + # shellcheck disable=SC2086 + all_interfaces=$(echo $all_interfaces $ctdb_ifaces | tr ' ' '\n' | sort -u) +} + +monitor_interfaces() +{ + get_all_interfaces + + down_interfaces_found=false + up_interfaces_found=false + + # Note that this loop must not exit early. It must process + # all interfaces so that the correct state for each interface + # is set in CTDB using setifacelink. + for _iface in $all_interfaces ; do + if interface_monitor "$_iface" ; then + up_interfaces_found=true + $CTDB setifacelink "$_iface" up >/dev/null 2>&1 + else + down_interfaces_found=true + $CTDB setifacelink "$_iface" down >/dev/null 2>&1 + fi + done + + if ! $down_interfaces_found ; then + return 0 + fi + + if ! $up_interfaces_found ; then + return 1 + fi + + if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" != "yes" ]; then + return 1 + fi + + return 0 +} + +# Sets: iface, ip, maskbits +get_iface_ip_maskbits () +{ + _iface_in="$1" + ip="$2" + _maskbits_in="$3" + + # Intentional word splitting here + # shellcheck disable=SC2046 + set -- $(ip_maskbits_iface "$ip") + if [ -n "$1" ] ; then + maskbits="$1" + iface="$2" + + if [ "$iface" != "$_iface_in" ] ; then + printf \ + 'WARNING: Public IP %s hosted on interface %s but VNN says %s\n' \ + "$ip" "$iface" "$_iface_in" + fi + if [ "$maskbits" != "$_maskbits_in" ] ; then + printf \ + 'WARNING: Public IP %s has %s bit netmask but VNN says %s\n' \ + "$ip" "$maskbits" "$_maskbits_in" + fi + else + die "ERROR: Unable to determine interface for IP ${ip}" + fi +} + +ip_block () +{ + _ip="$1" + _iface="$2" + + case "$_ip" in + *:*) _family="inet6" ;; + *) _family="inet" ;; + esac + + # Extra delete copes with previously killed script + iptables_wrapper "$_family" \ + -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null + iptables_wrapper "$_family" \ + -I INPUT -i "$_iface" -d "$_ip" -j DROP +} + +ip_unblock () +{ + _ip="$1" + _iface="$2" + + case "$_ip" in + *:*) _family="inet6" ;; + *) _family="inet" ;; + esac + + iptables_wrapper "$_family" \ + -D INPUT -i "$_iface" -d "$_ip" -j DROP 2>/dev/null +} + +ctdb_check_args "$@" + +case "$1" in +init) + # make sure that we only respond to ARP messages from the NIC where + # a particular ip address is associated. + get_proc sys/net/ipv4/conf/all/arp_filter >/dev/null 2>&1 && { + set_proc sys/net/ipv4/conf/all/arp_filter 1 + } + + _promote="sys/net/ipv4/conf/all/promote_secondaries" + get_proc "$_promote" >/dev/null 2>&1 || \ + die "Public IPs only supported if promote_secondaries is available" + + # make sure we drop any ips that might still be held if + # previous instance of ctdb got killed with -9 or similar + drop_all_public_ips + ;; + +startup) + monitor_interfaces + ;; + +takeip) + iface=$2 + ip=$3 + maskbits=$4 + + add_ip_to_iface "$iface" "$ip" "$maskbits" || { + exit 1; + } + + # In case a previous "releaseip" for this IP was killed... + ip_unblock "$ip" "$iface" + + flush_route_cache + ;; + +releaseip) + # releasing an IP is a bit more complex than it seems. Once the IP + # is released, any open tcp connections to that IP on this host will end + # up being stuck. Some of them (such as NFS connections) will be unkillable + # so we need to use the killtcp ctdb function to kill them off. We also + # need to make sure that no new connections get established while we are + # doing this! So what we do is this: + # 1) firewall this IP, so no new external packets arrive for it + # 2) find existing connections, and kill them + # 3) remove the IP from the interface + # 4) remove the firewall rule + shift + get_iface_ip_maskbits "$@" + + ip_block "$ip" "$iface" + + kill_tcp_connections "$iface" "$ip" + + delete_ip_from_iface "$iface" "$ip" "$maskbits" || { + ip_unblock "$ip" "$iface" + exit 1 + } + + ip_unblock "$ip" "$iface" + + flush_route_cache + ;; + +updateip) + # moving an IP is a bit more complex than it seems. + # First we drop all traffic on the old interface. + # Then we try to add the ip to the new interface and before + # we finally remove it from the old interface. + # + # 1) firewall this IP, so no new external packets arrive for it + # 2) remove the IP from the old interface (and new interface, to be sure) + # 3) add the IP to the new interface + # 4) remove the firewall rule + # 5) use ctdb gratarp to propagate the new mac address + # 6) use netstat -tn to find existing connections, and tickle them + _oiface=$2 + niface=$3 + _ip=$4 + _maskbits=$5 + + get_iface_ip_maskbits "$_oiface" "$_ip" "$_maskbits" + oiface="$iface" + + # Could check maskbits too. However, that should never change + # so we want to notice if it does. + if [ "$oiface" = "$niface" ] ; then + echo "Redundant \"updateip\" - ${ip} already on ${niface}" + exit 0 + fi + + ip_block "$ip" "$oiface" + + delete_ip_from_iface "$oiface" "$ip" "$maskbits" 2>/dev/null + delete_ip_from_iface "$niface" "$ip" "$maskbits" 2>/dev/null + + add_ip_to_iface "$niface" "$ip" "$maskbits" || { + ip_unblock "$ip" "$oiface" + exit 1 + } + + ip_unblock "$ip" "$oiface" + + flush_route_cache + + # propagate the new mac address + $CTDB gratarp "$ip" "$niface" + + # tickle all existing connections, so that dropped packets + # are retransmited and the tcp streams work + tickle_tcp_connections "$ip" + ;; + +monitor) + monitor_interfaces || exit 1 + ;; +esac + +exit 0 diff --git a/ctdb/events.d/11.natgw b/ctdb/events.d/11.natgw new file mode 100755 index 0000000..4dc023a --- /dev/null +++ b/ctdb/events.d/11.natgw @@ -0,0 +1,246 @@ +#!/bin/sh +# Script to set up one of the nodes as a NAT gateway for all other nodes. +# This is used to ensure that all nodes in the cluster can still originate +# traffic to the external network even if there are no public addresses +# available. +# + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +# service_name is used by various functions +# shellcheck disable=SC2034 +service_name=natgw + +loadconfig + +[ -n "$CTDB_NATGW_NODES" ] || exit 0 +export CTDB_NATGW_NODES + +service_state_dir=$(ctdb_setup_service_state_dir) || exit $? + +natgw_cfg_new="${service_state_dir}/cfg_new" +natgw_cfg_old="${service_state_dir}/cfg_old" +natgw_master_old="${service_state_dir}/master_old" + +ctdb_natgw_slave_only () +{ + _ip_address=$(ctdb_get_ip_address) + + awk -v my_ip="$_ip_address" \ + '$1 == my_ip { if ($2 ~ "slave-only") { exit 0 } else { exit 1 } }' \ + "$CTDB_NATGW_NODES" +} + +natgw_check_config () +{ + [ -r "$CTDB_NATGW_NODES" ] || \ + die "error: CTDB_NATGW_NODES=${CTDB_NATGW_NODES} unreadable" + if ! ctdb_natgw_slave_only ; then + [ -n "$CTDB_NATGW_PUBLIC_IP" ] || \ + die "Invalid configuration: CTDB_NATGW_PUBLIC_IP not set" + [ -n "$CTDB_NATGW_PUBLIC_IFACE" ] || \ + die "Invalid configuration: CTDB_NATGW_PUBLIC_IFACE not set" + fi + [ -n "$CTDB_NATGW_PRIVATE_NETWORK" ] || \ + die "Invalid configuration: CTDB_NATGW_PRIVATE_NETWORK not set" + + if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" = "yes" ] ; then + die "Invalid configuration: CTDB_PARTIALLY_ONLINE_INTERFACES=yes incompatible with NAT gateway" + fi + + # The default is to create a single default route + [ -n "$CTDB_NATGW_STATIC_ROUTES" ] || CTDB_NATGW_STATIC_ROUTES="0.0.0.0/0" +} + +natgw_write_config () +{ + _f="$1" + + cat >"$_f" </dev/null 2>&1 ; then + return 1 + fi + + echo "NAT gateway configuration has changed" + return 0 +} + +_natgw_clear () +{ + _ip="${CTDB_NATGW_PUBLIC_IP%/*}" + _maskbits="${CTDB_NATGW_PUBLIC_IP#*/}" + + delete_ip_from_iface \ + "$CTDB_NATGW_PUBLIC_IFACE" "$_ip" "$_maskbits" >/dev/null 2>&1 + for _net_gw in $CTDB_NATGW_STATIC_ROUTES ; do + _net="${_net_gw%@*}" + ip route del "$_net" metric 10 >/dev/null 2>/dev/null + done + + # Delete the masquerading setup from a previous iteration where we + # were the NAT-GW + iptables -D POSTROUTING -t nat \ + -s "$CTDB_NATGW_PRIVATE_NETWORK" ! -d "$CTDB_NATGW_PRIVATE_NETWORK" \ + -j MASQUERADE >/dev/null 2>/dev/null + + iptables -D INPUT -p tcp --syn -d "${_ip}/32" -j REJECT 2>/dev/null +} + +natgw_clear () +{ + if [ -r "$natgw_cfg_old" ] ; then + (. "$natgw_cfg_old" ; _natgw_clear) + else + _natgw_clear + fi +} + +natgw_set_master () +{ + set_proc sys/net/ipv4/ip_forward 1 + iptables -A POSTROUTING -t nat \ + -s "$CTDB_NATGW_PRIVATE_NETWORK" ! -d "$CTDB_NATGW_PRIVATE_NETWORK" \ + -j MASQUERADE + + # block all incoming connections to the NATGW IP address + ctdb_natgw_public_ip_host="${CTDB_NATGW_PUBLIC_IP%/*}/32" + iptables -D INPUT -p tcp --syn \ + -d "$ctdb_natgw_public_ip_host" -j REJECT 2>/dev/null + iptables -I INPUT -p tcp --syn \ + -d "$ctdb_natgw_public_ip_host" -j REJECT 2>/dev/null + + ip addr add "$CTDB_NATGW_PUBLIC_IP" dev "$CTDB_NATGW_PUBLIC_IFACE" + for _net_gw in $CTDB_NATGW_STATIC_ROUTES ; do + _net="${_net_gw%@*}" + if [ "$_net" != "$_net_gw" ] ; then + _gw="${_net_gw#*@}" + else + _gw="$CTDB_NATGW_DEFAULT_GATEWAY" + fi + + [ -n "$_gw" ] || continue + ip route add "$_net" metric 10 via "$_gw" + done +} + +natgw_set_slave () +{ + _natgwip="$1" + + for _net_gw in $CTDB_NATGW_STATIC_ROUTES ; do + _net="${_net_gw%@*}" + ip route add "$_net" via "$_natgwip" metric 10 + done +} + +natgw_ensure_master () +{ + # Intentional word splitting here + # shellcheck disable=SC2046 + set -- $("${CTDB_HELPER_BINDIR}/ctdb_natgw" master) + natgwmaster="${1:--1}" # Default is -1, for failure above + natgwip="$2" + + if [ "$natgwmaster" = "-1" ]; then + # Fail... + die "There is no NATGW master node" + fi +} + +natgw_master_has_changed () +{ + if [ -r "$natgw_master_old" ] ; then + read _old_natgwmaster <"$natgw_master_old" + else + _old_natgwmaster="" + fi + [ "$_old_natgwmaster" != "$natgwmaster" ] +} + +natgw_save_state () +{ + echo "$natgwmaster" >"$natgw_master_old" + # Created by natgw_config_has_changed() + mv "$natgw_cfg_new" "$natgw_cfg_old" +} + + +case "$1" in +setup) + natgw_check_config + ;; + +startup) + natgw_check_config + + # Error if CTDB_NATGW_PUBLIC_IP is listed in public addresses + ip_pat=$(echo "$CTDB_NATGW_PUBLIC_IP" | sed -e 's@\.@\\.@g') + if grep -q "^${ip_pat}[[:space:]]" \ + "${CTDB_PUBLIC_ADDRESSES:-${CTDB_BASE}/public_addresses}" ; then + die "ERROR: CTDB_NATGW_PUBLIC_IP same as a public address" + fi + + # do not send out arp requests from loopback addresses + set_proc sys/net/ipv4/conf/all/arp_announce 2 + ;; + +updatenatgw|ipreallocated) + natgw_check_config + + natgw_ensure_master + + natgw_config_has_changed || natgw_master_has_changed || exit 0 + + natgw_clear + + pnn=$(ctdb_get_pnn) + if [ "$pnn" = "$natgwmaster" ]; then + natgw_set_master + else + natgw_set_slave "$natgwip" + fi + + # flush our route cache + set_proc sys/net/ipv4/route/flush 1 + + # Only update saved state when NATGW successfully updated + natgw_save_state + ;; + +shutdown|removenatgw) + natgw_check_config + natgw_clear + ;; + +monitor) + natgw_check_config + + if [ -n "$CTDB_NATGW_PUBLIC_IFACE" ] ; then + interface_monitor "$CTDB_NATGW_PUBLIC_IFACE" || exit 1 + fi + ;; +esac + +exit 0 diff --git a/ctdb/events.d/11.routing b/ctdb/events.d/11.routing new file mode 100755 index 0000000..c8d875c --- /dev/null +++ b/ctdb/events.d/11.routing @@ -0,0 +1,49 @@ +#!/bin/sh + +# Attempt to add a set of static routes. +# +# Do this in "ipreallocated" rather than just "startup" because some +# of the routes might be missing because the corresponding interface +# has not previously had any IPs assigned or IPs were previously +# released and corresponding routes were dropped. +# +# Addition of some routes might fail, errors go to /dev/null. +# +# Routes to add are defined in $CTDB_BASE/static-routes. Syntax is: +# +# IFACE NET/MASK GATEWAY +# +# Example: +# +# bond1 10.3.3.0/24 10.0.0.1 + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig + +[ -f "${CTDB_BASE}/static-routes" ] || { + exit 0 +} + +case "$1" in +ipreallocated) + while read iface dest gw; do + ip route add "$dest" via "$gw" dev "$iface" >/dev/null 2>&1 + done <"${CTDB_BASE}/static-routes" + ;; + +updateip) + oiface=$2 + niface=$3 + while read iface dest gw; do + if [ "$niface" = "$iface" -o "$oiface" = "$iface" ] ; then + ip route add "$dest" via "$gw" dev "$iface" >/dev/null 2>&1 + fi + done <"${CTDB_BASE}/static-routes" + ;; +esac + +exit 0 diff --git a/ctdb/events.d/13.per_ip_routing b/ctdb/events.d/13.per_ip_routing new file mode 100755 index 0000000..4fff73b --- /dev/null +++ b/ctdb/events.d/13.per_ip_routing @@ -0,0 +1,442 @@ +#!/bin/sh + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig + +# service_name is used by various functions +# shellcheck disable=SC2034 +service_name=per_ip_routing + +# Do nothing if unconfigured +[ -n "$CTDB_PER_IP_ROUTING_CONF" ] || exit 0 + +table_id_prefix="ctdb." + +[ -n "$CTDB_PER_IP_ROUTING_RULE_PREF" ] || \ + die "error: CTDB_PER_IP_ROUTING_RULE_PREF not configured" + +[ "$CTDB_PER_IP_ROUTING_TABLE_ID_LOW" -lt "$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" ] 2>/dev/null || \ + die "error: CTDB_PER_IP_ROUTING_TABLE_ID_LOW[$CTDB_PER_IP_ROUTING_TABLE_ID_LOW] and/or CTDB_PER_IP_ROUTING_TABLE_ID_HIGH[$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH] improperly configured" + +if [ "$CTDB_PER_IP_ROUTING_TABLE_ID_LOW" -le 253 -a \ + 255 -le "$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" ] ; then + die "error: range CTDB_PER_IP_ROUTING_TABLE_ID_LOW[$CTDB_PER_IP_ROUTING_TABLE_ID_LOW]..CTDB_PER_IP_ROUTING_TABLE_ID_HIGH[$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH] must not include 253-255" +fi + +have_link_local_config () +{ + [ "$CTDB_PER_IP_ROUTING_CONF" = "__auto_link_local__" ] +} + +if ! have_link_local_config && [ ! -r "$CTDB_PER_IP_ROUTING_CONF" ] ; then + die "error: CTDB_PER_IP_ROUTING_CONF=$CTDB_PER_IP_ROUTING_CONF file not found" +fi + +service_state_dir=$(ctdb_setup_service_state_dir) || exit $? + +###################################################################### + +ipv4_is_valid_addr() +{ + _ip="$1" + + _count=0 + # Get the shell to break up the address into 1 word per octet + # Intentional word splitting here + # shellcheck disable=SC2086 + for _o in $(export IFS="." ; echo $_ip) ; do + # The 2>/dev/null stops output from failures where an "octet" + # is not numeric. The test will still fail. + if ! [ 0 -le $_o -a $_o -le 255 ] 2>/dev/null ; then + return 1 + fi + _count=$((_count + 1)) + done + + # A valid IPv4 address has 4 octets + [ $_count -eq 4 ] +} + +ensure_ipv4_is_valid_addr () +{ + _event="$1" + _ip="$2" + + ipv4_is_valid_addr "$_ip" || { + echo "$0: $_event not an ipv4 address skipping IP:$_ip" + exit 0 + } +} + +ipv4_host_addr_to_net () +{ + _host="$1" + _maskbits="$2" + + # Convert the host address to an unsigned long by splitting out + # the octets and doing the math. + _host_ul=0 + # Intentional word splitting here + # shellcheck disable=SC2086 + for _o in $(export IFS="." ; echo $_host) ; do + _host_ul=$(( (_host_ul << 8) + _o)) # work around Emacs color bug + done + + # Calculate the mask and apply it. + _mask_ul=$(( 0xffffffff << (32 - _maskbits) )) + _net_ul=$(( _host_ul & _mask_ul )) + + # Now convert to a network address one byte at a time. + _net="" + for _o in $(seq 1 4) ; do + _net="$((_net_ul & 255))${_net:+.}${_net}" + _net_ul=$((_net_ul >> 8)) + done + + echo "${_net}/${_maskbits}" +} + +###################################################################### + +ensure_rt_tables () +{ + rt_tables="$CTDB_SYS_ETCDIR/iproute2/rt_tables" + rt_tables_lock="${service_state_dir}/rt_tables_lock" + + # This file should always exist. Even if this didn't exist on the + # system, adding a route will have created it. What if we startup + # and immediately shutdown? Let's be sure. + if [ ! -f "$rt_tables" ] ; then + mkdir -p "${rt_tables%/*}" # dirname + touch "$rt_tables" + fi +} + +# Setup a table id to use for the given IP. We don't need to know it, +# it just needs to exist in /etc/iproute2/rt_tables. Fail if no free +# table id could be found in the configured range. +ensure_table_id_for_ip () +{ + _ip=$1 + + ensure_rt_tables + + # Maintain a table id for each IP address we've ever seen in + # rt_tables. We use a "ctdb." prefix on the label. + _label="${table_id_prefix}${_ip}" + + # This finds either the table id corresponding to the label or a + # new unused one (that is greater than all the used ones in the + # range). + ( + # Note that die() just gets us out of the subshell... + flock --timeout 30 9 || \ + die "ensure_table_id_for_ip: failed to lock file $rt_tables" + + _new="$CTDB_PER_IP_ROUTING_TABLE_ID_LOW" + while read _t _l ; do + # Skip comments + case "$_t" in + \#*) continue ;; + esac + # Found existing: done + if [ "$_l" = "$_label" ] ; then + return 0 + fi + # Potentially update the new table id to be used. The + # redirect stops error spam for a non-numeric value. + if [ "$_new" -le "$_t" -a \ + "$_t" -le "$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" ] 2>/dev/null ; then + _new=$((_t + 1)) + fi + done <"$rt_tables" + + # If the new table id is legal then add it to the file and + # print it. + if [ "$_new" -le "$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" ] ; then + printf "%d\t%s\n" "$_new" "$_label" >>"$rt_tables" + return 0 + else + return 1 + fi + ) 9>"$rt_tables_lock" +} + +# Clean up all the table ids that we might own. +clean_up_table_ids () +{ + ensure_rt_tables + + ( + # Note that die() just gets us out of the subshell... + flock --timeout 30 9 || \ + die "clean_up_table_ids: failed to lock file $rt_tables" + + # Delete any items from the file that have a table id in our + # range or a label matching our label. Preserve comments. + _tmp="${rt_tables}.$$.ctdb" + awk -v min="$CTDB_PER_IP_ROUTING_TABLE_ID_LOW" \ + -v max="$CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" \ + -v pre="$table_id_prefix" \ + '/^#/ || + !(min <= $1 && $1 <= max) && + !(index($2, pre) == 1) { + print $0 }' "$rt_tables" >"$_tmp" + + mv "$_tmp" "$rt_tables" + ) 9>"$rt_tables_lock" +} + +###################################################################### + +# This prints the config for an IP, which is either relevant entries +# from the config file or, if set to the magic link local value, some +# link local routing config for the IP. +get_config_for_ip () +{ + _ip="$1" + + if have_link_local_config ; then + # When parsing public_addresses also split on '/'. This means + # that we get the maskbits as item #2 without further parsing. + while IFS="/$IFS" read _i _maskbits _x ; do + if [ "$_ip" = "$_i" ] ; then + printf "%s" "$_ip "; ipv4_host_addr_to_net "$_ip" "$_maskbits" + fi + done <"${CTDB_PUBLIC_ADDRESSES:-/dev/null}" + else + while read _i _rest ; do + if [ "$_ip" = "$_i" ] ; then + printf "%s\t%s\n" "$_ip" "$_rest" + fi + done <"$CTDB_PER_IP_ROUTING_CONF" + fi +} + +ip_has_configuration () +{ + _ip="$1" + + _conf=$(get_config_for_ip "$_ip") + [ -n "$_conf" ] +} + +add_routing_for_ip () +{ + _iface="$1" + _ip="$2" + + # Do nothing if no config for this IP. + ip_has_configuration "$_ip" || return 0 + + ensure_table_id_for_ip "$_ip" || \ + die "add_routing_for_ip: out of table ids in range $CTDB_PER_IP_ROUTING_TABLE_ID_LOW - $CTDB_PER_IP_ROUTING_TABLE_ID_HIGH" + + _pref="$CTDB_PER_IP_ROUTING_RULE_PREF" + _table_id="${table_id_prefix}${_ip}" + + del_routing_for_ip "$_ip" >/dev/null 2>&1 + + ip rule add from "$_ip" pref "$_pref" table "$_table_id" || \ + die "add_routing_for_ip: failed to add rule for $_ip" + + # Add routes to table for any lines matching the IP. + get_config_for_ip "$_ip" | + while read _i _dest _gw ; do + _r="$_dest ${_gw:+via} $_gw dev $_iface table $_table_id" + # Intentionally unquoted multi-word value here + # shellcheck disable=SC2086 + ip route add $_r || \ + die "add_routing_for_ip: failed to add route: $_r" + done +} + +del_routing_for_ip () +{ + _ip="$1" + + _pref="$CTDB_PER_IP_ROUTING_RULE_PREF" + _table_id="${table_id_prefix}${_ip}" + + # Do this unconditionally since we own any matching table ids. + # However, print a meaningful message if something goes wrong. + _cmd="ip rule del from $_ip pref $_pref table $_table_id" + _out=$($_cmd 2>&1) || \ + cat <&1 | sed -e 's@^.@ &@' +} + +###################################################################### + +flush_rules_and_routes () +{ + ip rule show | + while read _p _x _i _x _t ; do + # Remove trailing colon after priority/preference. + _p="${_p%:}" + # Only remove rules that match our priority/preference. + [ "$CTDB_PER_IP_ROUTING_RULE_PREF" = "$_p" ] || continue + + echo "Removing ip rule for public address $_i for routing table $_t" + ip rule del from "$_i" table "$_t" pref "$_p" + ip route flush table "$_t" 2>/dev/null + done +} + +# Add any missing routes. Some might have gone missing if, for +# example, all IPs on the network were removed (possibly if the +# primary was removed). If $1 is "force" then (re-)add all the +# routes. +add_missing_routes () +{ + $CTDB ip -v -X | { + read _x # skip header line + + # Read the rest of the lines. We're only interested in the + # "IP" and "ActiveInterface" columns. The latter is only set + # for addresses local to this node, making it easy to skip + # non-local addresses. For each IP local address we check if + # the relevant routing table is populated and populate it if + # not. + while IFS="|" read _x _ip _x _iface _x ; do + [ -n "$_iface" ] || continue + + _table_id="${table_id_prefix}${_ip}" + if [ -z "$(ip route show table "$_table_id" 2>/dev/null)" -o \ + "$1" = "force" ] ; then + add_routing_for_ip "$_iface" "$_ip" + fi + done + } || exit $? +} + +# Remove rules/routes for addresses that we're not hosting. If a +# releaseip event failed in an earlier script then we might not have +# had a chance to remove the corresponding rules/routes. +remove_bogus_routes () +{ + # Get a IPs current hosted by this node, each anchored with '@'. + _ips=$($CTDB ip -v -X | awk -F'|' 'NR > 1 && $4 != "" {printf "@%s@\n", $2}') + + # x is intentionally ignored + # shellcheck disable=SC2034 + ip rule show | + while read _p _x _i _x _t ; do + # Remove trailing colon after priority/preference. + _p="${_p%:}" + # Only remove rules that match our priority/preference. + [ "$CTDB_PER_IP_ROUTING_RULE_PREF" = "$_p" ] || continue + # Only remove rules for which we don't have an IP. This could + # be done with grep, but let's do it with shell prefix removal + # to avoid unnecessary processes. This falls through if + # "@${_i}@" isn't present in $_ips. + [ "$_ips" = "${_ips#*@${_i}@}" ] || continue + + echo "Removing ip rule/routes for unhosted public address $_i" + del_routing_for_ip "$_i" + done +} + +###################################################################### + +service_reconfigure () +{ + add_missing_routes "force" + remove_bogus_routes + + # flush our route cache + set_proc sys/net/ipv4/route/flush 1 +} + +###################################################################### + +ctdb_check_args "$@" + +case "$1" in +startup) + flush_rules_and_routes + + # make sure that we only respond to ARP messages from the NIC + # where a particular ip address is associated. + get_proc sys/net/ipv4/conf/all/arp_filter >/dev/null 2>&1 && { + set_proc sys/net/ipv4/conf/all/arp_filter 1 + } + ;; + +shutdown) + flush_rules_and_routes + clean_up_table_ids + ;; + +takeip) + iface=$2 + ip=$3 + # maskbits included here so argument order is obvious + # shellcheck disable=SC2034 + maskbits=$4 + + ensure_ipv4_is_valid_addr "$1" "$ip" + add_routing_for_ip "$iface" "$ip" + + # flush our route cache + set_proc sys/net/ipv4/route/flush 1 + + $CTDB gratarp "$ip" "$iface" + ;; + +updateip) + # oiface, maskbits included here so argument order is obvious + # shellcheck disable=SC2034 + oiface=$2 + niface=$3 + ip=$4 + # shellcheck disable=SC2034 + maskbits=$5 + + ensure_ipv4_is_valid_addr "$1" "$ip" + add_routing_for_ip "$niface" "$ip" + + # flush our route cache + set_proc sys/net/ipv4/route/flush 1 + + $CTDB gratarp "$ip" "$niface" + tickle_tcp_connections "$ip" + ;; + +releaseip) + iface=$2 + ip=$3 + # maskbits included here so argument order is obvious + # shellcheck disable=SC2034 + maskbits=$4 + + ensure_ipv4_is_valid_addr "$1" "$ip" + del_routing_for_ip "$ip" + ;; + +ipreallocated) + add_missing_routes + remove_bogus_routes + ;; + +reconfigure) + ctdb_service_reconfigure + ;; +esac + +exit 0 diff --git a/ctdb/events.d/20.multipathd b/ctdb/events.d/20.multipathd new file mode 100755 index 0000000..3dd58da --- /dev/null +++ b/ctdb/events.d/20.multipathd @@ -0,0 +1,83 @@ +#!/bin/sh +# ctdb event script for monitoring the multipath daemon +# +# Configure monitporing of multipath devices by listing the device serials +# in /etc/ctdb/multipathd : +# CTDB_MONITOR_MPDEVICES="device1 device2 ..." +# + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +# service_name is used by various functions +# shellcheck disable=SC2034 +service_name="multipathd" + +loadconfig + +[ -n "$CTDB_MONITOR_MPDEVICES" ] || exit 0 + +service_state_dir=$(ctdb_setup_service_state_dir) || exit $? + +multipath_fail="${service_state_dir}/fail" + +multipathd_check_background() +{ + for _device in $CTDB_MONITOR_MPDEVICES; do + # Check multipath knows about the device + _out=$(multipath -ll "$_device") + if [ -z "$_out" ] ; then + echo "ERROR: device \"${_device}\" not known to multipathd" \ + >"$multipath_fail" + exit 1 + fi + + # Check for at least 1 active path + if ! echo "$_out" | grep 'prio=.* status=active' >/dev/null 2>&1 ; then + echo "ERROR: multipath device \"${_device}\" has no active paths" \ + >"$multipath_fail" + exit 1 + fi + done + exit 0 +} + +multipathd_check() +{ + # Run the actual check in the background since the call to + # multipath may block + multipathd_check_background /dev/null 2>&1 & + _pid="$!" + _timeleft=10 + + while [ $_timeleft -gt 0 ]; do + _timeleft=$((_timeleft - 1)) + + # see if the process still exists + kill -0 $_pid >/dev/null 2>&1 || { + if wait $_pid ; then + return 0 + else + cat "$multipath_fail" + rm -f "$multipath_fail" + return 1 + fi + } + sleep 1 + done + + echo "ERROR: callout to multipath checks hung" + # If hung then this probably won't work, but worth trying... + kill -9 $_pid >/dev/null 2>&1 + return 1 +} + +case "$1" in +monitor) + multipathd_check || die "multipath monitoring failed" + ;; +esac + +exit 0 diff --git a/ctdb/events.d/31.clamd b/ctdb/events.d/31.clamd new file mode 100755 index 0000000..1b76365 --- /dev/null +++ b/ctdb/events.d/31.clamd @@ -0,0 +1,45 @@ +#!/bin/sh +# event script to manage clamd in a cluster environment + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +detect_init_style + +case $CTDB_INIT_STYLE in +redhat) + service_name="clamd" + # service_config is used by loadconfig() + # shellcheck disable=SC2034 + service_config="clamd" + ;; +*) + service_name="clamav" + # service_config is used by loadconfig() + # shellcheck disable=SC2034 + service_config="clamav" + ;; +esac + +loadconfig + +is_ctdb_managed_service || exit 0 + +case "$1" in +startup) + service "$service_name" stop > /dev/null 2>&1 + service "$service_name" start || exit $? + ;; + +shutdown) + service "$service_name"_stop + ;; + +monitor) + ctdb_check_unix_socket "$CTDB_CLAMD_SOCKET" || exit $? + ;; +esac + +exit 0 diff --git a/ctdb/events.d/40.vsftpd b/ctdb/events.d/40.vsftpd new file mode 100755 index 0000000..f3c4848 --- /dev/null +++ b/ctdb/events.d/40.vsftpd @@ -0,0 +1,56 @@ +#!/bin/sh +# event strict to manage vsftpd in a cluster environment + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +service_name="vsftpd" + +service_reconfigure () +{ + service $service_name restart +} + +loadconfig + +is_ctdb_managed_service || exit 0 + +case "$1" in +startup) + service "$service_name" stop > /dev/null 2>&1 + service "$service_name" start + ctdb_counter_init + ;; + +shutdown) + service "$service_name" stop + ;; + +takeip|releaseip) + ctdb_service_set_reconfigure + ;; + +ipreallocated) + if ctdb_service_needs_reconfigure ; then + ctdb_service_reconfigure + fi + ;; + +monitor) + if ctdb_check_tcp_ports 21 ; then + ctdb_counter_init + else + ctdb_counter_incr + num_fails=$(ctdb_counter_get) + if [ "$num_fails" -ge 2 ] ; then + die "ERROR: ${num_fails} consecutive failures for vsftpd, marking node unhealthy" + elif [ "$num_fails" -eq 1 ] ; then + echo "WARNING: vsftpd not listening but less than 2 consecutive failures, not unhealthy yet" + fi + fi + ;; +esac + +exit 0 diff --git a/ctdb/events.d/41.httpd b/ctdb/events.d/41.httpd new file mode 100755 index 0000000..b9aa7e1 --- /dev/null +++ b/ctdb/events.d/41.httpd @@ -0,0 +1,84 @@ +#!/bin/sh +# event script to manage httpd in a cluster environment + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +detect_init_style + +case $CTDB_INIT_STYLE in +redhat) + service_name="httpd" + # service_config is used by loadconfig() + # shellcheck disable=SC2034 + service_config="http" + ;; +suse|debian|*) + service_name="apache2" + # service_config is used by loadconfig() + # shellcheck disable=SC2034 + service_config="apache2" + ;; +esac + +# RHEL5 sometimes use a SIGKILL to terminate httpd, which then leaks +# semaphores. This is a hack to clean them up. +cleanup_httpd_semaphore_leak() { + killall -q -0 "$service_name" || + for i in $(ipcs -s | awk '$3 == "apache" { print $2 }') ; do + ipcrm -s "$i" + done +} + +########## + +service_start () +{ + cleanup_httpd_semaphore_leak + service $service_name start +} +service_stop () +{ + service $service_name stop + killall -q -9 $service_name || true +} + +loadconfig + +is_ctdb_managed_service || exit 0 + +case "$1" in +startup) + service_start + ctdb_counter_int + ;; + +shutdown) + service_stop + ;; + +monitor) + if ctdb_check_tcp_ports 80 >/dev/null 2>/dev/null ; then + ctdb_counter_init + else + ctdb_counter_incr + num_fails=$(ctdb_counter_get) + if [ "$num_fails" -eq 2 ] ; then + echo "HTTPD is not running. Trying to restart HTTPD." + service_stop + service_start + exit 0 + elif [ "$num_fails" -ge 5 ] ; then + echo "HTTPD is not running. Trying to restart HTTPD." + service_stop + service_start + exit 1 + fi + fi + ;; +esac + +exit 0 + diff --git a/ctdb/events.d/49.winbind b/ctdb/events.d/49.winbind new file mode 100755 index 0000000..5e93737 --- /dev/null +++ b/ctdb/events.d/49.winbind @@ -0,0 +1,59 @@ +#!/bin/sh +# ctdb event script for winbind + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +CTDB_SERVICE_WINBIND=${CTDB_SERVICE_WINBIND:-winbind} + +# service_name is used by various functions +# shellcheck disable=SC2034 +service_name="winbind" + +loadconfig + +service_start () +{ + service "$CTDB_SERVICE_WINBIND" stop >/dev/null 2>&1 + killall -0 -q winbindd && { + sleep 1 + # make absolutely sure winbindd is dead + killall -q -9 winbindd + } + + service "$CTDB_SERVICE_WINBIND" start || \ + die "Failed to start winbind" +} + +service_stop () +{ + service "$CTDB_SERVICE_WINBIND" stop +} + +########################### + +is_ctdb_managed_service || exit 0 + +########################### + +case "$1" in +startup) + service_start + ;; + +shutdown) + service_stop + ;; + +monitor) + if ! out=$(wbinfo -p 2>&1) ; then + echo "ERROR: wbinfo -p returned error" + echo "$out" + exit 1 + fi + ;; +esac + +exit 0 diff --git a/ctdb/events.d/50.samba b/ctdb/events.d/50.samba new file mode 100755 index 0000000..4c32e4e --- /dev/null +++ b/ctdb/events.d/50.samba @@ -0,0 +1,188 @@ +#!/bin/sh +# ctdb event script for Samba + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +detect_init_style + +case $CTDB_INIT_STYLE in + suse) + CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-smb} + CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-nmb} + ;; + debian) + CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-smbd} + CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-nmbd} + ;; + *) + # Use redhat style as default: + CTDB_SERVICE_SMB=${CTDB_SERVICE_SMB:-smb} + CTDB_SERVICE_NMB=${CTDB_SERVICE_NMB:-""} + ;; +esac + +# service_name is used by various functions +# shellcheck disable=SC2034 +service_name="samba" + +loadconfig + +service_state_dir=$(ctdb_setup_service_state_dir) || exit $? + +service_start () +{ + # make sure samba is not already started + service "$CTDB_SERVICE_SMB" stop > /dev/null 2>&1 + if [ -n "$CTDB_SERVICE_NMB" ] ; then + service "$CTDB_SERVICE_NMB" stop > /dev/null 2>&1 + fi + killall -0 -q smbd && { + sleep 1 + # make absolutely sure samba is dead + killall -q -9 smbd + } + killall -0 -q nmbd && { + sleep 1 + # make absolutely sure samba is dead + killall -q -9 nmbd + } + + # start Samba service. Start it reniced, as under very heavy load + # the number of smbd processes will mean that it leaves few cycles + # for anything else + net serverid wipe + + if [ -n "$CTDB_SERVICE_NMB" ] ; then + nice_service "$CTDB_SERVICE_NMB" start || die "Failed to start nmbd" + fi + + nice_service "$CTDB_SERVICE_SMB" start || die "Failed to start samba" +} + +service_stop () +{ + service "$CTDB_SERVICE_SMB" stop + program_stack_traces "smbd" 5 + if [ -n "$CTDB_SERVICE_NMB" ] ; then + service "$CTDB_SERVICE_NMB" stop + fi +} + +###################################################################### +# Show the testparm output using a cached smb.conf to avoid delays due +# to registry access. + +smbconf_cache="$service_state_dir/smb.conf.cache" + +testparm_foreground_update () +{ + _timeout="$1" + + # No need to remove these temporary files, since there are only 2 + # of them. + _out="${smbconf_cache}.out" + _err="${smbconf_cache}.err" + + timeout "$_timeout" testparm -v -s >"$_out" 2>"$_err" + case $? in + 0) : ;; + 124) + if [ -f "$smbconf_cache" ] ; then + echo "WARNING: smb.conf cache update timed out - using old cache file" + return 1 + else + echo "ERROR: smb.conf cache create failed - testparm command timed out" + exit 1 + fi + ;; + *) + if [ -f "$smbconf_cache" ] ; then + echo "WARNING: smb.conf cache update failed - using old cache file" + cat "$_err" + return 1 + else + echo "ERROR: smb.conf cache create failed - testparm failed with:" + cat "$_err" + exit 1 + fi + esac + + # Only using $$ here to avoid a collision. This is written into + # CTDB's own state directory so there is no real need for a secure + # temporary file. + _tmpfile="${smbconf_cache}.$$" + # Patterns to exclude... + _pat='^[[:space:]]+(registry[[:space:]]+shares|include|copy|winbind[[:space:]]+separator)[[:space:]]+=' + grep -Ev "$_pat" <"$_out" >"$_tmpfile" + mv "$_tmpfile" "$smbconf_cache" # atomic + + return 0 +} + +testparm_background_update () +{ + _timeout="$1" + + testparm_foreground_update "$_timeout" >/dev/null 2>&1 /dev/null +} + +list_samba_shares () +{ + testparm_cat | + sed -n -e 's@^[[:space:]]*path[[:space:]]*=[[:space:]]@@p' | + sed -e 's/"//g' +} + +list_samba_ports () +{ + testparm_cat --parameter-name="smb ports" | + sed -e 's@,@ @g' +} + +########################### + +is_ctdb_managed_service || exit 0 + +########################### + +case "$1" in +startup) + service_start + ;; + +shutdown) + service_stop + ;; + +monitor) + testparm_foreground_update 10 + ret=$? + + smb_ports="$CTDB_SAMBA_CHECK_PORTS" + if [ -z "$smb_ports" ] ; then + smb_ports=$(list_samba_ports) + [ -n "$smb_ports" ] || die "Failed to set smb ports" + fi + # Intentionally unquoted multi-word value here + # shellcheck disable=SC2086 + ctdb_check_tcp_ports $smb_ports || exit $? + + if [ "$CTDB_SAMBA_SKIP_SHARE_CHECK" != "yes" ] ; then + list_samba_shares | ctdb_check_directories || exit $? + fi + + if [ $ret -ne 0 ] ; then + testparm_background_update 10 + fi + ;; +esac + +exit 0 diff --git a/ctdb/events.d/60.nfs b/ctdb/events.d/60.nfs new file mode 100755 index 0000000..9b64d6e --- /dev/null +++ b/ctdb/events.d/60.nfs @@ -0,0 +1,300 @@ +#!/bin/sh +# script to manage nfs in a clustered environment + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +# service_name is used by various functions +# shellcheck disable=SC2034 +service_name="nfs" + +loadconfig +service_state_dir=$(ctdb_setup_service_state_dir) || exit $? + +###################################################################### + +service_reconfigure () +{ + # Restart lock manager, notify clients + if [ -x "${CTDB_BASE}/statd-callout" ] ; then + "${CTDB_BASE}/statd-callout" notify & + fi >/dev/null 2>&1 +} + +###################################################################### + +###################################################### +# Check the health of NFS services +# +# Use .check files in $CTDB_NFS_CHECKS_DIR. +# Default is "${CTDB_BASE}/nfs-checks.d/" +###################################################### +nfs_check_services () +{ + _dir="${CTDB_NFS_CHECKS_DIR:-${CTDB_BASE}/nfs-checks.d}" + + # Files must end with .check - avoids editor backups, RPM fu, ... + for _f in "$_dir"/[0-9][0-9].*.check ; do + [ -r "$_f" ] || continue + + _t="${_f%.check}" + _progname="${_t##*/[0-9][0-9].}" + + nfs_check_service "$_progname" <"$_f" + done +} + +###################################################### +# Check the health of an NFS service +# +# $1 - progname, passed to rpcinfo (looked up in /etc/rpc) +# +# Reads variables from stdin +# +# Variables are: +# +# * family - "tcp" or "udp" or space separated list +# default: tcp, not used with "service_check_cmd" +# * version - optional, RPC service version number +# default is to omit to check for any version, +# not used with "service_check_cmd" +# * unhealthy_after - number of check fails before unhealthy +# default: 1 +# * restart_every - number of check fails before restart +# default: 0, meaning no restart +# * service_stop_cmd - command to stop service +# default: no default, must be provided if +# restart_every > 0 +# * service_start_cmd - command to start service +# default: no default, must be provided if +# restart_every > 0 +# * service_check_cmd - command to check health of service +# default is to check RPC service using rpcinfo +# * service_debug_cmd - command to debug a service after trying to stop it; +# for example, it can be useful to print stack +# traces of threads that have not exited, since +# they may be stuck doing I/O; +# no default, see also function program_stack_traces() +# +# Quoting in values is not preserved +# +###################################################### +nfs_check_service () +{ + _progname="$1" + + # This sub-shell is created to intentionally limit the scope of + # variable values read from the .check files. + # shellcheck disable=SC2030 + ( + # Subshell to restrict scope variables... + + # Defaults + family="tcp" + version="" + unhealthy_after=1 + restart_every=0 + service_stop_cmd="" + service_start_cmd="" + service_check_cmd="" + service_debug_cmd="" + + # Eval line-by-line. Expands variable references in values. + # Also allows variable name checking, which seems useful. + while read _line ; do + case "$_line" in + \#*|"") : ;; # Ignore comments, blank lines + + family=*|version=*|\ + unhealthy_after=*|restart_every=*|\ + service_stop_cmd=*|service_start_cmd=*|\ + service_check_cmd=*|service_debug_cmd=*) + + eval "$_line" + ;; + *) + echo "ERROR: Unknown variable for ${_progname}: ${_line}" + exit 1 + esac + done + + _service_name="nfs_${_progname}" + + _ok=false + if [ -n "$service_check_cmd" ] ; then + # Using eval means variables can contain semicolon separated commands + if eval "$service_check_cmd" ; then + _ok=true + else + _err="monitoring service \"${_progname}\" failed" + fi + else + if nfs_check_rpcinfo \ + "$_progname" "$version" "$family" >/dev/null ; then + _ok=true + else + _err="$ctdb_check_rpc_out" + fi + fi + + if $_ok ; then + if [ $unhealthy_after -ne 1 -o $restart_every -ne 0 ] ; then + ctdb_counter_init "$_service_name" + fi + exit 0 + fi + + ctdb_counter_incr "$_service_name" + _failcount=$(ctdb_counter_get "$_service_name") + + _unhealthy=false + if [ "$unhealthy_after" -gt 0 ] ; then + if [ "$_failcount" -ge "$unhealthy_after" ] ; then + _unhealthy=true + echo "ERROR: $_err" + fi + fi + + if [ "$restart_every" -gt 0 ] ; then + if [ $((_failcount % restart_every)) -eq 0 ] ; then + if ! $_unhealthy ; then + echo "WARNING: $_err" + fi + nfs_restart_service + fi + fi + + if $_unhealthy ; then + exit 1 + fi + + return 0 + ) || exit 1 +} + +# Uses: service_stop_cmd, service_start_cmd, service_debug_cmd +# This function is called within the sub-shell that shellcheck thinks +# loses the above variable values. +# shellcheck disable=SC2031 +nfs_restart_service () +{ + if [ -z "$service_stop_cmd" -o -z "$service_start_cmd" ] ; then + die "ERROR: Can not restart service \"${_progname}\" without corresponding service_start_cmd/service_stop_cmd settings" + fi + + echo "Trying to restart service \"${_progname}\"..." + # Using eval means variables can contain semicolon separated commands + eval "$service_stop_cmd" + if [ -n "$service_debug_cmd" ] ; then + eval "$service_debug_cmd" + fi + background_with_logging eval "$service_start_cmd" +} + +###################################################### +# Check an RPC service with rpcinfo +###################################################### +ctdb_check_rpc () +{ + _progname="$1" # passed to rpcinfo (looked up in /etc/rpc) + _version="$2" # optional, not passed if empty/unset + _family="${3:-tcp}" # optional, default is "tcp" + + case "$_family" in + tcp6|udp6) + _localhost="${CTDB_RPCINFO_LOCALHOST6:-::1}" + ;; + *) + _localhost="${CTDB_RPCINFO_LOCALHOST:-127.0.0.1}" + esac + + # $_version is not quoted because it is optional + # shellcheck disable=SC2086 + if ! ctdb_check_rpc_out=$(rpcinfo -T "$_family" "$_localhost" \ + "$_progname" $_version 2>&1) ; then + ctdb_check_rpc_out="$_progname failed RPC check: +$ctdb_check_rpc_out" + echo "$ctdb_check_rpc_out" + return 1 + fi +} + +nfs_check_rpcinfo () +{ + _progname="$1" # passed to rpcinfo (looked up in /etc/rpc) + _versions="$2" # optional, space separated, not passed if empty/unset + _families="${3:-tcp}" # optional, space separated, default is "tcp" + + for _family in $_families ; do + if [ -n "$_versions" ] ; then + for _version in $_versions ; do + ctdb_check_rpc "$_progname" "$_version" "$_family" || return $? + done + else + ctdb_check_rpc "$_progname" "" "$_family" || return $? + fi + done +} + +################################################################## +# use statd-callout to update NFS lock info +################################################################## +nfs_update_lock_info () +{ + if [ -x "$CTDB_BASE/statd-callout" ] ; then + "$CTDB_BASE/statd-callout" update + fi +} + +###################################################################### + +nfs_callout_init "$service_state_dir" + +is_ctdb_managed_service || exit 0 + +case "$1" in +startup) + nfs_callout "$@" || exit $? + ;; + +shutdown) + nfs_callout "$@" || exit $? + ;; + +takeip) + nfs_callout "$@" || exit $? + ctdb_service_set_reconfigure + ;; + +releaseip) + nfs_callout "$@" || exit $? + ctdb_service_set_reconfigure + ;; + +ipreallocated) + if ctdb_service_needs_reconfigure ; then + ctdb_service_reconfigure + fi + ;; + +monitor) + nfs_callout "monitor-pre" || exit $? + + # Check that directories for shares actually exist + if [ "$CTDB_NFS_SKIP_SHARE_CHECK" != "yes" ] ; then + nfs_callout "monitor-list-shares" | ctdb_check_directories || \ + exit $? + fi + + update_tickles 2049 + nfs_update_lock_info + + nfs_check_services + + nfs_callout "monitor-post" || exit $? + ;; +esac + +exit 0 diff --git a/ctdb/events.d/70.iscsi b/ctdb/events.d/70.iscsi new file mode 100755 index 0000000..8851c59 --- /dev/null +++ b/ctdb/events.d/70.iscsi @@ -0,0 +1,89 @@ +#!/bin/sh + +# CTDB event script for TGTD based iSCSI + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +# service_name is used by various functions +# shellcheck disable=SC2034 +service_name="iscsi" + +loadconfig + +is_ctdb_managed_service || exit 0 + +[ -z "$CTDB_START_ISCSI_SCRIPTS" ] && { + echo "No iscsi start script directory found" + exit 0 +} + +case "$1" in +ipreallocated) + all_ips=$($CTDB -X ip | tail -n +2) + + # Block the iSCSI port. Only block for the address families + # we have configured. This copes with, for example, ip6tables + # being unavailable on an IPv4-only system. + have_ipv4=false + have_ipv6=false + # x is intentionally ignored + # shellcheck disable=SC2034 + while IFS='|' read x ip pnn x ; do + case "$ip" in + *:*) have_ipv6=true ;; + *) have_ipv4=true ;; + esac + done </dev/null 2>/dev/null + + pnn=$(ctdb_get_pnn) + [ -n "$pnn" ] || die "Failed to get node pnn" + + # Start iSCSI daemon + tgtd >/dev/null 2>&1 + + # Run a script for each currently hosted public IP address + ips=$(echo "$all_ips" | awk -F'|' -v pnn="$pnn" '$3 == pnn {print $2}') + for ip in $ips ; do + script="${CTDB_START_ISCSI_SCRIPTS}/${ip}.sh" + if [ -x "$script" ] ; then + echo "Starting iSCSI service for public address ${ip}" + "$script" + fi + done + + # Unblock iSCSI port. These can be unconditional (compared to + # blocking above), since errors are redirected. + while iptables -D INPUT -p tcp --dport 3260 -j DROP >/dev/null 2>&1 ; do + : + done + while ip6tables -D INPUT -p tcp --dport 3260 -j DROP >/dev/null 2>&1 ; do + : + done + + ;; + +shutdown) + # Shutdown iSCSI daemon when ctdb goes down + killall -9 tgtd >/dev/null 2>&1 + ;; + +monitor) + ctdb_check_tcp_ports 3260 || exit $? + ;; +esac + +exit 0 diff --git a/ctdb/events.d/91.lvs b/ctdb/events.d/91.lvs new file mode 100755 index 0000000..9725ee8 --- /dev/null +++ b/ctdb/events.d/91.lvs @@ -0,0 +1,128 @@ +#!/bin/sh +# script to manage the lvs ip multiplexer for a single public address cluster + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig ctdb + +[ -n "$CTDB_LVS_NODES" ] || exit 0 +export CTDB_LVS_NODES + +# type is commonly supported and more portable than which(1) +# shellcheck disable=SC2039 +if ! type ipvsadm >/dev/null 2>&1 ; then + echo "LVS configured but ipvsadm not found" + exit 0 +fi + + +lvs_slave_only () +{ + _ip_address=$(ctdb_get_ip_address) + awk -v my_ip="$_ip_address" \ + '$1 == my_ip { if ($2 ~ "slave-only") { exit 0 } else { exit 1 } }' \ + "$CTDB_LVS_NODES" +} + +lvs_check_config () +{ + [ -r "$CTDB_LVS_NODES" ] || \ + die "error: CTDB_LVS_NODES=${CTDB_LVS_NODES} unreadable" + [ -n "$CTDB_LVS_PUBLIC_IP" ] || \ + die "Invalid configuration: CTDB_LVS_PUBLIC_IP not set" + if ! lvs_slave_only ; then + [ -n "$CTDB_LVS_PUBLIC_IFACE" ] || \ + die "Invalid configuration: CTDB_LVS_PUBLIC_IFACE not set" + fi + + if [ "$CTDB_PARTIALLY_ONLINE_INTERFACES" = "yes" ] ; then + die "Invalid configuration: CTDB_PARTIALLY_ONLINE_INTERFACES=yes incompatible with LVS" + fi +} + +case "$1" in +setup) + lvs_check_config + ;; +startup) + lvs_check_config + + ipvsadm -D -t "$CTDB_LVS_PUBLIC_IP" >/dev/null 2>&1 + ipvsadm -D -u "$CTDB_LVS_PUBLIC_IP" >/dev/null 2>&1 + + ip addr add "${CTDB_LVS_PUBLIC_IP}/32" dev lo scope host + + # do not respond to ARPs that are for ip addresses with scope 'host' + set_proc_maybe sys/net/ipv4/conf/all/arp_ignore 3 + # do not send out arp requests from loopback addresses + set_proc_maybe sys/net/ipv4/conf/all/arp_announce 2 + ;; + +shutdown) + lvs_check_config + + ipvsadm -D -t "$CTDB_LVS_PUBLIC_IP" + ipvsadm -D -u "$CTDB_LVS_PUBLIC_IP" + + ip addr del "${CTDB_LVS_PUBLIC_IP}/32" dev lo >/dev/null 2>&1 + + flush_route_cache + ;; + +ipreallocated) + lvs_check_config + + # Kill connections + ipvsadm -D -t "$CTDB_LVS_PUBLIC_IP" >/dev/null 2>&1 + ipvsadm -D -u "$CTDB_LVS_PUBLIC_IP" >/dev/null 2>&1 + kill_tcp_connections_local_only \ + "$CTDB_LVS_PUBLIC_IFACE" "$CTDB_LVS_PUBLIC_IP" + + pnn=$(ctdb_get_pnn) + lvsmaster=$("${CTDB_HELPER_BINDIR}/ctdb_lvs" master) + if [ "$pnn" != "$lvsmaster" ] ; then + # This node is not the LVS master so change the IP address + # to have scope "host" so this node won't respond to ARPs + ip addr del "${CTDB_LVS_PUBLIC_IP}/32" dev lo >/dev/null 2>&1 + ip addr add "${CTDB_LVS_PUBLIC_IP}/32" dev lo scope host + exit 0 + fi + + # Change the scope so this node starts responding to ARPs + ip addr del "${CTDB_LVS_PUBLIC_IP}/32" dev lo >/dev/null 2>&1 + ip addr add "${CTDB_LVS_PUBLIC_IP}/32" dev lo >/dev/null 2>&1 + + ipvsadm -A -t "$CTDB_LVS_PUBLIC_IP" -p 1999999 -s lc + ipvsadm -A -u "$CTDB_LVS_PUBLIC_IP" -p 1999999 -s lc + + # Add all nodes (except this node) as LVS servers + "${CTDB_HELPER_BINDIR}/ctdb_lvs" list | + awk -v pnn="$pnn" '$1 != pnn { print $2 }' | + while read ip ; do + ipvsadm -a -t "$CTDB_LVS_PUBLIC_IP" -r "$ip" -g + ipvsadm -a -u "$CTDB_LVS_PUBLIC_IP" -r "$ip" -g + done + + # Add localhost too... + ipvsadm -a -t "$CTDB_LVS_PUBLIC_IP" -r 127.0.0.1 + ipvsadm -a -u "$CTDB_LVS_PUBLIC_IP" -r 127.0.0.1 + + $CTDB gratarp \ + "$CTDB_LVS_PUBLIC_IP" "$CTDB_LVS_PUBLIC_IFACE" >/dev/null 2>&1 + + flush_route_cache + ;; + +monitor) + lvs_check_config + + if [ -n "$CTDB_LVS_PUBLIC_IFACE" ] ; then + interface_monitor "$CTDB_LVS_PUBLIC_IFACE" || exit 1 + fi + ;; +esac + +exit 0 diff --git a/ctdb/events.d/99.timeout b/ctdb/events.d/99.timeout new file mode 100755 index 0000000..4aa5940 --- /dev/null +++ b/ctdb/events.d/99.timeout @@ -0,0 +1,25 @@ +#!/bin/sh +# +# Event script to just sleep longer than the timeout +# in the monitor action. The purpose is to trigger +# the event timeout mechanism. + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; dirname "$PWD") + +. "${CTDB_BASE}/functions" + +loadconfig ctdb + +[ "$CTDB_RUN_TIMEOUT_MONITOR" = "yes" ] || exit 0 + +case "$1" in +monitor) + TIMEOUT=$($CTDB getvar EventScriptTimeout | awk '{print $3}') + echo "sleeping for $((TIMEOUT * 2)) seconds..." + sleep $((TIMEOUT * 2)) + ;; +esac + +exit 0 + diff --git a/ctdb/events.d/README b/ctdb/events.d/README new file mode 100644 index 0000000..69f5904 --- /dev/null +++ b/ctdb/events.d/README @@ -0,0 +1,193 @@ +The events.d/ directory contains event scripts used by CTDB. Event +scripts are triggered on certain events, such as startup, monitoring +or public IP allocation. Scripts may be specific to services, +networking or internal CTDB operations. + +All event scripts start with the prefix 'NN.' where N is a digit. The +event scripts are run in sequence based on NN. Thus 10.interface will +be run before 60.nfs. It is recommended to keep each NN unique. +However, scripts with the same NN prefix will be executed in +alphanumeric sort order. + +As a special case, any eventscript that ends with a '~' character will be +ignored since this is a common postfix that some editors will append to +older versions of a file. Similarly, any eventscript with multiple '.'s +will be ignored as package managers can create copies with additional +suffix starting with '.' (e.g. .rpmnew, .dpkg-dist). + +Only executable event scripts are run by CTDB. Any event script that +does not have execute permission is ignored. + +The eventscripts are called with varying number of arguments. The +first argument is the event name and the rest of the arguments depend +on the event name. + +Event scripts must return 0 for success and non-zero for failure. + +Output of event scripts is logged. On failure the output of the +failing event script is included in the output of "ctdb scriptstatus". + +The following events are supported (with arguments shown): + +init + + This event is triggered once when CTDB is starting up. This + event is used to do some basic cleanup and initialisation. + + During the "init" event CTDB is not listening on its Unix + domain socket, so the "ctdb" CLI will not work. + + Failure of this event will cause CTDB to terminate. + + Example: 00.ctdb creates $CTDB_SCRIPT_VARDIR + +setup + + This event is triggered once, after the "init" event has + completed. + + For this and any subsequent events the CTDB Unix domain socket + is available, so the "ctdb" CLI will work. + + Failure of this event will cause CTDB to terminate. + + Example: 00.ctdb processes tunables defined in the CTDB + configuration using CTDB_SET_=. + +startup + + This event is triggered after the "setup" event has completed + and CTDB has finished its initial database recovery. + + This event starts all services that are managed by CTDB. Each + service that is managed by CTDB should implement this event + and use it to (re)start the service. + + If the "startup" event fails then CTDB will retry it until it + succeeds. There is no limit on the number of retries. + + Example: 50.samba uses this event to start the Samba daemon if + CTDB_MANAGES_SAMBA=yes. + +shutdown + + This event is triggered when CTDB is shutting down. + + This event shuts down all services that are managed by CTDB. + Each service that is managed by CTDB should implement this + event and use it to stop the service. + + Example: 50.samba uses this event to shut down the Samba + daemon if CTDB_MANAGES_SAMBA=yes. + +monitor + + This event is run periodically. The interval between + successive "monitor" events is configured using the + MonitorInterval tunable, which defaults to 15 seconds. + + This event is triggered by CTDB to continuously monitor that + all managed services are healthy. If all event scripts + complete then the monitor event successfully then the node is + marked HEALTHY. If any event script fails then no subsequent + scripts will be run for that event and the node is marked + UNHEALTHY. + + Each service that is managed by CTDB should implement this + event and use it to monitor the service. + + Example: 10.interface checks that each configured interface + for public IP addresses has a physical link established. + +startrecovery + + This event is triggered every time a database recovery process + is started. + + This is rarely used. + +recovered + + This event is triggered every time a database recovery process + is completed. + + This is rarely used. + +takeip + + This event is triggered for each public IP address taken by a + node during IP address (re)assignment. Multiple "takeip" + events can be run in parallel if multiple IP addresses are + being assigned. + + Example: In 10.interface the "ip" command (from the Linux + iproute2 package) is used to add the specified public IP + address to the specified interface. The "ip" command can + safely be run concurrently. However, the "iptables" command + cannot be run concurrently so a wrapper is used to serialise + runs using exclusive locking. + + If substantial work is required to reconfigure a service when + a public IP address is taken over it can be better to defer + service reconfiguration to the "ipreallocated" event, after + all IP addresses have been assigned. + + Example: 60.nfs uses ctdb_service_set_reconfigure() to flag + that public IP addresses have changed so that service + reconfiguration will occur in the "ipreallocated" event. + +releaseip + + This event is triggered for each public IP address released by + a node during IP address (re)assignment. Multiple "releaseip" + events can be run in parallel if multiple IP addresses are + being unassigned. + + In all other regards, this event is analogous to the "takeip" + event above. + +updateip + + This event is triggered for each public IP address moved + between interfaces on a node during IP address (re)assignment. + Multiple "updateip" events can be run in parallel if multiple + IP addresses are being moved. + + This event is only used if multiple interfaces are capable of + hosting an IP address, as specified in the public addresses + configuration file. + + This event is similar to the "takeip" event above. + +ipreallocated + + This event is triggered after "releaseip", "takeip" and + "updateip" events during public IP address (re)assignment. + + This event is used to reconfigure services. + + This event runs even if public IP addresses on a node have not + been changed. This allows reconfiguration to depend on the + states of other nodes rather that just IP addresses. + + Example: 11.natgw recalculates the NAT gateway master and + updates the relevant network configuration on each node if the + NAT gateway master has changed. + +Additional notes for "takeip", "releaseip", "updateip", +ipreallocated": + +* Failure of any of these events causes IP allocation to be retried. + +* The "ipreallocated" event is run on all nodes. It is even run if no + "takeip", "releaseip" or "updateip" events were triggered. + +* An event script can use ctdb_service_set_reconfigure() in "takeip" + or "releaseip" events to flag that its service needs to be + reconfigured. The event script can then define a + service_reconfigure() function, which will be implicitly run before + the "ipreallocated" event. This is a useful way of performing + reconfiguration that is conditional upon public IP address changes. + + This means an explicit "ipreallocated" event handler is usually not + necessary. diff --git a/ctdb/functions b/ctdb/functions new file mode 100644 index 0000000..3c2a962 --- /dev/null +++ b/ctdb/functions @@ -0,0 +1,1020 @@ +# Hey Emacs, this is a -*- shell-script -*- !!! + +# utility functions for ctdb event scripts + +if [ -z "$CTDB_BASE" ] ; then + echo 'CTDB_BASE unset in CTDB functions file' + exit 1 +fi +export CTDB_BASE + +# CTDB_VARDIR is used elsewhere +# shellcheck disable=SC2034 +CTDB_VARDIR="/var/lib/ctdb" +ctdb_rundir="/var/run/ctdb" + +CTDB="${CTDB:-/usr/bin/ctdb}" + +# Only (and always) override these variables in test code + +if [ -z "$CTDB_SCRIPT_VARDIR" ] ; then + CTDB_SCRIPT_VARDIR="/var/lib/ctdb/state" +fi + +if [ -z "$CTDB_SYS_ETCDIR" ] ; then + CTDB_SYS_ETCDIR="/etc" +fi + +if [ -z "$CTDB_HELPER_BINDIR" ] ; then + CTDB_HELPER_BINDIR="/usr/lib/x86_64-linux-gnu/ctdb" +fi + +####################################### +# pull in a system config file, if any + +rewrite_ctdb_options () +{ + case "$CTDB_DBDIR" in + tmpfs|tmpfs:*) + _opts_defaults="mode=700" + # Get any extra options specified after colon + if [ "$CTDB_DBDIR" = "tmpfs" ] ; then + _opts="" + else + _opts="${CTDB_DBDIR#tmpfs:}" + fi + # It is OK to repeat mount options - last value wins. + # CTDB_DBDIR_TMPFS_OPTIONS is used by ctdbd_wrapper + # shellcheck disable=SC2034 + CTDB_DBDIR_TMPFS_OPTIONS="${_opts_defaults}${_opts:+,}${_opts}" + + CTDB_DBDIR="${ctdb_rundir}/CTDB_DBDIR" + ;; + *) + # shellcheck disable=SC2034 + CTDB_DBDIR_TMPFS_OPTIONS="" + esac +} + +_loadconfig() { + + if [ -z "$1" ] ; then + foo="${service_config:-${service_name}}" + if [ -n "$foo" ] ; then + loadconfig "$foo" + return + fi + fi + + if [ "$1" != "ctdb" ] ; then + loadconfig "ctdb" + fi + + if [ -z "$1" ] ; then + return + fi + + if [ -f "${CTDB_SYS_ETCDIR}/sysconfig/$1" ]; then + . "${CTDB_SYS_ETCDIR}/sysconfig/$1" + elif [ -f "${CTDB_SYS_ETCDIR}/default/$1" ]; then + . "${CTDB_SYS_ETCDIR}/default/$1" + elif [ -f "${CTDB_BASE}/sysconfig/$1" ]; then + . "${CTDB_BASE}/sysconfig/$1" + fi + + if [ "$1" = "ctdb" ] ; then + _config="${CTDBD_CONF:-${CTDB_BASE}/ctdbd.conf}" + if [ -r "$_config" ] ; then + . "$_config" + fi + rewrite_ctdb_options + fi +} + +loadconfig () { + _loadconfig "$@" +} + +############################################################## + +die () +{ + _msg="$1" + _rc="${2:-1}" + + echo "$_msg" >&2 + exit "$_rc" +} + +# Log given message or stdin to either syslog or a CTDB log file +# $1 is the tag passed to logger if syslog is in use. +script_log () +{ + _tag="$1" ; shift + + case "$CTDB_LOGGING" in + file:*|"") + if [ -n "$CTDB_LOGGING" ] ; then + _file="${CTDB_LOGGING#file:}" + else + _file="/var/log/ctdb/log.ctdb" + fi + { + if [ -n "$*" ] ; then + echo "$*" + else + cat + fi + } >>"$_file" + ;; + *) + # Handle all syslog:* variants here too. There's no tool to do + # the lossy things, so just use logger. + logger -t "ctdbd: ${_tag}" "$@" + ;; + esac +} + +# When things are run in the background in an eventscript then logging +# output might get lost. This is the "solution". :-) +background_with_logging () +{ + ( + "$@" 2>&1 "$_pnn_file" + fi + + cat "$_pnn_file" +} + +# Cached retrieval of private IP address from local node. This never +# changes. +ctdb_get_ip_address () +{ + _ip_addr_file="${CTDB_SCRIPT_VARDIR}/my-ip-address" + if [ ! -f "$_ip_addr_file" ] ; then + $CTDB -X nodestatus | + awk -F '|' 'NR == 2 { print $3 }' >"$_ip_addr_file" + fi + + # ip_address is used by caller + # shellcheck disable=SC2034 + cat "$_ip_addr_file" +} + +###################################################### +# wrapper around /proc/ settings to allow them to be hooked +# for testing +# 1st arg is relative path under /proc/, 2nd arg is value to set +set_proc () +{ + echo "$2" >"/proc/$1" +} + +set_proc_maybe () +{ + if [ -w "/proc/$1" ] ; then + set_proc "$1" "$2" + fi +} + +###################################################### +# wrapper around getting file contents from /proc/ to allow +# this to be hooked for testing +# 1st arg is relative path under /proc/ +get_proc () +{ + cat "/proc/$1" +} + +###################################################### +# Print up to $_max kernel stack traces for processes named $_program +program_stack_traces () +{ + _prog="$1" + _max="${2:-1}" + + _count=1 + for _pid in $(pidof "$_prog") ; do + [ "$_count" -le "$_max" ] || break + + # Do this first to avoid racing with process exit + _stack=$(get_proc "${_pid}/stack" 2>/dev/null) + if [ -n "$_stack" ] ; then + echo "Stack trace for ${_prog}[${_pid}]:" + echo "$_stack" + _count=$((_count + 1)) + fi + done +} + +###################################################### +# Ensure $service_name is set +assert_service_name () +{ + [ -n "$service_name" ] || die "INTERNAL ERROR: \$service_name not set" +} + +###################################################### +# check a set of directories is available +# return 1 on a missing directory +# directories are read from stdin +###################################################### +ctdb_check_directories_probe() +{ + while IFS="" read d ; do + case "$d" in + *%*) + continue + ;; + *) + [ -d "${d}/." ] || return 1 + esac + done +} + +###################################################### +# check a set of directories is available +# directories are read from stdin +###################################################### +ctdb_check_directories() +{ + ctdb_check_directories_probe || { + echo "ERROR: $service_name directory \"$d\" not available" + exit 1 + } +} + +###################################################### +# check a set of tcp ports +# usage: ctdb_check_tcp_ports +###################################################### + +# Check whether something is listening on all of the given TCP ports +# using the "ctdb checktcpport" command. +ctdb_check_tcp_ports() +{ + if [ -z "$1" ] ; then + echo "INTERNAL ERROR: ctdb_check_tcp_ports - no ports specified" + exit 1 + fi + + for _p ; do # process each function argument (port) + _cmd="$CTDB checktcpport $_p" + _out=$($_cmd 2>&1) + _ret=$? + case "$_ret" in + 0) + echo "$service_name not listening on TCP port $_p" + return 1 + ;; + 98) + # Couldn't bind, something already listening, next port + continue + ;; + *) + echo "unexpected error (${_ret}) running \"${_cmd}\"" + if [ -n "$_out" ] ; then + echo "$_out" + fi + return $_ret + ;; + esac + done + + # All ports listening + return 0 +} + +###################################################### +# check a unix socket +# usage: ctdb_check_unix_socket SERVICE_NAME +###################################################### +ctdb_check_unix_socket() { + socket_path="$1" + [ -z "$socket_path" ] && return + + if ! netstat --unix -a -n | grep -q "^unix.*LISTEN.*${socket_path}$"; then + echo "ERROR: $service_name socket $socket_path not found" + return 1 + fi +} + +################################################ +# kill off any TCP connections with the given IP +################################################ +kill_tcp_connections () +{ + _iface="$1" + _ip="$2" + + _oneway=false + if [ "$3" = "oneway" ] ; then + _oneway=true + fi + + get_tcp_connections_for_ip "$_ip" | { + _killcount=0 + _connections="" + _nl=" +" + while read _dst _src; do + _destport="${_dst##*:}" + __oneway=$_oneway + case $_destport in + # we only do one-way killtcp for CIFS + 139|445) __oneway=true ;; + esac + + _connections="${_connections}${_nl}${_src} ${_dst}" + if ! $__oneway ; then + _connections="${_connections}${_nl}${_dst} ${_src}" + fi + + _killcount=$((_killcount + 1)) + done + + if [ $_killcount -eq 0 ] ; then + return + fi + + echo "$_connections" | \ + "${CTDB_HELPER_BINDIR}/ctdb_killtcp" "$_iface" || { + echo "Failed to kill TCP connections" + return + } + + _connections=$(get_tcp_connections_for_ip "$_ip") + if [ -z "$_connections" ] ; then + _remaining=0 + else + _remaining=$(echo "$_connections" | wc -l) + fi + + _actually_killed=$((_killcount - _remaining)) + + _t="${_actually_killed}/${_killcount}" + echo "Killed ${_t} TCP connections to released IP $_ip" + + if [ -n "$_connections" ] ; then + echo "Remaining connections:" + echo "$_connections" | sed -e 's|^| |' + fi + } +} + +################################################################## +# kill off the local end for any TCP connections with the given IP +################################################################## +kill_tcp_connections_local_only () +{ + kill_tcp_connections "$@" "oneway" +} + +################################################################## +# tickle any TCP connections with the given IP +################################################################## +tickle_tcp_connections () +{ + _ip="$1" + + # Get connections, both directions + _conns=$(get_tcp_connections_for_ip "$_ip" | \ + awk '{ print $1, $2 ; print $2, $1 }') + + echo "$_conns" | awk '{ print "Tickle TCP connection", $1, $2 }' + echo "$_conns" | ctdb tickle +} + +get_tcp_connections_for_ip () +{ + _ip="$1" + + ss -tn state established "src [$_ip]" | awk 'NR > 1 {print $3, $4}' +} + +######################################################## + +add_ip_to_iface () +{ + _iface=$1 + _ip=$2 + _maskbits=$3 + + # Ensure interface is up + ip link set "$_iface" up || \ + die "Failed to bringup interface $_iface" + + # Only need to define broadcast for IPv4 + case "$_ip" in + *:*) _bcast="" ;; + *) _bcast="brd +" ;; + esac + + # Intentionally unquoted multi-word value here + # shellcheck disable=SC2086 + ip addr add "$_ip/$_maskbits" $_bcast dev "$_iface" || { + echo "Failed to add $_ip/$_maskbits on dev $_iface" + return 1 + } + + # Wait 5 seconds for IPv6 addresses to stop being tentative... + if [ -z "$_bcast" ] ; then + for _x in $(seq 1 10) ; do + ip addr show to "${_ip}/128" | grep -q "tentative" || break + sleep 0.5 + done + + # If the address was a duplicate then it won't be on the + # interface so flag an error. + _t=$(ip addr show to "${_ip}/128") + case "$_t" in + "") + echo "Failed to add $_ip/$_maskbits on dev $_iface" + return 1 + ;; + *tentative*|*dadfailed*) + echo "Failed to add $_ip/$_maskbits on dev $_iface" + ip addr del "$_ip/$_maskbits" dev "$_iface" + return 1 + ;; + esac + fi +} + +delete_ip_from_iface() +{ + _iface=$1 + _ip=$2 + _maskbits=$3 + + # This could be set globally for all interfaces but it is probably + # better to avoid surprises, so limit it the interfaces where CTDB + # has public IP addresses. There isn't anywhere else convenient + # to do this so just set it each time. This is much cheaper than + # remembering and re-adding secondaries. + set_proc "sys/net/ipv4/conf/${_iface}/promote_secondaries" 1 + + ip addr del "$_ip/$_maskbits" dev "$_iface" || { + echo "Failed to del $_ip on dev $_iface" + return 1 + } +} + +# If the given IP is hosted then print 2 items: maskbits and iface +ip_maskbits_iface () +{ + _addr="$1" + + case "$_addr" in + *:*) _bits=128 ;; + *) _bits=32 ;; + esac + ip addr show to "${_addr}/${_bits}" 2>/dev/null | \ + awk 'NR == 1 { iface = $2; sub(":$", "", iface) ; + sub("@.*", "", iface) } + $1 ~ /inet/ { mask = $2; sub(".*/", "", mask); + print mask, iface }' +} + +drop_ip () +{ + _addr="${1%/*}" # Remove optional maskbits + + # Intentional word splitting here + # shellcheck disable=SC2046 + set -- $(ip_maskbits_iface "$_addr") + if [ -n "$1" ] ; then + _maskbits="$1" + _iface="$2" + echo "Removing public address $_addr/$_maskbits from device $_iface" + delete_ip_from_iface "$_iface" "$_addr" "$_maskbits" >/dev/null 2>&1 + fi +} + +drop_all_public_ips () +{ + # _x is intentionally ignored + # shellcheck disable=SC2034 + while read _ip _x ; do + drop_ip "$_ip" + done <"${CTDB_PUBLIC_ADDRESSES:-/dev/null}" +} + +flush_route_cache () +{ + set_proc_maybe sys/net/ipv4/route/flush 1 + set_proc_maybe sys/net/ipv6/route/flush 1 +} + +######################################################## +# Interface monitoring + +# If the interface is a virtual one (e.g. VLAN) then get the +# underlying interface +interface_get_real () +{ + # Output of "ip link show " + _iface_info="$1" + + # Extract the full interface description to see if it is a VLAN + _t=$(echo "$_iface_info" | + awk 'NR == 1 { iface = $2; sub(":$", "", iface) ; + print iface }') + case "$_t" in + *@*) + # VLAN: use the underlying interface, after the '@' + echo "${_t##*@}" + ;; + *) + # Not a regular VLAN. For backward compatibility, assume + # there is some other sort of VLAN that doesn't have the + # '@' in the output and only use what is before a '.'. If + # there is no '.' then this will be the whole interface + # name. + echo "${_t%%.*}" + esac +} + +# Check whether an interface is operational +interface_monitor () +{ + _iface="$1" + + _iface_info=$(ip link show "$_iface" 2>&1) || { + echo "ERROR: Monitored interface ${_iface} does not exist" + return 1 + } + + + # If the interface is a virtual one (e.g. VLAN) then get the + # underlying interface. + _realiface=$(interface_get_real "$_iface_info") + + if _bi=$(get_proc "net/bonding/${_realiface}" 2>/dev/null) ; then + # This is a bond: various monitoring strategies + echo "$_bi" | grep -q 'Currently Active Slave: None' && { + echo "ERROR: No active slaves for bond device ${_realiface}" + return 1 + } + echo "$_bi" | grep -q '^MII Status: up' || { + echo "ERROR: public network interface ${_realiface} is down" + return 1 + } + echo "$_bi" | grep -q '^Bonding Mode: IEEE 802.3ad Dynamic link aggregation' && { + # This works around a bug in the driver where the + # overall bond status can be up but none of the actual + # physical interfaces have a link. + echo "$_bi" | grep 'MII Status:' | tail -n +2 | grep -q '^MII Status: up' || { + echo "ERROR: No active slaves for 802.ad bond device ${_realiface}" + return 1 + } + } + + return 0 + else + # Not a bond + case "$_iface" in + lo*) + # loopback is always working + return 0 + ;; + ib*) + # we don't know how to test ib links + return 0 + ;; + *) + ethtool "$_iface" | grep -q 'Link detected: yes' || { + # On some systems, this is not successful when a + # cable is plugged but the interface has not been + # brought up previously. Bring the interface up + # and try again... + ip link set "$_iface" up + ethtool "$_iface" | grep -q 'Link detected: yes' || { + echo "ERROR: No link on the public network interface ${_iface}" + return 1 + } + } + return 0 + ;; + esac + fi +} + +######################################################## +# Simple counters +_ctdb_counter_common () { + _service_name="${1:-${service_name:-${script_name}}}" + _counter_file="${CTDB_SCRIPT_VARDIR}/failcount/${_service_name}" + mkdir -p "${_counter_file%/*}" # dirname +} +# Some code passes an argument +# shellcheck disable=SC2120 +ctdb_counter_init () { + _ctdb_counter_common "$1" + + >"$_counter_file" +} +ctdb_counter_incr () { + _ctdb_counter_common "$1" + + # unary counting using newlines! + echo >>"$_counter_file" +} +ctdb_counter_get () { + _ctdb_counter_common "$1" + # unary counting! + stat -c "%s" "$_counter_file" 2>/dev/null || echo 0 +} + +######################################################## + +ctdb_setup_service_state_dir () +{ + _s="${1:-${service_name}}" + + _service_state_dir="${CTDB_SCRIPT_VARDIR}/service_state/${_s}" + mkdir -p "$_service_state_dir" || + die "Error creating state dir \"${_service_state_dir}\"" + + echo "$_service_state_dir" +} + +################################################################## +# Reconfigure a service on demand + +_ctdb_service_reconfigure_common () +{ + _d="${CTDB_SCRIPT_VARDIR}/service_status/${service_name}" + mkdir -p "$_d" + _ctdb_service_reconfigure_flag="$_d/reconfigure" +} + +ctdb_service_needs_reconfigure () +{ + _ctdb_service_reconfigure_common + [ -e "$_ctdb_service_reconfigure_flag" ] +} + +ctdb_service_set_reconfigure () +{ + _ctdb_service_reconfigure_common + >"$_ctdb_service_reconfigure_flag" +} + +ctdb_service_unset_reconfigure () +{ + _ctdb_service_reconfigure_common + rm -f "$_ctdb_service_reconfigure_flag" +} + +ctdb_service_reconfigure () +{ + echo "Reconfiguring service \"${service_name}\"..." + ctdb_service_unset_reconfigure + service_reconfigure || return $? + # Intentionally have this use $service_name as default + # shellcheck disable=SC2119 + ctdb_counter_init +} + +# Default service_reconfigure() function does nothing. +service_reconfigure () +{ + : +} + +################################################################## +# Does CTDB manage this service? + +ctdb_compat_managed_service () +{ + if [ "$1" = "yes" -a "$2" = "$service_name" ] ; then + CTDB_MANAGED_SERVICES="$CTDB_MANAGED_SERVICES $2" + fi +} + +is_ctdb_managed_service () +{ + assert_service_name + + # $t is used just for readability and to allow better accurate + # matching via leading/trailing spaces + t=" $CTDB_MANAGED_SERVICES " + + # Return 0 if "$service_name" appears in $t + if [ "${t#* ${service_name} }" != "${t}" ] ; then + return 0 + fi + + # If above didn't match then update $CTDB_MANAGED_SERVICES for + # backward compatibility and try again. + ctdb_compat_managed_service "$CTDB_MANAGES_VSFTPD" "vsftpd" + ctdb_compat_managed_service "$CTDB_MANAGES_SAMBA" "samba" + ctdb_compat_managed_service "$CTDB_MANAGES_WINBIND" "winbind" + ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD" "apache2" + ctdb_compat_managed_service "$CTDB_MANAGES_HTTPD" "httpd" + ctdb_compat_managed_service "$CTDB_MANAGES_ISCSI" "iscsi" + ctdb_compat_managed_service "$CTDB_MANAGES_CLAMD" "clamd" + ctdb_compat_managed_service "$CTDB_MANAGES_NFS" "nfs" + + t=" $CTDB_MANAGED_SERVICES " + + # Return 0 if "$service_name" appears in $t + [ "${t#* ${service_name} }" != "${t}" ] +} + +# Default service_start() and service_stop() functions. + +# These may be overridden in an eventscript. +service_start () +{ + service "$service_name" start +} + +service_stop () +{ + service "$service_name" stop +} + +################################################################## + +# This exists only for backward compatibility with 3rd party scripts +# that call it +ctdb_standard_event_handler () +{ + : +} + +iptables_wrapper () +{ + _family="$1" ; shift + if [ "$_family" = "inet6" ] ; then + _iptables_cmd="ip6tables" + else + _iptables_cmd="iptables" + fi + + # iptables doesn't like being re-entered, so flock-wrap it. + flock -w 30 "${CTDB_SCRIPT_VARDIR}/iptables.flock" "$_iptables_cmd" "$@" +} + +# AIX (and perhaps others?) doesn't have mktemp +# type is commonly supported and more portable than which(1) +# shellcheck disable=SC2039 +if ! type mktemp >/dev/null 2>&1 ; then + mktemp () + { + _dir=false + if [ "$1" = "-d" ] ; then + _dir=true + shift + fi + _d="${TMPDIR:-/tmp}" + _hex10=$(dd if=/dev/urandom count=20 2>/dev/null | \ + md5sum | \ + sed -e 's@\(..........\).*@\1@') + _t="${_d}/tmp.${_hex10}" + ( + umask 077 + if $_dir ; then + mkdir "$_t" + else + >"$_t" + fi + ) + echo "$_t" + } +fi + +###################################################################### +# NFS callout handling + +nfs_callout_init () +{ + _state_dir="$1" + + if [ -z "$CTDB_NFS_CALLOUT" ] ; then + CTDB_NFS_CALLOUT="${CTDB_BASE}/nfs-linux-kernel-callout" + fi + # Always export, for statd callout + export CTDB_NFS_CALLOUT + + # If the callout wants to use this then it must create it + export CTDB_NFS_CALLOUT_STATE_DIR="${_state_dir}/callout-state" + + # Export, if set, for use by clustered NFS callouts + if [ -n "$CTDB_NFS_STATE_FS_TYPE" ] ; then + export CTDB_NFS_STATE_FS_TYPE + fi + if [ -n "$CTDB_NFS_STATE_MNT" ] ; then + export CTDB_NFS_STATE_MNT + fi + + nfs_callout_cache="${_state_dir}/nfs_callout_cache" + nfs_callout_cache_callout="${nfs_callout_cache}/CTDB_NFS_CALLOUT" + nfs_callout_cache_ops="${nfs_callout_cache}/ops" +} + +nfs_callout_register () +{ + mkdir -p "$nfs_callout_cache_ops" + rm -f "$nfs_callout_cache_ops"/* + + echo "$CTDB_NFS_CALLOUT" >"$nfs_callout_cache_callout" + + _t=$(eval "$CTDB_NFS_CALLOUT" "register") + if [ -n "$_t" ] ; then + echo "$_t" | + while IFS="" read _op ; do + touch "${nfs_callout_cache_ops}/${_op}" + done + else + touch "${nfs_callout_cache_ops}/ALL" + fi +} + +nfs_callout () +{ + # Re-run registration if $CTDB_NFS_CALLOUT has changed + _prev="" + if [ -r "$nfs_callout_cache_callout" ] ; then + read _prev <"$nfs_callout_cache_callout" + fi + if [ "$CTDB_NFS_CALLOUT" != "$_prev" ] ; then + nfs_callout_register + fi + + # Run the operation if it is registered... + if [ -e "${nfs_callout_cache_ops}/${1}" ] || \ + [ -e "${nfs_callout_cache_ops}/ALL" ]; then + eval "$CTDB_NFS_CALLOUT" "$@" + fi +} + +######################################################## +# tickle handling +######################################################## + +update_tickles () +{ + _port="$1" + + tickledir="${CTDB_SCRIPT_VARDIR}/tickles" + mkdir -p "$tickledir" + + # What public IPs do I hold? + _pnn=$(ctdb_get_pnn) + _ips=$($CTDB -X ip | awk -F'|' -v pnn="$_pnn" '$3 == pnn {print $2}') + + # IPs and port as ss filters + _ip_filter="" + for _ip in $_ips ; do + _ip_filter="${_ip_filter}${_ip_filter:+ || }src [${_ip}]" + done + _port_filter="sport == :${_port}" + + # Record connections to our public IPs in a temporary file. + # This temporary file is in CTDB's private state directory and + # $$ is used to avoid a very rare race involving CTDB's script + # debugging. No security issue, nothing to see here... + _my_connections="${tickledir}/${_port}.connections.$$" + # Parentheses are needed around the filters for precedence but + # the parentheses can't be empty! + ss -tn state established \ + "${_ip_filter:+( ${_ip_filter} )}" \ + "${_port_filter:+( ${_port_filter} )}" | + awk 'NR > 1 {print $4, $3}' | + sort >"$_my_connections" + + # Record our current tickles in a temporary file + _my_tickles="${tickledir}/${_port}.tickles.$$" + for _i in $_ips ; do + $CTDB -X gettickles "$_i" "$_port" | + awk -F'|' 'NR > 1 { printf "%s:%s %s:%s\n", $2, $3, $4, $5 }' + done | + sort >"$_my_tickles" + + # Add tickles for connections that we haven't already got tickles for + comm -23 "$_my_connections" "$_my_tickles" | \ + $CTDB addtickle + + # Remove tickles for connections that are no longer there + comm -13 "$_my_connections" "$_my_tickles" | \ + $CTDB deltickle + + rm -f "$_my_connections" "$_my_tickles" + + # Remove stale files from killed scripts + # Files can't have spaces in name, more portable than -print0/-0 + # shellcheck disable=SC2038 + (cd "$tickledir" && find . -type f -mmin +10 | xargs -r rm) +} + +######################################################## +# load a site local config file +######################################################## + +[ -n "$CTDB_RC_LOCAL" -a -x "$CTDB_RC_LOCAL" ] && { + . "$CTDB_RC_LOCAL" +} + +[ -x "${CTDB_BASE}/rc.local" ] && { + . "${CTDB_BASE}/rc.local" +} + +[ -d "${CTDB_BASE}/rc.local.d" ] && { + for i in "${CTDB_BASE}/rc.local.d"/* ; do + [ -x "$i" ] && . "$i" + done +} + +script_name="${0##*/}" # basename diff --git a/ctdb/gcore_trace.sh b/ctdb/gcore_trace.sh new file mode 100755 index 0000000..4d3e1d1 --- /dev/null +++ b/ctdb/gcore_trace.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +gcore -o "/var/log/core" "$1" 2>&1 | logger -t "ctdb:gcore_trace" diff --git a/ctdb/nfs-checks.d/00.portmapper.check b/ctdb/nfs-checks.d/00.portmapper.check new file mode 100644 index 0000000..24def35 --- /dev/null +++ b/ctdb/nfs-checks.d/00.portmapper.check @@ -0,0 +1,2 @@ +# portmapper +unhealthy_after=1 diff --git a/ctdb/nfs-checks.d/10.status.check b/ctdb/nfs-checks.d/10.status.check new file mode 100644 index 0000000..dfa5c59 --- /dev/null +++ b/ctdb/nfs-checks.d/10.status.check @@ -0,0 +1,7 @@ +# status +version="1" +restart_every=2 +unhealthy_after=6 +service_stop_cmd="killall -q -9 rpc.statd" +service_start_cmd="rpc.statd ${STATD_HA_CALLOUT:+-H} $STATD_HA_CALLOUT ${STATD_HOSTNAME:+-n} $STATD_HOSTNAME ${STATD_PORT:+-p} $STATD_PORT ${STATD_OUTGOING_PORT:+-o} $STATD_OUTGOING_PORT" +service_debug_cmd="program_stack_traces rpc.statd 5" diff --git a/ctdb/nfs-checks.d/20.nfs.check b/ctdb/nfs-checks.d/20.nfs.check new file mode 100644 index 0000000..dad1cdc --- /dev/null +++ b/ctdb/nfs-checks.d/20.nfs.check @@ -0,0 +1,7 @@ +# nfs +version="3" +restart_every=10 +unhealthy_after=2 +service_stop_cmd="$CTDB_NFS_CALLOUT stop nfs" +service_start_cmd="$CTDB_NFS_CALLOUT start nfs" +service_debug_cmd="program_stack_traces nfsd 5" diff --git a/ctdb/nfs-checks.d/30.nlockmgr.check b/ctdb/nfs-checks.d/30.nlockmgr.check new file mode 100644 index 0000000..6660ca0 --- /dev/null +++ b/ctdb/nfs-checks.d/30.nlockmgr.check @@ -0,0 +1,6 @@ +# nlockmgr +version="4" +restart_every=2 +unhealthy_after=6 +service_stop_cmd="$CTDB_NFS_CALLOUT stop nlockmgr" +service_start_cmd="$CTDB_NFS_CALLOUT start nlockmgr" diff --git a/ctdb/nfs-checks.d/40.mountd.check b/ctdb/nfs-checks.d/40.mountd.check new file mode 100644 index 0000000..56b3fd2 --- /dev/null +++ b/ctdb/nfs-checks.d/40.mountd.check @@ -0,0 +1,7 @@ +# mountd +version="1" +restart_every=2 +unhealthy_after=6 +service_stop_cmd="killall -q -9 rpc.mountd" +service_start_cmd="rpc.mountd $RPCMOUNTDOPTS ${MOUNTD_PORT:+-p} $MOUNTD_PORT" +service_debug_cmd="program_stack_traces rpc.mountd 5" diff --git a/ctdb/nfs-checks.d/50.rquotad.check b/ctdb/nfs-checks.d/50.rquotad.check new file mode 100644 index 0000000..b7bd9d2 --- /dev/null +++ b/ctdb/nfs-checks.d/50.rquotad.check @@ -0,0 +1,7 @@ +# rquotad +version="1" +restart_every=2 +unhealthy_after=6 +service_stop_cmd="killall -q -9 rpc.rquotad" +service_start_cmd="rpc.rquotad ${RQUOTAD_PORT:+-p} $RQUOTAD_PORT" +service_debug_cmd="program_stack_traces rpc.rquotad 5" diff --git a/ctdb/nfs-checks.d/README b/ctdb/nfs-checks.d/README new file mode 100644 index 0000000..044067a --- /dev/null +++ b/ctdb/nfs-checks.d/README @@ -0,0 +1,31 @@ +NFS check configuration files. + +Files are named NN.RPCSERVICE.check. Files without a .check suffix +are ignored. + +Supported variables are: + +* family - "tcp" or "udp" or space separated list + default: tcp, not used with "service_check_cmd" +* version - optional, RPC service version number + default is to omit to check for any version, + not used with "service_check_cmd" +* unhealthy_after - number of check fails before unhealthy + default: 1 +* restart_every - number of check fails before restart + default: 0, meaning no restart +* service_stop_cmd - command to stop service + default: no default, must be provided if + restart_every > 0 +* service_start_cmd - command to start service + default: no default, must be provided if + restart_every > 0 +* service_check_cmd - command to check health of service + default is to check RPC service using rpcinfo +* service_debug_cmd - command to debug a service after trying to stop it; + for example, it can be useful to print stack + traces of threads that have not exited, since + they may be stuck doing I/O; + no default, see also function program_stack_traces() + +Quoting inside values is not preserved. diff --git a/ctdb/nfs-linux-kernel-callout b/ctdb/nfs-linux-kernel-callout new file mode 100755 index 0000000..9b72446 --- /dev/null +++ b/ctdb/nfs-linux-kernel-callout @@ -0,0 +1,253 @@ +#!/bin/sh + +# Exit on 1st error +set -e + +# NFS exports file. Some code below keeps a cache of output derived +# from exportfs(8). When this file is updated the cache is invalid +# and needs to be regenerated. +# +# To change the file, edit the default value below. Do not set +# CTDB_NFS_EXPORTS_FILE - it isn't a configuration variable, just a +# hook for testing. +nfs_exports_file="${CTDB_NFS_EXPORTS_FILE:-/var/lib/nfs/etab}" + +# Red Hat +nfs_service="nfs" +nfslock_service="nfslock" +nfs_config="/etc/sysconfig/nfs" + +# SUSE +#nfs_service="nfsserver" +#nfslock_service="" +#nfs_config="/etc/sysconfig/nfs" + +# Debian +#nfs_service="nfs-kernel-server" +#nfslock_service="" +#nfs_config="/etc/default/nfs-kernel-server" + +# Override for unit testing +if [ -z "$PROCFS_PATH" ] ; then + PROCFS_PATH="/proc" +fi + +################################################## + +usage () +{ + _c=$(basename "$0") + cat <"${PROCFS_PATH}/fs/nfsd/threads" + basic_stop "nfs" >/dev/null 2>&1 || true + pkill -9 nfsd + ;; + nlockmgr) + basic_stop "nfslock" >/dev/null 2>&1 || true + ;; + *) + usage + esac +} + +service_start () +{ + case "$1" in + nfs) + basic_start "nfs" + ;; + nlockmgr) + basic_start "nfslock" + ;; + *) + usage + esac +} + +################################################## +# service init startup and final shutdown + +nfs_shutdown () +{ + basic_stop "nfs" +} + +nfs_startup () +{ + basic_stop "nfs" || true + basic_start "nfs" + _f="${PROCFS_PATH}/sys/net/ipv4/tcp_tw_recycle" + if [ "$_f" ] ; then + echo 1 >"$_f" + fi +} + +################################################## +# monitor-post support + +nfs_check_thread_count () +{ + # Load NFS configuration to get desired number of threads. + if [ -r "$nfs_config" ] ; then + . "$nfs_config" + fi + + # If $RPCNFSDCOUNT/$USE_KERNEL_NFSD_NUMBER isn't set then we could + # guess the default from the initscript. However, let's just + # assume that those using the default don't care about the number + # of threads and that they have switched on this feature in error. + _configured_threads="${RPCNFSDCOUNT:-${USE_KERNEL_NFSD_NUMBER}}" + [ -n "$_configured_threads" ] || return 0 + + _threads_file="${PROCFS_PATH}/fs/nfsd/threads" + + # nfsd should be running the configured number of threads. If + # there are a different number of threads then tell nfsd the + # correct number. + read _running_threads <"$_threads_file" || { + echo "WARNING: Reading \"${_threads_file}\" unexpectedly failed" + exit 0 + } + + # Intentionally not arithmetic comparison - avoids extra errors + # when above read fails in an unexpected way... + if [ "$_running_threads" != "$_configured_threads" ] ; then + echo "Attempting to correct number of nfsd threads from ${_running_threads} to ${_configured_threads}" + echo "$_configured_threads" >"$_threads_file" + fi +} + +################################################## +# list share directories + +nfs_monitor_list_shares () +{ + _cache_file="${CTDB_NFS_CALLOUT_STATE_DIR}/list_shares_cache" + # -nt operator is well supported in Linux: dash, bash, ksh, ... + # shellcheck disable=SC2039 + if [ ! -r "$nfs_exports_file" ] || [ ! -r "$_cache_file" ] || \ + [ "$nfs_exports_file" -nt "$_cache_file" ] ; then + mkdir -p "$CTDB_NFS_CALLOUT_STATE_DIR" + # We could just use the contents of $nfs_exports_file. + # However, let's regard that file as internal to NFS and use + # exportfs, which is the public API. + if ! _exports=$(exportfs -v) ; then + echo "WARNING: failed to run exportfs to list NFS shares" >&2 + return + fi + + echo "$_exports" | + grep '^/' | + sed -e 's@[[:space:]][[:space:]]*[^[:space:]()][^[:space:]()]*([^[:space:]()][^[:space:]()]*)$@@' | + sort -u >"$_cache_file" + fi + + cat "$_cache_file" +} + +################################################## + +nfs_register () +{ + cat </dev/null 2>&1 + ;; + healthy) + mail -s "$(hostname) is HEALTHY" foo@example.com /dev/null 2>&1 + ;; + esac + +When adding programs please note the exclusion patterns in notify.sh. diff --git a/ctdb/notify.sh b/ctdb/notify.sh new file mode 100755 index 0000000..d98f046 --- /dev/null +++ b/ctdb/notify.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# This script is activated by setting CTDB_NOTIFY_SCRIPT=/etc/ctdb/notify.sh +# in /etc/sysconfig/ctdb + +# This is script is invoked from ctdb when certain events happen. See +# /etc/ctdb/notify.d/README for more details. + +d=$(dirname "$0") +nd="${d}/notify.d" + +ok=true + +for i in "${nd}/"* ; do + # Don't run files matching basename + case "${i##*/}" in + *~|*,|*.rpm*|*.swp|README) continue ;; + esac + + # Files must be executable + [ -x "$i" ] || continue + + # Flag failures + "$i" "$1" || ok=false +done + +$ok diff --git a/ctdb/statd-callout b/ctdb/statd-callout new file mode 100755 index 0000000..38f847b --- /dev/null +++ b/ctdb/statd-callout @@ -0,0 +1,221 @@ +#!/bin/sh + +# This must run as root as CTDB tool commands need to access CTDB socket +[ "$(id -u)" -eq 0 ] || exec sudo "$0" "$@" + +# this script needs to be installed so that statd points to it with the -H +# command line argument. The easiest way to do that is to put something like this in +# /etc/sysconfig/nfs: +# STATD_HOSTNAME="myhostname -H /etc/ctdb/statd-callout" + +[ -n "$CTDB_BASE" ] || \ + CTDB_BASE=$(d=$(dirname "$0") ; cd -P "$d" ; echo "$PWD") + +. "${CTDB_BASE}/functions" + +# Overwrite this so we get some logging +die () +{ + script_log "statd-callout" "$@" + exit 1 +} + +loadconfig ctdb +loadconfig nfs + +[ -n "$NFS_HOSTNAME" ] || \ + die "NFS_HOSTNAME is not configured. statd-callout failed" + +# A handy newline +nl=" +" + +service_state_dir=$(ctdb_setup_service_state_dir "statd-callout") || exit $? + +cd "$service_state_dir" || \ + die "Failed to change directory to \"${service_state_dir}\"" + +pnn=$(ctdb_get_pnn) + +case "$1" in + # Keep a single file to keep track of the last "add-client" or + # "del-client'. These get pushed to ctdb.tdb during "update", + # which will generally be run once each "monitor" cycle. In this + # way we avoid scalability problems with flood of persistent + # transactions after a "notify" when all the clients re-take their + # locks. + + add-client) + # statd does not tell us to which IP the client connected so + # we must add it to all the IPs that we serve + cip="$2" + date=$(date '+%s') + # x is intentionally ignored + # shellcheck disable=SC2034 + $CTDB ip -X | + tail -n +2 | + while IFS="|" read x sip node x ; do + [ "$node" = "$pnn" ] || continue # not us + key="statd-state@${sip}@${cip}" + echo "\"${key}\" \"${date}\"" >"$key" + done + ;; + + del-client) + # statd does not tell us from which IP the client disconnected + # so we must add it to all the IPs that we serve + cip="$2" + # x is intentionally ignored + # shellcheck disable=SC2034 + $CTDB ip -X | + tail -n +2 | + while IFS="|" read x sip node x ; do + [ "$node" = "$pnn" ] || continue # not us + key="statd-state@${sip}@${cip}" + echo "\"${key}\" \"\"" >"$key" + done + ;; + + update) + files=$(echo statd-state@*) + if [ "$files" = "statd-state@*" ] ; then + # No files! + exit 0 + fi + # Filter out lines for any IP addresses that are not currently + # hosted public IP addresses. + ctdb_ips=$($CTDB ip | tail -n +2) + sed_expr=$(echo "$ctdb_ips" | + awk -v pnn="$pnn" 'pnn == $2 { + ip = $1; gsub(/\./, "\\.", ip); + printf "/statd-state@%s@/p\n", ip }') + # Intentional multi-word expansion for multiple files + # shellcheck disable=SC2086 + if sed -n "$sed_expr" $files | $CTDB ptrans "ctdb.tdb" ; then + rm $files + fi + ;; + + notify) + # we must restart the lockmanager (on all nodes) so that we get + # a clusterwide grace period (so other clients don't take out + # conflicting locks through other nodes before all locks have been + # reclaimed) + + # we need these settings to make sure that no tcp connections survive + # across a very fast failover/failback + #echo 10 > /proc/sys/net/ipv4/tcp_fin_timeout + #echo 0 > /proc/sys/net/ipv4/tcp_max_tw_buckets + #echo 0 > /proc/sys/net/ipv4/tcp_max_orphans + + # Delete the notification list for statd, we don't want it to + # ping any clients + rm -f /var/lib/nfs/statd/sm/* + rm -f /var/lib/nfs/statd/sm.bak/* + + # we must keep a monotonically increasing state variable for the entire + # cluster so state always increases when ip addresses fail from one + # node to another + # We use epoch and hope the nodes are close enough in clock. + # Even numbers mean service is shut down, odd numbers mean + # service is started. + # Intentionally round to an even number + # shellcheck disable=SC2017 + state_even=$(( $(date '+%s') / 2 * 2)) + + # We must also let some time pass between stopping and + # restarting the lock manager. Otherwise there is a window + # where the lock manager will respond "strangely" immediately + # after restarting it, which causes clients to fail to reclaim + # their locks. + nfs_callout_init + "$CTDB_NFS_CALLOUT" "stop" "nlockmgr" >/dev/null 2>&1 + sleep 2 + "$CTDB_NFS_CALLOUT" "start" "nlockmgr" >/dev/null 2>&1 + + # we now need to send out additional statd notifications to ensure + # that clients understand that the lockmanager has restarted. + # we have three cases: + # 1, clients that ignore the ip address the stat notification came from + # and ONLY care about the 'name' in the notify packet. + # these clients ONLY work with lock failover IFF that name + # can be resolved into an ipaddress that matches the one used + # to mount the share. (==linux clients) + # This is handled when starting lockmanager above, but those + # packets are sent from the "wrong" ip address, something linux + # clients are ok with, buth other clients will barf at. + # 2, Some clients only accept statd packets IFF they come from the + # 'correct' ip address. + # 2a,Send out the notification using the 'correct' ip address and also + # specify the 'correct' hostname in the statd packet. + # Some clients require both the correct source address and also the + # correct name. (these clients also ONLY work if the ip addresses + # used to map the share can be resolved into the name returned in + # the notify packet.) + # 2b,Other clients require that the source ip address of the notify + # packet matches the ip address used to take out the lock. + # I.e. that the correct source address is used. + # These clients also require that the statd notify packet contains + # the name as the ip address used when the lock was taken out. + # + # Both 2a and 2b are commonly used in lockmanagers since they maximize + # probability that the client will accept the statd notify packet and + # not just ignore it. + # For all IPs we serve, collect info and push to the config database + + # Construct a sed expression to take catdb output and produce pairs of: + # server-IP client-IP + # but only for the server-IPs that are hosted on this node. + ctdb_all_ips=$($CTDB ip all | tail -n +2) + sed_expr=$(echo "$ctdb_all_ips" | + awk -v pnn="$pnn" 'pnn == $2 { + ip = $1; gsub(/\./, "\\.", ip); + printf "s/^key.*=.*statd-state@\\(%s\\)@\\([^\"]*\\).*/\\1 \\2/p\n", ip }') + + statd_state=$($CTDB catdb ctdb.tdb | sed -n "$sed_expr" | sort) + [ -n "$statd_state" ] || exit 0 + + smnotify="${CTDB_HELPER_BINDIR}/smnotify" + prev="" + echo "$statd_state" | { + # This all needs to be in the same command group at the + # end of the pipe so it doesn't get lost when the loop + # completes. + items="" + while read sip cip ; do + # Collect item to delete from the DB + key="statd-state@${sip}@${cip}" + item="\"${key}\" \"\"" + items="${items}${items:+${nl}}${item}" + + # NOTE: Consider optimising smnotify to read all the + # data from stdin and then run it in the background. + + # Reset stateval for each serverip + [ "$sip" = "$prev" ] || stateval="$state_even" + # Send notifies for server shutdown + "$smnotify" --client="$cip" --ip="$sip" \ + --server="$sip" --stateval="$stateval" + "$smnotify" --client="$cip" --ip="$sip" \ + --server="$NFS_HOSTNAME" --stateval="$stateval" + # Send notifies for server startup + stateval=$((stateval + 1)) + "$smnotify" --client="$cip" --ip="$sip" \ + --server="$sip" --stateval="$stateval" + "$smnotify" --client="$cip" --ip="$sip" \ + --server="$NFS_HOSTNAME" --stateval="$stateval" + done + + echo "$items" | $CTDB ptrans "ctdb.tdb" + } + + # Remove any stale touch files (i.e. for IPs not currently + # hosted on this node and created since the last "update"). + # There's nothing else we can do with them at this stage. + echo "$ctdb_all_ips" | + awk -v pnn="$pnn" 'pnn != $2 { print $1 }' | + while read sip ; do + rm -f "statd-state@${sip}@"* + done + ;; +esac diff --git a/default/winbind b/default/winbind new file mode 100644 index 0000000..3ef6e88 --- /dev/null +++ b/default/winbind @@ -0,0 +1,11 @@ +# Defaults for winbind initscript +# sourced by /etc/init.d/winbind +# + +# +# This is a POSIX shell fragment +# + + +# Winbind configuration +#WINBINDD_OPTS="-n" diff --git a/group b/group index 8f52e69..7b66700 100644 --- a/group +++ b/group @@ -77,3 +77,4 @@ libvirt:x:136:frank libvirt-qemu:x:64055:libvirt-qemu libvirt-dnsmasq:x:137: mysql:x:138: +winbindd_priv:x:139: diff --git a/group- b/group- index a952acc..8f52e69 100644 --- a/group- +++ b/group- @@ -76,3 +76,4 @@ gdm:x:118: libvirt:x:136:frank libvirt-qemu:x:64055:libvirt-qemu libvirt-dnsmasq:x:137: +mysql:x:138: diff --git a/gshadow b/gshadow index a6adaca..abef6e4 100644 --- a/gshadow +++ b/gshadow @@ -77,3 +77,4 @@ libvirt:!::frank libvirt-qemu:!::libvirt-qemu libvirt-dnsmasq:!:: mysql:!:: +winbindd_priv:!:: diff --git a/gshadow- b/gshadow- index 4384bce..a6adaca 100644 --- a/gshadow- +++ b/gshadow- @@ -76,3 +76,4 @@ gdm:!:: libvirt:!::frank libvirt-qemu:!::libvirt-qemu libvirt-dnsmasq:!:: +mysql:!:: diff --git a/init.d/ctdb b/init.d/ctdb new file mode 100755 index 0000000..b9c0a6d --- /dev/null +++ b/init.d/ctdb @@ -0,0 +1,175 @@ +#!/bin/sh + +# Start and stop CTDB (Clustered TDB daemon) +# +# chkconfig: - 90 01 +# +# description: Starts and stops CTDB +# pidfile: /var/run/ctdb/ctdbd.pid +# config: /etc/sysconfig/ctdb + +### BEGIN INIT INFO +# Provides: ctdb +# Required-Start: $local_fs $syslog $network $remote_fs +# Required-Stop: $local_fs $syslog $network $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: start and stop ctdb service +# Description: Start and stop CTDB (Clustered TDB daemon) +### END INIT INFO + +# Source function library. +if [ -f /etc/init.d/functions ] ; then + # Red Hat + . /etc/init.d/functions +elif [ -f /etc/rc.d/init.d/functions ] ; then + # Red Hat + . /etc/rc.d/init.d/functions +elif [ -f /etc/rc.status ] ; then + # SUSE + . /etc/rc.status + rc_reset + LC_ALL=en_US.UTF-8 +elif [ -f /lib/lsb/init-functions ] ; then + # Debian + . /lib/lsb/init-functions +fi + +# Avoid using root's TMPDIR +unset TMPDIR + +[ -n "$CTDB_BASE" ] || export CTDB_BASE="/etc/ctdb" + +. "${CTDB_BASE}/functions" +loadconfig "network" +loadconfig "ctdb" + +# check networking is up (for redhat) +if [ "$NETWORKING" = "no" ] ; then + exit 0 +fi + +detect_init_style +export CTDB_INIT_STYLE + +ctdbd="${CTDBD:-/usr/sbin/ctdbd}" +ctdbd_wrapper="${CTDBD_WRAPPER:-/usr/sbin/ctdbd_wrapper}" +pidfile="${CTDB_PIDFILE:-/var/run/ctdb/ctdbd.pid}" + +############################################################ + +start() +{ + printf "Starting ctdbd service: " + + case "$CTDB_INIT_STYLE" in + suse) + startproc \ + "$ctdbd_wrapper" "$pidfile" "start" + rc_status -v + ;; + redhat) + daemon --pidfile "$pidfile" \ + "$ctdbd_wrapper" "$pidfile" "start" + RETVAL=$? + echo + [ $RETVAL -eq 0 ] && touch /var/lock/subsys/ctdb || RETVAL=1 + return $RETVAL + ;; + debian) + eval start-stop-daemon --start --quiet --background --exec \ + "$ctdbd_wrapper" "$pidfile" "start" + ;; + esac +} + +stop() +{ + printf "Shutting down ctdbd service: " + + case "$CTDB_INIT_STYLE" in + suse) + "$ctdbd_wrapper" "$pidfile" "stop" + rc_status -v + ;; + redhat) + "$ctdbd_wrapper" "$pidfile" "stop" + RETVAL=$? + # Common idiom in Red Hat init scripts - success() always + # succeeds so this does behave like if-then-else + # shellcheck disable=SC2015 + [ $RETVAL -eq 0 ] && success || failure + echo "" + [ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/ctdb + return $RETVAL + ;; + debian) + "$ctdbd_wrapper" "$pidfile" "stop" + log_end_msg $? + ;; + esac +} + +restart() +{ + stop + start +} + +check_status () +{ + # Backward compatibility. When we arrange to pass --pidfile to + # ctdbd we also create the directory that will contain it. If + # that directory is missing then we don't use the pidfile to check + # status. Note that this probably won't work if + # $CTDB_VALGRIND="yes" but this doesn't need full backward + # compatibility because it is a debug option. + _d=$(dirname "$pidfile") + if [ -d "$_d" ] ; then + _pf_opt="-p $pidfile" + else + _pf_opt="" + fi + + case "$CTDB_INIT_STYLE" in + suse) + checkproc $_pf_opt "$ctdbd" + rc_status -v + ;; + redhat) + status $_pf_opt -l "ctdb" "$ctdbd" + ;; + debian) + status_of_proc $_pf_opt "$ctdbd" "ctdb" + ;; + esac +} + +############################################################ + +case "$1" in + start) + start + ;; + stop) + stop + ;; + restart|reload|force-reload) + restart + ;; + status) + check_status + ;; + condrestart|try-restart) + if check_status >/dev/null ; then + restart + fi + ;; + cron) + # used from cron to auto-restart ctdb + check_status >/dev/null 2>&1 || restart + ;; + *) + echo "Usage: $0 {start|stop|restart|reload|force-reload|status|cron|condrestart|try-restart}" + exit 1 +esac diff --git a/init.d/nmbd b/init.d/nmbd new file mode 100755 index 0000000..c9105ef --- /dev/null +++ b/init.d/nmbd @@ -0,0 +1,84 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: nmbd +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# X-Start-Before: smbd +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Samba NetBIOS nameserver (nmbd) +# Description: NetBIOS name server to provide NetBIOS over IP naming services +# to clients +### END INIT INFO + + +PIDDIR=/var/run/samba +NMBDPID=$PIDDIR/nmbd.pid + +# clear conflicting settings from the environment +unset TMPDIR + +# See if the daemons are there +test -x /usr/sbin/nmbd || exit 0 + +. /lib/lsb/init-functions + +case $1 in + start) + SERVER_ROLE=`samba-tool testparm --parameter-name="server role" 2>/dev/null | tail -1` + if [ "$SERVER_ROLE" = "active directory domain controller" ]; then + exit 0 + fi + + if [ -n `which testparm` ] + then + NMBD_DISABLED=`testparm -s --parameter-name='disable netbios' 2>/dev/null` + fi + if [ "$NMBD_DISABLED" != Yes ]; then + log_daemon_msg "Starting NetBIOS name server" nmbd + # Make sure we have our PIDDIR, even if it's on a tmpfs + install -o root -g root -m 755 -d $PIDDIR + + if ! start-stop-daemon --start --quiet --oknodo --exec /usr/sbin/nmbd --pidfile $NMBDPID -- -D + then + log_end_msg 1 + exit 1 + fi + log_end_msg 0 + fi + + ;; + stop) + + log_daemon_msg "Stopping NetBIOS name server" nmbd + + start-stop-daemon --stop --quiet --pidfile $NMBDPID + # Wait a little and remove stale PID file + sleep 1 + if [ -f $NMBDPID ] && ! ps h `cat $NMBDPID` > /dev/null + then + # Stale PID file (nmbd was succesfully stopped), + # remove it (should be removed by nmbd itself IMHO.) + rm -f $NMBDPID + fi + + log_end_msg 0 + + ;; + restart|force-reload) + $0 stop + sleep 1 + $0 start + ;; + status) + status_of_proc -p $NMBDPID /usr/sbin/nmbd nmbd + exit $? + ;; + *) + echo "Usage: /etc/init.d/nmbd {start|stop|restart|force-reload|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/init.d/samba-ad-dc b/init.d/samba-ad-dc new file mode 100755 index 0000000..ba4a7d0 --- /dev/null +++ b/init.d/samba-ad-dc @@ -0,0 +1,93 @@ +#! /bin/sh + +### BEGIN INIT INFO +# Provides: samba-ad-dc +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Samba daemons for the AD DC +# Description: Meta-service to provide AD and SMB/CIFS services to clients +### END INIT INFO + +# +# Start/stops the Samba daemon (samba). +# Adapted from the Samba 3 packages. +# + +PIDDIR=/var/run/samba +SAMBAPID=$PIDDIR/samba.pid + +# clear conflicting settings from the environment +unset TMPDIR + +# See if the daemon and the config file are there +test -x /usr/sbin/samba -a -r /etc/samba/smb.conf || exit 0 + +. /lib/lsb/init-functions + +case "$1" in + start) + SERVER_ROLE=`samba-tool testparm --parameter-name="server role" 2>/dev/null | tail -1` + if [ "$SERVER_ROLE" != "active directory domain controller" ]; then + exit 0 + fi + + + # CVE-2013-4475 + KEYFILE=/var/lib/samba/private/tls/key.pem + if [ -e $KEYFILE ] + then + KEYPERMS=`stat -c %a $KEYFILE` + if [ "$KEYPERMS" != "600" ] + then + echo "wrong permission on $KEYFILE, must be 600" + echo "samba will not start (CVE-2013-4475)" + echo "Removing all tls .pem files will cause an auto-regeneration with the correct permissions." + exit 1 + fi + fi + + log_daemon_msg "Starting Samba AD DC daemon" "samba" + # Make sure we have our PIDDIR, even if it's on a tmpfs + install -o root -g root -m 755 -d $PIDDIR + + if ! start-stop-daemon --start --quiet --oknodo --exec /usr/sbin/samba --pidfile $SAMBAPID -- -D; then + log_end_msg 1 + exit 1 + fi + + log_end_msg 0 + ;; + stop) + log_daemon_msg "Stopping Samba AD DC daemon" "samba" + + start-stop-daemon --stop --quiet --pidfile $SAMBAPID + # Wait a little and remove stale PID file + sleep 1 + if [ -f $SAMBAPID ] && ! ps h `cat $SAMBAPID` > /dev/null + then + # Stale PID file (samba was succesfully stopped), + # remove it (should be removed by samba itself IMHO.) + rm -f $SAMBAPID + fi + + log_end_msg 0 + + ;; + restart|force-reload) + $0 stop + sleep 1 + $0 start + ;; + status) + status_of_proc -p $SAMBAPID /usr/sbin/samba samba + exit $? + ;; + *) + echo "Usage: /etc/init.d/samba-ad-dc {start|stop|restart|force-reload|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/init.d/smbd b/init.d/smbd new file mode 100755 index 0000000..41b6e11 --- /dev/null +++ b/init.d/smbd @@ -0,0 +1,84 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: smbd +# Required-Start: $network $local_fs $remote_fs +# Required-Stop: $network $local_fs $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Should-Start: slapd cups +# Should-Stop: slapd cups +# Short-Description: Samba SMB/CIFS daemon (smbd) +# Description: server to provide SMB/CIFS services to clients +### END INIT INFO + + +PIDDIR=/var/run/samba +SMBDPID=$PIDDIR/smbd.pid + +# clear conflicting settings from the environment +unset TMPDIR + +# See if the daemons are there +test -x /usr/sbin/smbd || exit 0 + +. /lib/lsb/init-functions + +case $1 in + start) + SERVER_ROLE=`samba-tool testparm --parameter-name="server role" 2>/dev/null | tail -1` + if [ "$SERVER_ROLE" = "active directory domain controller" ]; then + exit 0 + fi + + log_daemon_msg "Starting SMB/CIFS daemon" smbd + # Make sure we have our PIDDIR, even if it's on a tmpfs + install -o root -g root -m 755 -d $PIDDIR + + if ! start-stop-daemon --start --quiet --oknodo --exec /usr/sbin/smbd --pidfile $SMBDPID -- -D; then + log_end_msg 1 + exit 1 + fi + + log_end_msg 0 + ;; + stop) + + log_daemon_msg "Stopping SMB/CIFS daemon" smbd + + start-stop-daemon --stop --quiet --pidfile $SMBDPID + # Wait a little and remove stale PID file + sleep 1 + if [ -f $SMBDPID ] && ! ps h `cat $SMBDPID` > /dev/null + then + # Stale PID file, remove it (should be removed by + # smbd itself IMHO). + rm -f $SMBDPID + fi + + log_end_msg 0 + + ;; + reload) + log_daemon_msg "Reloading /etc/samba/smb.conf" smbd + + start-stop-daemon --stop --quiet --signal HUP --pidfile $SMBDPID + + log_end_msg 0 + ;; + restart|force-reload) + $0 stop + sleep 1 + $0 start + ;; + status) + status_of_proc -p $SMBDPID /usr/sbin/smbd smbd + exit $? + ;; + *) + echo "Usage: /etc/init.d/smbd {start|stop|reload|restart|force-reload|status}" + exit 1 + ;; +esac + +exit 0 diff --git a/init.d/winbind b/init.d/winbind new file mode 100755 index 0000000..717d1f8 --- /dev/null +++ b/init.d/winbind @@ -0,0 +1,62 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: winbind +# Required-Start: $network $remote_fs $syslog +# Required-Stop: $network $remote_fs $syslog +# Should-Start: samba +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Samba Winbind daemon +# Description: Name Service Switch daemon for resolving names from NT servers +### END INIT INFO + + +PATH=/sbin:/bin:/usr/sbin:/usr/bin + +[ -r /etc/default/winbind ] && . /etc/default/winbind + +DAEMON=/usr/sbin/winbindd +PIDDIR=/var/run/samba +WINBINDPID=$PIDDIR/winbindd.pid + +# clear conflicting settings from the environment +unset TMPDIR + +# See if the daemon is there +test -x $DAEMON || exit 0 + +SERVER_ROLE=`samba-tool testparm --parameter-name="server role" 2>/dev/null | tail -1` +if [ "$SERVER_ROLE" = "active directory domain controller" ]; then + exit 0 +fi + +. /lib/lsb/init-functions + +case "$1" in + start) + log_daemon_msg "Starting the Winbind daemon" "winbind" + + start-stop-daemon --start --quiet --oknodo --exec $DAEMON --pidfile $WINBINDPID -- $WINBINDD_OPTS + + log_end_msg $? + ;; + + stop) + log_daemon_msg "Stopping the Winbind daemon" "winbind" + start-stop-daemon --stop --quiet --oknodo --exec $DAEMON --pidfile $WINBINDPID + log_end_msg $? + ;; + + restart|force-reload) + $0 stop && sleep 2 && $0 start + ;; + + status) + status_of_proc -p $WINBINDPID $DAEMON winbind && exit 0 || exit $? + ;; + *) + echo "Usage: /etc/init.d/winbind {start|stop|restart|force-reload|status}" + exit 1 + ;; +esac diff --git a/logrotate.d/ctdb b/logrotate.d/ctdb new file mode 100644 index 0000000..c1bf28a --- /dev/null +++ b/logrotate.d/ctdb @@ -0,0 +1,9 @@ +/var/log/ctdb/log.ctdb { + weekly + missingok + rotate 7 + copytruncate + compress + delaycompress + notifempty +} diff --git a/logrotate.d/samba b/logrotate.d/samba new file mode 100644 index 0000000..8a9632b --- /dev/null +++ b/logrotate.d/samba @@ -0,0 +1,40 @@ +/var/log/samba/log.smbd { + weekly + missingok + rotate 7 + postrotate + [ ! -x /usr/bin/smbcontrol ] || /usr/bin/smbcontrol smbd reload-config + endscript + compress + delaycompress + notifempty +} + +/var/log/samba/log.nmbd { + weekly + missingok + rotate 7 + postrotate + [ ! -x /usr/bin/smbcontrol ] || /usr/bin/smbcontrol nmbd reload-config + endscript + compress + delaycompress + notifempty +} + +/var/log/samba/log.samba { + weekly + missingok + rotate 7 + postrotate + if [ -d /run/systemd/system ] && command systemctl >/dev/null 2>&1 && systemctl is-active --quiet samba-ad-dc; then + systemctl kill --kill-who all --signal=SIGHUP samba-ad-dc + elif [ -f /var/run/samba/samba.pid ]; then + # This only sends to main pid, See #803924 + kill -HUP `cat /var/run/samba/samba.pid` + fi + endscript + compress + delaycompress + notifempty +} diff --git a/logrotate.d/winbind b/logrotate.d/winbind new file mode 100644 index 0000000..43ae1af --- /dev/null +++ b/logrotate.d/winbind @@ -0,0 +1,15 @@ +/var/log/samba/log.winbindd { + weekly + missingok + rotate 7 + postrotate + if [ -x /usr/bin/smbcontrol ]; then + /usr/bin/smbcontrol winbindd reload-config + elif [ -f /var/run/samba/winbindd.pid ]; then + kill -HUP `cat /var/run/samba/winbindd.pid` + fi + endscript + compress + delaycompress + notifempty +} diff --git a/rc0.d/K01ctdb b/rc0.d/K01ctdb new file mode 120000 index 0000000..659fb4e --- /dev/null +++ b/rc0.d/K01ctdb @@ -0,0 +1 @@ +../init.d/ctdb \ No newline at end of file diff --git a/rc0.d/K01nmbd b/rc0.d/K01nmbd new file mode 120000 index 0000000..1d81ff4 --- /dev/null +++ b/rc0.d/K01nmbd @@ -0,0 +1 @@ +../init.d/nmbd \ No newline at end of file diff --git a/rc0.d/K01samba-ad-dc b/rc0.d/K01samba-ad-dc new file mode 120000 index 0000000..fd0c8b9 --- /dev/null +++ b/rc0.d/K01samba-ad-dc @@ -0,0 +1 @@ +../init.d/samba-ad-dc \ No newline at end of file diff --git a/rc0.d/K01smbd b/rc0.d/K01smbd new file mode 120000 index 0000000..a691ef6 --- /dev/null +++ b/rc0.d/K01smbd @@ -0,0 +1 @@ +../init.d/smbd \ No newline at end of file diff --git a/rc0.d/K01winbind b/rc0.d/K01winbind new file mode 120000 index 0000000..132ebe4 --- /dev/null +++ b/rc0.d/K01winbind @@ -0,0 +1 @@ +../init.d/winbind \ No newline at end of file diff --git a/rc1.d/K01ctdb b/rc1.d/K01ctdb new file mode 120000 index 0000000..659fb4e --- /dev/null +++ b/rc1.d/K01ctdb @@ -0,0 +1 @@ +../init.d/ctdb \ No newline at end of file diff --git a/rc1.d/K01nmbd b/rc1.d/K01nmbd new file mode 120000 index 0000000..1d81ff4 --- /dev/null +++ b/rc1.d/K01nmbd @@ -0,0 +1 @@ +../init.d/nmbd \ No newline at end of file diff --git a/rc1.d/K01samba-ad-dc b/rc1.d/K01samba-ad-dc new file mode 120000 index 0000000..fd0c8b9 --- /dev/null +++ b/rc1.d/K01samba-ad-dc @@ -0,0 +1 @@ +../init.d/samba-ad-dc \ No newline at end of file diff --git a/rc1.d/K01smbd b/rc1.d/K01smbd new file mode 120000 index 0000000..a691ef6 --- /dev/null +++ b/rc1.d/K01smbd @@ -0,0 +1 @@ +../init.d/smbd \ No newline at end of file diff --git a/rc1.d/K01winbind b/rc1.d/K01winbind new file mode 120000 index 0000000..132ebe4 --- /dev/null +++ b/rc1.d/K01winbind @@ -0,0 +1 @@ +../init.d/winbind \ No newline at end of file diff --git a/rc2.d/S01ctdb b/rc2.d/S01ctdb new file mode 120000 index 0000000..659fb4e --- /dev/null +++ b/rc2.d/S01ctdb @@ -0,0 +1 @@ +../init.d/ctdb \ No newline at end of file diff --git a/rc2.d/S01nmbd b/rc2.d/S01nmbd new file mode 120000 index 0000000..1d81ff4 --- /dev/null +++ b/rc2.d/S01nmbd @@ -0,0 +1 @@ +../init.d/nmbd \ No newline at end of file diff --git a/rc2.d/S01samba-ad-dc b/rc2.d/S01samba-ad-dc new file mode 120000 index 0000000..fd0c8b9 --- /dev/null +++ b/rc2.d/S01samba-ad-dc @@ -0,0 +1 @@ +../init.d/samba-ad-dc \ No newline at end of file diff --git a/rc2.d/S01smbd b/rc2.d/S01smbd new file mode 120000 index 0000000..a691ef6 --- /dev/null +++ b/rc2.d/S01smbd @@ -0,0 +1 @@ +../init.d/smbd \ No newline at end of file diff --git a/rc2.d/S01winbind b/rc2.d/S01winbind new file mode 120000 index 0000000..132ebe4 --- /dev/null +++ b/rc2.d/S01winbind @@ -0,0 +1 @@ +../init.d/winbind \ No newline at end of file diff --git a/rc3.d/S01ctdb b/rc3.d/S01ctdb new file mode 120000 index 0000000..659fb4e --- /dev/null +++ b/rc3.d/S01ctdb @@ -0,0 +1 @@ +../init.d/ctdb \ No newline at end of file diff --git a/rc3.d/S01nmbd b/rc3.d/S01nmbd new file mode 120000 index 0000000..1d81ff4 --- /dev/null +++ b/rc3.d/S01nmbd @@ -0,0 +1 @@ +../init.d/nmbd \ No newline at end of file diff --git a/rc3.d/S01samba-ad-dc b/rc3.d/S01samba-ad-dc new file mode 120000 index 0000000..fd0c8b9 --- /dev/null +++ b/rc3.d/S01samba-ad-dc @@ -0,0 +1 @@ +../init.d/samba-ad-dc \ No newline at end of file diff --git a/rc3.d/S01smbd b/rc3.d/S01smbd new file mode 120000 index 0000000..a691ef6 --- /dev/null +++ b/rc3.d/S01smbd @@ -0,0 +1 @@ +../init.d/smbd \ No newline at end of file diff --git a/rc3.d/S01winbind b/rc3.d/S01winbind new file mode 120000 index 0000000..132ebe4 --- /dev/null +++ b/rc3.d/S01winbind @@ -0,0 +1 @@ +../init.d/winbind \ No newline at end of file diff --git a/rc4.d/S01ctdb b/rc4.d/S01ctdb new file mode 120000 index 0000000..659fb4e --- /dev/null +++ b/rc4.d/S01ctdb @@ -0,0 +1 @@ +../init.d/ctdb \ No newline at end of file diff --git a/rc4.d/S01nmbd b/rc4.d/S01nmbd new file mode 120000 index 0000000..1d81ff4 --- /dev/null +++ b/rc4.d/S01nmbd @@ -0,0 +1 @@ +../init.d/nmbd \ No newline at end of file diff --git a/rc4.d/S01samba-ad-dc b/rc4.d/S01samba-ad-dc new file mode 120000 index 0000000..fd0c8b9 --- /dev/null +++ b/rc4.d/S01samba-ad-dc @@ -0,0 +1 @@ +../init.d/samba-ad-dc \ No newline at end of file diff --git a/rc4.d/S01smbd b/rc4.d/S01smbd new file mode 120000 index 0000000..a691ef6 --- /dev/null +++ b/rc4.d/S01smbd @@ -0,0 +1 @@ +../init.d/smbd \ No newline at end of file diff --git a/rc4.d/S01winbind b/rc4.d/S01winbind new file mode 120000 index 0000000..132ebe4 --- /dev/null +++ b/rc4.d/S01winbind @@ -0,0 +1 @@ +../init.d/winbind \ No newline at end of file diff --git a/rc5.d/S01ctdb b/rc5.d/S01ctdb new file mode 120000 index 0000000..659fb4e --- /dev/null +++ b/rc5.d/S01ctdb @@ -0,0 +1 @@ +../init.d/ctdb \ No newline at end of file diff --git a/rc5.d/S01nmbd b/rc5.d/S01nmbd new file mode 120000 index 0000000..1d81ff4 --- /dev/null +++ b/rc5.d/S01nmbd @@ -0,0 +1 @@ +../init.d/nmbd \ No newline at end of file diff --git a/rc5.d/S01samba-ad-dc b/rc5.d/S01samba-ad-dc new file mode 120000 index 0000000..fd0c8b9 --- /dev/null +++ b/rc5.d/S01samba-ad-dc @@ -0,0 +1 @@ +../init.d/samba-ad-dc \ No newline at end of file diff --git a/rc5.d/S01smbd b/rc5.d/S01smbd new file mode 120000 index 0000000..a691ef6 --- /dev/null +++ b/rc5.d/S01smbd @@ -0,0 +1 @@ +../init.d/smbd \ No newline at end of file diff --git a/rc5.d/S01winbind b/rc5.d/S01winbind new file mode 120000 index 0000000..132ebe4 --- /dev/null +++ b/rc5.d/S01winbind @@ -0,0 +1 @@ +../init.d/winbind \ No newline at end of file diff --git a/rc6.d/K01ctdb b/rc6.d/K01ctdb new file mode 120000 index 0000000..659fb4e --- /dev/null +++ b/rc6.d/K01ctdb @@ -0,0 +1 @@ +../init.d/ctdb \ No newline at end of file diff --git a/rc6.d/K01nmbd b/rc6.d/K01nmbd new file mode 120000 index 0000000..1d81ff4 --- /dev/null +++ b/rc6.d/K01nmbd @@ -0,0 +1 @@ +../init.d/nmbd \ No newline at end of file diff --git a/rc6.d/K01samba-ad-dc b/rc6.d/K01samba-ad-dc new file mode 120000 index 0000000..fd0c8b9 --- /dev/null +++ b/rc6.d/K01samba-ad-dc @@ -0,0 +1 @@ +../init.d/samba-ad-dc \ No newline at end of file diff --git a/rc6.d/K01smbd b/rc6.d/K01smbd new file mode 120000 index 0000000..a691ef6 --- /dev/null +++ b/rc6.d/K01smbd @@ -0,0 +1 @@ +../init.d/smbd \ No newline at end of file diff --git a/rc6.d/K01winbind b/rc6.d/K01winbind new file mode 120000 index 0000000..132ebe4 --- /dev/null +++ b/rc6.d/K01winbind @@ -0,0 +1 @@ +../init.d/winbind \ No newline at end of file diff --git a/sudoers.d/ctdb b/sudoers.d/ctdb new file mode 100644 index 0000000..1c6619b --- /dev/null +++ b/sudoers.d/ctdb @@ -0,0 +1,3 @@ +Defaults!/etc/ctdb/statd-callout !requiretty + +rpcuser ALL=(ALL) NOPASSWD: /etc/ctdb/statd-callout diff --git a/systemd/system/multi-user.target.wants/ctdb.service b/systemd/system/multi-user.target.wants/ctdb.service new file mode 120000 index 0000000..2378aa0 --- /dev/null +++ b/systemd/system/multi-user.target.wants/ctdb.service @@ -0,0 +1 @@ +/lib/systemd/system/ctdb.service \ No newline at end of file diff --git a/systemd/system/multi-user.target.wants/nmbd.service b/systemd/system/multi-user.target.wants/nmbd.service new file mode 120000 index 0000000..d99ad38 --- /dev/null +++ b/systemd/system/multi-user.target.wants/nmbd.service @@ -0,0 +1 @@ +/lib/systemd/system/nmbd.service \ No newline at end of file diff --git a/systemd/system/multi-user.target.wants/smbd.service b/systemd/system/multi-user.target.wants/smbd.service new file mode 120000 index 0000000..8f4dc75 --- /dev/null +++ b/systemd/system/multi-user.target.wants/smbd.service @@ -0,0 +1 @@ +/lib/systemd/system/smbd.service \ No newline at end of file diff --git a/systemd/system/multi-user.target.wants/winbind.service b/systemd/system/multi-user.target.wants/winbind.service new file mode 120000 index 0000000..f492fd5 --- /dev/null +++ b/systemd/system/multi-user.target.wants/winbind.service @@ -0,0 +1 @@ +/lib/systemd/system/winbind.service \ No newline at end of file diff --git a/systemd/system/samba-ad-dc.service b/systemd/system/samba-ad-dc.service new file mode 120000 index 0000000..dc1dc0c --- /dev/null +++ b/systemd/system/samba-ad-dc.service @@ -0,0 +1 @@ +/dev/null \ No newline at end of file diff --git a/ufw/applications.d/samba b/ufw/applications.d/samba new file mode 100644 index 0000000..37bcfe2 --- /dev/null +++ b/ufw/applications.d/samba @@ -0,0 +1,4 @@ +[Samba] +title=LanManager-like file and printer server for Unix +description=The Samba software suite is a collection of programs that implements the SMB/CIFS protocol for unix systems, allowing you to serve files and printers to Windows, NT, OS/2 and DOS clients. This protocol is sometimes also referred to as the LanManager or NetBIOS protocol. +ports=137,138/udp|139,445/tcp -- 2.39.5