1 #!/usr/local/bin/python3 |
1 #!/usr/local/bin/python3 |
2 |
2 |
|
3 # Common modules |
3 import os |
4 import os |
4 import sys |
5 import sys |
5 import logging |
|
6 import logging.handlers |
|
7 import argparse |
6 import argparse |
8 import platform |
7 import platform |
9 |
8 import branding |
10 # |
9 # Own modules needed at this stage |
11 # Branding |
10 import locations |
12 # |
|
13 appname="borgend" |
|
14 appname_stylised="Borgend" |
|
15 |
|
16 # |
|
17 # Logging configuration |
|
18 # |
|
19 |
|
20 loglevel=logging.INFO |
|
21 logfmt="%(asctime)s:%(levelname)s:%(name)s:%(message)s" |
|
22 fifolog_capacity=1000 |
|
23 fifolog_fmt="%(asctime)s:%(levelname)s:%(message)s" |
|
24 |
|
25 # |
|
26 # Setup logger, needed by the config module to be loaded next |
|
27 # |
|
28 |
|
29 logger=logging.getLogger(appname) |
|
30 logger.handlers.clear() |
|
31 logger.setLevel(loglevel) |
|
32 stderrlog=logging.StreamHandler() |
|
33 stderrlog.setLevel(logging.WARNING) |
|
34 logger.addHandler(stderrlog) |
|
35 logger.propagate=True |
|
36 |
|
37 # |
|
38 # Import our own modules. This needs to be done here |
|
39 # for the things above to be available to them |
|
40 # |
|
41 |
|
42 import config |
|
43 from scheduler import Scheduler |
|
44 from fifolog import FIFOHandler |
|
45 from repository import Repository |
|
46 |
11 |
47 # |
12 # |
48 # Argument processing |
13 # Argument processing |
49 # |
14 # |
50 |
15 |
51 def do_args(): |
16 parser=argparse.ArgumentParser( |
52 parser=argparse.ArgumentParser( |
17 description=branding.appname_stylised + ": BorgBackup scheduler, queue, and tray icon.", |
53 description=appname_stylised + ': BorgBackup scheduler and tray icon.', |
18 epilog='Configuration file location:\n\n %s\n ' % locations.cfgfile, |
54 epilog='Configuration file location:\n\n %s\n ' % config.cfgfile, |
19 formatter_class=argparse.RawDescriptionHelpFormatter) |
55 formatter_class=argparse.RawDescriptionHelpFormatter) |
|
56 |
20 |
57 parser.add_argument( |
21 parser.add_argument( |
58 '--no-tray', |
22 '--no-tray', |
59 dest='notray', |
23 dest='notray', |
60 action='store_true', |
24 action='store_true', |
61 help='Do not show the tray icon') |
25 help='Do not show the tray icon') |
62 |
26 |
63 parser.add_argument( |
27 parser.add_argument( |
64 '--debug', |
28 '--debug', |
65 dest='debug', |
29 dest='debug', |
66 action='store_true', |
30 action='store_true', |
67 help='Set logging level to debug') |
31 help='Set logging level to debug') |
68 |
32 |
69 return parser.parse_args() |
33 args=parser.parse_args() |
70 |
34 |
71 # |
35 # |
72 # Main routine |
36 # Done parsing args, import our own modules, and launch everything |
73 # |
37 # |
74 |
38 |
75 # First, setup our own logging handlers |
39 import branding |
76 fifolog=FIFOHandler(fifolog_capacity) |
40 import config |
77 logger.addHandler(fifolog) |
41 import dreamtime |
78 fifolog.setFormatter(logging.Formatter(fifolog_fmt)) |
42 import loggers |
|
43 import logging |
|
44 from scheduler import Scheduler |
|
45 from repository import Repository |
|
46 from backup import Backup |
79 |
47 |
80 if __name__=="__main__": |
48 logger=loggers.mainlogger |
81 # Parse args. Let argparse handle errors/exit if there are any |
|
82 args= do_args() |
|
83 tray = None |
|
84 repos=[] |
|
85 backups=[] |
|
86 |
49 |
87 try: |
50 if args.debug: |
88 args=do_args() |
51 logger.setLevel(logging.DEBUG) |
89 |
52 |
90 if args.debug: |
53 tray = None |
91 logger.setLevel(logging.DEBUG) |
54 repos=[] |
|
55 backups=[] |
92 |
56 |
93 if not os.path.isdir(config.logs_dir): |
57 try: |
94 os.makedirs(config.logs_dir) |
58 dreamtime.start_monitoring() |
95 |
59 |
96 handler=logging.handlers.TimedRotatingFileHandler( |
60 scheduler = Scheduler() |
97 os.path.join(config.logs_dir, appname+'.log'), |
61 scheduler.start() |
98 when='D', interval=1) |
|
99 handler.setFormatter(logging.Formatter(logfmt)) |
|
100 logger.addHandler(handler) |
|
101 |
62 |
102 from threading import Thread |
63 repoconfigs=config.settings['repositories'] |
103 from backup import Backup |
|
104 from queue import Queue |
|
105 import signal, os |
|
106 import dreamtime |
|
107 |
64 |
108 dreamtime.start_monitoring() |
65 for i in range(len(repoconfigs)): |
|
66 logger.info('Setting up repository %d' % i) |
|
67 r=Repository(i, repoconfigs[i]) |
|
68 repos.append(r) |
109 |
69 |
110 scheduler = Scheduler() |
70 backupconfigs=config.settings['backups'] |
111 scheduler.start() |
|
112 |
71 |
113 repoconfigs=config.settings['repositories'] |
72 for i in range(len(backupconfigs)): |
|
73 logger.info('Setting up backup set %d' % i) |
|
74 b=Backup(i, backupconfigs[i], scheduler) |
|
75 backups.append(b) |
114 |
76 |
115 for i in range(len(repoconfigs)): |
77 for r in repos: |
116 logger.info('Setting up repository %d' % i) |
78 r.start() |
117 r=Repository(i, repoconfigs[i]) |
|
118 repos.append(r) |
|
119 |
79 |
120 backupconfigs=config.settings['backups'] |
80 for b in backups: |
|
81 b.start() |
121 |
82 |
122 for i in range(len(backupconfigs)): |
83 if args.notray or platform.system()!='Darwin': |
123 logger.info('Setting up backup set %d' % i) |
84 # Wait for scheduler to finish |
124 b=Backup(i, backupconfigs[i], scheduler) |
85 scheduler.join() |
125 backups.append(b) |
86 else: |
|
87 # Start UI, and let it handle exit control |
|
88 from ui import BorgendTray |
|
89 tray=BorgendTray(backups); |
|
90 tray.run() |
126 |
91 |
127 for r in repos: |
92 except Exception as err: |
128 r.start() |
93 # TODO: Should write errors here to stderr; |
|
94 # perhaps add an extra stderr logger for error level messages |
|
95 logger.exception("Exception fell through: exiting") |
129 |
96 |
130 for b in backups: |
97 finally: |
131 b.start() |
98 for b in backups: |
|
99 b.terminate() |
132 |
100 |
133 if args.notray or platform.system()!='Darwin': |
101 for r in repos: |
134 # Wait for scheduler to finish |
102 r.terminate() |
135 scheduler.join() |
|
136 else: |
|
137 # Start UI, and let it handle exit control |
|
138 from ui import BorgendTray |
|
139 tray=BorgendTray(backups); |
|
140 tray.run() |
|
141 |
103 |
142 except Exception as err: |
104 if tray: |
143 # TODO: Should write errors here to stderr; |
105 tray.quit() |
144 # perhaps add an extra stderr logger for error level messages |
106 else: |
145 logger.exception("Exception fell through to outer level: exiting") |
107 logging.shutdown() |
146 |
108 |
147 finally: |
|
148 for b in backups: |
|
149 b.terminate() |
|
150 |
|
151 for r in repos: |
|
152 r.terminate() |
|
153 |
|
154 if tray: |
|
155 tray.quit() |
|
156 else: |
|
157 logging.shutdown() |
|
158 |
|
159 # |
|
160 # This shit is fucked, disables ^C etc., and threading doesn't seem to help |
|
161 # |
|
162 |
|
163 # ui_thread=Thread(target=tray.run) |
|
164 # ui_thread.daemon=True |
|
165 # ui_thread.start() |
|
166 |
|
167 # def quit_signal_handler(signum, frame): |
|
168 # print('Signal handler called with signal %s' % str(signum)) |
|
169 # ui_thread.terminate() |
|
170 # os.exit() |
|
171 |
|
172 # signal.signal(signal.SIGTERM, quit_signal_handler) |
|
173 # signal.signal(signal.SIGINT, quit_signal_handler) |
|
174 |
|