def handle_api_list_installed(self, http_context): r = [] manager = PluginManager.get(aj.context) for name in manager: plugin = manager[name] r.append({ 'name': plugin['info']['name'], 'imported': plugin['imported'], 'crash': self.__serialize_exception( manager.get_crash(plugin['info']['name'])), 'path': plugin['path'], 'author': plugin['info']['author'], 'author_email': plugin['info']['email'], 'url': plugin['info']['url'], 'icon': plugin['info']['icon'], 'version': plugin['info']['version'], 'title': plugin['info']['title'], }) return r
def handle_view(self, http_context): if aj.dev: rebuild_all = http_context.env.get("HTTP_CACHE_CONTROL", None) == "no-cache" for provider in aj.plugin_providers: if isinstance(provider, DirectoryPluginProvider): logging.debug("Building resources in %s", provider.path) if rebuild_all: cmd = ["ajenti-dev-multitool", "--rebuild"] else: cmd = ["ajenti-dev-multitool", "--build"] p = subprocess.Popen(cmd, cwd=provider.path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) o, e = p.communicate() if p.returncode != 0: logging.error("Resource compilation failed") logging.error(o + e) manager = PluginManager.get(aj.context) path = manager.get_content_path("core", "content/pages/index.html") content = open(path).read() % { "prefix": http_context.prefix, "plugins": json.dumps(dict((manager[n]["info"]["name"], manager[n]["info"]["title"]) for n in manager)), "version": aj.version, "platform": aj.platform, "platformUnmapped": aj.platform_unmapped, "bootstrapColor": aj.config.data.get("color", None), } http_context.add_header("Content-Type", "text/html") http_context.respond_ok() return content
def handle_api_languages(self, http_context): mgr = PluginManager.get(aj.context) languages = set() for id in mgr: for lang in os.listdir(mgr.get_content_path(id, 'locale')): if lang != 'app.pot': languages.add(lang) return sorted(list(languages))
def get_augeas(self): return Augeas( modules=[{ 'name': 'Supervisor', 'lens': 'Supervisor.lns', 'incl': [ self.path, ] }], loadpath=PluginManager.get(aj.context).get_content_path('supervisor', ''), )
def make_report(e): """ Formats a bug report. """ import platform as _platform from aj import platform, platform_unmapped, platform_string, version, debug tb = traceback.format_exc() tb = '\n'.join(' ' + x for x in tb.splitlines()) import gevent import greenlet import psutil from aj.plugins import PluginManager return """Ajenti bug report -------------------- Info | Value ----- | ----- Ajenti | %s Platform | %s / %s / %s Architecture | %s Python | %s Debug | %s Loaded plugins | %s Library | Version ------- | ------- gevent | %s greenlet | %s psutil | %s %s """ % ( version, platform, platform_unmapped, platform_string, subprocess.check_output(['uname', '-m']).strip().decode(), '.'.join(str(x) for x in _platform.python_version_tuple()), debug, ', '.join( sorted(PluginManager.get(aj.context).get_loaded_plugins_list())) if aj.context else 'None', gevent.__version__, greenlet.__version__, psutil.__version__, tb, )
def get_augeas(self): return Augeas( modules=[{ 'name': 'Supervisor', 'lens': 'Supervisor.lns', 'incl': [ self.path, ] }], loadpath=PluginManager.get(aj.context).get_content_path( 'supervisor', ''), )
def make_report(e): """ Formats a bug report. """ import platform as _platform from aj import platform, platform_unmapped, platform_string, version, debug tb = traceback.format_exc(e) tb = '\n'.join(' ' + x for x in tb.splitlines()) import gevent import greenlet import psutil from aj.plugins import PluginManager return """Ajenti bug report -------------------- Info | Value ----- | ----- Ajenti | %s Platform | %s / %s / %s Architecture | %s Python | %s Debug | %s Loaded plugins | %s Library | Version ------- | ------- gevent | %s greenlet | %s psutil | %s %s """ % ( version, platform, platform_unmapped, platform_string, subprocess.check_output(['uname', '-mp']).strip(), '.'.join([str(x) for x in _platform.python_version_tuple()]), debug, ', '.join(sorted(PluginManager.get(aj.context).get_loaded_plugins_list())), gevent.__version__, greenlet.__version__, psutil.__version__, tb, )
def handle_view(self, http_context): if aj.dev: restricted_user = aj.config.data['restricted_user'] if os.getuid() != pwd.getpwnam(restricted_user).pw_uid: rebuild_all = http_context.env.get('HTTP_CACHE_CONTROL', None) == 'no-cache' for provider in aj.plugin_providers: if isinstance(provider, DirectoryPluginProvider): logging.debug('Building resources in %s', provider.path) if rebuild_all: cmd = ['ajenti-dev-multitool', '--rebuild'] else: cmd = ['ajenti-dev-multitool', '--build'] p = subprocess.Popen(cmd, cwd=provider.path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) o, e = p.communicate() if p.returncode != 0: logging.error('Resource compilation failed') logging.error(o + e) else: logging.warning("Cannot build resources as restricted user %s", restricted_user) manager = PluginManager.get(aj.context) path = manager.get_content_path('core', 'content/pages/index.html') content = open(path).read() % { 'prefix': http_context.prefix, 'plugins': json.dumps({ manager[n]['info']['name']: manager[n]['info']['title'] for n in manager }), 'config': json.dumps(aj.config.data), 'version': str(aj.version), 'platform': aj.platform, 'platformUnmapped': aj.platform_unmapped, 'bootstrapColor': aj.config.data.get('color', None), } http_context.add_header('Content-Type', 'text/html') http_context.respond_ok() return content
def handle_api_list_installed(self, http_context): r = [] for plugin in PluginManager.get(aj.context).get_all().values(): r.append({ 'name': plugin.name, 'active': plugin.active, 'crash': plugin.crash, 'path': plugin.path, 'author': plugin.author, 'author_email': plugin.email, 'url': plugin.url, 'icon': plugin.icon, 'version': plugin.version, 'title': plugin.title, }) return r
def handle_file(self, http_context, plugin=None, path=None): """ Connector to get a specific file from plugin. :param http_context: HttpContext :type http_context: HttpContext :param plugin: Plugin name :type plugin: string :param path: Path of the file :type path: string :return: Compressed content of the file :rtype: gzip """ if '..' in path: return http_context.respond_not_found() return http_context.file( PluginManager.get(aj.context).get_content_path(plugin, path))
def handle_api_list_installed(self, http_context): r = [] manager = PluginManager.get(aj.context) for name in manager: plugin = manager[name] r.append({ 'name': plugin['info']['name'], 'imported': plugin['imported'], 'crash': self.__serialize_exception(manager.get_crash(plugin['info']['name'])), 'path': plugin['path'], 'author': plugin['info']['author'], 'author_email': plugin['info']['email'], 'url': plugin['info']['url'], 'icon': plugin['info']['icon'], 'version': plugin['info']['version'], 'title': plugin['info']['title'], }) return r
def handle_api_languages(self, http_context): """ List all availables languages. :param http_context: HttpContext :type http_context: HttpContext :return: List of languages :rtype: list """ mgr = PluginManager.get(aj.context) languages = set() for id in mgr: locale_dir = mgr.get_content_path(id, 'locale') if os.path.isdir(locale_dir): for lang in os.listdir(locale_dir): if lang != 'app.pot': languages.add(lang) return sorted(list(languages))
def handle_api_list_installed(self, http_context): """ Get the plugin list from PluginManager. :param http_context: HttpContext :type http_context: HttpContext :return: List of plugin informations, one plugin per dict :rtype: list of dict """ r = [] manager = PluginManager.get(aj.context) for name in manager: plugin = manager[name] r.append({ 'name': plugin['info']['name'], 'imported': plugin['imported'], 'crash': self.__serialize_exception( manager.get_crash(plugin['info']['name'])), 'path': plugin['path'], 'author': plugin['info']['author'], 'author_email': plugin['info']['email'], 'url': plugin['info']['url'], 'icon': plugin['info']['icon'], 'version': plugin['info']['version'], 'title': plugin['info']['title'], 'documentation': plugin['info'].get('docs', ''), }) return r
def make_report(e): """ Formats a bug report. """ import platform as _platform from aj import platform, platform_unmapped, platform_string, version, debug tb = traceback.format_exc() tb = '\n'.join(' ' + x for x in tb.splitlines()) import gevent import greenlet import psutil from aj.plugins import PluginManager architecture = subprocess.check_output(['uname', '-m']).strip().decode() python = '.'.join(str(x) for x in _platform.python_version_tuple()) plugins = ', '.join( sorted(PluginManager.get( aj.context).get_loaded_plugins_list())) if aj.context else 'None' return f"""Ajenti bug report
def handle_view(self, http_context): if aj.dev: rebuild_all = http_context.env.get('HTTP_CACHE_CONTROL', None) == 'no-cache' for provider in aj.plugin_providers: if isinstance(provider, DirectoryPluginProvider): logging.debug('Building resources in %s', provider.path) if rebuild_all: cmd = ['ajenti-dev-multitool', '--rebuild'] else: cmd = ['ajenti-dev-multitool', '--build'] p = subprocess.Popen( cmd, cwd=provider.path, stdout=subprocess.PIPE, stderr=subprocess.PIPE ) o, e = p.communicate() if p.returncode != 0: logging.error('Resource compilation failed') logging.error(o + e) manager = PluginManager.get(aj.context) path = manager.get_content_path('core', 'content/pages/index.html') content = open(path).read() % { 'prefix': http_context.prefix, 'plugins': json.dumps( dict((manager[n]['info']['name'], manager[n]['info']['title']) for n in manager) ), 'config': json.dumps(aj.config.data), 'version': aj.version, 'platform': aj.platform, 'platformUnmapped': aj.platform_unmapped, 'bootstrapColor': aj.config.data.get('color', None), } http_context.add_header('Content-Type', 'text/html') http_context.respond_ok() return content
def handle_view(self, http_context): """ Catch all for all not handled url. Generate an appropriate page and reload all extern resources if ajenti is used in dev mode. This function is also called when a page is refreshed through F5. :param http_context: HttpContext :type http_context: HttpContext :return: Page content :rtype: string """ if aj.dev: restricted_user = aj.config.data['restricted_user'] if os.getuid() != pwd.getpwnam(restricted_user).pw_uid: rebuild_all = http_context.env.get('HTTP_CACHE_CONTROL', None) == 'no-cache' for provider in aj.plugin_providers: if isinstance(provider, DirectoryPluginProvider): logging.debug('Building resources in %s', provider.path) if rebuild_all: cmd = ['ajenti-dev-multitool', '--rebuild'] else: cmd = ['ajenti-dev-multitool', '--build'] p = subprocess.Popen(cmd, cwd=provider.path, stdout=subprocess.PIPE, stderr=subprocess.PIPE) o, e = p.communicate() if p.returncode != 0: logging.error('Resource compilation failed') logging.error((o + e).decode('utf-8')) else: logging.warning("Cannot build resources as restricted user %s", restricted_user) manager = PluginManager.get(aj.context) path = manager.get_content_path('core', 'content/pages/index.html') content = open(path).read() % { 'prefix': http_context.prefix, 'plugins': json.dumps({ manager[n]['info']['name']: manager[n]['info']['title'] for n in manager }), 'config': json.dumps(aj.config.get_non_sensitive_data()), 'version': str(aj.version), 'platform': aj.platform, 'platformUnmapped': aj.platform_unmapped, 'devMode': aj.dev, 'bootstrapColor': aj.config.data.get('color', None), } http_context.add_header('Content-Type', 'text/html') http_context.respond_ok() return content
def run(config=None, plugin_providers=None, product_name='ajenti', dev_mode=False, debug_mode=False, autologin=False): """ A global entry point for Ajenti. :param config: config file implementation instance to use :type config: :class:`aj.config.BaseConfig` :param plugin_providers: list of plugin providers to load plugins from :type plugin_providers: list(:class:`aj.plugins.PluginProvider`) :param str product_name: a product name to use :param bool dev_mode: enables dev mode (automatic resource recompilation) :param bool debug_mode: enables debug mode (verbose and extra logging) :param bool autologin: disables authentication and logs everyone in as the user running the panel. This is EXTREMELY INSECURE. """ if config is None: raise TypeError('`config` can\'t be None') reload(sys) if hasattr(sys, 'setdefaultencoding'): sys.setdefaultencoding('utf8') aj.product = product_name aj.debug = debug_mode aj.dev = dev_mode aj.dev_autologin = autologin aj.init() aj.log.set_log_params(tag='master', master_pid=os.getpid()) aj.context = Context() aj.config = config aj.plugin_providers = plugin_providers or [] logging.info('Loading config from %s', aj.config) aj.config.load() aj.config.ensure_structure() if aj.debug: logging.warning('Debug mode') if aj.dev: logging.warning('Dev mode') try: locale.setlocale(locale.LC_ALL, '') except locale.Error: logging.warning('Couldn\'t set default locale') # install a passthrough gettext replacement since all localization is handled in frontend # and _() is here only for string extraction __builtins__['_'] = lambda x: x logging.info('Ajenti Core %s', aj.version) logging.info('Master PID - %s', os.getpid()) logging.info('Detected platform: %s / %s', aj.platform, aj.platform_string) logging.info('Python version: %s', aj.python_version) # Load plugins PluginManager.get(aj.context).load_all_from(aj.plugin_providers) if len(PluginManager.get(aj.context)) == 0: logging.warning('No plugins were loaded!') if aj.config.data['bind']['mode'] == 'unix': path = aj.config.data['bind']['socket'] if os.path.exists(path): os.unlink(path) listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: listener.bind(path) except OSError: logging.error('Could not bind to %s', path) sys.exit(1) if aj.config.data['bind']['mode'] == 'tcp': host = aj.config.data['bind']['host'] port = aj.config.data['bind']['port'] listener = socket.socket( socket.AF_INET6 if ':' in host else socket.AF_INET, socket.SOCK_STREAM ) if aj.platform not in ['freebsd', 'osx']: try: listener.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1) except socket.error: logging.warning('Could not set TCP_CORK') listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) logging.info('Binding to [%s]:%s', host, port) try: listener.bind((host, port)) except socket.error as e: logging.error('Could not bind: %s', str(e)) sys.exit(1) # Fix stupid socketio bug (it tries to do *args[0][0]) socket.socket.__getitem__ = lambda x, y: None listener.listen(10) gateway = GateMiddleware.get(aj.context) application = HttpRoot(HttpMiddlewareAggregator([gateway])).dispatch aj.server = SocketIOServer( listener, log=open(os.devnull, 'w'), application=application, handler_class=RequestHandler, policy_server=False, transports=[ str('websocket'), str('xhr-polling'), str('jsonp-polling'), ], ) if aj.config.data['ssl']['enable'] and aj.config.data['bind']['mode'] == 'tcp': aj.server.ssl_args = {'server_side': True} cert_path = aj.config.data['ssl']['certificate'] if aj.config.data['ssl']['fqdn_certificate']: fqdn_cert_path = aj.config.data['ssl']['fqdn_certificate'] else: fqdn_cert_path = cert_path context = gevent.ssl.SSLContext(ssl.PROTOCOL_TLS) context.load_cert_chain(certfile=fqdn_cert_path, keyfile=fqdn_cert_path) context.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 context.set_ciphers('ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM') if aj.config.data['ssl']['client_auth']['enable']: logging.info('Enabling SSL client authentication') context.load_verify_locations(cafile=cert_path) if aj.config.data['ssl']['client_auth']['force']: context.verify_mode = ssl.CERT_REQUIRED else: context.verify_mode = ssl.CERT_OPTIONAL ## Test callback : client_certificate_callback must return None to get forward # context.set_servername_callback(AuthenticationService.get(aj.context).client_certificate_callback) aj.server.wrap_socket = lambda socket, **args:context.wrap_socket(sock=socket, server_side=True) logging.info('SSL enabled') # auth.log try: syslog.openlog( ident=str(aj.product), facility=syslog.LOG_AUTH, ) except Exception as e: syslog.openlog(aj.product) def cleanup(): if hasattr(cleanup, 'started'): return cleanup.started = True logging.info('Process %s exiting normally', os.getpid()) gevent.signal(signal.SIGINT, lambda: None) gevent.signal(signal.SIGTERM, lambda: None) if aj.master: gateway.destroy() p = psutil.Process(os.getpid()) for c in p.children(recursive=True): try: os.killpg(c.pid, signal.SIGTERM) os.killpg(c.pid, signal.SIGKILL) except OSError: pass def signal_handler(): cleanup() sys.exit(0) gevent.signal(signal.SIGINT, signal_handler) gevent.signal(signal.SIGTERM, signal_handler) aj.server.serve_forever() if not aj.master: # child process, server is stopped, wait until killed gevent.wait() if hasattr(aj.server, 'restart_marker'): logging.warning('Restarting by request') cleanup() fd = 20 # Close all descriptors. Creepy thing while fd > 2: try: os.close(fd) logging.debug('Closed descriptor #%i', fd) except OSError: pass fd -= 1 logging.warning('Will restart the process now') if '-d' in sys.argv: sys.argv.remove('-d') os.execv(sys.argv[0], sys.argv) else: if aj.master: logging.debug('Server stopped') cleanup()
def __init__(self, http_context): self.cache = {} self.use_cache = not aj.debug self.mgr = PluginManager.get(aj.context)
def handle_file(self, http_context, plugin=None, path=None): if '..' in path: return http_context.respond_not_found() return http_context.file(PluginManager.get(aj.context).get_content_path(plugin, path))
def handle_file(self, http_context, plugin=None, path=None): if '..' in path: return http_context.respond_not_found() return http_context.file( PluginManager.get(aj.context).get_content_path(plugin, path))
def getPluginPath(self): manager = PluginManager.get(aj.context) plugin = manager['coach'] return plugin['path']
def run(config=None, plugin_providers=None, product_name='ajenti', dev_mode=False, debug_mode=False, autologin=False): """ A global entry point for Ajenti. :param config: config file implementation instance to use :type config: :class:`aj.config.BaseConfig` :param plugin_providers: list of plugin providers to load plugins from :type plugin_providers: list(:class:`aj.plugins.PluginProvider`) :param str product_name: a product name to use :param bool dev_mode: enables dev mode (automatic resource recompilation) :param bool debug_mode: enables debug mode (verbose and extra logging) :param bool autologin: disables authentication and logs everyone in as the user running the panel. This is EXTREMELY INSECURE. """ if config is None: raise TypeError('`config` can\'t be None') reload(sys) if hasattr(sys, 'setdefaultencoding'): sys.setdefaultencoding('utf8') aj.product = product_name aj.debug = debug_mode aj.dev = dev_mode aj.dev_autologin = autologin aj.init() aj.log.set_log_params(tag='master', master_pid=os.getpid()) aj.context = Context() aj.config = config aj.plugin_providers = plugin_providers or [] logging.info(f'Loading config from {aj.config}') aj.config.load() aj.config.ensure_structure() logging.info('Loading users from /etc/ajenti/users.yml') aj.users = AjentiUsers(aj.config.data['auth']['users_file']) aj.users.load() logging.info('Loading smtp config from /etc/ajenti/smtp.yml') aj.smtp_config = SmtpConfig() aj.smtp_config.load() aj.smtp_config.ensure_structure() if aj.debug: logging.warning('Debug mode') if aj.dev: logging.warning('Dev mode') try: locale.setlocale(locale.LC_ALL, '') except locale.Error: logging.warning('Couldn\'t set default locale') # install a passthrough gettext replacement since all localization is handled in frontend # and _() is here only for string extraction __builtins__['_'] = lambda x: x logging.info(f'Ajenti Core {aj.version}') logging.info(f'Master PID - {os.getpid()}') logging.info(f'Detected platform: {aj.platform} / {aj.platform_string}') logging.info(f'Python version: {aj.python_version}') # Load plugins PluginManager.get(aj.context).load_all_from(aj.plugin_providers) if len(PluginManager.get(aj.context)) == 0: logging.warning('No plugins were loaded!') if aj.config.data['bind']['mode'] == 'unix': path = aj.config.data['bind']['socket'] if os.path.exists(path): os.unlink(path) listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: listener.bind(path) except OSError: logging.error(f'Could not bind to {path}') sys.exit(1) if aj.config.data['bind']['mode'] == 'tcp': host = aj.config.data['bind']['host'] port = aj.config.data['bind']['port'] listener = socket.socket( socket.AF_INET6 if ':' in host else socket.AF_INET, socket.SOCK_STREAM) if aj.platform not in ['freebsd', 'osx']: try: listener.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1) except socket.error: logging.warning('Could not set TCP_CORK') listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) logging.info(f'Binding to [{host}]:{port}') try: listener.bind((host, port)) except socket.error as e: logging.error(f'Could not bind: {str(e)}') sys.exit(1) listener.listen(10) gateway = GateMiddleware.get(aj.context) middleware_stack = HttpMasterMiddleware.all(aj.context) + [gateway] sio = Server(async_mode='gevent', cors_allowed_origins=aj.config.data['trusted_domains']) application = WSGIApp( sio, HttpRoot(HttpMiddlewareAggregator(middleware_stack)).dispatch) sio.register_namespace(SocketIONamespace(context=aj.context)) if aj.config.data['ssl']['enable'] and aj.config.data['bind'][ 'mode'] == 'tcp': aj.server = pywsgi.WSGIServer( listener, log=open(os.devnull, 'w'), application=application, handler_class=RequestHandler, policy_server=False, ) aj.server.ssl_args = {'server_side': True} cert_path = aj.config.data['ssl']['certificate'] if aj.config.data['ssl']['fqdn_certificate']: fqdn_cert_path = aj.config.data['ssl']['fqdn_certificate'] else: fqdn_cert_path = cert_path context = gevent.ssl.SSLContext(ssl.PROTOCOL_TLS) context.load_cert_chain(certfile=fqdn_cert_path, keyfile=fqdn_cert_path) context.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 context.set_ciphers( 'ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM') if aj.config.data['ssl']['client_auth']['enable']: logging.info('Enabling SSL client authentication') context.load_verify_locations(cafile=cert_path) if aj.config.data['ssl']['client_auth']['force']: context.verify_mode = ssl.CERT_REQUIRED else: context.verify_mode = ssl.CERT_OPTIONAL ## Test callback : client_certificate_callback must return None to get forward # context.set_servername_callback(AuthenticationService.get(aj.context).client_certificate_callback) aj.server.wrap_socket = lambda socket, **args: context.wrap_socket( sock=socket, server_side=True) logging.info('SSL enabled') if aj.config.data['ssl']['force']: from aj.https_redirect import http_to_https target_url = f'https://{aj.config.data["name"]}:{port}' gevent.spawn(http_to_https, target_url) logging.info( f'HTTP to HTTPS redirection activated to {target_url}') else: # No policy_server argument for http aj.server = pywsgi.WSGIServer( listener, log=open(os.devnull, 'w'), application=application, handler_class=RequestHandler, ) # auth.log try: syslog.openlog( ident=str(aj.product), facility=syslog.LOG_AUTH, ) except Exception as e: syslog.openlog(aj.product) def cleanup(): if hasattr(cleanup, 'started'): return cleanup.started = True logging.info(f'Process {os.getpid()} exiting normally') gevent.signal_handler(signal.SIGINT, lambda: None) gevent.signal_handler(signal.SIGTERM, lambda: None) if aj.master: gateway.destroy() p = psutil.Process(os.getpid()) for c in p.children(recursive=True): try: os.killpg(c.pid, signal.SIGTERM) os.killpg(c.pid, signal.SIGKILL) except OSError: pass def signal_handler(): cleanup() sys.exit(0) gevent.signal_handler(signal.SIGINT, signal_handler) gevent.signal_handler(signal.SIGTERM, signal_handler) aj.server.serve_forever() if not aj.master: # child process, server is stopped, wait until killed gevent.wait() if hasattr(aj.server, 'restart_marker'): logging.warning('Restarting by request') cleanup() fd = 20 # Close all descriptors. Creepy thing while fd > 2: try: os.close(fd) logging.debug(f'Closed descriptor #{fd:d}') except OSError: pass fd -= 1 restart() else: if aj.master: logging.debug('Server stopped') cleanup()
def run(config=None, plugin_providers=None, product_name='ajenti', dev_mode=False, debug_mode=False, autologin=False): """ A global entry point for Ajenti. :param config: config file implementation instance to use :type config: :class:`aj.config.BaseConfig` :param plugin_providers: list of plugin providers to load plugins from :type plugin_providers: list(:class:`aj.plugins.PluginProvider`) :param str product_name: a product name to use :param bool dev_mode: enables dev mode (automatic resource recompilation) :param bool debug_mode: enables debug mode (verbose and extra logging) :param bool autologin: disables authentication and logs everyone in as the user running the panel. This is EXTREMELY INSECURE. """ if config is None: raise TypeError('`config` can\'t be None') reload(sys) sys.setdefaultencoding('utf8') aj.product = product_name aj.debug = debug_mode aj.dev = dev_mode aj.dev_autologin = autologin aj.init() aj.log.set_log_params(tag='master', master_pid=os.getpid()) aj.context = Context() aj.config = config aj.plugin_providers = plugin_providers or [] logging.info('Loading config from %s', aj.config) aj.config.load() aj.config.ensure_structure() if aj.debug: logging.warn('Debug mode') if aj.dev: logging.warn('Dev mode') try: locale.setlocale(locale.LC_ALL, '') except locale.Error: logging.warning('Couldn\'t set default locale') # install a passthrough gettext replacement since all localization is handled in frontend # and _() is here only for string extraction __builtins__['_'] = lambda x: x logging.info('Ajenti Core %s', aj.version) logging.info('Detected platform: %s / %s', aj.platform, aj.platform_string) # Load plugins PluginManager.get(aj.context).load_all_from(aj.plugin_providers) if len(PluginManager.get(aj.context)) == 0: logging.warn('No plugins were loaded!') if aj.config.data['bind']['mode'] == 'unix': path = aj.config.data['bind']['socket'] if os.path.exists(path): os.unlink(path) listener = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) try: listener.bind(path) except OSError: logging.error('Could not bind to %s', path) sys.exit(1) if aj.config.data['bind']['mode'] == 'tcp': host = aj.config.data['bind']['host'] port = aj.config.data['bind']['port'] listener = socket.socket( socket.AF_INET6 if ':' in host else socket.AF_INET, socket.SOCK_STREAM ) if aj.platform not in ['freebsd', 'osx']: try: listener.setsockopt(socket.IPPROTO_TCP, socket.TCP_CORK, 1) except socket.error: logging.warn('Could not set TCP_CORK') listener.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) logging.info('Binding to [%s]:%s', host, port) try: listener.bind((host, port)) except socket.error as e: logging.error('Could not bind: %s', str(e)) sys.exit(1) # Fix stupid socketio bug (it tries to do *args[0][0]) socket.socket.__getitem__ = lambda x, y: None listener.listen(10) gateway = GateMiddleware.get(aj.context) application = HttpRoot(HttpMiddlewareAggregator([gateway])).dispatch aj.server = SocketIOServer( listener, log=open(os.devnull, 'w'), application=application, handler_class=RequestHandler, policy_server=False, transports=[ str('websocket'), str('flashsocket'), str('xhr-polling'), str('jsonp-polling'), ], ) if aj.config.data['ssl']['enable'] and aj.config.data['bind']['mode'] == 'tcp': try: context = SSL.Context(SSL.TLSv1_2_METHOD) except: context = SSL.Context(SSL.TLSv1_METHOD) context.set_session_id(str(id(context))) context.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3) context.set_cipher_list('ALL:!ADH:!EXP:!LOW:!RC2:!3DES:!SEED:!RC4:+HIGH:+MEDIUM') certificate = crypto.load_certificate( crypto.FILETYPE_PEM, open(aj.config.data['ssl']['certificate']).read() ) private_key = crypto.load_privatekey( crypto.FILETYPE_PEM, open(aj.config.data['ssl']['certificate']).read() ) context.use_certificate(certificate) context.use_privatekey(private_key) if aj.config.data['ssl']['client_auth']['enable']: # todo harden files logging.info('Enabling SSL client authentication') context.add_client_ca(certificate) context.get_cert_store().add_cert(certificate) verify_flags = SSL.VERIFY_PEER if aj.config.data['ssl']['client_auth']['force']: verify_flags |= SSL.VERIFY_FAIL_IF_NO_PEER_CERT context.set_verify(verify_flags, AuthenticationService.get(aj.context).client_certificate_callback) context.set_verify_depth(0) aj.server.ssl_args = {'server_side': True} aj.server.wrap_socket = lambda socket, **ssl: SSLSocket(context, socket) logging.info('SSL enabled') # auth.log try: syslog.openlog( ident=str(aj.product), facility=syslog.LOG_AUTH, ) except: syslog.openlog(aj.product) def cleanup(): if hasattr(cleanup, 'started'): return cleanup.started = True logging.info('Process %s exiting normally', os.getpid()) gevent.signal(signal.SIGINT, lambda: None) gevent.signal(signal.SIGTERM, lambda: None) if aj.master: gateway.destroy() p = psutil.Process(os.getpid()) for c in p.children(recursive=True): try: os.killpg(c.pid, signal.SIGTERM) os.killpg(c.pid, signal.SIGKILL) except OSError: pass def signal_handler(): cleanup() sys.exit(0) gevent.signal(signal.SIGINT, signal_handler) gevent.signal(signal.SIGTERM, signal_handler) aj.server.serve_forever() if not aj.master: # child process, server is stopped, wait until killed gevent.wait() if hasattr(aj.server, 'restart_marker'): logging.warn('Restarting by request') cleanup() fd = 20 # Close all descriptors. Creepy thing while fd > 2: try: os.close(fd) logging.debug('Closed descriptor #%i', fd) except OSError: pass fd -= 1 logging.warn('Will restart the process now') if '-d' in sys.argv: sys.argv.remove('-d') os.execv(sys.argv[0], sys.argv) else: if aj.master: logging.debug('Server stopped') cleanup()