scripts/bin/rsync-backup.py

155 lines
2.9 KiB
Python
Executable file

#!/usr/bin/env python3
import subprocess, sys, time, traceback
from configparser import ConfigParser
from getpass import getpass, getuser
from pathlib import Path
exit=False
sudo = {}
procs = []
def Rsync(src, dest, archive=False, bwlimit=None, exclude=[], user=None):
cmd = []
current_user = getuser()
if user and user != current_user:
cmd.extend(['sudo', '-Su', user])
if not sudo.get(user):
sudo[user] = getpass(f'[pysudo] password for {current_user}: ')
cmd.append('rsync')
cmd.append('--partial')
cmd.append('--progress')
cmd.append('--numeric-ids')
if archive:
cmd.append('-aAXhx')
cmd.append('--delete')
if bwlimit:
cmd.append(f'--bwlimit={int(bwlimit)*1000}')
if type(exclude) == str:
exclude = [exclude]
for line in exclude:
cmd.append('--exclude')
cmd.append(line)
for line in [src, dest]:
if not line.endswith('/'):
line += '/'
cmd.append(line)
proc = subprocess.Popen(cmd, stdin=subprocess.PIPE)
if user and user != current_user:
password = sudo[user] + '\r'
proc.communicate(input=password.encode())
return proc
def load_config(path):
path = Path(path)
new_config = {}
config = ConfigParser()
config.read(path)
if not path.is_file():
print('Config file not found at', path)
return
config = dict(config)
if not config.get('DEFAULT'):
print('Missing "DEFAULT" section in config')
return
if not config['DEFAULT'].get('backup_dir'):
print('Missing "backup_dir" setting')
return
for category, var in config.items():
if category == 'DEFAULT':
new_config['backup_dir'] = var['backup_dir']
continue
new_config[category] = {}
for k,v in var.items():
new_config[category][k] = v
return new_config
def main(config):
global exit
global procs
backup_dir = Path(config.get('backup_dir'))
try:
for name, data in config.items():
if name == 'backup_dir':
continue
exclude = data.get('exclude', [])
user = data.get('user')
bwlimit = data.get('bwlimit')
if not data.get('src'):
print(f'Missing source directory for {name}')
continue
data['dest'] = str(backup_dir.joinpath(name))
for path in ['src', 'dest']:
if not data[path].endswith('/'):
data[path] += '/'
src = data.get('src')
dest = data.get('dest')
print(f'Running backup for {name}')
proc = Rsync(src, dest, archive=True, exclude=exclude, bwlimit=bwlimit, user=user)
if not proc:
print(f'Failed to run backup for {name}')
else:
procs.append(proc)
proc.wait()
if exit:
break
except KeyboardInterrupt:
print('Bye!')
for proc in procs:
if proc.poll() == None:
print('Terminating proc:', ' '.join(proc.args))
proc.terminate()
if __name__ == '__main__':
args = sys.argv[1:]
configfile = Path.home().joinpath('.config', 'barkshark', 'pysync.ini')
config = {}
if len(args) > 0:
configfile = Path(args[0])
config = load_config(configfile)
if not config:
print('failed to load config')
sys.exit()
main(config)