Skip to content
checkservices 5.25 KiB
Newer Older
Seblu's avatar
Seblu committed
#!/bin/bash
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
# Copyright © Sébastien Luttringer
Seblu's avatar
Seblu committed
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.

# Check running systemd services for binary update
# Convenient way to restart updated systemd service after upgrade
Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
# disable grep options to avoid non default behaviour
unset GREP_OPTIONS

Seblu's avatar
Seblu committed
# Systemd cgroup path
systemd_cgroup_base_path='/sys/fs/cgroup/systemd'

Seblu's avatar
Seblu committed

Seblu's avatar
Seblu committed
if [[ -t 1 ]]; then
	shopt -s xpg_echo
	c_title='\e[1;33m'
	c_svc='\e[1;31m'
	c_warn='\e[5;30;43m'
	c_error='\e[5;30;41m'
Seblu's avatar
Seblu committed
	c_rst='\e[m'
fi

Seblu's avatar
Seblu committed
usage() {
	echo "usage ${0##*/} [options]"
	echo 'options:'
	echo '   -h: this help' >&2
	echo '   -r: restart services' >&2
	echo '   -R: reload services' >&2
Seblu's avatar
Seblu committed
	echo "   -s: serialize action" >&2
Seblu's avatar
Seblu committed
	echo "   -p: don't call pacdiff before action" >&2
Seblu's avatar
Seblu committed
	echo "   -n: don't call systemd daemon-reload" >&2
	echo "   -N: don't display status of restart/reload units" >&2
Seblu's avatar
Seblu committed
	echo "   -u: list service in users slice" >&2
Seblu's avatar
Seblu committed
	echo "   -v: verbose mode" >&2
Seblu's avatar
Seblu committed
	exit 1
}

# usage : in_array( $needle, $haystack )
# return : 0 - found
#          1 - not found
in_array() {
	local needle=$1; shift
	local item
	for item in "$@"; do
	   [[ $item = $needle ]] && return 0 # Found
	done
	return 1 # Not Found
}

Seblu's avatar
Seblu committed
while getopts 'hrRsnNuvp' opt; do
Seblu's avatar
Seblu committed
	case $opt in
		r) systemd_cmd='restart';;
		R) systemd_cmd='reload';;
Seblu's avatar
Seblu committed
		s) serial=true;;
		n) no_reload=1;;
		N) no_status=1;;
Seblu's avatar
Seblu committed
		p) no_pacdiff=1;;
Seblu's avatar
Seblu committed
		l) list=true;;
Seblu's avatar
Seblu committed
		u) user_slice=true;;
Seblu's avatar
Seblu committed
		v) verbose=true;;
Seblu's avatar
Seblu committed
		*) usage;;
	esac
