def test_listen_fds_default_unset(): os.environ['LISTEN_FDS'] = '1' os.environ['LISTEN_PID'] = str(os.getpid()) assert listen_fds(False) == [3] assert listen_fds() == [3] assert listen_fds() == []
def test_listen_fds_default_unset(): os.environ["LISTEN_FDS"] = "1" os.environ["LISTEN_PID"] = str(os.getpid()) assert listen_fds(False) == [3] assert listen_fds() == [3] assert listen_fds() == []
def test_listen_fds(): os.environ['LISTEN_FDS'] = '3' os.environ['LISTEN_PID'] = str(os.getpid()) assert listen_fds(False) == [3, 4, 5] assert listen_fds(True) == [3, 4, 5] assert listen_fds() == []
def test_listen_fds(): os.environ["LISTEN_FDS"] = "3" os.environ["LISTEN_PID"] = str(os.getpid()) assert listen_fds(False) == [3, 4, 5] assert listen_fds(True) == [3, 4, 5] assert listen_fds() == []
def test_listen_fds_no_fds(): # make sure we have no fds to listen to os.unsetenv('LISTEN_FDS') os.unsetenv('LISTEN_PID') assert listen_fds() == [] assert listen_fds(True) == [] assert listen_fds(False) == []
def _get_systemd_socket(self, address): fds = sd.listen_fds() if not fds: return address elif len(fds) > 1: raise ValueError('Too many listening sockets', fds) if isinstance(address, tuple): port = address[1] # systemd uses IPv6 if not sd.is_socket_inet(fds[0], family=socket.AF_INET6, type=socket.SOCK_STREAM, listening=True, port=port): raise ValueError("FD {} is not TCP IPv6 socket on port {}", fds[0], port) logger.info('Using systemd socket activation on port %i', port) sock = socket.fromfd(fds[0], socket.AF_INET6, socket.SOCK_STREAM) else: if not sd.is_socket_unix( fds[0], socket.SOCK_STREAM, listening=True, path=address): raise ValueError("FD {} is not Unix stream socket on path {}", fds[0], address) logger.info('Using systemd socket activation on path %s', address) sock = socket.fromfd(fds[0], socket.AF_UNIX, socket.SOCK_STREAM) if sys.version_info[0] < 3: # Python 2.7's socket.fromfd() returns _socket.socket sock = socket.socket(_sock=sock) return sock
def _get_systemd_socket(self, address): fds = sd.listen_fds() if not fds: return address elif len(fds) > 1: raise ValueError('Too many listening sockets', fds) if isinstance(address, tuple): port = address[1] # systemd uses IPv6 if not sd.is_socket_inet(fds[0], family=socket.AF_INET6, type=socket.SOCK_STREAM, listening=True, port=port): raise ValueError("FD {} is not TCP IPv6 socket on port {}", fds[0], port) logger.info('Using systemd socket activation on port %i', port) sock = socket.fromfd(fds[0], socket.AF_INET6, socket.SOCK_STREAM) else: if not sd.is_socket_unix(fds[0], socket.SOCK_STREAM, listening=True, path=address): raise ValueError("FD {} is not Unix stream socket on path {}", fds[0], address) logger.info('Using systemd socket activation on path %s', address) sock = socket.fromfd(fds[0], socket.AF_UNIX, socket.SOCK_STREAM) if sys.version_info[0] < 3: # Python 2.7's socket.fromfd() returns _socket.socket sock = socket.socket(_sock=sock) return sock
def patched_init(self, addr, log_requests=1): self.request_queue_size = min(socket.SOMAXCONN, 256) SimpleXMLRPCServer.__init__(self, addr, SilenceableXMLRPCRequestHandler, log_requests, bind_and_activate=False) self.socket = socket.fromfd(listen_fds()[0], self.address_family, self.socket_type)
def map_fds(): """Return the list of inherited sockets. Return null if there arenone. """ fds = {} for frozen_fd in frozenset(daemon.listen_fds()): journal.send("processing fd: {0}".format(frozen_fd)) if daemon.is_socket(frozen_fd): sock_obj = socket.socket(fileno=frozen_fd) journal.send("created socket: name={0}".format( sock_obj.getsockname())) fds[frozen_fd] = sock_obj return fds
def listen_fds_with_names(): '''Tries to get file descriptors from (in order): - listen_fds_with_names() # Not yet merged, see https://github.com/systemd/python-systemd/pull/60 - listen_fds() # With own hack in case it finds $LISTEN_FDNAMES until listen_fds_with_names() is supported - None In the first two cases, it returns a hash of {fd: name} pairs''' try: from systemd.daemon import listen_fds_with_names # We have the real McCoy return listen_fds_with_names() except ImportError: # Try to fall back to listen_fds(), # possbily emulating listen_fds_with_names() here try: from systemd.daemon import listen_fds except ImportError: if os.path.exists('/run/systemd/system') and 'LISTEN_FDS' in os.environ: logging.error('Software from https://github.com/systemd/python-systemd/ missing; do `apt install python3-systemd` or `pip3 install systemd-python`. Please note the similarly-named `pip3 install python-systemd` does not provide the interfaces needed and may actually need to be UNINSTALLED first!') raise else: logging.info('Please `apt install python3-systemd` for future compatibility') return None fds = listen_fds() if fds: listeners = {} if 'LISTEN_FDNAMES' in os.environ: # Evil hack, should not be here! # Is here only because it seems unlikely # https://github.com/systemd/python-systemd/pull/60 # will be merged and distributed anyting soon ;-(. # Diverges from original if not enough fdnames are provided # (but this should not happen anyway). names = os.environ['LISTEN_FDNAMES'].split(':') else: names = () for i in range(0, len(fds)): if i < len(names): listeners[fds[i]] = names[i] else: listeners[fds[i]] = 'unknown' return listeners else: return None
def get_activation_socket(): def sock_from_fd(fd): if is_socket(fd): if is_socket_unix(fd): return socket.socket(family=socket.AF_UNIX, type=socket.SOCK_STREAM, fileno=fd) for fam in [socket.AF_INET6, socket.AF_INET]: for t in [socket.SOCK_STREAM]: if is_socket(fd, family=fam, type=t): return socket.socket(family=fam, type=t, fileno=fd) return socket.socket(fileno=fd) return None fds = listen_fds() if len(fds) != 1: raise Exception("socket activation required") sock = sock_from_fd(fds[0]) if not sock: raise Exception("socket activation required") return sock
def main(args=None): import argparse parser = argparse.ArgumentParser( usage='%(prog)s [options] [ [--] arguments ]', # argparse fails to build that for $REASONS description='Tool to generate and keep tinydns' ' zone file with dynamic dns entries for remote hosts.') parser.add_argument('zone_file', nargs='?', help='Path to tinydns zone file with client Ed25519 (base64-encoded)' ' pubkeys and timestamps in comments before entries.' ' Basically any line with IPs that has comment in the form of' ' "dynamic: <ts> <pubkey> <pubkey2> ..." immediately before it (no empty lines' ' or other comments separating these) can be updated by packet with' ' proper ts/signature.') parser.add_argument('update_command', nargs='*', help='Optional command to run on zone file updates and its arguments (if any).' ' If --uid is specified, all commands will be run after dropping privileges.' ' Use "--" before it to make sure that none of its args get interpreted by this script.') parser.add_argument('-g', '--genkey', action='store_true', help='Generate a new random signing/verify' ' Ed25519 keypair, print both keys to stdout and exit.') parser.add_argument('-b', '--bind', metavar='[host:]port', default=bytes(default_port), help='Host/port to bind listening socket to (default: %(default)s).') parser.add_argument('-v', '--ip-af', metavar='{ 4 | 6 }', choices=('4', '6'), default=socket.AF_UNSPEC, help='Resolve hostname(s) (if any) using specified address family version.' ' Either "4" or "6", no restriction is appled by default.') parser.add_argument('--systemd', action='store_true', help='Receive socket fd from systemd, send systemd startup notification.' ' This allows for systemd socket activation and running the' ' app from unprivileged uid right from systemd service file.' ' Requires systemd python bindings.') parser.add_argument('-u', '--uid', metavar='uid[:gid]', help='User (name) or uid (and optional group/gid) to run as after opening socket.' ' WARNING: not a proper daemonization - does not do setsid, chdir,' ' closes fds, sets optional gids, etc - just setresuid/setresgid. Use systemd for that.') parser.add_argument('--update-timestamps', action='store_true', help='Usually, when no addresses are changed, zone file does not get updated.' ' This option forces updates to timestamps in addr-block headers.') parser.add_argument('-d', '--debug', action='store_true', help='Verbose operation mode.') opts = parser.parse_args(sys.argv[1:] if args is None else args) global log import logging logging.basicConfig(level=logging.DEBUG if opts.debug else logging.WARNING) log = logging.getLogger() if opts.genkey: signing_key = key_generate() print('Signing key (for client only):\n ', key_encode(signing_key), '\n') print('Verify key (for this script):\n ', key_encode(key_get_vk(signing_key)), '\n') return if not opts.zone_file: parser.error('Zone file path must be specified') if isinstance(opts.ip_af, types.StringTypes): opts.ip_af = {'4': socket.AF_INET, '6': socket.AF_INET6}[opts.ip_af] try: host, port = opts.bind.rsplit(':', 1) except ValueError: host, port = default_bind, opts.bind socktype, port = socket.SOCK_DGRAM, int(port) af, addr = get_socket_info(host, port, family=opts.ip_af, socktype=socktype) sock = None if opts.systemd: from systemd import daemon try: sock, = daemon.listen_fds() except ValueError as err: log.info('Unable to get socket from systemd, will create it manually') else: sock = socket.fromfd(af, socktype) daemon.notify('READY=1') daemon.notify('STATUS=Listening for update packets') if not sock: log.debug('Binding to: %r (port: %s, af: %s, socktype: %s)', addr, port, af, socktype) sock = socket.socket(af, socktype) sock.bind((addr, port)) if opts.uid: drop_privileges(opts.uid) with open(opts.zone_file, 'rb'): pass # access check zone_update_loop( opts.zone_file, sock, opts.update_command, force_updates=opts.update_timestamps )
def main(): parser = ArgumentParser( description="Listens for commands as output by `hades-dhcp-script`.", epilog=f"""\ This server listens on a socket for commands communicating lease events. For detailed information about the functionality see `hades-dhcp-script --help`. It is the server component for what could have been a single python program, however because of performance reasons, it was necessary to circumvent the need for a complete python interpreter startup every time such a notification happens.\ """, parents=[common_parser], ) parser.add_argument( '--socket', nargs='?', default=constants.AUTH_DHCP_SCRIPT_SOCKET, help= f"Socket to listen on. Default: {constants.AUTH_DHCP_SCRIPT_SOCKET}") args = parser.parse_args() SCRIPT_SOCKET = args.socket setup_cli_logging(parser.prog, args) try: config = load_config(args.config) except ConfigError as e: print_config_error(e) return os.EX_CONFIG fds = listen_fds() if len(fds) == 0: logger.info( "Opening UNIX socket at %s.", SCRIPT_SOCKET, ) sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) try: os.unlink(SCRIPT_SOCKET) except FileNotFoundError: pass sock.bind(SCRIPT_SOCKET) sock.listen(Server.request_queue_size) elif len(fds) == 1: logger.info("Using systemd activation socket") sock = fds[0] if not is_socket_unix(sock, socket.SOCK_STREAM): logger.critical( "Passed socket is not an AF_UNIX SOCK_STREAM socket") return os.EX_USAGE else: logger.critical( "More than one (%d) socket passed via socket activation", len(fds), ) return os.EX_USAGE engine = db.create_engine( config, pool_size=1, max_overflow=2, pool_pre_ping=True, pool_reset_on_return='rollback', ) try: engine.connect() except DBAPIError as e: logger.critical("Could not connect to database", exc_info=e) return os.EX_TEMPFAIL server = Server(sock, engine) server.serve_forever() return os.EX_OK