def start_transient_unit(cmd="/bin/sleep 15", delay_in_seconds=5): a_cmd = [c.encode() for c in shlex.split(cmd)] random_unit_name = "myservice.{r}.{t}".format(r=random.randint(0, 100), t=time.time()) timer_unit_name = "{}.timer".format(random_unit_name).encode() service_unit_name = "{}.service".format(random_unit_name).encode() timer_unit_properties = { b"Description": b"Example of transient timer unit", b"TimersMonotonic": [(b"OnBootSec", delay_in_seconds * 1000000)], b"RemainAfterElapse": False, } service_unit_properties = { b"Description": b"Example of transient unit", b"ExecStart": [(a_cmd[0], a_cmd, False)], b"RemainAfterExit": True, } with Manager() as manager: manager.Manager.StartTransientUnit( timer_unit_name, b"fail", timer_unit_properties, [(service_unit_name, service_unit_properties)], ) print("started {} as timer and service".format(random_unit_name))
def start_transient_unit(cmd='/bin/sleep 15'): a_cmd = [c.encode() for c in shlex.split(cmd)] random_unit_name = 'myservice.{r}.{t}.service'.format( r=random.randint(0, 100), t=time.time()).encode() unit = { b'Description': b'Example of transient unit', b'ExecStart': [(a_cmd[0], a_cmd, False)], b'RemainAfterExit': True} with Manager() as manager: manager.Manager.StartTransientUnit( random_unit_name, b'fail', unit) with Unit(random_unit_name) as unit: while True: print( 'service `{cmd}` (name={random_unit_name}) has MainPID ' '{unit.Service.MainPID}'.format(**locals())) if unit.Service.MainPID == 0: print( 'service finished with ' '{unit.Service.ExecMainStatus}/{unit.Service.Result} ' 'will stop it and then... bye'.format(**locals())) unit.Unit.Stop(b'replace') break print('service still runing, sleeping by 5 seconds') time.sleep(5)
def read_services_dbus(self): services = {} with Manager() as manager: for _unit in manager.Manager.ListUnits(): idx = _unit[0].find(b"maverick-") if idx == -1: continue service_name = _unit[0][9:-8].decode() category = None if "@" in service_name: category = service_name.split("@")[-1].strip() unit = Unit(_unit[0]) services[unit.path] = { "unit": unit, "path": unit.path, "category_name": category, "category_display_name": category, "command": _unit[0][0:-8].decode(), "service_name": _unit[0][9:-8].decode(), "service_display_name": _unit[1].decode(), "last_update": int(time.time()), "enabled": None, "running": True if _unit[4] == b"running" else False, } for service in services: application_log.debug(f"Adding service: {services[service]}") return services
def start_transient_unit(cmd="/bin/sleep 15"): a_cmd = [c.encode() for c in shlex.split(cmd)] random_unit_name = "myservice.{r}.{t}.service".format( r=random.randint(0, 100), t=time.time()).encode() unit = { b"Description": b"Example of transient unit", b"ExecStart": [(a_cmd[0], a_cmd, False)], b"RemainAfterExit": True, } with Manager() as manager: manager.Manager.StartTransientUnit(random_unit_name, b"fail", unit) with Unit(random_unit_name) as unit: while True: print("service `{cmd}` (name={random_unit_name}) has MainPID " "{unit.Service.MainPID}".format(**locals())) if unit.Service.MainPID == 0: print("service finished with " "{unit.Service.ExecMainStatus}/{unit.Service.Result} " "will stop it and then... bye".format(**locals())) unit.Unit.Stop(b"replace") break print("service still runing, sleeping by 5 seconds") time.sleep(5)
def start_transient_unit(cmd="/bin/sleep 15"): a_cmd = [c.encode() for c in shlex.split(cmd)] random_unit_name = "myservice.{r}.{t}.service".format( r=random.randint(0, 100), t=time.time()).encode() unit = { b"Description": b"Example of transient unit", b"ExecStart": [(a_cmd[0], a_cmd, False)], b"RemainAfterExit": True, } # if we need interactive prompts for passwords, we can create our own DBus object. # if we dont need interactive, we would just do `with Manager() as manager:`. with DBus(interactive=True) as bus, Manager(bus=bus) as manager: manager.Manager.StartTransientUnit(random_unit_name, b"fail", unit) with Unit(random_unit_name, bus=bus) as unit: while True: print("service `{cmd}` (name={random_unit_name}) has MainPID " "{unit.Service.MainPID}".format(**locals())) if unit.Service.MainPID == 0: print( "service finished with " "{unit.Service.ExecMainStatus}/{unit.Service.Result} " "will stop it and then... bye".format(**locals())) unit.Unit.Stop(b"replace") break print("service still running, sleeping by 5 seconds") time.sleep(5)
class ServiceController(object): """ A simple wrapper around pystemd to manage systemd services """ manager = Manager(_autoload=True) def __init__(self, unit): """ param: unit: a systemd unit name (ie str2str_tcp.service...) """ self.unit = Unit(bytes(unit, 'utf-8'), _autoload=True) def isActive(self): if self.unit.Unit.ActiveState == b'active': return True elif self.unit.Unit.ActiveState == b'activating': #TODO manage this transitionnal state differently return True else: return False def get_nrestart(self): """ Get the number of restarts since the last service startup """ return self.unit.Service.NRestarts def get_result(self): """ Get the service return status. success => it's ok exit-code => str2str doesn't start successfully We can read a success between the startup and the first error """ return self.unit.Service.Result.decode() def getUser(self): return self.unit.Service.User.decode() def status(self): """ get the unit status: auto-restart: the service will restart later start: the service is starting running; the service is running """ return (self.unit.Unit.SubState).decode() def start(self): self.manager.Manager.EnableUnitFiles(self.unit.Unit.Names, False, True) return self.unit.Unit.Start(b'replace') def stop(self): self.manager.Manager.DisableUnitFiles(self.unit.Unit.Names, False) return self.unit.Unit.Stop(b'replace') def restart(self): return self.unit.Unit.Restart(b'replace')
def list_units(): with Manager() as manager: print("Version", manager.Manager.Version) print("Architecture", manager.Manager.Architecture) # List Units for unit, state in manager.Manager.ListUnitFiles(): print(" {} is {}".format(unit, state))
def start_webserver(listen_stream="0.0.0.0:7042",): a_cmd = [ "/usr/bin/python3", "-c", textwrap.dedent( """ import socket with socket.fromfd(3, socket.AF_INET, socket.SOCK_STREAM) as s: conn, addr = s.accept() with conn: print(f"got connection from {addr}") while True: data = conn.recv(1024) if not data: break print(data) conn.sendall(data) """ ), ] random_unit_name = "myservice.{r}.{t}".format( r=random.randint(0, 100), t=time.time() ) socket_unit_name = "{}.socket".format(random_unit_name).encode() service_unit_name = "{}.service".format(random_unit_name).encode() socket_unit_properties = { b"Description": b"Example of transient socket unit", b"Listen": [("Stream", listen_stream)], } service_unit_properties = { b"Description": b"Example of transient unit", b"ExecStart": [(a_cmd[0], a_cmd, False)], b"RemainAfterExit": True, } with Manager() as manager: manager.Manager.StartTransientUnit( socket_unit_name, b"fail", socket_unit_properties, [(service_unit_name, service_unit_properties)], ) print("started {} as socket and service".format(random_unit_name))
def set_persistence(unit: Unit, enable: bool = True) -> None: """Set the Persistence of a systemd Unit. Args: unit (Unit): A systemd Unit object, already loaded. enable (bool, optional): Wether to enable or disable the Unit. Defaults to True. """ with Manager() as mgr, perms.run_as(0, 0): if enable: # Enable Unit file named "x.service", not in runtime (persistently), and force link = mgr.Manager.EnableUnitFiles([unit.Unit.Id], False, True)[1] if link: print(f"Linked Unit: '{link[0][1].decode()}'.") else: raise ValueError("Unit already linked") else: link = mgr.Manager.DisableUnitFiles([unit.Unit.Id], False) if link: print(f"Unlinked Unit: '{link[0][1].decode()}'.") else: raise ValueError("Unit already unlinked")
class ServiceController(object): """ A simple wrapper around pystemd to manage systemd services """ manager = Manager(_autoload=True) def __init__(self, unit): """ param: unit: a systemd unit name (ie str2str_tcp.service...) """ self.unit = Unit(bytes(unit, 'utf-8'), _autoload=True) def isActive(self): if self.unit.Unit.ActiveState == b'active': return True elif self.unit.Unit.ActiveState == b'activating': #TODO manage this transitionnal state differently return True else: return False def getUser(self): return self.unit.Service.User.decode() def status(self): return (self.unit.Unit.SubState).decode() def start(self): self.manager.Manager.EnableUnitFiles(self.unit.Unit.Names, False, True) return self.unit.Unit.Start(b'replace') def stop(self): self.manager.Manager.DisableUnitFiles(self.unit.Unit.Names, False) return self.unit.Unit.Stop(b'replace') def restart(self): return self.unit.Unit.Restart(b'replace')
class McUnit: """ A class to represent a Unit of the minecraft service. The factory function discover_units expects to find installations of Minecraft Server in folders under /home/minecraft/MinecraftServers and returns a list of McUnit """ repr_format = "{:3.2s}{:20.19s}{:11.10s}{:8.7s}{:9.8s}{:10.9s}{:15.14s}{:4.4s}" heading = repr_format.format("No", "Name", "State", "SubSt", "Auto Start", "GameMode", "World", "Wlds") config_name = "server.properties" # keep a dbus and a manager for the lifetime of the class/app dbus = DBus(user_mode=True) dbus.open() manager = Manager(bus=dbus) manager.load() def __init__(self, name, unit, unit_name, num): self.name = name self.unit = unit self.unit_name = unit_name self.num = num self.config_path = Config.mc_root / name / self.config_name self.properties = Properties(self.config_path) self.properties.read() self.world = self.properties["level-name"] self.mode = self.properties["gamemode"] self.worlds = [ f.name for f in (Config.mc_root / name).iterdir() if f.name not in Config.non_worlds and f.is_dir() ] @classmethod def discover_units(cls): # factory function that queries systemd files and returns a list # of McUints (1 systemd unit per Minecraft installation) mc_installs = list(Config.mc_root.glob("*")) mc_names = [ str(mc.name) for mc in mc_installs if not str(mc.name).startswith(".") ] units = [] for i, mc_name in enumerate(mc_names): unit_name = Config.unit_name_format.format(mc_name) unit = Unit(unit_name.encode("utf8"), bus=cls.dbus) unit.load() units.append(McUnit(mc_name, unit, unit_name, i)) return units def __repr__(self): state = self.unit.Unit.ActiveState.decode("utf8") sub_state = self.unit.Unit.SubState.decode("utf8") enabled = self.manager.Manager.GetUnitFileState( self.unit_name).decode() return self.repr_format.format(str(self.num), self.name, state, sub_state, enabled, self.mode, self.world, str(len(self.worlds))) def get_world_path(self, world_num): if not len(self.worlds) >= world_num >= 0: raise ValueError(f"world number {world_num} does not exist") return Config.mc_root / self.name / self.worlds[world_num] def set_world(self, world_num): self.properties.read() if not len(self.worlds) >= world_num >= 0: raise ValueError(f"world number {world_num} does not exist") self.properties["level-name"] = self.worlds[world_num] self.properties.write() self.world = self.worlds[world_num] def start(self): print(f"Starting {self.name} ...") self.unit.Start(b"replace") def stop(self): print(f"Stopping {self.name} ...") self.unit.Stop(b"replace") # wait for the service to complete termination while self.running: sleep(.2) def restart(self): # unit.Restart does not work for some reason self.stop() self.start() def enable(self): self.manager.Manager.EnableUnitFiles([self.unit_name], False, False) def disable(self): self.manager.Manager.DisableUnitFiles([self.unit_name], False) def console(self): cmd = Config.screen_cmd_format.format(self.name) os.system(cmd) @property def running(self): state = self.unit.Unit.ActiveState.decode("utf8") return state != "inactive" actions = { "s": start, "k": stop, "e": enable, "d": disable, "r": restart, "c": console, }