1 | #!/usr/bin/python |
||
2 | # -*- coding: utf-8 -*- |
||
3 | |||
4 | import argparse |
||
5 | import sys |
||
6 | import logging |
||
7 | import logging.handlers |
||
8 | import os |
||
9 | import metadata |
||
10 | import doorpi |
||
11 | from resource import getrlimit, RLIMIT_NOFILE |
||
12 | |||
13 | TRACE_LEVEL = 5 |
||
14 | LOG_FORMAT = '%(asctime)s [%(levelname)s] \t[%(name)s] %(message)s' |
||
15 | |||
16 | logger = logging.getLogger(__name__) |
||
17 | |||
18 | log_level = logging.INFO |
||
19 | |||
20 | |||
21 | def add_trace_level(): |
||
22 | logging.addLevelName(TRACE_LEVEL, "TRACE") |
||
23 | def trace(self, message, *args, **kws): |
||
24 | # Yes, logger takes its '*args' as 'args'. |
||
25 | self._log(TRACE_LEVEL, message, args, **kws) |
||
26 | logging.Logger.trace = trace |
||
27 | |||
28 | |||
29 | def init_logger(arguments): |
||
30 | add_trace_level() |
||
31 | |||
32 | global log_level |
||
33 | if '--debug' in arguments: log_level = logging.DEBUG |
||
34 | if '--trace' in arguments: log_level = TRACE_LEVEL |
||
35 | |||
36 | logging.basicConfig( |
||
37 | level = log_level, |
||
38 | format = LOG_FORMAT |
||
39 | # datefmt = '%m/%d/%Y %I:%M:%S %p' |
||
40 | ) |
||
41 | |||
42 | return logging.getLogger(__name__) |
||
43 | |||
44 | |||
45 | def parse_arguments(argv): |
||
46 | arg_parser = argparse.ArgumentParser( |
||
47 | prog=argv[0], |
||
48 | formatter_class=argparse.RawDescriptionHelpFormatter, |
||
49 | description=metadata.description, |
||
50 | epilog = metadata.epilog |
||
51 | ) |
||
52 | |||
53 | arg_parser.add_argument( |
||
54 | '-V', '--version', |
||
55 | action='version', |
||
56 | version='{0} {1}'.format(metadata.project, metadata.version) |
||
57 | ) |
||
58 | arg_parser.add_argument('--debug', action="store_true") |
||
59 | arg_parser.add_argument('--trace', action="store_true") |
||
60 | arg_parser.add_argument('--test', action="store_true") |
||
61 | arg_parser.add_argument( |
||
62 | '-c', '--configfile', |
||
63 | help='configfile for DoorPi - https://github.com/motom001/DoorPi/wiki for more help', |
||
64 | dest='configfile' |
||
65 | ) |
||
66 | try: |
||
67 | if len(sys.argv) > 1 and sys.argv[1] in ['start', 'stop', 'restart', 'status']: |
||
68 | return arg_parser.parse_args(args=sys.argv[2:]) |
||
69 | else: |
||
70 | return arg_parser.parse_args(args=sys.argv[1:]) |
||
71 | except IOError: |
||
72 | print("EXCEPTION: configfile does not exist or is not readable") |
||
73 | print("please refer to the DoorPi wiki for more information ") |
||
74 | print("<https://github.com/motom001/DoorPi/wiki>") |
||
75 | raise SystemExit(1) |
||
76 | |||
77 | |||
78 | def files_preserve_by_path(*paths): |
||
79 | wanted = [] |
||
80 | for path in paths: |
||
81 | fd = os.open(path, os.O_RDONLY) |
||
82 | try: |
||
83 | wanted.append(os.fstat(fd)[1:3]) |
||
84 | finally: |
||
85 | os.close(fd) |
||
86 | |||
87 | def fd_wanted(fd): |
||
88 | try: |
||
89 | return os.fstat(fd)[1:3] in wanted |
||
90 | except OSError: |
||
91 | return False |
||
92 | |||
93 | fd_max = getrlimit(RLIMIT_NOFILE)[1] |
||
94 | return [fd for fd in xrange(fd_max) if fd_wanted(fd)] |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
Loading history...
|
|||
95 | |||
96 | |||
97 | def main_as_daemon(argv): |
||
98 | if argv[1] is 'reload': |
||
99 | print('not implemeted yet - use restart instead') |
||
100 | return 1 |
||
101 | if argv[1] in ['stop']: |
||
102 | parsed_arguments = None |
||
103 | else: |
||
104 | parsed_arguments = parse_arguments(argv) |
||
105 | |||
106 | if not os.path.exists(metadata.log_folder): |
||
107 | os.makedirs(metadata.log_folder) |
||
108 | |||
109 | log_file = os.path.join(metadata.log_folder, "doorpi.log") |
||
110 | logrotating = logging.handlers.RotatingFileHandler( |
||
111 | log_file, |
||
112 | maxBytes=5000000, |
||
113 | backupCount=10 |
||
114 | ) |
||
115 | global log_level |
||
116 | logrotating.setLevel(log_level) |
||
117 | logrotating.setFormatter(logging.Formatter(LOG_FORMAT)) |
||
118 | |||
119 | logging.getLogger('').addHandler(logrotating) |
||
120 | |||
121 | print(metadata.epilog) |
||
122 | |||
123 | from daemon import runner |
||
124 | from daemon.runner import DaemonRunnerInvalidActionError |
||
125 | from daemon.runner import DaemonRunnerStartFailureError |
||
126 | from daemon.runner import DaemonRunnerStopFailureError |
||
127 | |||
128 | daemon_runner = runner.DaemonRunner(doorpi.DoorPi(parsed_arguments)) |
||
129 | #This ensures that the logger file handle does not get closed during daemonization |
||
130 | daemon_runner.daemon_context.files_preserve = files_preserve_by_path(log_file) |
||
131 | try: |
||
132 | daemon_runner.do_action() |
||
133 | except DaemonRunnerStopFailureError as ex: |
||
134 | print("can't stop DoorPi daemon - maybe it's not running? (Message: %s)" % ex) |
||
135 | return 1 |
||
136 | except DaemonRunnerStartFailureError as ex: |
||
137 | print("can't start DoorPi daemon - maybe it's running already? (Message: %s)" % ex) |
||
138 | return 1 |
||
139 | except Exception as ex: |
||
140 | print("Exception NameError: %s" % ex) |
||
141 | finally: |
||
142 | doorpi.DoorPi().destroy() |
||
143 | return 0 |
||
144 | |||
145 | |||
146 | def main_as_application(argv): |
||
147 | |||
148 | parsed_arguments = parse_arguments(argv) |
||
149 | logger.info(metadata.epilog) |
||
150 | logger.debug('loaded with arguments: %s', str(argv)) |
||
151 | |||
152 | try: doorpi.DoorPi(parsed_arguments).run() |
||
153 | except KeyboardInterrupt: logger.info("KeyboardInterrupt -> DoorPi will shutdown") |
||
154 | except Exception as ex: logger.exception("Exception NameError: %s", ex) |
||
155 | finally: doorpi.DoorPi().destroy() |
||
156 | |||
157 | return 0 |
||
158 | |||
159 | |||
160 | def entry_point(): |
||
161 | init_logger(sys.argv) |
||
162 | |||
163 | """Zero-argument entry point for use with setuptools/distribute.""" |
||
164 | if len(sys.argv) > 1 and sys.argv[1] in ['status']: |
||
165 | raise SystemExit(get_status_from_doorpi(sys.argv)) |
||
0 ignored issues
–
show
Comprehensibility
Best Practice
introduced
by
|
|||
166 | elif len(sys.argv) > 1 and sys.argv[1] in ['start', 'stop', 'restart', 'reload']: |
||
167 | raise SystemExit(main_as_daemon(sys.argv)) |
||
168 | else: |
||
169 | raise SystemExit(main_as_application(sys.argv)) |
||
170 | |||
171 | if __name__ == '__main__': |
||
172 | entry_point() |
||
173 |