| @ -0,0 +1,115 @@ | |||
| #!/usr/bin/env python3 | |||
| import os | |||
| import logging | |||
| import string | |||
| import hashlib | |||
| import time | |||
| class Cloner: | |||
| def __init__(self, targetDir: str, backupDir: str): | |||
| self.targetDir = targetDir | |||
| self.backupDir = backupDir | |||
| self.interval = 1 | |||
| def handleDir(self, dirPath: str): | |||
| backupDirPath = dirPath.replace(self.targetDir, self.backupDir) | |||
| if not os.path.isdir(backupDirPath): | |||
| os.system('mkdir {path}'.format(path=backupDirPath)) | |||
| with os.scandir(dirPath) as it: | |||
| for entry in it: | |||
| if entry.is_dir(): | |||
| self.handleDir(entry.path) | |||
| elif entry.is_file(): | |||
| self.handleFile(entry.path) | |||
| def handleFile(self, fileName: str): | |||
| cloneFileName = fileName.replace(self.targetDir, self.backupDir) | |||
| if os.path.exists(cloneFileName): | |||
| with open(cloneFileName + '.md5', 'r') as cloneHashFile, open(fileName, 'rb') as targetFile: | |||
| cloneHashFileContent = cloneHashFile.read() | |||
| targetFileContent = targetFile.read() | |||
| realHash = hashlib.md5(targetFileContent).hexdigest() | |||
| if realHash != cloneHashFileContent: | |||
| os.system('cp {backup} {real}'.format( | |||
| backup=cloneFileName, real=fileName)) | |||
| logging.warning( | |||
| '{file} failed check, restoring...'.format(file=fileName)) | |||
| else: | |||
| logging.info('{file} is fine'.format(file=fileName)) | |||
| else: | |||
| logging.warning( | |||
| '{file} is a new file, added to backup dir'.format(file=fileName)) | |||
| os.system('cp {real} {clone}'.format( | |||
| real=fileName, clone=cloneFileName)) | |||
| with open(cloneFileName + '.md5', 'w') as cloneHashFile, open(fileName, 'rb') as targetFile: | |||
| targetFileContent = targetFile.read() | |||
| cloneHashFile.write(hashlib.md5(targetFileContent).hexdigest()) | |||
| def setLog(self, logFileName: str): | |||
| logFormatter = logging.Formatter( | |||
| fmt="[%(asctime)s][%(levelname)s] %(message)s", | |||
| datefmt='%d-%b-%y %H:%M:%S') | |||
| rootLogger = logging.getLogger() | |||
| fileHandler = logging.FileHandler(logFileName, mode='a') | |||
| fileHandler.setFormatter(logFormatter) | |||
| rootLogger.addHandler(fileHandler) | |||
| # # uncomment this to enable console logging | |||
| # consoleHandler = logging.StreamHandler() | |||
| # consoleHandler.setFormatter(logFormatter) | |||
| # rootLogger.addHandler(consoleHandler) | |||
| rootLogger.setLevel(logging.WARNING) | |||
| def setInterval(self, interval: int): | |||
| self.interval = interval | |||
| def start(self): | |||
| while True: | |||
| self.handleDir(self.targetDir) | |||
| time.sleep(5) | |||
| if __name__ == "__main__": | |||
| import argparse | |||
| parser = argparse.ArgumentParser() | |||
| parser.add_argument( | |||
| '--target', | |||
| metavar='PATH', | |||
| type=str, | |||
| required=True, | |||
| help='directory to clone and monitor') | |||
| parser.add_argument( | |||
| '--backup', | |||
| metavar='PATH', | |||
| type=str, | |||
| required=True, | |||
| help='backup directory to store clone' | |||
| ) | |||
| parser.add_argument( | |||
| '--interval', | |||
| metavar='SECOND', | |||
| type=int, | |||
| default=1, | |||
| help='interval in second(s) which the scripts run, default is 1' | |||
| ) | |||
| parser.add_argument( | |||
| '--log', | |||
| metavar='LOGFILE', | |||
| type=str, | |||
| default='/var/log/py-dir-watcher/log.txt', | |||
| help='specify custom logfile target, default is \'/var/log/py-dir-watcher/log.txt\' ' | |||
| ) | |||
| args = parser.parse_args() | |||
| app = Cloner(args.target, args.backup) | |||
| app.setLog(args.log) | |||
| app.setInterval(args.interval) | |||
| app.start() | |||