1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
#!/usr/bin/env python3
import os
import argparse
import asyncio
import grp
import logging
import pwd
import signal
import sys
from functools import partial
from .asdnotify import AsyncSystemdNotifier
from . import utils
from . import defaults
from .proactive_fetcher import STSProactiveFetcher
from .responder import STSSocketmapResponder
def parse_args():
parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("-v", "--verbosity",
help="logging verbosity",
type=utils.check_loglevel,
choices=utils.LogLevel,
default=utils.LogLevel.info)
parser.add_argument("-c", "--config",
help="config file location",
metavar="FILE",
default=defaults.CONFIG_LOCATION)
parser.add_argument("-g", "--group",
help="change eGID to this group")
parser.add_argument("-l", "--logfile",
help="log file location",
metavar="FILE")
parser.add_argument("--disable-uvloop",
help="do not use uvloop even if it is available",
action="store_true")
parser.add_argument("-p", "--pidfile",
help="name of the file to write the current pid to")
parser.add_argument("-u", "--user",
help="change eUID to this user")
return parser.parse_args()
def exit_handler(exit_event, signum, frame): # pragma: no cover pylint: disable=unused-argument
logger = logging.getLogger('MAIN')
if exit_event.is_set():
logger.warning("Got second exit signal! Terminating hard.")
os._exit(1) # pylint: disable=protected-access
else:
logger.warning("Got first exit signal! Terminating gracefully.")
exit_event.set()
async def heartbeat():
""" Hacky coroutine which keeps event loop spinning with some interval
even if no events are coming. This is required to handle Futures and
Events state change when no events are occurring."""
while True:
await asyncio.sleep(.5)
async def amain(cfg, loop): # pragma: no cover
logger = logging.getLogger("MAIN")
proactive_fetch_enabled = cfg['proactive_policy_fetching']['enabled']
# Create policy cache
cache = utils.create_cache(cfg["cache"]["type"],
cfg["cache"]["options"])
await cache.setup()
# Construct request handler
responder = STSSocketmapResponder(cfg, loop, cache)
await responder.start()
logger.info("Server started.")
# Conditionally construct proactive policy fetcher
proactive_fetcher = None
if proactive_fetch_enabled:
proactive_fetcher = STSProactiveFetcher(cfg, loop, cache)
await proactive_fetcher.start()
logger.info("Proactive policy fetcher started.")
else:
logger.info("Proactive policy fetching is disabled.")
exit_event = asyncio.Event()
beat = asyncio.ensure_future(heartbeat())
sig_handler = partial(exit_handler, exit_event)
signal.signal(signal.SIGTERM, sig_handler)
signal.signal(signal.SIGINT, sig_handler)
async with AsyncSystemdNotifier() as notifier:
await notifier.notify(b"READY=1")
await exit_event.wait()
logger.debug("Eventloop interrupted. Shutting down server...")
await notifier.notify(b"STOPPING=1")
beat.cancel()
await responder.stop()
if proactive_fetch_enabled:
await proactive_fetcher.stop()
await cache.teardown()
def main(): # pragma: no cover
args = parse_args()
if args.pidfile is not None:
with open(args.pidfile, 'w', encoding='ascii') as pid_file:
pid_file.write(str(os.getpid()))
if args.group is not None:
try:
group = grp.getgrnam(args.group)
os.setegid(group.gr_gid)
except Exception as exc:
print("Unable to change eGID to '{}': {}".format(args.group, exc), file=sys.stderr)
return os.EX_OSERR
if args.user is not None:
try:
passwd = pwd.getpwnam(args.user)
os.seteuid(passwd.pw_uid)
except Exception as exc:
print("Unable to change eUID to '{}': {}".format(args.user, exc), file=sys.stderr)
return os.EX_OSERR
with utils.AsyncLoggingHandler(args.logfile) as log_handler:
logger = utils.setup_logger('MAIN', args.verbosity, log_handler)
utils.setup_logger('STS', args.verbosity, log_handler)
utils.setup_logger('PF', args.verbosity, log_handler)
utils.setup_logger('RES', args.verbosity, log_handler)
logger.info("MTA-STS daemon starting...")
# Read config and populate with defaults
cfg = utils.load_config(args.config)
# Construct event loop
logger.info("Starting eventloop...")
if not args.disable_uvloop:
if utils.enable_uvloop():
logger.info("uvloop enabled.")
else:
logger.info("uvloop is not available. "
"Falling back to built-in event loop.")
evloop = asyncio.get_event_loop()
logger.info("Eventloop started.")
evloop.run_until_complete(amain(cfg, evloop))
evloop.close()
logger.info("Server finished its work.")
return os.EX_OK
|