diff --git a/README.md b/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..f1508d5ae0e40436888d7edd323e183820665c50
--- /dev/null
+++ b/README.md
@@ -0,0 +1,25 @@
+dist-sync
+=========
+
+Simple python program to keep (rpm-based) distribution packages/files
+locally up to date. Requires dnf-plugins-core > 3.0.1 (or backported 
+--downloadcomps functionality)
+
+
+```
+usage: dist-sync [-h] [-v] [--cleanup] [--commit] [--reposync] [--rsync]
+                 [--nocreaterepo]
+                 PATH [PATH ...]
+
+positional arguments:
+  PATH            RPM distribution directory
+
+optional arguments:
+  -h, --help      show this help message and exit
+  -v, --verbose   print progress information
+  --cleanup       remove files that has disappeared from remote repository
+  --commit        move updated files to active location
+  --reposync      Run rsync actions as well as reposync
+  --rsync         Run rsync actions as well as reposync
+  --nocreaterepo  Skip createrepo
+```
\ No newline at end of file
diff --git a/dist-sync b/dist-sync
index 8951c19bf90c3a6e8cc5cfd7cf4e2765d2b40fff..0e4c25ad43db52ccb8bef990db6261d4aa928afc 100755
--- a/dist-sync
+++ b/dist-sync
@@ -1,4 +1,4 @@
-#!/usr/bin/python
+#!/usr/bin/python3
 
 import argparse
 import contextlib
@@ -8,7 +8,6 @@ import subprocess
 import sys
 import tempfile
 import threading
-import lockfile
 import fcntl
 import errno
 import time
@@ -51,7 +50,7 @@ def file_lock(lock_file, progress=None):
         try:
             fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
             break
-        except IOError, e:
+        except IOError as e:
             if e.errno != errno.EAGAIN:
                 raise
             pass
@@ -105,8 +104,8 @@ def read_files(path):
     return result
 
 def read_rpms(path):
-    return dict(filter(lambda (k,v): k.endswith('.rpm'),
-                       read_files(path).iteritems()))
+    return dict([ (k,v) for k,v in read_files(path).items()
+                  if k.endswith('.rpm')])
 
 class Repo:
 
@@ -123,7 +122,7 @@ class Repo:
     def add(self, target, source):
         if target == 'ARCH':
             self.arch = source
-        elif target in ['baseurl', 'mirrorlist']:
+        elif target in ['baseurl', 'mirrorlist', 'metalink' ]:
             if self.mirrored:
                 raise Exception('Multiply mirrored %s, %s' % 
                                 (self.mirrored, (target, source)))
@@ -280,16 +279,13 @@ class Repo:
             # so let us put everything in a subdirectory manually
             packages = '%s/Packages' % (destdir)
             cmd = []
-            cmd.extend(['/usr/local/bin/reposync', 
-                        '--arch', self.arch,
-                        '--releasever', self.release,
+            cmd.extend(['dnf', 'reposync',
                         '--config', conf_name,
-                        '--download_path', packages,
-                        '--norepopath',
-                        '--download-metadata',
-                        '--delete' ])
+                        '--download-path', os.path.dirname(destdir),
+                        '--downloadcomps'
+                        ] + [ '--verbose' for i in range(args.verbose) ])
             if args.verbose:
-                print ' '.join(cmd)
+                print(' '.join(cmd))
             p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
             def progress(pipe):
                 while True:
@@ -298,7 +294,7 @@ class Repo:
                         break
                     if not args.verbose:
                         continue
-                    m = re.match('.*[^0-9](\d+)/(\d+).*', l)
+                    m = re.match(b'.*[^0-9](\d+)/(\d+).*', l)
                     if m:
                         n = int(m.group(1))
                         m = int(m.group(2))
@@ -346,20 +342,18 @@ class Repo:
                                  '-exec', 'rm', '{}', ';'])
                 comps = filter(lambda p: re.match('^.*comps.*\.xml$', p), 
                                os.listdir('.'))
-                comps = sorted(comps,
-                               cmp=lambda a,b: cmp(os.stat(a).st_mtime,
-                                                   os.stat(b).st_mtime))
+                comps = sorted(comps, key=lambda p: os.stat(p).st_mtime)
                 if len(comps) == 0:
                     comps = []
                     pass
                 else:
                     comps = [ '-g', comps[-1] ] 
                     pass
