Clone directory (recursive) and restore any changes made to it.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

116 line
3.8 KiB

  1. #!/usr/bin/env python3
  2. import os
  3. import logging
  4. import string
  5. import hashlib
  6. import time
  7. class Cloner:
  8. def __init__(self, targetDir: str, backupDir: str):
  9. self.targetDir = targetDir
  10. self.backupDir = backupDir
  11. self.interval = 1
  12. def handleDir(self, dirPath: str):
  13. backupDirPath = dirPath.replace(self.targetDir, self.backupDir)
  14. if not os.path.isdir(backupDirPath):
  15. os.mkdir(backupDirPath)
  16. with os.scandir(dirPath) as it:
  17. for entry in it:
  18. if entry.is_dir():
  19. self.handleDir(entry.path)
  20. elif entry.is_file():
  21. self.handleFile(entry.path)
  22. def handleFile(self, targetFileName: str):
  23. cloneFileName = targetFileName.replace(self.targetDir, self.backupDir)
  24. cloneHashFileName = cloneFileName + '.md5'
  25. if os.path.exists(cloneFileName):
  26. with open(cloneHashFileName, 'r') as cloneHashFile, open(targetFileName, 'rb') as targetFile:
  27. cloneHashFileContent = cloneHashFile.read()
  28. targetFileContent = targetFile.read()
  29. realHash = hashlib.md5(targetFileContent).hexdigest()
  30. if realHash != cloneHashFileContent:
  31. os.system('cp {backup} {real}'.format(
  32. backup=cloneFileName, real=targetFileName))
  33. logging.warning(
  34. '{file} failed check, restoring...'.format(file=targetFileName))
  35. else:
  36. logging.info('{file} is fine'.format(file=targetFileName))
  37. else:
  38. logging.warning(
  39. '{file} is a new file, added to backup dir'.format(file=targetFileName))
  40. os.system('cp {real} {clone}'.format(
  41. real=targetFileName, clone=cloneFileName))
  42. with open(cloneHashFileName, 'w') as cloneHashFile, open(targetFileName, 'rb') as targetFile:
  43. targetFileContent = targetFile.read()
  44. cloneHashFile.write(hashlib.md5(targetFileContent).hexdigest())
  45. def setLog(self, logFileName: str):
  46. logFormatter = logging.Formatter(
  47. fmt="[%(asctime)s][%(levelname)s] %(message)s",
  48. datefmt='%d-%b-%y %H:%M:%S')
  49. rootLogger = logging.getLogger()
  50. fileHandler = logging.FileHandler(logFileName, mode='a')
  51. fileHandler.setFormatter(logFormatter)
  52. rootLogger.addHandler(fileHandler)
  53. # # uncomment this to enable console logging
  54. # consoleHandler = logging.StreamHandler()
  55. # consoleHandler.setFormatter(logFormatter)
  56. # rootLogger.addHandler(consoleHandler)
  57. rootLogger.setLevel(logging.WARNING)
  58. def setInterval(self, interval: int):
  59. self.interval = interval
  60. def start(self):
  61. while True:
  62. self.handleDir(self.targetDir)
  63. time.sleep(5)
  64. if __name__ == "__main__":
  65. import argparse
  66. parser = argparse.ArgumentParser()
  67. parser.add_argument(
  68. '--target',
  69. metavar='PATH',
  70. type=str,
  71. required=True,
  72. help='directory to clone and monitor')
  73. parser.add_argument(
  74. '--backup',
  75. metavar='PATH',
  76. type=str,
  77. required=True,
  78. help='backup directory to store clone'
  79. )
  80. parser.add_argument(
  81. '--interval',
  82. metavar='SECOND',
  83. type=int,
  84. default=1,
  85. help='interval in second(s) which the scripts run, default is 1'
  86. )
  87. parser.add_argument(
  88. '--log',
  89. metavar='LOGFILE',
  90. type=str,
  91. default='/var/log/py-dir-watcher/log.txt',
  92. help='specify custom logfile target, default is \'/var/log/py-dir-watcher/log.txt\' '
  93. )
  94. args = parser.parse_args()
  95. app = Cloner(args.target, args.backup)
  96. app.setLog(args.log)
  97. app.setInterval(args.interval)
  98. app.start()