def post_restore(self, site, dbpasswd): users.SystemUser("mailpile").add() for r, d, f in os.walk(site.path): for x in d: os.chmod(os.path.join(r, x), 0o755) for x in f: os.chmod(os.path.join(r, x), 0o644) services.get(site.name).enable()
def post_restore(self, site, dbpasswd): users.SystemUser("mailpile").add() for r, d, f in os.walk(site.path): for x in d: os.chmod(os.path.join(root, x), 0755) for x in f: os.chmod(os.path.join(root, x), 0644) services.get(site.name).enable()
def add_domain(domain, reload=True): # Adds a system domain for use with XMPP with open("/etc/prosody/conf.d/{0}.cfg.lua".format(domain.name), "w") as f: data = ['VirtualHost "{0}"\n'.format(domain.name)] data.append(DEFAULT_CONFIG.format(domain.name)) f.writelines(data) if reload: services.get("prosody").restart()
def add_domain(domain, reload=True): # Adds a system domain for use with XMPP with open("/etc/prosody/conf.d/%s.cfg.lua" % domain.name, "w") as f: data = ['VirtualHost "%s"\n' % domain.name] data.append(DEFAULT_CONFIG % domain.name) f.writelines(data) if reload: services.get("prosody").restart()
def uninstall(self, force=False, nthread=NotificationThread()): """ Uninstall the arkOS application from the system. :param bool force: Uninstall the app even if others depend on it? :param NotificationThread nthread: notification thread to use """ signals.emit("apps", "pre_remove", self) msg = "Uninstalling application..." nthread.update(Notification("info", "Apps", msg)) exclude = ["openssl", "openssh", "nginx", "python2", "git", "nodejs", "npm"] # Make sure this app can be successfully removed, and if so also remove # any system-level packages that *only* this app requires for x in get(installed=True): for item in x.dependencies: if item["type"] == "app" and item["package"] == self.id \ and not force: exc_str = "{0} depends on this application" raise errors.InvalidConfigError(exc_str.format(x.name)) elif item["type"] == "system": exclude.append(item["package"]) # Stop any running services associated with this app for item in self.dependencies: if item["type"] == "system" and not item["package"] in exclude: if item.get("daemon"): try: services.get(item["daemon"]).stop() services.get(item["daemon"]).disable() except: pass pacman.remove([item["package"]], purge=config.get("apps", "purge")) # Remove the app's directory and cleanup the app object shutil.rmtree(os.path.join(config.get("apps", "app_dir"), self.id)) self.loadable = False self.installed = False # Regenerate the firewall and re-block the abandoned ports regen_fw = False for x in self.services: if x["ports"]: regen_fw = True if regen_fw: tracked_services.deregister(self.id) ports = [] for s in self.services: if s.get("default_policy", 0) and s["ports"]: ports.append(s["ports"]) if ports and config.get("general", "enable_upnp"): tracked_services.close_all_upnp(ports) smsg = "{0} uninstalled successfully".format(self.name) nthread.complete(Notification("success", "Apps", smsg)) signals.emit("apps", "post_remove", self)
def post_restore(self, site, dbpasswd): nodejs.install_from_package(site.path) users.SystemUser("haste").add() uid = users.get_system("haste").uid for r, d, f in os.walk(site.path): for x in d: os.chown(os.path.join(r, x), uid, -1) for x in f: os.chown(os.path.join(r, x), uid, -1) services.get(site.id).enable()
def update(self, pkg, ver): # General update procedure shell('unzip -o -d %s %s' % (self.path, pkg)) uid = users.get_system("ghost").uid for r, d, f in os.walk(self.path): for x in d: os.chown(os.path.join(r, x), uid, -1) for x in f: os.chown(os.path.join(r, x), uid, -1) nodejs.install_from_package(self.path, 'production', {'sqlite': '/usr/bin/sqlite3', 'python': '/usr/bin/python2'}) services.get(self.id).restart()
def update(self, pkg, ver): # General update procedure shell('unzip -o -d {0} {1}'.format(self.path, pkg)) uid = users.get_system("ghost").uid for r, d, f in os.walk(self.path): for x in d: os.chown(os.path.join(r, x), uid, -1) for x in f: os.chown(os.path.join(r, x), uid, -1) nodejs.install_from_package(self.path, 'production', { 'sqlite': '/usr/bin/sqlite3', 'python': '/usr/bin/python2' }) services.get(self.id).restart()
def disable_ssl(self): n = nginx.loadf('/etc/nginx/sites-available/%s' % self.id) for x in n.servers: if x.filter('Location', '/'): x.remove(x.filter('Location', '/')[0]) x.add(self.addtoblock[0]) nginx.dumpf(n, '/etc/nginx/sites-available/%s' % self.id) with open(os.path.join(self.path, 'config.js'), 'r') as f: data = f.read() data = data.replace('production: {\n url: \'https://', 'production: {\n url: \'http://') with open(os.path.join(self.path, 'config.js'), 'w') as f: f.write(data) services.get(self.id).restart()
def disable_ssl(self): n = nginx.loadf('/etc/nginx/sites-available/%s'%self.id) for x in n.servers: if x.filter('Location', '/'): x.remove(x.filter('Location', '/')[0]) x.add(self.addtoblock[0]) nginx.dumpf(n, '/etc/nginx/sites-available/%s'%self.id) with open(os.path.join(self.path, 'config.js'), 'r') as f: data = f.read() data = data.replace('production: {\n url: \'https://', 'production: {\n url: \'http://') with open(os.path.join(self.path, 'config.js'), 'w') as f: f.write(data) services.get(self.id).restart()
def get(self, id): try: svcs = services.get(id) except services.ActionError as e: if id: app.logger.error( "{0} service status could not be obtained. Failed with error: {1}" .format(id, e.emsg)) resp = jsonify( message="{0} service status could not be obtained.".format( id)) else: app.logger.error( "Service status could not be obtained. Failed with error: {0}" .format(e.emsg)) resp = jsonify(message="Service status could not be obtained.") resp.status_code = 422 return resp if id and not svcs: abort(404) if isinstance(svcs, services.Service): return jsonify(service=svcs.serialized) else: return jsonify(services=[ x.serialized for x in svcs if not any(y in x.name for y in ["systemd", "dbus"]) ])
def nginx_reload(): try: s = services.get("nginx") s.restart() return True except services.ActionError: return False
def add_share(self): """Add a share.""" config = configparser.ConfigParser() config.read(["/etc/samba/smb.conf"]) if config.has_section(self.id): raise errors.InvalidConfigError( "Share already present with this name" ) if not os.path.exists(self.path): os.makedirs(self.path) config.set("global", "security", "user") config.set("global", "map to guest", "bad user") config.add_section(self.id) config.set(self.id, "comment", self.comment) config.set(self.id, "path", self.path) config.set(self.id, "browseable", "yes") config.set(self.id, "public", "yes" if not self.valid_users else "no") config.set( self.id, "guest ok", "yes" if not self.valid_users else "no" ) config.set(self.id, "read only", "yes" if self.readonly else "no") config.set(self.id, "writable", "no" if self.readonly else "yes") if self.valid_users: config.set(self.id, "valid users", " ".join(self.valid_users)) with open("/etc/samba/smb.conf", "w") as f: config.write(f) svc = services.get("smbd") if svc: svc.restart()
def on_load(app): if app.id != "syncthing": return if not users.get_system("syncthing"): u = users.SystemUser('syncthing') u.add() u = users.get_system('syncthing') if not os.path.exists("/home/syncthing"): os.makedirs("/home/syncthing") os.chown("/home/syncthing", u.uid, 100) s = services.get("syncthing@syncthing") if not s.state == "running": s.start() count = 0 while count < 5: if not os.path.exists("/home/syncthing/.config/syncthing/config.xml"): time.sleep(5) count += 1 else: break if not os.path.exists("/home/syncthing/.config/syncthing/config.xml"): raise Exception("Syncthing taking too long to generate config, try again later") global api_key api_key = get_api_key() global my_id my_id = get_myid()
def put(self, id): data = request.get_json()["service"] svc = services.get(id) if id and not svc: abort(404) if not data.get("operation"): abort(400) try: if data["operation"] == "start": tag = "started" svc.start() elif data["operation"] == "stop": tag = "stopped" svc.stop() elif data["operation"] == "restart": tag = "restarted" svc.restart() elif data["operation"] == "real_restart": tag = "restarted" svc.restart(real=True) elif data["operation"] == "enable": tag = "enabled" svc.enable() elif data["operation"] == "disable": tag = "disabled" svc.disable() except services.ActionError as e: app.logger.error( "{0} service could not be {1}. Failed with error: {2}".format( id, tag, e.emsg)) resp = jsonify( message="{0} service could not be {1}.".format(id, tag)) resp.status_code = 422 return resp return jsonify(service=svc.serialized)
def put(self, id): data = json.loads(request.data)["service"] svc = services.get(id) if id and not svc: abort(404) if not data.get("operation"): abort(400) try: if data["operation"] == "start": tag = "started" svc.start() elif data["operation"] == "stop": tag = "stopped" svc.stop() elif data["operation"] == "restart": tag = "restarted" svc.restart() elif data["operation"] == "real_restart": tag = "restarted" svc.restart(real=True) elif data["operation"] == "enable": tag = "enabled" svc.enable() elif data["operation"] == "disable": tag = "disabled" svc.disable() except services.ActionError, e: resp = jsonify(message="%s service could not be %s." % (id, tag)) resp.status_code = 422 return resp
def enable_ssl(self, cfile, kfile): n = nginx.loadf('/etc/nginx/sites-available/%s'%self.id) for x in n.servers: if x.filter('Location', '/'): x.filter('Location', '/')[0].add( nginx.Key('proxy_set_header', 'X-Forwarded-For $proxy_add_x_forwarded_for'), nginx.Key('proxy_set_header', 'X-Forwarded-Proto $scheme') ) nginx.dumpf(n, '/etc/nginx/sites-available/%s'%self.id) with open(os.path.join(self.path, 'config.js'), 'r') as f: data = f.read() data = data.replace('production: {\n url: \'http://', 'production: {\n url: \'https://') with open(os.path.join(self.path, 'config.js'), 'w') as f: f.write(data) services.get(self.id).restart()
def save_config(config): api_key = get_api_key() api("http://*****:*****@syncthing") s.restart() return config
def post_restore(self, site, dbpasswd): nodejs.install_from_package(site.path, 'production', {'sqlite': '/usr/bin/sqlite3', 'python': '/usr/bin/python2'}) users.SystemUser("ghost").add() uid = users.get_system("ghost").uid for r, d, f in os.walk(site.path): for x in d: os.chown(os.path.join(r, x), uid, -1) for x in f: os.chown(os.path.join(r, x), uid, -1) s = services.get(site.id) if s: s.remove() cfg = { 'directory': site.path, 'user': '******', 'command': 'node %s'%os.path.join(site.path, 'index.js'), 'autostart': 'true', 'autorestart': 'true', 'environment': 'NODE_ENV="production"', 'stdout_logfile': '/var/log/ghost.log', 'stderr_logfile': '/var/log/ghost.log' } s = services.Service(site.id, "supervisor", cfg=cfg) s.add()
def on_load(app): # Make sure all system domains are registered with XMPP on system start if not app.id == "xmpp": return reload = False doms = domains.get() for x in doms: if not os.path.exists("/etc/prosody/conf.d/%s.cfg.lua" % x.name): add_domain(x, False) reload = True for x in os.listdir("/etc/prosody/conf.d"): if x.rstrip(".cfg.lua") not in [y.name for y in doms]: os.unlink(os.path.join("/etc/prosody/conf.d", x)) reload = True if reload: services.get("prosody").restart()
def php_reload(): """Reload PHP-FPM process.""" try: s = services.get("php-fpm") s.restart() except services.ActionError: pass
def disable(name): """Disable a service on boot""" try: service = services.get(name) service.disable() logger.success('ctl:svc:disable', 'Disabled {0}'.format(name)) except Exception as e: raise CLIException(str(e))
def stop(name): """Stop a service""" try: service = services.get(name) service.stop() logger.success('ctl:svc:stop', 'Stopped {0}'.format(name)) except Exception as e: raise CLIException(str(e))
def get(self, id): svcs = services.get(id) if id and not svcs: abort(404) if type(svcs) == list: return jsonify(services=[x.as_dict() for x in svcs if not any(y in x.name for y in ["systemd", "dbus"])]) else: return jsonify(service=svcs.as_dict())
def enable_ssl(self, cfile, kfile): n = nginx.loadf('/etc/nginx/sites-available/{0}'.format(self.id)) for x in n.servers: if x.filter('Location', '/'): x.filter('Location', '/')[0].add( nginx.Key('proxy_set_header', 'X-Forwarded-For $proxy_add_x_forwarded_for'), nginx.Key('proxy_set_header', 'X-Forwarded-Proto $scheme')) nginx.dumpf(n, '/etc/nginx/sites-available/{0}'.format(self.id)) with open(os.path.join(self.path, 'config.js'), 'r') as f: data = f.read() data = data.replace('production: {\n url: \'http://', 'production: {\n url: \'https://') with open(os.path.join(self.path, 'config.js'), 'w') as f: f.write(data) services.get(self.id).restart()
def on_load(app): # Make sure all system domains are registered with XMPP on system start if not app.id == "xmpp": return reload = False doms = domains.get() for x in doms: if not os.path.exists("/etc/prosody/conf.d/{0}.cfg.lua".format( x.name)): add_domain(x, False) reload = True for x in glob.glob("/etc/prosody/conf.d/*.cfg.lua"): basename = os.path.basename(x) if basename.split(".cfg.lua")[0] not in [y.name for y in doms]: os.unlink(x) reload = True if reload: services.get("prosody").restart()
def add_ssl(name, cert, key): # Add an SSL certificate to an XMPP domain domain = domains.get(name) with open("/etc/prosody/conf.d/%s.cfg.lua" % name, "w") as f: data = [ 'VirtualHost "%s"\n' % name, '\tssl = {\n', '\t\tkey = "%s";\n' % cert, '\t\tcertificate = "%s";\n' % key, '\t}\n' ] data.append(DEFAULT_CONFIG % domain.name) f.writelines(data) try: services.get("prosody").restart() except: pass return {"type": "app", "id": "xmpp_%s" % name, "aid": "xmpp", "sid": name, "name": "Chat Server (%s)" % name}
def disable_ssl(self): n = nginx.loadf('/etc/nginx/sites-available/%s'%self.id) for x in n.servers: if x.filter('Location', '/'): toremove = [] for y in x.filter('Location', '/')[0].all(): if y.value == 'X-Forwarded-For $proxy_add_x_forwarded_for' or \ y.value == 'X-Forwarded-Proto $scheme': toremove.append(y) for y in toremove: x.filter('Location', '/')[0].remove(y) nginx.dumpf(n, '/etc/nginx/sites-available/%s'%self.id) with open(os.path.join(self.path, 'config.js'), 'r') as f: data = f.read() data = data.replace('production: {\n url: \'https://', 'production: {\n url: \'http://') with open(os.path.join(self.path, 'config.js'), 'w') as f: f.write(data) services.get(self.id).restart()
def start(name): """Start a service""" try: service = services.get(name) service.start() if service.state == "running": logger.success('ctl:svc:start', 'Started {0}'.format(name)) else: logger.error('ctl:svc:start', 'Failed to start {0}'.format(name)) except Exception as e: raise CLIException(str(e))
def nginx_reload(): """ Reload nginx process. :returns: True if successful. """ try: s = services.get("nginx") s.restart() return True except services.ActionError: return False
def remove_share(self): """Remove a share.""" config = configparser.ConfigParser() config.read(["/etc/afp.conf"]) if not config.has_section(self.id): return config.remove_section(self.id) with open("/etc/afp.conf", "w") as f: config.write(f) svc = services.get("netatalk") if svc and svc.state: svc.restart()
def disable_ssl(self): n = nginx.loadf('/etc/nginx/sites-available/{0}'.format(self.id)) for x in n.servers: if x.filter('Location', '/'): toremove = [] for y in x.filter('Location', '/')[0].all(): if y.value == 'X-Forwarded-For '\ '$proxy_add_x_forwarded_for' or \ y.value == 'X-Forwarded-Proto $scheme': toremove.append(y) for y in toremove: x.filter('Location', '/')[0].remove(y) nginx.dumpf(n, '/etc/nginx/sites-available/{0}'.format(self.id)) with open(os.path.join(self.path, 'config.js'), 'r') as f: data = f.read() data = data.replace('production: {\n url: \'https://', 'production: {\n url: \'http://') with open(os.path.join(self.path, 'config.js'), 'w') as f: f.write(data) services.get(self.id).restart()
def remove_share(self): """Remove a share.""" config = configparser.ConfigParser() config.read(["/etc/samba/smb.conf"]) if not config.has_section(self.id): return config.remove_section(self.id) with open("/etc/samba/smb.conf", "w") as f: config.write(f) svc = services.get("smbd") if svc: svc.restart()
def add_ssl(name, cert, key): # Add an SSL certificate to an XMPP domain domain = domains.get(name) with open("/etc/prosody/conf.d/{0}.cfg.lua".format(name), "w") as f: data = [ 'VirtualHost "{0}"\n'.format(name), '\tssl = {{\n', '\t\tkey = "{0}";\n'.format(cert), '\t\tcertificate = "{0}";\n'.format(key), '\t}}\n' ] data.append(DEFAULT_CONFIG.format(domain.name)) f.writelines(data) try: services.get("prosody").restart() except: pass return { "type": "app", "id": "xmpp_{0}".format(name), "aid": "xmpp", "sid": name, "name": "Chat Server ({0})".format(name) }
def log(name): """Get logs since last boot for a particular service""" try: service = services.get(name) if not service: raise CLIException("No service found") if service.stype == "system": data = shell("journalctl -x -b 0 -u {0}".format(service.sfname)) click.echo_via_pager(data["stdout"].decode()) else: click.echo_via_pager(service.get_log()) except Exception as e: raise CLIException(str(e))
def change_admin_passwd(self): try: s = services.get("mysqld") if s.state != "running": s.start() except: return "" new_passwd = random_string()[:16] secrets.set("mysql", new_passwd) secrets.save() c = MySQLdb.connect('localhost', 'root', '', 'mysql') c.query('UPDATE user SET password=PASSWORD("'+new_passwd+'") WHERE User=\'root\'') c.query('FLUSH PRIVILEGES') c.commit() return new_passwd
def change_admin_passwd(self): try: s = services.get("mysqld") if s.state != "running": s.start() except: return "" new_passwd = random_string()[:16] secrets.set("mysql", new_passwd) secrets.save() c = MySQLdb.connect('localhost', 'root', '', 'mysql') c.query("UPDATE user SET password=PASSWORD(\"{0}\") " "WHERE User='******'".format(new_passwd)) c.query('FLUSH PRIVILEGES') c.commit() return new_passwd
def status(name): """Get service status""" try: service = services.get(name) if not service: raise CLIException("No service found") llen = len(service.name) if len(service.name) > 20 else 20 click.echo( click.style('{name: <{fill}}'.format(name=service.name, fill=llen + 3), fg="white", bold=True) + click.style(service.state.capitalize(), fg="green" if service.state == "running" else "red") + " " + click.style("Enabled" if service.enabled else "Disabled", fg="green" if service.enabled else "red")) except Exception as e: raise CLIException(str(e))
def list_services(): """List all services and statuses.""" try: data = [] svcs = services.get() llen = len(sorted(svcs, key=lambda x: len(x.name))[-1].name) for x in svcs: data.append( click.style('{name: <{fill}}'.format(name=x.name, fill=llen + 3), fg="white", bold=True) + click.style(x.state.capitalize(), fg="green" if x.state == "running" else "red") + " " + click.style("Enabled" if x.enabled else "Disabled", fg="green" if x.enabled else "red")) click.echo_via_pager("\n".join(data)) except Exception as e: raise CLIException(str(e))
def _install(id, load=True, cry=True): """ Utility function to download and install arkOS app packages. :param str id: ID of arkOS app to install :param bool load: Load the app after install? :param bool cry: Raise exception on dependency install failure? """ app_dir = config.get("apps", "app_dir") # Download and extract the app source package api_url = "https://{0}/api/v1/apps/{1}" data = api(api_url.format(config.get("general", "repo_server"), id), returns="raw", crit=True) path = os.path.join(app_dir, "{0}.tar.gz".format(id)) with open(path, "wb") as f: f.write(data) with tarfile.open(path, "r:gz") as t: t.extractall(app_dir) os.unlink(path) # Read the app's metadata and create an object with open(os.path.join(app_dir, id, "manifest.json")) as f: data = json.loads(f.read()) app = get(id) for x in data: setattr(app, x, data[x]) app.upgradable = "" app.installed = True for x in app.services: if x.get("type") == "system" and x.get("binary") \ and not x.get("ignore_on_install"): s = services.get(x["binary"]) if s: s.enable() if s.state != "running": try: s.start() except services.ActionError as e: logger.warning( "Apps", "{0} could not be automatically started." .format(s.name)) if load: app.load(cry=cry)
def add_share(self): """Add a share.""" config = configparser.ConfigParser() config.read(["/etc/afp.conf"]) if config.has_section(self.id): raise errors.InvalidConfigError( "Share already present with this name") if not os.path.exists(self.path): os.makedirs(self.path) config.add_section(self.id) config.set(self.id, "path", self.path) config.set(self.id, "read only", "yes" if self.readonly else "no") if self.valid_users: config.set(self.id, "valid users", " ".join(self.valid_users)) config.set("Global", "ldap_server", "localhost") config.set("Global", "ldap_auth_method", "none") config.set("Global", "ldap_userbase", "ou=users,dc=arkos-servers,dc=org") with open("/etc/afp.conf", "w") as f: config.write(f) svc = services.get("netatalk") if svc and svc.state: svc.restart()
def on_load(app): if app.id != "syncthing": return if not users.get_system("syncthing"): u = users.SystemUser('syncthing') u.add() u = users.get_system('syncthing') if not os.path.exists("/home/syncthing"): os.makedirs("/home/syncthing") os.chown("/home/syncthing", u.uid, 0o100) config_path = "/home/syncthing/.config/syncthing/config.xml" s = services.get("syncthing@syncthing") if not os.path.exists(config_path) and s.state != "running": s.restart() count = 0 while count < 5: if not os.path.exists(config_path): time.sleep(5) count += 1 else: break if not os.path.exists(config_path): raise Exception("Syncthing taking too long to generate config," " try again later")
def post_remove(self): services.get(self.id).remove()
def delete(self, id): svc = services.get(id) if id and not svc: abort(404) svc.remove() return Response(status=204)
def php_reload(): try: s = services.get("php-fpm") s.restart() except services.ActionError: pass
def remove_domain(domain, reload=True): # Removes a system domain from XMPP use if os.path.exists("/etc/prosody/conf.d/%s.cfg.lua" % domain.name): os.unlink("/etc/prosody/conf.d/%s.cfg.lua" % domain.name) if reload: services.get("prosody").restart()
def is_running(): s = services.get("radicale") if s: return s.state=="running" return False