done
shift $((OPTIND - 1));
(( $# > 0 )) && usage

# avoid to be sighup'ed by interactive shell
trap '' SIGHUP

Seblu's avatar
Seblu committed
# from now, we need to be root
(( $UID != 0 )) && echo 'You need to be root' && exit 1

Seblu's avatar
Seblu committed
# call pacdiff
[[ -z $no_pacdiff ]] && pacdiff

# reload units list
[[ -z $no_reload ]] && systemctl --system daemon-reload
Seblu's avatar
Seblu committed

# list of running services
Seblu's avatar
Seblu committed
declare -a services
services=($(systemctl --no-legend --full --type service --state running|cut -f1 -d' '))
Seblu's avatar
Seblu committed

# list of bus names
buses=($(dbus-send --system --dest=org.freedesktop.DBus --type=method_call --print-reply /org/freedesktop/DBus org.freedesktop.DBus.ListNames|sed -rn 's/\s*string "(.*)"/\1/p'))

# beggar count
Seblu's avatar
Seblu committed
declare -a needy=() pids=()
declare -i pid=0
Seblu's avatar
Seblu committed
for svc in "${services[@]}"; do
Seblu's avatar
Seblu committed
	unit_path="$(systemctl -p ControlGroup show "$svc"|cut -f 2 -d=)"
	busname="$(systemctl -p BusName show "$svc"|cut -f 2 -d=)"
	tasks_path="$systemd_cgroup_base_path/$unit_path/tasks"
	[[ -e "$tasks_path" ]] || {
		echo "${c_error}** Unable to get pid of $svc: No tasks file: $tasks_path${c_rst}" >&2
		continue
	}
Seblu's avatar
Seblu committed
	# check if unit is in system slice
	[[ "$unit_path" =~ /user\.slice/ && ! "$user_slice" == true ]] && continue
	pids=( $(< "$systemd_cgroup_base_path/$unit_path/tasks") )
	if (( "${#pids[*]}" == 0 )); then
		echo "${c_error}** Unable to get pid of $svc: Tasks file is empty${c_rst}" >&2
Seblu's avatar
Seblu committed
	deleted=''
	for pid in "${pids[@]}"; do
		maps_path="/proc/$pid/maps"
		[[ -e "$maps_path" ]] || {
			echo "${c_error}** Unable to get maps file of $svc for pid $pid${c_rst}" >&2
			continue
		}
		deleted=$(grep -F '(deleted)' "$maps_path" |sed -nr 's|^\S+ ..x. \S+ \S+ \S+ \s+||p'|sort|uniq)
		if [[ -n $deleted ]] || { [[ -n "$busname" ]] && ! in_array "$busname" "${buses[@]}"; }; then
Seblu's avatar
Seblu committed
			needy+=("$svc")
			if [[ $verbose ]]; then
				echo "${c_title}Service:${c_svc} $svc${c_rst}"
				echo "${c_title}Pid:${c_rst} $pid"
				echo "${c_title}Bus:${c_rst} $busname"
Seblu's avatar
Seblu committed
				echo "${c_title}Commands:${c_rst}"
				echo "systemctl reload '$svc'"
				echo "systemctl restart '$svc'"
				echo -n "${c_title}BusName on the system bus: ${c_rst}"
				in_array "$busname" "${buses[@]}" && echo 'Yes' || echo 'No'
Seblu's avatar
Seblu committed
				echo "${c_title}Deleted files:${c_rst}"
				echo "$deleted"
				echo
			else
				echo "systemctl restart '$svc'"
			fi
Seblu's avatar
Seblu committed
			if [[ $systemd_cmd ]]; then
				systemctl "$systemd_cmd" "$svc" &
				# wait process to terminate when serialize
				[[ -n $serial ]] && wait
				# display status directly when not serialize
				[[ -n $serial && -z $no_status ]] && systemctl --lines=0 status "$svc"
			fi
Seblu's avatar
Seblu committed
			break
Seblu's avatar
Seblu committed
	done
Seblu's avatar
Seblu committed
done
# show units status
Seblu's avatar
Seblu committed
if [[ -z $serial && -z $no_status && $systemd_cmd && ${needy[*]} ]]; then
	systemctl --lines=0 status "${needy[@]}"
fi

if [[ $verbose ]]; then
	systemctl --failed --all --no-pager --full list-units
else
	systemctl --failed --all --no-pager --no-legend --full list-units
fi
Seblu's avatar
Seblu committed
# warn if dbus was restart
if in_array dbus.service "${needy[@]}"; then
	echo "${c_warn}After dbus restart, you should run ${0##*/} twice${c_rst}" >&2
Seblu's avatar
Seblu committed
	if [[ -n "$systemd_cmd" && -z $CHECKSERVICE_WAS_RESTARTED ]]; then
		echo "${c_warn}Doing it for you. No need to thanks me!${c_rst}" >&2
Seblu's avatar
Seblu committed
		export CHECKSERVICE_WAS_RESTARTED=1
		exec "$0" "$@"
	fi
fi

Seblu's avatar
Seblu committed
exit 0