| #!/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) | |
|         cloneHashFileName = cloneFileName + '.md5' | |
|          | |
|         if os.path.exists(cloneFileName): | |
|             with open(cloneHashFileName, '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(cloneHashFileName, '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()
 |