diff --git a/.chrony.metadata b/.chrony.metadata
new file mode 100644
index 0000000..776de31
--- /dev/null
+++ b/.chrony.metadata
@@ -0,0 +1,2 @@
+79e9aeace143550300387a99f17bff04b45673f7 SOURCES/chrony-3.5.tar.gz
+84d41ec6da2317dab5e41d9b73ec028c78325700 SOURCES/clknetsim-3f5ef9.tar.gz
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e3ad503
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+SOURCES/chrony-3.5.tar.gz
+SOURCES/clknetsim-3f5ef9.tar.gz
diff --git a/SOURCES/chrony-dnssrv@.service b/SOURCES/chrony-dnssrv@.service
new file mode 100644
index 0000000..139ed28
--- /dev/null
+++ b/SOURCES/chrony-dnssrv@.service
@@ -0,0 +1,8 @@
+[Unit]
+Description=DNS SRV lookup of %I for chrony
+After=chronyd.service network-online.target
+Wants=network-online.target
+
+[Service]
+Type=oneshot
+ExecStart=/usr/libexec/chrony-helper update-dnssrv-servers %I
diff --git a/SOURCES/chrony-dnssrv@.timer b/SOURCES/chrony-dnssrv@.timer
new file mode 100644
index 0000000..8495e01
--- /dev/null
+++ b/SOURCES/chrony-dnssrv@.timer
@@ -0,0 +1,9 @@
+[Unit]
+Description=Periodic DNS SRV lookup of %I for chrony
+
+[Timer]
+OnActiveSec=0
+OnUnitInactiveSec=1h
+
+[Install]
+WantedBy=timers.target
diff --git a/SOURCES/chrony-service-helper.patch b/SOURCES/chrony-service-helper.patch
new file mode 100644
index 0000000..6376e0b
--- /dev/null
+++ b/SOURCES/chrony-service-helper.patch
@@ -0,0 +1,11 @@
+diff -up chrony-3.5/examples/chronyd.service.service-helper chrony-3.5/examples/chronyd.service
+--- chrony-3.5/examples/chronyd.service.service-helper	2019-05-10 12:22:57.000000000 +0200
++++ chrony-3.5/examples/chronyd.service	2019-05-14 13:42:38.069516800 +0200
+@@ -10,6 +10,7 @@ Type=forking
+ PIDFile=/run/chrony/chronyd.pid
+ EnvironmentFile=-/etc/sysconfig/chronyd
+ ExecStart=/usr/sbin/chronyd $OPTIONS
++ExecStartPost=/usr/libexec/chrony-helper update-daemon
+ PrivateTmp=yes
+ ProtectHome=yes
+ ProtectSystem=full
diff --git a/SOURCES/chrony.dhclient b/SOURCES/chrony.dhclient
new file mode 100644
index 0000000..be17e2a
--- /dev/null
+++ b/SOURCES/chrony.dhclient
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+SERVERFILE=$SAVEDIR/chrony.servers.$interface
+
+chrony_config() {
+	rm -f "$SERVERFILE"
+	if [ "$PEERNTP" != "no" ]; then
+		for server in $new_ntp_servers; do
+			echo "$server ${NTPSERVERARGS:-iburst}" >> "$SERVERFILE"
+		done
+		/usr/libexec/chrony-helper update-daemon || :
+	fi
+}
+
+chrony_restore() {
+	if [ -f "$SERVERFILE" ]; then
+		rm -f "$SERVERFILE"
+		/usr/libexec/chrony-helper update-daemon || :
+	fi
+}
diff --git a/SOURCES/chrony.helper b/SOURCES/chrony.helper
new file mode 100644
index 0000000..95414af
--- /dev/null
+++ b/SOURCES/chrony.helper
@@ -0,0 +1,265 @@
+#!/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@
+
+. $network_sysconfig_file &> /dev/null
+
+chrony_command() {
+    $chronyc -a -n -m "$1"
+}
+
+is_running() {
+    chrony_command "tracking" &> /dev/null
+}
+
+get_servers_files() {
+    [ "$PEERNTP" != "no" ] && echo "$dhclient_servers_files"
+    echo "$dnssrv_servers_files"
+}
+
+is_update_needed() {
+    for file in $(get_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 $(get_servers_files) 2> /dev/null)
+
+    all_servers=$(
+        echo "$all_servers_with_args" |
+            while read -r server serverargs; do
+                echo "$server"
+            done | sort -u)
+    added_servers=$( (
+        cat $added_servers_file 2> /dev/null
+        echo "$all_servers_with_args" |
+            while read -r 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 -r server; do
+            chrony_command "delete $server" &> /dev/null
+        done
+
+    added_servers=$(comm -12 <(echo -n "$added_servers") <(echo -n "$all_servers"))
+
+    if [ -n "$added_servers" ]; then
+        echo "$added_servers" > $added_servers_file
+    else
+        rm -f $added_servers_file
+    fi
+}
+
+get_dnssrv_servers() {
+    local name=$1 output
+
+    if ! command -v dig &> /dev/null; then
+        echo "Missing dig (DNS lookup utility)" >&2
+        return 1
+    fi
+
+    output=$(dig "$name" srv +short +ndots=2 +search 2> /dev/null) || return 0
+
+    echo "$output" | while read -r _ _ 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")
+    if [ -n "$servers" ]; then
+        echo "$servers" > "$srv_file"
+    else
+        rm -f "$srv_file"
+    fi
+}
+
+set_dnssrv_timer() {
+    local state=$1 name=$2
+    local srv_file=$helper_dir/dnssrv@$name servers
+    local timer
+
+    timer=$dnssrv_timer_prefix$(systemd-escape "$name").timer || return 1
+
+    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 -r line; do
+        if is_source_line "$line"; then
+            echo "$line"
+        fi
+    done < $chrony_conf
+}
+
+set_static_sources() {
+    local new_config tmp_conf
+
+    new_config=$(
+        sources=$(
+            while read -r line; do
+                is_source_line "$line" && echo "$line"
+            done)
+
+        while read -r line; do
+            if ! is_source_line "$line"; then
+                echo "$line"
+                continue
+            fi
+
+            tmp_sources=$(
+                local removed=0
+
+                echo "$sources" | while read -r line2; do
+                    if [ "$removed" -ne 0 ] || [ "$line" != "$line2" ]; then
+                        echo "$line2"
+                    else
+                        removed=1
+                    fi
+                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 $?
diff --git a/SOURCES/ntp2chrony.py b/SOURCES/ntp2chrony.py
new file mode 100644
index 0000000..b840225
--- /dev/null
+++ b/SOURCES/ntp2chrony.py
@@ -0,0 +1,671 @@
+#!/usr/bin/python
+#
+# Convert ntp configuration to chrony
+#
+# Copyright (C) 2018-2019  Miroslav Lichvar <mlichvar@redhat.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+
+import argparse
+import ipaddress
+import logging
+import os
+import os.path
+import re
+import subprocess
+import sys
+
+# python2 compatibility hacks
+if sys.version_info[0] < 3:
+    from io import open
+    reload(sys)
+    sys.setdefaultencoding("utf-8")
+
+class NtpConfiguration(object):
+    def __init__(self, root_dir, ntp_conf, step_tickers):
+        self.root_dir = root_dir if root_dir != "/" else ""
+        self.ntp_conf_path = ntp_conf
+        self.step_tickers_path = step_tickers
+
+        # Read and write files using an 8-bit transparent encoding
+        self.file_encoding = "latin-1"
+        self.enabled_services = set()
+        self.step_tickers = []
+        self.time_sources = []
+        self.fudges = {}
+        self.restrictions = {
+                # Built-in defaults
+                ipaddress.ip_network(u"0.0.0.0/0"): set(),
+                ipaddress.ip_network(u"::/0"): set(),
+        }
+        self.keyfile = ""
+        self.keys = []
+        self.trusted_keys = []
+        self.driftfile = ""
+        self.statistics = []
+        self.leapfile = ""
+        self.tos_options = {}
+        self.ignored_directives = set()
+        self.ignored_lines = []
+
+        #self.detect_enabled_services()
+        self.parse_step_tickers()
+        self.parse_ntp_conf()
+
+    def detect_enabled_services(self):
+        for service in ["ntpdate", "ntpd", "ntp-wait"]:
+            if os.path.islink("{}/etc/systemd/system/multi-user.target.wants/{}.service"
+                    .format(self.root_dir, service)):
+                self.enabled_services.add(service)
+        logging.info("Enabled services found in /etc/systemd/system: %s",
+                     " ".join(self.enabled_services))
+
+    def parse_step_tickers(self):
+        if not self.step_tickers_path:
+            return
+
+        path = os.path.join(self.root_dir, self.step_tickers_path)
+        if not os.path.isfile(path):
+            logging.info("Missing %s", path)
+            return
+
+        with open(path, encoding=self.file_encoding) as f:
+            for line in f:
+                line = line[:line.find('#')]
+
+                words = line.split()
+
+                if not words:
+                    continue
+
+                self.step_tickers.extend(words)
+
+    def parse_ntp_conf(self, path=None):
+        if path is None:
+            path = os.path.join(self.root_dir, self.ntp_conf_path)
+
+        with open(path, encoding=self.file_encoding) as f:
+            logging.info("Reading %s", path)
+
+            for line in f:
+                line = line[:line.find('#')]
+
+                words = line.split()
+
+                if not words:
+                    continue
+
+                if not self.parse_directive(words):
+                    self.ignored_lines.append(line)
+
+    def parse_directive(self, words):
+        name = words.pop(0)
+        if name.startswith("logconfig"):
+            name = "logconfig"
+
+        if words:
+            if name in ["server", "peer", "pool"]:
+                return self.parse_source(name, words)
+            elif name == "fudge":
+                return self.parse_fudge(words)
+            elif name == "restrict":
+                return self.parse_restrict(words)
+            elif name == "tos":
+                return self.parse_tos(words)
+            elif name == "includefile":
+                return self.parse_includefile(words)
+            elif name == "keys":
+                return self.parse_keys(words)
+            elif name == "trustedkey":
+                return self.parse_trustedkey(words)
+            elif name == "driftfile":
+                self.driftfile = words[0]
+            elif name == "statistics":
+                self.statistics = words
+            elif name == "leapfile":
+                self.leapfile = words[0]
+            else:
+                self.ignored_directives.add(name)
+                return False
+        else:
+            self.ignored_directives.add(name)
+            return False
+
+        return True
+
+    def parse_source(self, source_type, words):
+        ipv4_only = False
+        ipv6_only = False
+        source = {
+                "type": source_type,
+                "options": []
+        }
+
+        if words[0] == "-4":
+            ipv4_only = True
+            words.pop(0)
+        elif words[0] == "-6":
+            ipv6_only = True
+            words.pop(0)
+
+        if not words:
+            return False
+
+        source["address"] = words.pop(0)
+
+        # Check if -4/-6 corresponds to the address and ignore hostnames
+        if ipv4_only or ipv6_only:
+            try:
+                version = ipaddress.ip_address(source["address"]).version
+                if (ipv4_only and version != 4) or (ipv6_only and version != 6):
+                    return False
+            except ValueError:
+                return False
+
+        if source["address"].startswith("127.127."):
+            if not source["address"].startswith("127.127.1."):
+                # Ignore non-LOCAL refclocks
+                return False
+
+        while words:
+            if len(words) >= 2 and words[0] in ["minpoll", "maxpoll", "version", "key"]:
+                source["options"].append((words[0], words[1]))
+                words = words[2:]
+            elif words[0] in ["burst", "iburst", "noselect", "prefer", "true", "xleave"]:
+                source["options"].append((words[0],))
+                words.pop(0)
+            else:
+                return False
+
+        self.time_sources.append(source)
+        return True
+
+    def parse_fudge(self, words):
+        address = words.pop(0)
+        options = {}
+
+        while words:
+            if len(words) >= 2 and words[0] in ["stratum"]:
+                if not words[1].isdigit():
+                    return False
+                options[words[0]] = int(words[1])
+                words = words[2:]
+            elif len(words) >= 2:
+                words = words[2:]
+            else:
+                return False
+
+        self.fudges[address] = options
+        return True
+
+    def parse_restrict(self, words):
+        ipv4_only = False
+        ipv6_only = False
+        flags = set()
+        mask = ""
+
+        if words[0] == "-4":
+            ipv4_only = True
+            words.pop(0)
+        elif words[0] == "-6":
+            ipv6_only = True
+            words.pop(0)
+
+        if not words:
+            return False
+
+        address = words.pop(0)
+
+        while words:
+            if len(words) >= 2 and words[0] == "mask":
+                mask = words[1]
+                words = words[2:]
+            else:
+                if words[0] not in ["kod", "nomodify", "notrap", "nopeer", "noquery",
+                                    "limited", "ignore", "noserve"]:
+                    return False
+                flags.add(words[0])
+                words.pop(0)
+
+        # Convert to IP network(s), ignoring restrictions with hostnames
+        networks = []
+        if address == "default" and not mask:
+            if not ipv6_only:
+                networks.append(ipaddress.ip_network(u"0.0.0.0/0"))
+            if not ipv4_only:
+                networks.append(ipaddress.ip_network(u"::/0"))
+        else:
+            try:
+                if mask:
+                    networks.append(ipaddress.ip_network(u"{}/{}".format(address, mask)))
+                else:
+                    networks.append(ipaddress.ip_network(address))
+            except ValueError:
+                return False
+
+            if (ipv4_only and networks[-1].version != 4) or \
+                    (ipv6_only and networks[-1].version != 6):
+                return False
+
+        for network in networks:
+            self.restrictions[network] = flags
+
+        return True
+
+    def parse_tos(self, words):
+        options = {}
+        while words:
+            if len(words) >= 2 and words[0] in ["minsane", "orphan"]:
+                if not words[1].isdigit():
+                    return False
+                options[words[0]] = int(words[1])
+                words = words[2:]
+            elif len(words) >= 2 and words[0] in ["maxdist"]:
+                # Check if it is a float value
+                if not words[1].replace('.', '', 1).isdigit():
+                    return False
+                options[words[0]] = float(words[1])
+                words = words[2:]
+            else:
+                return False
+
+        self.tos_options.update(options)
+
+        return True
+
+    def parse_includefile(self, words):
+        path = os.path.join(self.root_dir, words[0])
+        if not os.path.isfile(path):
+            return False
+
+        self.parse_ntp_conf(path)
+        return True
+
+    def parse_keys(self, words):
+        keyfile = words[0]
+        path = os.path.join(self.root_dir, keyfile)
+        if not os.path.isfile(path):
+            logging.info("Missing %s", path)
+            return False
+
+        with open(path, encoding=self.file_encoding) as f:
+            logging.info("Reading %s", path)
+            keys = []
+            for line in f:
+                words = line.split()
+                if len(words) < 3 or not words[0].isdigit():
+                    continue
+                keys.append((int(words[0]), words[1], words[2]))
+
+            self.keyfile = keyfile
+            self.keys = keys
+
+        return True
+
+    def parse_trustedkey(self, words):
+        key_ranges = []
+        for word in words:
+            if word.isdigit():
+                key_ranges.append((int(word), int(word)))
+            elif re.match("^[0-9]+-[0-9]+$", word):
+                first, last = word.split("-")
+                key_ranges.append((int(first), int(last)))
+            else:
+                return False
+
+        self.trusted_keys = key_ranges
+        return True
+
+    def write_chrony_configuration(self, chrony_conf_path, chrony_keys_path,
+                                   dry_run=False, backup=False):
+        chrony_conf = self.get_chrony_conf(chrony_keys_path)
+        logging.debug("Generated %s:\n%s", chrony_conf_path, chrony_conf)
+
+        if not dry_run:
+            self.write_file(chrony_conf_path, 0o644, chrony_conf, backup)
+
+        chrony_keys = self.get_chrony_keys()
+        if chrony_keys:
+            logging.debug("Generated %s:\n%s", chrony_keys_path, chrony_keys)
+
+        if not dry_run:
+            self.write_file(chrony_keys_path, 0o640, chrony_keys, backup)
+
+    def get_processed_time_sources(self):
+        # Convert {0,1,2,3}.*pool.ntp.org servers to 2.*pool.ntp.org pools
+
+        # Make shallow copies of all sources (only type will be modified)
+        time_sources = [s.copy() for s in self.time_sources]
+
+        pools = {}
+        for source in time_sources:
+            if source["type"] != "server":
+                continue
+            m = re.match("^([0123])(\\.\\w+)?\\.pool\\.ntp\\.org$", source["address"])
+            if m is None:
+                continue
+            number = m.group(1)
+            zone = m.group(2)
+            if zone not in pools:
+                pools[zone] = []
+            pools[zone].append((int(number), source))
+
+        remove_servers = set()
+        for zone, pool in pools.items():
+            # sort and skip all pools not in [0, 3] range
+            pool.sort()
+            if [number for number, source in pool] != [0, 1, 2, 3]:
+                # only exact group of 4 servers can be converted, nothing to do here
+                continue
+            # verify that parameters are the same for all servers in the pool
+            if not all([p[1]["options"] == pool[0][1]["options"] for p in pool]):
+                break
+            remove_servers.update([pool[i][1]["address"] for i in [0, 1, 3]])
+            pool[2][1]["type"] = "pool"
+
+        processed_sources = []
+        for source in time_sources:
+            if source["type"] == "server" and source["address"] in remove_servers:
+                continue
+            processed_sources.append(source)
+        return processed_sources
+
+    def get_chrony_conf_sources(self):
+        conf = ""
+
+        if self.step_tickers:
+            conf += "# Specify NTP servers used for initial correction.\n"
+            conf += "initstepslew 0.1 {}\n".format(" ".join(self.step_tickers))
+            conf += "\n"
+
+        conf += "# Specify time sources.\n"
+
+        for source in self.get_processed_time_sources():
+            address = source["address"]
+            if address.startswith("127.127."):
+                if address.startswith("127.127.1."):
+                    continue
+                # No other refclocks are expected from the parser
+                assert False
+            else:
+                conf += "{} {}".format(source["type"], address)
+                for option in source["options"]:
+                    if option[0] in ["minpoll", "maxpoll", "version", "key",
+                                     "iburst", "noselect", "prefer", "xleave"]:
+                        conf += " {}".format(" ".join(option))
+                    elif option[0] == "burst":
+                        conf += " presend 6"
+                    elif option[0] == "true":
+                        conf += " trust"
+                    else:
+                        # No other options are expected from the parser
+                        assert False
+                conf += "\n"
+        conf += "\n"
+
+        return conf
+
+    def get_chrony_conf_allows(self):
+        allowed_networks = filter(lambda n: "ignore" not in self.restrictions[n] and
+                                    "noserve" not in self.restrictions[n],
+                                  self.restrictions.keys())
+
+        conf = ""
+        for network in sorted(allowed_networks, key=lambda n: (n.version, n)):
+            if network.num_addresses > 1:
+                conf += "allow {}\n".format(network)
+            else:
+                conf += "allow {}\n".format(network.network_address)
+
+        if conf:
+            conf = "# Allow NTP client access.\n" + conf
+            conf += "\n"
+
+        return conf
+
+    def get_chrony_conf_cmdallows(self):
+        allowed_networks = filter(lambda n: "ignore" not in self.restrictions[n] and
+                                    "noquery" not in self.restrictions[n] and
+                                    n != ipaddress.ip_network(u"127.0.0.1/32") and
+                                    n != ipaddress.ip_network(u"::1/128"),
+                                  self.restrictions.keys())
+
+        ip_versions = set()
+        conf = ""
+        for network in sorted(allowed_networks, key=lambda n: (n.version, n)):
+            ip_versions.add(network.version)
+            if network.num_addresses > 1:
+                conf += "cmdallow {}\n".format(network)
+            else:
+                conf += "cmdallow {}\n".format(network.network_address)
+
+        if conf:
+            conf = "# Allow remote monitoring.\n" + conf
+            if 4 in ip_versions:
+                conf += "bindcmdaddress 0.0.0.0\n"
+            if 6 in ip_versions:
+                conf += "bindcmdaddress ::\n"
+            conf += "\n"
+
+        return conf
+
+    def get_chrony_conf(self, chrony_keys_path):
+        local_stratum = 0
+        maxdistance = 0.0
+        minsources = 1
+        orphan_stratum = 0
+        logs = []
+
+        for source in self.time_sources:
+            address = source["address"]
+            if address.startswith("127.127.1."):
+                if address in self.fudges and "stratum" in self.fudges[address]:
+                    local_stratum = self.fudges[address]["stratum"]
+                else:
+                    local_stratum = 5
+
+        if "maxdist" in self.tos_options:
+            maxdistance = self.tos_options["maxdist"]
+        if "minsane" in self.tos_options:
+            minsources = self.tos_options["minsane"]
+        if "orphan" in self.tos_options:
+            orphan_stratum = self.tos_options["orphan"]
+
+        if "clockstats" in self.statistics:
+            logs.append("refclocks");
+        if "loopstats" in self.statistics:
+            logs.append("tracking")
+        if "peerstats" in self.statistics:
+            logs.append("statistics");
+        if "rawstats" in self.statistics:
+            logs.append("measurements")
+
+        conf = "# This file was converted from {}{}.\n".format(
+                    self.ntp_conf_path,
+                    " and " + self.step_tickers_path if self.step_tickers_path else "")
+        conf += "\n"
+
+        if self.ignored_lines:
+            conf += "# The following directives were ignored in the conversion:\n"
+
+            for line in self.ignored_lines:
+                # Remove sensitive information
+                line = re.sub(r"\s+pw\s+\S+", " pw XXX", line.rstrip())
+                conf += "# " + line + "\n"
+            conf += "\n"
+
+        conf += self.get_chrony_conf_sources()
+
+        conf += "# Record the rate at which the system clock gains/losses time.\n"
+        if not self.driftfile:
+            conf += "#"
+        conf += "driftfile /var/lib/chrony/drift\n"
+        conf += "\n"
+
+        conf += "# Allow the system clock to be stepped in the first three updates\n"
+        conf += "# if its offset is larger than 1 second.\n"
+        conf += "makestep 1.0 3\n"
+        conf += "\n"
+
+        conf += "# Enable kernel synchronization of the real-time clock (RTC).\n"
+        conf += "rtcsync\n"
+        conf += "\n"
+
+        conf += "# Enable hardware timestamping on all interfaces that support it.\n"
+        conf += "#hwtimestamp *\n"
+        conf += "\n"
+
+        if maxdistance > 0.0:
+            conf += "# Specify the maximum distance of sources to be selectable.\n"
+            conf += "maxdistance {}\n".format(maxdistance)
+            conf += "\n"
+
+        conf += "# Increase the minimum number of selectable sources required to adjust\n"
+        conf += "# the system clock.\n"
+        if minsources > 1:
+            conf += "minsources {}\n".format(minsources)
+        else:
+            conf += "#minsources 2\n"
+        conf += "\n"
+
+        conf += self.get_chrony_conf_allows()
+
+        conf += self.get_chrony_conf_cmdallows()
+
+        conf += "# Serve time even if not synchronized to a time source.\n"
+        if orphan_stratum > 0 and orphan_stratum < 16:
+            conf += "local stratum {} orphan\n".format(orphan_stratum)
+        elif local_stratum > 0 and local_stratum < 16:
+            conf += "local stratum {}\n".format(local_stratum)
+        else:
+            conf += "#local stratum 10\n"
+        conf += "\n"
+
+        conf += "# Specify file containing keys for NTP authentication.\n"
+        conf += "keyfile {}\n".format(chrony_keys_path)
+        conf += "\n"
+
+        conf += "# Get TAI-UTC offset and leap seconds from the system tz database.\n"
+        conf += "leapsectz right/UTC\n"
+        conf += "\n"
+
+        conf += "# Specify directory for log files.\n"
+        conf += "logdir /var/log/chrony\n"
+        conf += "\n"
+
+        conf += "# Select which information is logged.\n"
+        if logs:
+            conf += "log {}\n".format(" ".join(logs))
+        else:
+            conf += "#log measurements statistics tracking\n"
+
+        return conf
+
+    def get_chrony_keys(self):
+        if not self.keyfile:
+            return ""
+
+        keys = "# This file was converted from {}.\n".format(self.keyfile)
+        keys += "\n"
+
+        for key in self.keys:
+            key_id = key[0]
+            key_type = key[1]
+            password = key[2]
+
+            if key_type in ["m", "M"]:
+                key_type = "MD5"
+            elif key_type not in ["MD5", "SHA1", "SHA256", "SHA384", "SHA512"]:
+                continue
+
+            prefix = "ASCII" if len(password) <= 20 else "HEX"
+
+            for first, last in self.trusted_keys:
+                if first <= key_id <= last:
+                    trusted = True
+                    break
+            else:
+                trusted = False
+
+            # Disable keys that were not marked as trusted
+            if not trusted:
+                keys += "#"
+
+            keys += "{} {} {}:{}\n".format(key_id, key_type, prefix, password)
+
+        return keys
+
+    def write_file(self, path, mode, content, backup):
+        path = self.root_dir + path
+        if backup and os.path.isfile(path):
+            os.rename(path, path + ".old")
+
+        with open(os.open(path, os.O_CREAT | os.O_WRONLY | os.O_EXCL, mode), "w",
+                  encoding=self.file_encoding) as f:
+            logging.info("Writing %s", path)
+            f.write(u"" + content)
+
+        # Fix SELinux context if restorecon is installed
+        try:
+            subprocess.call(["restorecon", path])
+        except OSError:
+            pass
+
+
+def main():
+    parser = argparse.ArgumentParser(description="Convert ntp configuration to chrony.")
+    parser.add_argument("-r", "--root", dest="roots", default=["/"], nargs="+",
+                        metavar="DIR", help="specify root directory (default /)")
+    parser.add_argument("--ntp-conf", action="store", default="/etc/ntp.conf",
+                        metavar="FILE", help="specify ntp config (default /etc/ntp.conf)")
+    parser.add_argument("--step-tickers", action="store", default="",
+                        metavar="FILE", help="specify ntpdate step-tickers config (no default)")
+    parser.add_argument("--chrony-conf", action="store", default="/etc/chrony.conf",
+                        metavar="FILE", help="specify chrony config (default /etc/chrony.conf)")
+    parser.add_argument("--chrony-keys", action="store", default="/etc/chrony.keys",
+                        metavar="FILE", help="specify chrony keyfile (default /etc/chrony.keys)")
+    parser.add_argument("-b", "--backup", action="store_true", help="backup existing configs before writing")
+    parser.add_argument("-L", "--ignored-lines", action="store_true", help="print ignored lines")
+    parser.add_argument("-D", "--ignored-directives", action="store_true",
+                        help="print names of ignored directives")
+    parser.add_argument("-n", "--dry-run", action="store_true", help="don't make any changes")
+    parser.add_argument("-v", "--verbose", action="count", default=0, help="increase verbosity")
+
+    args = parser.parse_args()
+
+    logging.basicConfig(format="%(message)s",
+                        level=[logging.ERROR, logging.INFO, logging.DEBUG][min(args.verbose, 2)])
+
+    for root in args.roots:
+        conf = NtpConfiguration(root, args.ntp_conf, args.step_tickers)
+
+        if args.ignored_lines:
+            for line in conf.ignored_lines:
+                print(line)
+
+        if args.ignored_directives:
+            for directive in conf.ignored_directives:
+                print(directive)
+
+        conf.write_chrony_configuration(args.chrony_conf, args.chrony_keys, args.dry_run, args.backup)
+
+if __name__ == "__main__":
+    main()
diff --git a/SPECS/chrony.spec b/SPECS/chrony.spec
new file mode 100644
index 0000000..ee7d947
--- /dev/null
+++ b/SPECS/chrony.spec
@@ -0,0 +1,553 @@
+%global _hardened_build 1
+%global clknetsim_ver 3f5ef9
+%global ntp2chrony_ver 2a0512
+%bcond_without debug
+
+Name:           chrony
+Version:        3.5
+Release:        1%{?dist}
+Summary:        An NTP client/server
+
+Group:          System Environment/Daemons
+License:        GPLv2
+URL:            https://chrony.tuxfamily.org
+Source0:        https://download.tuxfamily.org/chrony/chrony-%{version}%{?prerelease}.tar.gz
+Source1:        chrony.dhclient
+Source2:        chrony.helper
+Source3:        chrony-dnssrv@.service
+Source4:        chrony-dnssrv@.timer
+# simulator for test suite
+Source10:       https://github.com/mlichvar/clknetsim/archive/%{clknetsim_ver}/clknetsim-%{clknetsim_ver}.tar.gz
+# script for converting ntp configuration to chrony
+Source11:       https://github.com/mlichvar/ntp2chrony/raw/%{ntp2chrony_ver}/ntp2chrony/ntp2chrony.py
+%{?gitpatch:Patch0: chrony-%{version}%{?prerelease}-%{gitpatch}.patch.gz}
+
+# add NTP servers from DHCP when starting service
+Patch2:         chrony-service-helper.patch
+
+BuildRequires:  libcap-devel libedit-devel nettle-devel pps-tools-devel
+%ifarch %{ix86} x86_64 %{arm} aarch64 mipsel mips64el ppc64 ppc64le s390 s390x
+BuildRequires:  libseccomp-devel
+%endif
+BuildRequires:  gcc bison systemd
+BuildRequires:  kernel-headers > 4.18.0-87
+
+Requires(pre):  shadow-utils
+%{?systemd_requires}
+
+# install timedated implementation that can control chronyd service
+Recommends:     timedatex
+
+# suggest drivers for hardware reference clocks
+Suggests:       ntp-refclock
+
+%description
+chrony is a versatile implementation of the Network Time Protocol (NTP).
+It can synchronise the system clock with NTP servers, reference clocks
+(e.g. GPS receiver), and manual input using wristwatch and keyboard. It
+can also operate as an NTPv4 (RFC 5905) server and peer to provide a time
+service to other computers in the network.
+
+%if 0%{!?vendorzone:1}
+%global vendorzone %(source /etc/os-release && echo ${ID}.)
+%endif
+
+%prep
+%setup -q -n %{name}-%{version}%{?prerelease} -a 10
+%{?gitpatch:%patch0 -p1}
+%patch2 -p1 -b .service-helper
+
+%{?gitpatch: echo %{version}-%{gitpatch} > version.txt}
+
+# review changes in packaged configuration files and scripts
+md5sum -c <<-EOF | (! grep -v 'OK$')
+        47ad7eccc410b981d2f2101cf5682616  examples/chrony-wait.service
+        e473a9fab7fe200cacce3dca8b66290b  examples/chrony.conf.example2
+        96999221eeef476bd49fe97b97503126  examples/chrony.keys.example
+        6a3178c4670de7de393d9365e2793740  examples/chrony.logrotate
+        8748a663f0b1943ea491858f414a6b26  examples/chrony.nm-dispatcher
+        b23bcc3bd78e195ca2849459e459f3ed  examples/chronyd.service
+EOF
+
+# don't allow packaging without vendor zone
+test -n "%{vendorzone}"
+
+# use example chrony.conf as the default config with some modifications:
+# - use our vendor zone (2.*pool.ntp.org names include IPv6 addresses)
+# - enable leapsectz to get TAI-UTC offset and leap seconds from tzdata
+# - enable keyfile
+sed -e 's|^\(pool \)\(pool.ntp.org\)|\12.%{vendorzone}\2|' \
+    -e 's|#\(leapsectz\)|\1|' \
+    -e 's|#\(keyfile\)|\1|' \
+        < examples/chrony.conf.example2 > chrony.conf
+
+touch -r examples/chrony.conf.example2 chrony.conf
+
+# regenerate the file from getdate.y
+rm -f getdate.c
+
+mv clknetsim-%{clknetsim_ver}* test/simulation/clknetsim
+
+install -m 644 -p %{SOURCE11} ntp2chrony.py
+
+%build
+%configure \
+%{?with_debug: --enable-debug} \
+        --enable-ntp-signd \
+        --enable-scfilter \
+        --docdir=%{_docdir} \
+        --with-ntp-era=$(date -d '1970-01-01 00:00:00+00:00' +'%s') \
+        --with-user=chrony \
+        --with-hwclockfile=%{_sysconfdir}/adjtime \
+        --with-sendmail=%{_sbindir}/sendmail
+make %{?_smp_mflags}
+
+%install
+make install DESTDIR=$RPM_BUILD_ROOT
+
+rm -rf $RPM_BUILD_ROOT%{_docdir}
+
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/{sysconfig,logrotate.d}
+mkdir -p $RPM_BUILD_ROOT%{_localstatedir}/{lib,log}/chrony
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/NetworkManager/dispatcher.d
+mkdir -p $RPM_BUILD_ROOT%{_sysconfdir}/dhcp/dhclient.d
+mkdir -p $RPM_BUILD_ROOT%{_libexecdir}
+mkdir -p $RPM_BUILD_ROOT{%{_unitdir},%{_prefix}/lib/systemd/ntp-units.d}
+
+install -m 644 -p chrony.conf $RPM_BUILD_ROOT%{_sysconfdir}/chrony.conf
+
+install -m 640 -p examples/chrony.keys.example \
+        $RPM_BUILD_ROOT%{_sysconfdir}/chrony.keys
+install -m 755 -p examples/chrony.nm-dispatcher \
+        $RPM_BUILD_ROOT%{_sysconfdir}/NetworkManager/dispatcher.d/20-chrony
+install -m 755 -p %{SOURCE1} \
+        $RPM_BUILD_ROOT%{_sysconfdir}/dhcp/dhclient.d/chrony.sh
+install -m 644 -p examples/chrony.logrotate \
+        $RPM_BUILD_ROOT%{_sysconfdir}/logrotate.d/chrony
+
+install -m 644 -p examples/chronyd.service \
+        $RPM_BUILD_ROOT%{_unitdir}/chronyd.service
+install -m 644 -p examples/chrony-wait.service \
+        $RPM_BUILD_ROOT%{_unitdir}/chrony-wait.service
+install -m 644 -p %{SOURCE3} $RPM_BUILD_ROOT%{_unitdir}/chrony-dnssrv@.service
+install -m 644 -p %{SOURCE4} $RPM_BUILD_ROOT%{_unitdir}/chrony-dnssrv@.timer
+
+install -m 755 -p %{SOURCE2} $RPM_BUILD_ROOT%{_libexecdir}/chrony-helper
+
+cat > $RPM_BUILD_ROOT%{_sysconfdir}/sysconfig/chronyd <<EOF
+# Command-line options for chronyd
+OPTIONS=""
+EOF
+
+touch $RPM_BUILD_ROOT%{_localstatedir}/lib/chrony/{drift,rtc}
+
+echo 'chronyd.service' > \
+        $RPM_BUILD_ROOT%{_prefix}/lib/systemd/ntp-units.d/50-chronyd.list
+
+%check
+# set random seed to get deterministic results
+export CLKNETSIM_RANDOM_SEED=24502
+make %{?_smp_mflags} -C test/simulation/clknetsim
+make quickcheck
+
+%pre
+getent group chrony > /dev/null || /usr/sbin/groupadd -r chrony
+getent passwd chrony > /dev/null || /usr/sbin/useradd -r -g chrony \
+       -d %{_localstatedir}/lib/chrony -s /sbin/nologin chrony
+:
+
+%post
+# fix PIDFile in local chronyd.service on upgrades from chrony < 3.3-2
+if grep -q 'PIDFile=%{_localstatedir}/run/chronyd.pid' \
+                %{_sysconfdir}/systemd/system/chronyd.service 2> /dev/null && \
+        ! grep -qi '^[ '$'\t'']*pidfile' %{_sysconfdir}/chrony.conf 2> /dev/null
+then
+        sed -i '/PIDFile=/s|/run/|/run/chrony/|' \
+                %{_sysconfdir}/systemd/system/chronyd.service
+fi
+# workaround for late reload of unit file (#1614751)
+%{_bindir}/systemctl daemon-reload
+%systemd_post chronyd.service chrony-wait.service
+
+%preun
+%systemd_preun chronyd.service chrony-wait.service
+
+%postun
+%systemd_postun_with_restart chronyd.service
+
+%files
+%{!?_licensedir:%global license %%doc}
+%license COPYING
+%doc FAQ NEWS README ntp2chrony.py
+%config(noreplace) %{_sysconfdir}/chrony.conf
+%config(noreplace) %verify(not md5 size mtime) %attr(640,root,chrony) %{_sysconfdir}/chrony.keys
+%config(noreplace) %{_sysconfdir}/logrotate.d/chrony
+%config(noreplace) %{_sysconfdir}/sysconfig/chronyd
+%{_sysconfdir}/NetworkManager/dispatcher.d/20-chrony
+%{_sysconfdir}/dhcp/dhclient.d/chrony.sh
+%{_bindir}/chronyc
+%{_sbindir}/chronyd
+%{_libexecdir}/chrony-helper
+%{_prefix}/lib/systemd/ntp-units.d/*.list
+%{_unitdir}/chrony*.service
+%{_unitdir}/chrony*.timer
+%{_mandir}/man[158]/%{name}*.[158]*
+%dir %attr(-,chrony,chrony) %{_localstatedir}/lib/chrony
+%ghost %attr(-,chrony,chrony) %{_localstatedir}/lib/chrony/drift
+%ghost %attr(-,chrony,chrony) %{_localstatedir}/lib/chrony/rtc
+%dir %attr(-,chrony,chrony) %{_localstatedir}/log/chrony
+
+%changelog
+* Tue May 21 2019 Miroslav Lichvar <mlichvar@redhat.com> 3.5-1
+- update to 3.5 (#1685469 #1677218)
+- fix shellcheck warnings in helper scripts (#1711948)
+- update ntp2chrony script
+
+* Mon Aug 13 2018 Miroslav Lichvar <mlichvar@redhat.com> 3.3-3
+- fix PIDFile in local chronyd.service on upgrades from chrony < 3.3-2
+  (#1614800)
+- add workaround for late reload of unit file (#1614751)
+
+* Mon Jun 18 2018 Miroslav Lichvar <mlichvar@redhat.com> 3.3-2
+- move pidfile to /var/run/chrony to allow chronyd to remove it on exit
+  (#1584585)
+- avoid blocking in getrandom system call (#1592425)
+
+* Thu Apr 05 2018 Miroslav Lichvar <mlichvar@redhat.com> 3.3-1
+- update to 3.3
+- enable keyfile by default again
+- update ntp2chrony script
+
+* Mon Mar 19 2018 Miroslav Lichvar <mlichvar@redhat.com> 3.3-0.2.pre1
+- include ntp2chrony script in documentation (#1530987)
+
+* Thu Mar 15 2018 Miroslav Lichvar <mlichvar@redhat.com> 3.3-0.1.pre1
+- update to 3.3-pre1
+- switch to nettle for crypto hashing
+- add gcc to build requirements
+
+* Wed Feb 07 2018 Fedora Release Engineering <releng@fedoraproject.org> - 3.2-4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_28_Mass_Rebuild
+
+* Tue Jan 30 2018 Miroslav Lichvar <mlichvar@redhat.com> 3.2-3
+- use systemd macro for scriptlet dependencies
+
+* Thu Jan 25 2018 Miroslav Lichvar <mlichvar@redhat.com> 3.2-2
+- fix chronyc getting stuck in infinite loop after clock step
+- don't allow packaging without vendor zone
+- suggest ntp-refclock
+- remove obsolete dependency
+- update description
+
+* Fri Sep 15 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.2-1
+- update to 3.2
+- get TAI-UTC offset and leap seconds from tzdata by default
+
+* Tue Aug 29 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.2-0.4.pre2
+- update to 3.2-pre2
+
+* Wed Aug 02 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.2-0.3.pre1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Binutils_Mass_Rebuild
+
+* Wed Jul 26 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.2-0.2.pre1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_27_Mass_Rebuild
+
+* Tue Jul 25 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.2-0.1.pre1
+- update to 3.2-pre1
+
+* Thu May 04 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.1-5
+- check PEERNTP variable before loading existing dhclient files
+
+* Thu Apr 20 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.1-4
+- use ID from /etc/os-release to set pool.ntp.org vendor zone (#1443599)
+- fix seccomp filter for new glibc once again
+- don't drop PHC samples with zero delay
+
+* Mon Mar 13 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.1-3
+- fix seccomp filter for new glibc
+
+* Fri Feb 10 2017 Fedora Release Engineering <releng@fedoraproject.org> - 3.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
+* Tue Jan 31 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.1-1
+- update to 3.1
+- enable seccomp support on more archs
+- package chronyd sysconfig file
+
+* Tue Jan 24 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.1-0.1.pre1
+- update to 3.1-pre1
+
+* Mon Jan 16 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.0-1
+- update to 3.0
+
+* Fri Jan 06 2017 Miroslav Lichvar <mlichvar@redhat.com> 3.0-0.3.pre3
+- update to 3.0-pre3
+
+* Thu Dec 15 2016 Miroslav Lichvar <mlichvar@redhat.com> 3.0-0.2.pre2
+- update to 3.0-pre2
+- enable support for MS-SNTP authentication in Samba
+
+* Fri Dec 09 2016 Miroslav Lichvar <mlichvar@redhat.com> 3.0-0.1.pre1
+- update to 3.0-pre1
+
+* Mon Nov 21 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.4.1-1
+- update to 2.4.1
+
+* Thu Oct 27 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.4-4
+- avoid AVC denials in chrony-wait service (#1350815)
+
+* Tue Sep 13 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.4-3
+- fix chrony-helper to escape names of systemd units (#1374767)
+
+* Tue Jun 28 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.4-2
+- fix chrony-helper to exit with correct status (#1350531)
+
+* Tue Jun 07 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.4-1
+- update to 2.4
+- don't require info
+
+* Mon May 16 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.4-0.1.pre1
+- update to 2.4-pre1
+- extend chrony-helper to allow management of static sources (#1331655)
+
+* Tue Feb 16 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.3-1
+- update to 2.3
+
+* Tue Feb 02 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.3-0.1.pre1
+- update to 2.3-pre1
+
+* Thu Jan 21 2016 Miroslav Lichvar <mlichvar@redhat.com> 2.2.1-1
+- update to 2.2.1 (CVE-2016-1567)
+- set NTP era split explicitly
+
+* Mon Oct 19 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.2-1
+- update to 2.2
+
+* Fri Oct 09 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.2-0.2.pre2
+- update to 2.2-pre2
+- require libseccomp-devel on supported archs only
+
+* Fri Oct 02 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.2-0.1.pre1
+- update to 2.2-pre1
+- enable seccomp support
+- use weak dependency for timedatex on Fedora 24 and later
+
+* Tue Jun 23 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.1.1-1
+- update to 2.1.1
+- add -n option to gzip command to not save timestamp
+
+* Mon Jun 22 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.1-1
+- update to 2.1
+- extend chrony-helper to allow using servers from DNS SRV records (#1234406)
+- set random seed in testing to get deterministic results
+
+* Wed Jun 17 2015 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 2.1-0.2.pre1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_23_Mass_Rebuild
+
+* Wed Jun 10 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.1-0.1.pre1
+- update to 2.1-pre1
+
+* Mon Apr 27 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.0-1
+- update to 2.0
+
+* Wed Apr 08 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.0-0.3.pre2
+- update to 2.0-pre2 (CVE-2015-1853 CVE-2015-1821 CVE-2015-1822)
+
+* Thu Jan 29 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.0-0.2.pre1
+- require timedatex (#1136905)
+
+* Tue Jan 27 2015 Miroslav Lichvar <mlichvar@redhat.com> 2.0-0.1.pre1
+- update to 2.0-pre1
+
+* Thu Sep 11 2014 Miroslav Lichvar <mlichvar@redhat.com> 1.31-1
+- update to 1.31
+- add servers from DHCP with iburst option by default
+- use upstream configuration files and scripts
+- don't package configuration examples
+- compress chrony.txt
+
+* Thu Aug 21 2014 Miroslav Lichvar <mlichvar@redhat.com> 1.31-0.1.pre1
+- update to 1.31-pre1
+- use license macro if available
+
+* Sat Aug 16 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.30-3
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_22_Mass_Rebuild
+
+* Fri Aug 15 2014 Miroslav Lichvar <mlichvar@redhat.com> 1.30-2
+- reconnect client sockets (#1124059)
+
+* Tue Jul 01 2014 Miroslav Lichvar <mlichvar@redhat.com> 1.30-1
+- update to 1.30
+- enable debug messages
+
+* Mon Jun 09 2014 Miroslav Lichvar <mlichvar@redhat.com> 1.30-0.1.pre1
+- update to 1.30-pre1
+- execute test suite
+- avoid calling systemctl in helper script
+- call chronyc directly from logrotate and NM dispatcher scripts
+- add conflict with systemd-timesyncd service
+
+* Sat Jun 07 2014 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.29.1-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_21_Mass_Rebuild
+
+* Fri Jan 31 2014 Miroslav Lichvar <mlichvar@redhat.com> 1.29.1-1
+- update to 1.29.1 (CVE-2014-0021)
+- replace hardening build flags with _hardened_build
+
+* Tue Nov 19 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.29-3
+- let systemd remove pid file (#974305)
+
+* Thu Oct 03 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.29-2
+- add ordering dependency to not start chronyd before ntpd stopped
+
+* Thu Aug 08 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.29-1
+- update to 1.29 (CVE-2012-4502, CVE-2012-4503)
+
+* Sat Aug 03 2013 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.28-2
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_20_Mass_Rebuild
+
+* Wed Jul 17 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.28-1
+- update to 1.28
+- change default makestep limit to 10 seconds
+
+* Mon Jun 24 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.28-0.2.pre1
+- buildrequire systemd-units
+
+* Fri Jun 21 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.28-0.1.pre1
+- update to 1.28-pre1
+- listen for commands only on localhost by default
+
+* Thu May 09 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.27-3
+- disable chrony-wait service by default (#961047)
+- drop old systemd scriptlets
+- don't own ntp-units.d directory
+- move files from /lib
+- remove unncessary dependency on syslog target
+
+* Tue Mar 12 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.27-2
+- suppress error messages from tr when generating key (#907914)
+- fix delta calculation with extreme frequency offsets
+
+* Fri Feb 01 2013 Miroslav Lichvar <mlichvar@redhat.com> 1.27-1
+- update to 1.27
+- start chrony-wait service with chronyd
+- start chronyd service after sntp
+- remove obsolete macros
+
+* Tue Sep 11 2012 Miroslav Lichvar <mlichvar@redhat.com> 1.27-0.5.pre1.git1ca844
+- update to git snapshot 1ca844
+- update systemd integration (#846303)
+- use systemd macros if available (#850151)
+- use correct vendor pool.ntp.org zone on RHEL (#845981)
+- don't log output of chrony-wait service
+
+* Wed Jul 18 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.27-0.4.pre1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_18_Mass_Rebuild
+
+* Fri Apr 27 2012 Miroslav Lichvar <mlichvar@redhat.com> 1.27-0.3.pre1
+- update service file for systemd-timedated-ntp target (#816493)
+
+* Fri Apr 06 2012 Miroslav Lichvar <mlichvar@redhat.com> 1.27-0.2.pre1
+  use systemctl is-active instead of status in chrony-helper (#794771)
+
+* Tue Feb 28 2012 Miroslav Lichvar <mlichvar@redhat.com> 1.27-0.1.pre1
+- update to 1.27-pre1
+- generate SHA1 command key instead of MD5
+
+* Wed Feb 15 2012 Miroslav Lichvar <mlichvar@redhat.com> 1.26-6.20110831gitb088b7
+- remove old servers on DHCP update (#787042)
+
+* Fri Feb 10 2012 Miroslav Lichvar <mlichvar@redhat.com> 1.26-5.20110831gitb088b7
+- improve chrony-helper to keep track of servers added from DHCP (#787042)
+- fix dhclient script to always return with zero exit code (#767859)
+
+* Thu Jan 12 2012 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.26-4.20110831gitb088b7
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_17_Mass_Rebuild
+
+* Tue Sep 06 2011 Miroslav Lichvar <mlichvar@redhat.com> 1.26-3.20110831gitb088b7
+- update to git snapshot 20110831gitb088b7
+- on first start generate password with 16 chars
+- change systemd service type to forking
+- add forced-command to chrony-helper (#735821)
+
+* Mon Aug 15 2011 Miroslav Lichvar <mlichvar@redhat.com> 1.26-2
+- fix iburst with very high jitters and long delays
+- use timepps header from pps-tools-devel
+
+* Wed Jul 13 2011 Miroslav Lichvar <mlichvar@redhat.com> 1.26-1
+- update to 1.26
+- read options from sysconfig file if it exists
+
+* Fri Jun 24 2011 Miroslav Lichvar <mlichvar@redhat.com> 1.26-0.1.pre1
+- update to 1.26-pre1
+- fix service name in %%triggerun
+- drop SysV init script
+- add chrony-wait service
+
+* Fri May 06 2011 Bill Nottingham <notting@redhat.com> 1.25-2
+- fix systemd scriptlets for the upgrade case
+
+* Wed May 04 2011 Miroslav Lichvar <mlichvar@redhat.com> 1.25-1
+- update to 1.25
+
+* Wed Apr 20 2011 Miroslav Lichvar <mlichvar@redhat.com> 1.25-0.3.pre2
+- update to 1.25-pre2
+- link with -Wl,-z,relro,-z,now options
+
+* Tue Feb 08 2011 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.25-0.2.pre1
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_15_Mass_Rebuild
+
+* Tue Feb 01 2011 Miroslav Lichvar <mlichvar@redhat.com> 1.25-0.1.pre1
+- update to 1.25-pre1
+- use iburst, four pool servers, rtcsync, stratumweight in default config
+- add systemd support
+- drop sysconfig file 
+- suppress install-info errors
+
+* Thu Apr 29 2010 Miroslav Lichvar <mlichvar@redhat.com> 1.24-4.20100428git73d775
+- update to 20100428git73d775
+- replace initstepslew directive with makestep in default config
+- add NetworkManager dispatcher script
+- add dhclient script
+- retry server/peer name resolution at least once to workaround
+  NetworkManager race condition on boot
+- don't verify chrony.keys
+
+* Fri Mar 12 2010 Miroslav Lichvar <mlichvar@redhat.com> 1.24-3.20100302git5fb555
+- update to snapshot 20100302git5fb555
+- compile with PPS API support
+
+* Thu Feb 04 2010 Miroslav Lichvar <mlichvar@redhat.com> 1.24-1
+- update to 1.24 (#555367, CVE-2010-0292 CVE-2010-0293 CVE-2010-0294)
+- modify default config
+  - step clock on start if it is off by more than 100 seconds
+  - disable client log
+- build with -fPIE on sparc
+
+* Tue Dec 15 2009 Miroslav Lichvar <mlichvar@redhat.com> 1.24-0.1.pre1
+- update to 1.24-pre1
+
+* Fri Jul 24 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.23-7.20081106gitbe42b4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_12_Mass_Rebuild
+
+* Fri Jul 17 2009 Miroslav Lichvar <mlichvar@redhat.com> 1.23-6.20081106gitbe42b4
+- switch to editline
+- support arbitrary chronyc commands in init script
+
+* Mon Jun 08 2009 Dan Horak <dan[at]danny.cz> 1.23-5.20081106gitbe42b4
+- add patch with support for s390/s390x
+
+* Mon Mar 09 2009 Miroslav Lichvar <mlichvar@redhat.com> 1.23-4.20081106gitbe42b4
+- fix building with broken libcap header (#483548)
+
+* Mon Feb 23 2009 Fedora Release Engineering <rel-eng@lists.fedoraproject.org> - 1.23-3.20081106gitbe42b4
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_11_Mass_Rebuild
+
+* Wed Nov 19 2008 Miroslav Lichvar <mlichvar@redhat.com> 1.23-2.20081106gitbe42b4
+- fix info uninstall
+- generate random command key in init script
+- support cyclelogs, online, offline commands in init script
+- add logrotate script
+
+* Tue Nov 11 2008 Miroslav Lichvar <mlichvar@redhat.com> 1.23-1.20081106gitbe42b4
+- initial release