251 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
			
		
		
	
	
			251 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Bash
		
	
	
	
	
	
#!/bin/bash
 | 
						|
# This script configures running chronyd to use NTP servers obtained from
 | 
						|
# DHCP and _ntp._udp DNS SRV records. Files with servers from DHCP are managed
 | 
						|
# externally (e.g. by a dhclient script). Files with servers from DNS SRV
 | 
						|
# records are updated here using the dig utility. The script can also list
 | 
						|
# and set static sources in the chronyd configuration file.
 | 
						|
 | 
						|
chronyc=/usr/bin/chronyc
 | 
						|
chrony_conf=/etc/chrony.conf
 | 
						|
chrony_service=chronyd.service
 | 
						|
helper_dir=/var/run/chrony-helper
 | 
						|
added_servers_file=$helper_dir/added_servers
 | 
						|
 | 
						|
network_sysconfig_file=/etc/sysconfig/network
 | 
						|
dhclient_servers_files=/var/lib/dhclient/chrony.servers.*
 | 
						|
dnssrv_servers_files=$helper_dir/dnssrv@*
 | 
						|
dnssrv_timer_prefix=chrony-dnssrv@
 | 
						|
 | 
						|
chrony_command() {
 | 
						|
    $chronyc -a -n -m "$1"
 | 
						|
}
 | 
						|
 | 
						|
is_running() {
 | 
						|
    chrony_command "tracking" &> /dev/null
 | 
						|
}
 | 
						|
 | 
						|
is_update_needed() {
 | 
						|
    for file in $dhclient_servers_files $dnssrv_servers_files \
 | 
						|
            $added_servers_file; do
 | 
						|
        [ -e "$file" ] && return 0
 | 
						|
    done
 | 
						|
    return 1
 | 
						|
}
 | 
						|
 | 
						|
update_daemon() {
 | 
						|
    local all_servers_with_args all_servers added_servers
 | 
						|
 | 
						|
    if ! is_running; then
 | 
						|
        rm -f $added_servers_file
 | 
						|
        return 0
 | 
						|
    fi
 | 
						|
 | 
						|
    all_servers_with_args=$(
 | 
						|
        cat $dhclient_servers_files $dnssrv_servers_files 2> /dev/null)
 | 
						|
 | 
						|
    all_servers=$(
 | 
						|
        echo "$all_servers_with_args" |
 | 
						|
            while read server serverargs; do
 | 
						|
                echo "$server"
 | 
						|
            done | sort -u)
 | 
						|
    added_servers=$( (
 | 
						|
        cat $added_servers_file 2> /dev/null
 | 
						|
        echo "$all_servers_with_args" |
 | 
						|
            while read server serverargs; do
 | 
						|
                [ -z "$server" ] && continue
 | 
						|
                chrony_command "add server $server $serverargs" &> /dev/null &&
 | 
						|
                    echo "$server"
 | 
						|
            done) | sort -u)
 | 
						|
 | 
						|
    comm -23 <(echo -n "$added_servers") <(echo -n "$all_servers") |
 | 
						|
        while read server; do
 | 
						|
            chrony_command "delete $server" &> /dev/null
 | 
						|
        done
 | 
						|
 | 
						|
    added_servers=$(comm -12 <(echo -n "$added_servers") <(echo -n "$all_servers"))
 | 
						|
 | 
						|
    [ -n "$added_servers" ] && echo "$added_servers" > $added_servers_file ||
 | 
						|
        rm -f $added_servers_file
 | 
						|
}
 | 
						|
 | 
						|
get_dnssrv_servers() {
 | 
						|
    local name=$1
 | 
						|
 | 
						|
    if ! command -v dig &> /dev/null; then
 | 
						|
        echo "Missing dig (DNS lookup utility)" >&2
 | 
						|
        return 1
 | 
						|
    fi
 | 
						|
 | 
						|
    (
 | 
						|
        . $network_sysconfig_file &> /dev/null
 | 
						|
 | 
						|
        output=$(dig "$name" srv +short +ndots=2 +search 2> /dev/null)
 | 
						|
        [ $? -ne 0 ] && return 0
 | 
						|
 | 
						|
        echo "$output" | while read prio weight port target; do
 | 
						|
            server=${target%.}
 | 
						|
            [ -z "$server" ] && continue
 | 
						|
            echo "$server port $port ${NTPSERVERARGS:-iburst}"
 | 
						|
        done
 | 
						|
    )
 | 
						|
}
 | 
						|
 | 
						|
