#!/usr/bin/env python3 # # Copyright 2011 - 2019 Patrick Ulbrich # Copyright 2011 Leighton Earl # Copyright 2011 Ralf Hersel # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301, USA. # import gi gi.require_version('GLib', '2.0') from gi.repository import GObject, GLib from dbus.mainloop.glib import DBusGMainLoop import threading import argparse import logging import os import signal from Mailnag.common.utils import fix_cwd fix_cwd() from Mailnag.common.config import cfg_exists from Mailnag.common.dist_cfg import APP_VERSION from Mailnag.common.utils import set_procname, init_logging, shutdown_existing_instance from Mailnag.common.subproc import terminate_subprocesses from Mailnag.common.exceptions import InvalidOperationException from Mailnag.daemon.mailnagdaemon import MailnagDaemon PROGNAME = 'mailnagd' LOG_LEVEL = logging.DEBUG def cleanup(daemon): event = threading.Event() def thread(): if daemon != None: daemon.dispose() terminate_subprocesses(timeout = 3.0) event.set() threading.Thread(target = thread).start() event.wait(10.0) if not event.is_set(): logging.warning('Cleanup takes too long. Enforcing termination.') os._exit(os.EX_SOFTWARE) if threading.active_count() > 1: logging.warning('There are still active threads. Enforcing termination.') os._exit(os.EX_SOFTWARE) def get_args(): parser = argparse.ArgumentParser(prog=PROGNAME) parser.add_argument('-q', '--quiet', action = 'store_true', help = "don't print log messages to stdout") parser.add_argument('-v', '--version', action = 'version', version = '%s %s' % (PROGNAME, APP_VERSION)) return parser.parse_args() def sigterm_handler(mainloop): if mainloop != None: mainloop.quit() def main(): mainloop = GLib.MainLoop() daemon = None set_procname(PROGNAME) DBusGMainLoop(set_as_default = True) GLib.unix_signal_add(GLib.PRIORITY_HIGH, signal.SIGTERM, sigterm_handler, mainloop) # Get commandline arguments args = get_args() # Shut down an (possibly) already running Mailnag daemon # (must be called before instantiation of the DBUSService). shutdown_existing_instance() # Note: don't start logging before an existing Mailnag # instance has been shut down completely (will corrupt logfile). init_logging(enable_stdout = (not args.quiet), \ enable_syslog = True, log_level = LOG_LEVEL) try: if not cfg_exists(): logging.critical( "Cannot find configuration file. " + \ "Please run the mailnag command to setup this program.") exit(1) def fatal_error_hdlr(ex): # Note: don't raise an exception # (e.g InvalidOperationException) # in the error handler. mainloop.quit() def shutdown_request_hdlr(): if not mainloop.is_running(): raise InvalidOperationException( "Mainloop is not running") mainloop.quit() daemon = MailnagDaemon( fatal_error_hdlr, shutdown_request_hdlr) daemon.init() # start mainloop for DBus communication mainloop.run() except KeyboardInterrupt: pass # ctrl+c pressed finally: logging.info('Shutting down...') cleanup(daemon) if __name__ == '__main__': main()