-                cmd = list([ '/usr/bin/createrepo', '-c', 'cache' ] +
+                cmd = list([ '/usr/bin/createrepo_c', '-c', 'cache' ] +
                            comps +
                            ['-v', '--update', '--pretty', '.'])
                 if args.verbose:
-                    print ' '.join(cmd)
+                    print(' '.join(cmd))
                 p = subprocess.Popen(cmd,
                                      stdout=subprocess.PIPE,
                                      stderr=subprocess.STDOUT)
@@ -370,7 +364,7 @@ class Repo:
                             break
                         if not args.verbose:
                             continue
-                        m = re.match('^Worker\s+[^:]:\s+reading\s+(.*)', l)
+                        m = re.match(b'^Worker\s+[^:]:\s+reading\s+(.*)', l)
                         if m:
                             if args.verbose == 1:
                                 sys.stderr.write('\r%-60.60s\r' % m.group(1))
@@ -378,7 +372,7 @@ class Repo:
                                 sys.stderr.write('%s\n' % m.group(1))
                             sys.stderr.flush()
                             continue
-                        m = re.match('^Using data .* for (\S+)$', l)
+                        m = re.match(b'^Using data .* for (\S+)$', l)
                         if m:
                             if args.verbose == 1:
                                 sys.stderr.write('\r%-60.60s\r' % m.group(1))
@@ -407,7 +401,7 @@ class Repo:
                 pass
             for cachedir,_,files in os.walk('.'):
                 for f in files:
-                    os.chmod('%s/%s' % (cachedir, f), 0644)
+                    os.chmod('%s/%s' % (cachedir, f), 0o644)
                     pass
                 pass
             pass
@@ -475,12 +469,12 @@ def parse_config(config):
     return result
 
 if __name__ == '__main__':
-    parser = argparse.ArgumentParser(usage='%(prog)s [options] DISTRIBUTION*')
-    parser.add_argument('distribution', 
-                        metavar='DISTRIBUTION', 
+    parser = argparse.ArgumentParser()
+    parser.add_argument('path', 
+                        metavar='PATH', 
                         type=str,
                         nargs='+', 
-                        help='a rpm distribution directory ')    
+                        help='RPM distribution directory ')
     parser.add_argument('-v', '--verbose', 
                         default=0,
                         action='count',
@@ -491,6 +485,9 @@ if __name__ == '__main__':
     parser.add_argument('--commit',
                         action='store_true',
                         help='move updated files to active location')
+    parser.add_argument('--reposync',
+                        action='store_true',
+                        help='Run rsync actions as well as reposync')
     parser.add_argument('--rsync',
                         action='store_true',
                         help='Run rsync actions as well as reposync')
@@ -498,30 +495,45 @@ if __name__ == '__main__':
                         action='store_true',
                         help='Skip createrepo')
     args = parser.parse_args()
-    for d in args.distribution:
+
+    todo = []
+    for p in args.path:
+        if os.path.isfile(p) and os.path.basename(p) == 'REPOS':
+            todo.append((os.path.dirname(p), p))
+            continue
+        if os.path.isdir(p) and os.path.isfile(os.path.join(p, 'REPOS')):
+            todo.append((p, os.path.join(p, 'REPOS')))
+            continue
+        raise Exception("No 'REPOS' file in '%s'" % p)
+
+    for d,r in todo:
         lockfile = open('%s/LOCK' % d, 'a')
         def progress(f):
             log('Waiting for %s to be unlocked' % f.name)
             pass
         log('%s:' % os.path.realpath(d))
         with file_lock(lockfile, progress=progress):
-            repofile = open('%s/REPOS' % d)
+            repofile = open(r)
             repos = parse_config(repofile)
-            if args.commit:
-                log('Commiting changes...')
+            if args.rsync:
+                log("Running 'rsync'")
                 for repo in repos:
-                    repo.commit(args)
+                    repo.rsync(args)
                     pass
                 pass
-            else:
+            if args.reposync:
+                log("Running 'dnf reposync'...")
                 for repo in repos:
-                    if args.rsync:
-                        repo.rsync(args)
-                        pass
                     repo.get_updates(args)
                     repo.update_db(args)
                     pass
                 pass
+            if args.commit:
+                log('Commiting changes...')
+                for repo in repos:
+                    repo.commit(args)
+                    pass
+                pass
             pass
         pass
     pass