check_dnssrv_name() {
 | 
						|
    local name=$1
 | 
						|
 | 
						|
    if [ -z "$name" ]; then
 | 
						|
        echo "No DNS SRV name specified" >&2
 | 
						|
        return 1
 | 
						|
    fi
 | 
						|
 | 
						|
    if [ "${name:0:9}" != _ntp._udp ]; then
 | 
						|
        echo "DNS SRV name $name doesn't start with _ntp._udp" >&2
 | 
						|
        return 1
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
update_dnssrv_servers() {
 | 
						|
    local name=$1
 | 
						|
    local srv_file=$helper_dir/dnssrv@$name servers
 | 
						|
 | 
						|
    check_dnssrv_name "$name" || return 1
 | 
						|
 | 
						|
    servers=$(get_dnssrv_servers "$name")
 | 
						|
    [ -n "$servers" ] && echo "$servers" > "$srv_file" || rm -f "$srv_file"
 | 
						|
}
 | 
						|
 | 
						|
set_dnssrv_timer() {
 | 
						|
    local state=$1 name=$2
 | 
						|
    local srv_file=$helper_dir/dnssrv@$name servers
 | 
						|
    local timer=$dnssrv_timer_prefix$(systemd-escape "$name").timer
 | 
						|
 | 
						|
    check_dnssrv_name "$name" || return 1
 | 
						|
 | 
						|
    if [ "$state" = enable ]; then
 | 
						|
        systemctl enable "$timer"
 | 
						|
        systemctl start "$timer"
 | 
						|
    elif [ "$state" = disable ]; then
 | 
						|
        systemctl stop "$timer"
 | 
						|
        systemctl disable "$timer"
 | 
						|
        rm -f "$srv_file"
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
list_dnssrv_timers() {
 | 
						|
    systemctl --all --full -t timer list-units | grep "^$dnssrv_timer_prefix" | \
 | 
						|
            sed "s|^$dnssrv_timer_prefix\(.*\)\.timer.*|\1|" |
 | 
						|
        while read -r name; do
 | 
						|
            systemd-escape --unescape "$name"
 | 
						|
        done
 | 
						|
}
 | 
						|
 | 
						|
prepare_helper_dir() {
 | 
						|
    mkdir -p $helper_dir
 | 
						|
    exec 100> $helper_dir/lock
 | 
						|
    if ! flock -w 20 100; then
 | 
						|
        echo "Failed to lock $helper_dir" >&2
 | 
						|
        return 1
 | 
						|
    fi
 | 
						|
}
 | 
						|
 | 
						|
is_source_line() {
 | 
						|
    local pattern="^[ \t]*(server|pool|peer|refclock)[ \t]+[^ \t]+"
 | 
						|
    [[ "$1" =~ $pattern ]]
 | 
						|
}
 | 
						|
 | 
						|
list_static_sources() {
 | 
						|
    while read line; do
 | 
						|
        is_source_line "$line" && echo "$line" || :
 | 
						|
    done < $chrony_conf
 | 
						|
}
 | 
						|
 | 
						|
set_static_sources() {
 | 
						|
    local new_config tmp_conf
 | 
						|
 | 
						|
    new_config=$(
 | 
						|
        sources=$(
 | 
						|
            while read line; do
 | 
						|
                is_source_line "$line" && echo "$line"
 | 
						|
            done)
 | 
						|
 | 
						|
        while read line; do
 | 
						|
            if ! is_source_line "$line"; then
 | 
						|
                echo "$line"
 | 
						|
                continue
 | 
						|
            fi
 | 
						|
 | 
						|
            tmp_sources=$(
 | 
						|
                local removed=0
 | 
						|
 | 
						|
                echo "$sources" | while read line2; do
 | 
						|
                    [ "$removed" -ne 0 -o "$line" != "$line2" ] && \
 | 
						|
                        echo "$line2" || removed=1
 | 
						|
                done)
 | 
						|
 | 
						|
            [ "$sources" == "$tmp_sources" ] && continue
 | 
						|
            sources=$tmp_sources
 | 
						|
            echo "$line"
 | 
						|
        done < $chrony_conf
 | 
						|
 | 
						|
        echo "$sources"
 | 
						|
    )
 | 
						|
 | 
						|
    tmp_conf=${chrony_conf}.tmp
 | 
						|
 | 
						|
    cp -a $chrony_conf $tmp_conf &&
 | 
						|
        echo "$new_config" > $tmp_conf &&
 | 
						|
        mv $tmp_conf $chrony_conf || return 1
 | 
						|
 | 
						|
    systemctl try-restart $chrony_service
 | 
						|
}
 | 
						|
 | 
						|
print_help() {
 | 
						|
    echo "Usage: $0 COMMAND"
 | 
						|
    echo
 | 
						|
    echo "Commands:"
 | 
						|
    echo "	update-daemon"
 | 
						|
    echo "	update-dnssrv-servers NAME"
 | 
						|
    echo "	enable-dnssrv NAME"
 | 
						|
    echo "	disable-dnssrv NAME"
 | 
						|
    echo "	list-dnssrv"
 | 
						|
    echo "	list-static-sources"
 | 
						|
    echo "	set-static-sources < sources.list"
 | 
						|
    echo "	is-running"
 | 
						|
    echo "	command CHRONYC-COMMAND"
 | 
						|
}
 | 
						|
 | 
						|
case "$1" in
 | 
						|
    update-daemon|add-dhclient-servers|remove-dhclient-servers)
 | 
						|
        is_update_needed || exit 0
 | 
						|
        prepare_helper_dir && update_daemon
 | 
						|
        ;;
 | 
						|
    update-dnssrv-servers)
 | 
						|
        prepare_helper_dir && update_dnssrv_servers "$2" && update_daemon
 | 
						|
        ;;
 | 
						|
    enable-dnssrv)
 | 
						|
        set_dnssrv_timer enable "$2"
 | 
						|
        ;;
 | 
						|
    disable-dnssrv)
 | 
						|
        set_dnssrv_timer disable "$2" && prepare_helper_dir && update_daemon
 | 
						|
        ;;
 | 
						|
    list-dnssrv)
 | 
						|
        list_dnssrv_timers
 | 
						|
        ;;
 | 
						|
    list-static-sources)
 | 
						|
        list_static_sources
 | 
						|
        ;;
 | 
						|
    set-static-sources)
 | 
						|
        set_static_sources
 | 
						|
        ;;
 | 
						|
    is-running)
 | 
						|
        is_running
 | 
						|
        ;;
 | 
						|
    command|forced-command)
 | 
						|
        chrony_command "$2"
 | 
						|
        ;;
 | 
						|
    *)
 | 
						|
        print_help
 | 
						|
        exit 2
 | 
						|
esac
 | 
						|
 | 
						|
exit $? |