def _create_local_socket(self, init=0): """Create local Unix-socket to listen commands instead of signals. If I{init} is set then close old socket""" if init and self.__local_sock: try: os.unlink(self.__local_sock.getsockname()) self.__local_sock.shutdown(socket.SHUT_RDWR) logging.info('old local socket closed') except Exception as e: logging.warn('old local socket is already closed') try: os.unlink(self._config['server']['sock']) except: pass try: self.__local_sock = \ socket.socket(socket.AF_UNIX, socket.SOCK_STREAM, 0) # Non-blocking mode self.__local_sock.settimeout(0.0) self.__local_sock.bind(self._config['server']['sock']) self.__local_sock.listen(1) # Max num of queued connections except Exception as e: logging.critical('cannot create local socket') logging.error(e) raise e logging.info('local socket created and binded on %s' % \ self.__local_sock.getsockname())
def main(self): """Start main loop. From that moment the process will loop forever in this method""" logging.info('*' *50) logging.info('python %s' % sys.version.replace('\n', '')) logging.info('server started on %s:%s' % self.socket.getsockname()) logging.info('SSL enabled' if self.cfg_ssl_enabled() else \ 'SSL disabled') logging.info('clients certificate ' + \ ('used' if self.cfg_certs_used() else 'not used')) logging.info( 'local socket activated on %s' % self.__local_sock.getsockname()) logging.info('pid (%d) written to %s' % (os.getpid(), self.pidfile)) while 1: try: self.handle_request() # If we received shutdown request from local socket is_quit = self.handle_local_request() if is_quit: logging.info('stopping daemon') break except Exception as e: if self.__by_signal_do: self.__by_signal_do = 0 continue from traceback import format_exception i = sys.exc_info() logging.error('\n'.join(format_exception(i[0], i[1], i[2]))) del i break
def finish_request(self, request, client_address): try: return ThreadingTCPServer.finish_request(self, request, client_address) except Exception as e: from traceback import format_exception i = sys.exc_info() logging.error('\n'.join(format_exception(i[0], i[1], i[2])).strip())
def handle_local_request(self): """Accept local socket connections""" c = None is_quit = 0 try: # Are we have connection try? r, w, e = select.select([self.__local_sock.fileno()], [], [], self._config['server']['wait']) if not r: return c, addr, = self.__local_sock.accept() logging.info('local connection accepted') # Check if socket is ready for reading msg = b'' r, w, e = select.select([c.fileno()], [], [], 0) if r: msg = c.recv(32, socket.MSG_DONTWAIT) else: logging.warn('socket not ready for reading') raise socket.error(100, 'socket not ready for reading') reply = b'unknown command' if msg == b'config': logging.info('request server configuration') reply = cpickle_dumps(self._config) elif msg == b'status': logging.info('request server status') reply = b'started' elif msg == b'shutdown': logging.info('request server shutdown') is_quit = 1 reply = b'stopped' elif not msg: logging.info('no request') else: logging.info('unknown request') # Check if socket is ready for writing r, w, e = select.select([], [c.fileno()], [], 0) if w: c.send(reply, socket.MSG_DONTWAIT) else: logging.warn('socket not ready for writing') raise socket.error(101, 'socket not ready for writing') except Exception as e: logging.error(e) if c and not c._closed: c.close() logging.info('local connection closed') return is_quit
def log_my_message(self, obj, msg_type='info'): """Log message in format: <client_address> <message>""" if msg_type not in logging.LEVELS: logging.error('<log_my_message>: msg_type unknown') return c_addr = '%s:%s ' % self.client_address if hasattr(obj, '__str__') and isinstance(getattr(obj, '__str__'), collections.Callable): logging._log(msg_type, c_addr + str(obj)) else: logging._log(msg_type, c_addr + repr(obj))
def daemonize(self, parent1=None, parent2=None): """ Do the UNIX double-fork magic, see Stevens' I{"Advanced Programming in the UNIX Environment"} for details (B{ISBN 0201563177}) on U{http://www.erlenstar.demon.co.uk/unix/faq_2.html#SEC16}. I{parent1}, I{parent2} - continue parent process with this functions. """ # Check for a pidfile to see if the daemon already runned if os.path.isfile(self.pidfile): msg_stdout('PID-file %s found! Remove it manually' % self.pidfile) return #logging.info('fork #1 process') try: pid = os.fork() if pid >0: if parent1: parent1(pid) # Exit first parent sys.exit() except OSError as e: logging.error('fork #1 failed: %d (%s)' % (e.errno, e.strerror)) sys.exit(1) # Decouple from parent environment os.setsid() os.umask(0) # Do second fork #logging.info('fork #2 process') try: pid = os.fork() if pid >0: if parent2: parent2(pid) # Exit from second parent sys.exit() except OSError as e: logging.error('fork #2 failed: %d (%s)' % (e.errno, e.strerror)) sys.exit(1) atexit.register(self.delpid) pid = str(os.getpid()) open(self.pidfile,'w+').write('%s\n' % pid) logging.info('write PID (%s) to %s' % (pid, self.pidfile)) return 1
def handle_sigusr1(self, signum, frame): """Handle signal SIGUSR1 to reload server configuration""" logging.info('reload server configuration (by signal)') # Catch exception in the I{main} method caused by received signal self.__by_signal_do = 1 config = get_conf(CFG_PATH) # PID file cannot be changed config['server']['pid'] = self._config['server']['pid'] # Log file path changed if self._config['server']['log'] != config['server']['log']: logging.close(0) logging.init(0, filename=config['server']['log']) # Local socket path changed init_loc = (config['server']['sock'] != self._config['server']['sock']) # SSL option changed and/or server ip/port, we need to # shutdown current socket and open new one init_srv = (config['ssl'] != self._config['ssl'] \ or config['server']['port'] != self._config['server']['port'] \ or config['server']['ip'] != self._config['server']['ip']) self._config = config.copy() if init_srv: try: self.socket.shutdown(socket.SHUT_RDWR) self._create_bind_activate(1) except Exception as e: # @todo: fix: if cannot bind new socket, do not reload config self.__by_signal_do = 0 logging.error(e) if init_loc: try: self._create_local_socket(1) except Exception as e: pass self.socket.settimeout(config['server']['timeout']) # Non-blocking mode self.__local_sock.settimeout(0.0) logging.info('server configuration reloaded') del config
def _create_bind_activate(self, init=0): """Create socket, bind and activate server""" try: self._create_new_socket(init) except Exception as e: logging.critical('cannot create new socket') logging.error(e) raise e try: self.server_bind() self.server_activate() except Exception as e: logging.critical('cannot bind/activate server: %s' %e) raise e logging.info('server binded and activated on %s:%s' % \ self.socket.getsockname())
def _create_new_socket(self, init): """Create new socket. I{init} >0 if need to replace internal server address used in the base class to create and bind socket""" if init: self.server_address = \ (self._config['server']['ip'], self._config['server']['port'],) self.socket = socket.socket(self.address_family, self.socket_type) self.socket.settimeout(self._config['server']['timeout']) logging.info('network socket created') if self._config['ssl'].get('enable', 0) ==0: logging.info('SSL disabled') return logging.info('SSL enabled') try: logging.info('socket replaced by SSLSocket') import ssl if self._config['ssl'].get('verify_client', 0) ==0: logging.info('clients certificates not used') self.socket = ssl.SSLSocket(None, self._config['ssl']['pkey_file'], self._config['ssl']['cert_file'], True, ssl.CERT_NONE, ssl.PROTOCOL_SSLv3) else: logging.info('clients certificate required') self.socket = ssl.SSLSocket(None, self._config['ssl']['pkey_file'], self._config['ssl']['cert_file'], True, ssl.CERT_REQUIRED, ssl.PROTOCOL_SSLv3, self._config['ssl']['verify_loc']) self.socket.settimeout(self._config['server']['timeout']) except Exception as e: logging.error(e) raise RuntimeError('SSL error')