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 stop(self): logging.info('stopping daemon') # Get the pid from the pidfile try: pf = open(self.pidfile, 'r') pid = int(pf.read().strip()) pf.close() except IOError: pid = None #print (sys.stderr, sys.stdout) if not pid: logging.warn('daemon is already stopped') sys.stderr.write('daemon is already stopped\n') return # Try killing the daemon process try: while 1: os.kill(pid, signal.SIGTERM) sleep(0.1) except OSError as err: # PID file is present, but no process! err = '%s' %err if err.find('No such process') >0: if os.path.isfile(self.pidfile): os.remove(self.pidfile) else: sys.stderr.write(err) sys.exit(1)
def clean_on_exit(fp_sock): """Method executed on process exit""" logging.info('clean up on process exit') try: os.remove(fp_sock) except: pass logging.info('shutdown logging system') logging.close()
def log_message(self, format, *args): """Rewrite default log handler. Log client IP address/port and verbose response of HTTP response code""" format = '%s:%s ' % self.client_address + format if args and len(args) >2 and args[1].isdigit() and \ self.responses.get(int(args[1])): format += ' %s' args += (self.responses.get(int(args[1]))[0],) logging.info(format % args)
def handle_sigusr2(self, signum, frame): """Handle signal SIGUSR2 to reload aliases configuration""" logging.info('reload aliases configuration (by signal)') # Catch received signal in the I{main} method self.__by_signal_do = 1 aliases = get_aliases(self._config['get']['aliases_file']) try: check_aliases(self._config['get']['base_dir'], aliases) self._aliases = aliases.copy() logging.info('aliases configuration reloaded') except Exception as e: logging.warn('aliases configuration error') del aliases
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 get_request(self): sock, addr = ThreadingTCPServer.get_request(self) logging.info('%s:%s incoming request' % addr) return sock, addr
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 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 _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')
server = AsyncHTTPServer(config, SFSHTTPHandler) msg_stdout('Network socket created') server._aliases = aliases.copy() # Catch SIGUSR1 signal to reload server configuration signal.signal(signal.SIGUSR1, server.handle_sigusr1) # Catch SIGUSR2 signal to reload aliases configuration signal.signal(signal.SIGUSR2, server.handle_sigusr2) msg_stdout('Signal handlers registered') if server.activate(): msg_stdout('Server activated', flush_buf=1) # Send message to local socket in the first parent process r = server.daemonize() if r: logging.info('forked as daemon process') # Close logging, init new to file logging.close(0) logging.init(0, filename=config['server']['log']) # Delete local socket on FS when process exit atexit.register(clean_on_exit, config['server']['sock']) # Start main loop server.main() else: msg_stdout('Error: can\'t daemonize', 2) elif sys.argv[1].lower() == 'stop': msg_sock(config['server'], 'shutdown', 0) elif sys.argv[1].lower() == 'reload': signal_to_daemon(config['server']['pid'], signal.SIGUSR1)