def __init__(self, config=None, server=False):
        self.config = WesDaemonConfig(config, server)

        logging.debug("Creating Entropy Engine")

        self.engine = EntropyEngine(
            self.config.drbg_spec, self.config.seed_source,
            self.config.reseed_rate, self.config.stream_source)

        self.connections = {}
        self.requests = {}
        self.serversocket = None
        self.epoll = None
        self.running = True
        self.active_timeout = {}
        self.start_time = time.time()

        self.drbg_fail_count = 0
 def reset_drbg(self):
     """get new instance of drbg if errors occurs in current instance"""
     self.engine = EntropyEngine(
         self.config.drbg_spec, self.config.seed_source,
         self.config.reseed_rate, self.config.stream_source)
class WesEntropyDaemon(object):
    """WES Entropy Daemon"""

    if DEBUG:
        PAGE_SIZE = 64
        READ_SIZE = 8
    else:
        PAGE_SIZE = 1024
        READ_SIZE = 96

    def __init__(self, config=None, server=False):
        self.config = WesDaemonConfig(config, server)

        logging.debug("Creating Entropy Engine")

        self.engine = EntropyEngine(
            self.config.drbg_spec, self.config.seed_source,
            self.config.reseed_rate, self.config.stream_source)

        self.connections = {}
        self.requests = {}
        self.serversocket = None
        self.epoll = None
        self.running = True
        self.active_timeout = {}
        self.start_time = time.time()

        self.drbg_fail_count = 0

    def create_server_socket(self):
        """Create a server socket and epoll for daemon"""
        # import ipdb; ipdb.set_trace()
        socket_dir = os.path.dirname(self.config.socket_path)
        if not os.path.exists(socket_dir):
            raise WesEntropyException(
                "Socket directory \'%s\' does not exist.\n" % socket_dir +
                "Please create it by running the following command:\n" +
                "sudo mkdir %s && sudo chmod 777 %s" % (socket_dir, socket_dir))
        try:
            os.unlink(self.config.socket_path)
        except OSError:
            if os.path.exists(self.config.socket_path):
                raise WesEntropyException("Socket path already exists (%s)." %
                                          self.config.socket_path)

        try:
            self.serversocket = socket.socket(socket.AF_UNIX,
                                              socket.SOCK_STREAM)
            self.serversocket.setsockopt(socket.SOL_SOCKET,
                                         socket.SO_REUSEADDR, 1)

            self.serversocket.bind(self.config.socket_path)
            self.serversocket.listen(1)
            self.serversocket.setblocking(0)
            os.chmod(self.config.socket_path, 0766)
            logging.debug(
                "Daemon listening on '%s'", self.config.socket_path)
        except IOError as error:
            if error.errno == errno.EADDRINUSE:
                raise WesEntropyException(
                    "Address already in use. (error: %s)" %
                    errno.errorcode[error.errno])
            else:
                raise WesEntropyException(
                    "Error setting up socket server (%s). (error: %s)" %
                    (self.config.socket_path, errno.errorcode[error.errno]))

        try:
            self.epoll = select.epoll()
            self.epoll.register(self.serversocket.fileno(), select.EPOLLIN)
            logging.debug("Registered server socket with epoll.")
        except IOError as error:
            raise WesEntropyException(
                "Serversocket already registered with epoll. (error: %s)" %
                errno.errorcode[error.errno])

    def get_events(self, pending_requests):
        """Get any pending socket events"""
        if pending_requests:
            poll_timeout = 0.0
        else:
            poll_timeout = 0.1

        try:
            events = self.epoll.poll(poll_timeout)
            #logging.debug("S: Polled with timeout %s, got %s events.",
            #              poll_timeout,
            #              len(events))
        except select.error as error:
            if error.errno == errno.EINTR:
                logging.warning(
                    "Epoll was interupted by signal. (error: %s).",
                    errno.errorcode[error.errno])
            else:
                raise WesEntropyException(
                    "Error calling epoll. (error: %s)." %
                    errno.errorcode[error.errno])

        return events

    def setup_new_client(self):
        """Establish a new client connection"""
        # pylint: disable=E1101
        add_client = True

        try:
            connection, dummy_address = self.serversocket.accept()
            logging.debug(
                "Accepted client with fd: %s", connection.fileno())

            connection.setblocking(0)
        except IOError as error:
            logging.error("Error accepting client connection. (error: %s)",
                          errno.errorcode[error.errno])
            add_client = False

        try:
            self.epoll.register(connection.fileno(), select.EPOLLIN)
        except select.error as error:
            if (error.errno == errno.ENOSPC or
                    error.errno == errno.ENOMEM):
                logging.warning(
                    "Cannot add more clients to epoll instance, "
                    "dropping client. (error: %s)",
                    errno.errorcode[error.errno])
                connection.shutdown()
                connection.close()
                add_client = False
            elif error.errno == errno.EEXIST:
                logging.warning(
                    "Client already registered to epoll. (error: %s)",
                    errno.errorcode[error.errno])
            else:
                raise WesEntropyException("Error in epoll. (error: %s)" %
                                          errno.errorcode[error.errno])

        # If there wasn't an error, add client to connections
        if add_client:
            self.connections[connection.fileno()] = connection
            logging.debug("Set up new client: %i", connection.fileno())
            self.requests[connection.fileno()] = {'bytes_req'   : 0,
                                                  'partial_req' : "",
                                                  'send_stats'  : False}
        # pylint: enable=E1101

    def reset_drbg(self):
        """get new instance of drbg if errors occurs in current instance"""
        self.engine = EntropyEngine(
            self.config.drbg_spec, self.config.seed_source,
            self.config.reseed_rate, self.config.stream_source)

    def handle_error(self, fileno):
        """Cleanup after client connection error"""
        # Clean up send timeout
        if fileno in self.active_timeout:
            del self.active_timeout[fileno]

        try:
            logging.debug("Unregistering %s from epoll.", fileno)
            self.epoll.unregister(fileno)
        except select.error as error:
            if error.errno == errno.ENOENT:
                logging.error(
                    "Error unregistering %s from epoll. (error: %s)",
                    fileno, errno.errorcode[error.errno])
            else:
                raise WesEntropyException(
                    "Error unregistering client from epoll. (error: %s)" %
                    errno.errorcode[error.errno])
        try:
            self.connections[fileno].close()
        except IOError as error:
            logging.error("Error on socket close. (error: %s)",
                          errno.errorcode[error.errno])

        logging.debug("Closed connection to %i.", fileno)
        if fileno in self.connections:
            del self.connections[fileno]
        if fileno in self.requests:
            del self.requests[fileno]

    def read_request(self, fileno):
        """Store information about the data request for processing"""
        try:
            new_request = self.connections[fileno].recv(
                WesEntropyDaemon.READ_SIZE)
            request = self.requests[fileno]['partial_req'] + new_request
        except IOError as error:
            if error.errno == errno.EAGAIN or error.errno == errno.EWOULDBLOCK:
                logging.warning("No data for recv to get. (error: %s)",
                                errno.errorcode[error.errno])
            else:
                logging.error("Error on recv (error: %s)",
                              errno.errorcode[error.errno])
                logging.debug("Calling handle_error on %s.", fileno)
                self.handle_error(fileno)

        num_requests = len(request) / 8

        logging.debug("Current num_requests: %i", num_requests)

        for index in range(num_requests):
            msg = DaemonMsg(request[8*index:8*index+8])
            if msg.is_rand_request():
                self.requests[fileno]['bytes_req'] += msg.get_num_bytes()
            elif msg.is_stats_request():
                self.requests[fileno]['send_stats'] = True
            else:
                continue

        self.requests[fileno]['partial_req'] = request[num_requests*8:]

    def get_random_data(self):
        """Get random data"""
        rtn = ""
        num_errors = 0
        while len(rtn) < WesEntropyDaemon.PAGE_SIZE:
            # Request to Generate is in bits
            retcode, data = self.engine.generate(WesEntropyDaemon.PAGE_SIZE * 8)
            if retcode == 'ERROR':
                logging.debug("Ran generate, got: %s - %s", retcode, data)
            else:
                logging.debug("Ran generate, got: %s", retcode)
            if retcode == 'SUCCESS':
                rtn += data
                num_errors = 0
            elif retcode == 'DRBG_ONLY':
                logging.warning(
                    "Error with stream source, using DRBG only.")
                rtn += data
                num_errors = 0
            else:
                num_errors += 1
                time.sleep(0.1)
                if num_errors == 10:
                    logging.warning("DRBG Error: %s: %s", retcode, data)
                    return None

        return rtn[:WesEntropyDaemon.PAGE_SIZE]

    #pylint: disable=R0912
    def serve_requests(self):
        """Serve one buffer to each client that requests data.

        Return True if there are pending requests upon return.
        """
        pending_requests = False

        #logging.debug("Clients waiting: %s", str(self.connections.keys()))
        for fileno in self.connections.keys():
            if self.requests[fileno]['send_stats']:
                try:
                    stats = self.engine.get_stats()
                    stats['info']['daemon_uptime'] = (
                        time.time() - self.start_time)
                    self.connections[fileno].send(repr(stats))
                except IOError as error:
                    pass
                self.handle_error(fileno)
                continue

            if self.requests[fileno]['bytes_req'] > 0:
                try:
                    random_data = self.get_random_data()

                    if random_data:
                        byteswritten = self.connections[fileno].send(
                            random_data)

                        self.requests[fileno]['bytes_req'] -= byteswritten
                        if self.requests[fileno]['bytes_req'] < 0:
                            self.requests[fileno]['bytes_req'] = 0

                        logging.debug(
                            "Sent %i bytes to client, outstanding bytes: %i",
                            byteswritten,
                            self.requests[fileno]['bytes_req'])

                        # Mark that fileno doesn't have timeout
                        if fileno in self.active_timeout:
                            del self.active_timeout[fileno]
                        self.drbg_fail_count = 0
                    else:
                        logging.warning("Error getting data from DRBG.")
                        self.drbg_fail_count += 1
                        if self.drbg_fail_count < 5:
                            logging.warning("Resetting DRBG.")
                            self.reset_drbg()
                        else:
                            raise WesEntropyException("DRBG fatal error.")

                except IOError as error:
                    if (error.errno == errno.EAGAIN or
                            error.errno == errno.EWOULDBLOCK):
                        # No data sent, 1 min timeout
                        if self.send_timeout(fileno):
                            logging.error(
                                "Client %i hasn't accepted data "
                                "for 1 minute, closing connection.",
                                fileno)
                            self.handle_error(fileno)
                    else:
                        logging.debug(
                            "Error sending data to client %i, "
                            "closing connection. (error: %s)",
                            fileno, errno.errorcode[error.errno])
                        self.handle_error(fileno)

                if fileno in self.requests:
                    if self.requests[fileno]['bytes_req'] > 0:
                        pending_requests = True
                    else:
                        self.requests[fileno]['bytes_req'] = 0

        return pending_requests
    #pylint: enable=R0912

    def send_timeout(self, fileno):
        """Timeout for socket sends that return EWOULDBLOCK or EAGAIN"""
        # New timeout for fileno? Add "fileno : curr_time" to dict
        if fileno not in self.active_timeout:
            self.active_timeout[fileno] = time.time()

        # If 1 min has passed since timeout start return true
        if time.time() >= self.active_timeout[fileno] + 60:
            logging.debug("Timeout for %s is at %s sec.",
                          fileno, time.time() - self.active_timeout[fileno])
            return True
        else:
            logging.debug("Timeout for %s has expired.",
                          fileno)
            return False

    #pylint: disable=W0613
    @classmethod
    def sig_handler(cls, sig_num, stack_frame):
        """Wrapper function for cleanup to pass as the signal handler."""
        cls.cleanup()
    #pylint: enable=W0613

    @classmethod
    def cleanup(cls):
        """Clean up and close server socket"""
        if cls.wes_daemon is not None:
            # fails if called multiple times - should we put a check?
            if cls.wes_daemon.epoll is not None:
                try:
                    cls.wes_daemon.epoll.unregister(
                        cls.wes_daemon.serversocket.fileno())
                    cls.wes_daemon.epoll.close()
                    logging.debug("Cleaned up epoll.")
                except select.error as error:
                    logging.warning(
                        "Failed to cleanup epoll. (error: %s)",
                        errno.errorcode[error.errno])

            if cls.wes_daemon.serversocket is not None:
                try:
                    cls.wes_daemon.serversocket.close()
                    logging.debug("Cleaned up server socket.")
                except IOError as error:
                    logging.warning(
                        "Failed to cleanup server socket. (error: %s)",
                        errno.errorcode[error.errno])
        cls.wes_daemon = None

    def process_events(self, pending_requests):
        """Process events returned from epoll"""
        events = self.get_events(pending_requests)

        #logging.debug("Events: ", str(events))

        for fileno, event in events:
            if fileno == self.serversocket.fileno():
                self.setup_new_client()

            elif event & (select.EPOLLERR | select.EPOLLHUP):
                if event & select.EPOLLERR:
                    logging.debug(
                        "Encountered epoll error with client %s.",
                        fileno)
                else:
                    logging.debug("Client %s disconnected.", fileno)
                self.handle_error(fileno)

            elif event & select.EPOLLIN:
                self.read_request(fileno)

    def main_loop(self):
        """main loop for running wesentropyd"""
        # pylint: disable=W0703
        try:
            self.create_server_socket()

            pending_requests = False
            while self.running:
                self.process_events(pending_requests)
                pending_requests = self.serve_requests()
        except Exception as error:
            if isinstance(error, WesEntropyException):
                logging.error(error.message)
            else:
                logging.error("%s", traceback.format_exc())
                logging.error("Unknown error. (error %s)",
                              error.message)
        finally:
            self.engine.cleanup()
            self.cleanup()
        # pylint: enable=W0703

    def testing_get_partial_req(self, fileno):
        """Test function"""
        return self.requests[fileno]

    def __repr__(self):
        return ('WesEntropyDaemon(server_address=%s, '
                'connections=%s, requests=%s)' %
                (self.config.socket_path, self.connections, self.requests))

    wes_daemon = None

    @classmethod
    def start_daemon(cls, config, server, verbose, asdaemon):
        '''Start WES Entropy Daemon'''
            #TODO: change to /var/log/wes_entropy/wes_entropy.log

        log_format = '%(levelname)s: %(threadName)s %(message)s'
        if asdaemon:
            d_config = WesDaemonConfig(config, server)
            lock_file = os.path.join(d_config.working_dir, 'wesentropy.pid')

            if not os.path.exists(d_config.working_dir):
                os.makedirs(d_config.working_dir)


            daemon_context = daemon.DaemonContext(
                working_directory=d_config.working_dir,
                umask=0o001,
                pidfile=lockfile.FileLock(lock_file))

            daemon_context.signal_map = {
                signal.SIGTERM: cls.sig_handler,
                signal.SIGHUP: cls.sig_handler}

            with daemon_context:
                if verbose:
                    logging.basicConfig(format=log_format,
                                        filename='wes_entropy.log',
                                        level=logging.DEBUG)
                else:
                    logging.basicConfig(format=log_format,
                                        filename='wes_entropy.log')

                cls.wes_daemon = WesEntropyDaemon(
                    config=config, server=server)
                cls.wes_daemon.main_loop()
        else:
            if verbose:
                logging.basicConfig(format=log_format,
                                    level=logging.DEBUG)
            else:
                logging.basicConfig(format=log_format)

            cls.wes_daemon = WesEntropyDaemon(
                config=config, server=server)
            cls.wes_daemon.main_loop()