Compare commits

...

2 commits

2 changed files with 205 additions and 0 deletions

154
rsync-backup.py Executable file
View file

@ -0,0 +1,154 @@
#!/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)

51
tootctl.py Executable file
View file

@ -0,0 +1,51 @@
#!/usr/bin/env python3
import os, sys
from os import environ
from pathlib import Path
from subprocess import Popen
home = Path(environ.get('HOME', os.getcwd())).resolve()
rubydir = home.joinpath('.rbenv/shims/ruby')
mastodir = Path(environ.get('MASTO_DIR', home.joinpath('.local/opt/mastodon')))
tootctl = mastodir.joinpath('bin/tootctl')
jemalloc = Path('/usr/lib/x86_64-linux-gnu/libjemalloc.so.2')
args = sys.argv[1:] if len(sys.argv) > 1 else []
environment = environ.copy()
environment.update({
'RAILS_ENV': 'production',
'NODE_ENV': 'production',
})
def debian_11_check():
info = {}
if not jemalloc.exists():
return False
for line in open('/etc/os-release', 'r').readlines():
key, value = line.split('=', 1)
value = value.replace('\n', '')
if value.startswith('"') and value.endswith('"'):
value = value[1:-1]
info[key] = value
return info['ID'].lower() == 'debian' and info['VERSION_ID'] == "11"
if __name__ == '__main__':
if debian_11_check():
environment['LD_PRELOAD'] = str(jemalloc)
proc = Popen([str(rubydir), str(tootctl), *args], env=environment, cwd=mastodir)
try:
proc.wait()
except KeyboardInterrupt:
proc.terminate()