Das folgende Python‑Programm erstellt eine einfache, übersichtliche grafische Oberfläche (GUI) unter Linux. Es zeigt alle aktuellen systemd‑Dienste an und bietet Schaltflächen zum Aktualisieren, Starten, Stoppen, Neustarten sowie zum Aktivieren/Deaktivieren von Diensten.
- Die Liste der Dienste wird über den Befehl
systemctl --type=service --all --no-legend --plaineingelesen und zuverlässig ausgewertet. - Jede Zeile enthält die Spalten: Unit, Load, Active, Sub und Description.
- Aktionen wie Start, Stop, Restart, Enable oder Disable benötigen in der Regel Administratorrechte. Das Programm versucht dafür pkexec zu verwenden (grafischer Authentifizierungsdialog). Falls pkexec nicht vorhanden ist, wird auf sudo im Terminal zurückgegriffen.
Funktionsumfang der Oberfläche
- Filterfeld: Dienste können nach Namen, Status oder Beschreibung durchsucht werden.
- Aktualisieren‑Button: Lädt die Liste neu.
- Dienstliste: Anzeige in einer Tabelle mit Spalten für Unit, Status und Beschreibung.
- Aktionsbuttons: Start, Stop, Restart, Enable, Disable – je nach aktuellem Status aktiv oder deaktiviert.
- Statuszeile: Zeigt Rückmeldungen zu ausgeführten Aktionen an.
Installation und Nutzung
- Abhängigkeiten:
- Starten:
- Datei speichern als
systemd_gui.py - Ausführen mit:
python3 systemd_gui.py
- Datei speichern als
#!/usr/bin/env python3
import subprocess
import shlex
import sys
from gi.repository import Gtk, GObject
# Hilfsfunktion: Shell-Befehl ausführen und Ausgabe zurückgeben
def run_cmd(cmd):
try:
out = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT)
return out.decode("utf-8", errors="replace")
except subprocess.CalledProcessError as e:
return e.output.decode("utf-8", errors="replace")
# Liste aller Dienste abrufen
def list_services():
"""
Nutzt `systemctl --type=service --all --no-legend --plain`
und zerlegt die Ausgabe in Spalten.
"""
cmd = "systemctl --type=service --all --no-legend --plain"
output = run_cmd(cmd)
services = []
for line in output.splitlines():
# Zeilen enthalten: UNIT LOAD ACTIVE SUB DESCRIPTION
parts = line.split(None, 4)
if len(parts) < 5:
parts += [""] * (5 - len(parts))
unit, load, active, sub, description = parts
services.append({
"unit": unit,
"load": load,
"active": active,
"sub": sub,
"description": description
})
return services
# Prüfen, ob Dienst aktiviert ist
def service_enabled(unit):
out = run_cmd(f"systemctl is-enabled {shlex.quote(unit)}").strip()
return out == "enabled"
# Prüfen, ob Dienst überhaupt aktivierbar ist (nicht "static")
def service_has_install(unit):
out = run_cmd(f"systemctl show {shlex.quote(unit)} -p CanInstall")
return "CanInstall=yes" in out
# Aktion auf Dienst ausführen (start/stop/restart/enable/disable)
def run_action(unit, action):
base = f"systemctl {action} {shlex.quote(unit)}"
if action in {"start", "stop", "restart", "enable", "disable"}:
# Für Aktionen mit Root-Rechten: pkexec bevorzugt, sonst sudo
pkexec_path = subprocess.run(["which", "pkexec"], capture_output=True, text=True)
if pkexec_path.returncode == 0:
cmd = f"pkexec {base}"
else:
cmd = f"sudo {base}"
else:
cmd = base
return run_cmd(cmd)
# Hauptfenster
class ServiceList(Gtk.Window):
def __init__(self):
super().__init__(title="systemd Dienste")
self.set_default_size(1000, 600)
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
self.add(vbox)
# Suchfeld + Aktualisieren-Button
toolbar = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
vbox.pack_start(toolbar, False, False, 0)
self.search_entry = Gtk.SearchEntry()
self.search_entry.set_placeholder_text("Filter (Unit, Beschreibung, Status)")
self.search_entry.connect("search-changed", self.on_search_changed)
toolbar.pack_start(self.search_entry, True, True, 0)
refresh_btn = Gtk.Button(label="Aktualisieren")
refresh_btn.connect("clicked", self.on_refresh_clicked)
toolbar.pack_start(refresh_btn, False, False, 0)
# Datenmodell für Dienste
self.store = Gtk.ListStore(str, str, str, str, str, str)
# Spalten: unit, load, active, sub, enabled, description
self.filtered = self.store.filter_new()
self.filtered.set_visible_func(self.filter_func)
# Tabellenansicht
self.view = Gtk.TreeView(model=self.filtered)
self.view.get_selection().set_mode(Gtk.SelectionMode.SINGLE)
def add_col(title, idx, expand=False):
renderer = Gtk.CellRendererText()
column = Gtk.TreeViewColumn(title, renderer, text=idx)
column.set_expand(expand)
self.view.append_column(column)
add_col("Unit", 0, True)
add_col("Load", 1)
add_col("Active", 2)
add_col("Sub", 3)
add_col("Enabled", 4)
add_col("Beschreibung", 5, True)
scroller = Gtk.ScrolledWindow()
scroller.add(self.view)
vbox.pack_start(scroller, True, True, 0)
# Aktionsbuttons
action_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
vbox.pack_start(action_box, False, False, 6)
self.start_btn = Gtk.Button(label="Start")
self.stop_btn = Gtk.Button(label="Stop")
self.restart_btn = Gtk.Button(label="Restart")
self.enable_btn = Gtk.Button(label="Enable")
self.disable_btn = Gtk.Button(label="Disable")
for btn in [self.start_btn, self.stop_btn, self.restart_btn, self.enable_btn, self.disable_btn]:
action_box.pack_start(btn, False, False, 0)
btn.set_sensitive(False)
self.start_btn.connect("clicked", self.action_clicked, "start")
self.stop_btn.connect("clicked", self.action_clicked, "stop")
self.restart_btn.connect("clicked", self.action_clicked, "restart")
self.enable_btn.connect("clicked", self.action_clicked, "enable")
self.disable_btn.connect("clicked", self.action_clicked, "disable")
# Auswahländerung überwachen
self.view.get_selection().connect("changed", self.on_selection_changed)
# Statuszeile
self.status = Gtk.Label(xalign=0)
vbox.pack_start(self.status, False, False, 6)
self.populate()
# Dienste laden
def populate(self):
self.store.clear()
services = list_services()
for svc in services:
enabled = "n/a"
try:
if service_has_install(svc["unit"]):
enabled = "enabled" if service_enabled(svc["unit"]) else "disabled"
else:
enabled = "static"
except Exception:
enabled = "unknown"
self.store.append([
svc["unit"],
svc["load"],
svc["active"],
svc["sub"],
enabled,
svc["description"]
])
self.status.set_text(f"{len(services)} Dienste geladen.")
def on_refresh_clicked(self, _btn):
self.populate()
def on_search_changed(self, entry):
self.filtered.refilter()
# Filterfunktion für Suchfeld
def filter_func(self, model, iter, _data=None):
q = self.search_entry.get_text().strip().lower()
if not q:
return True
fields = [model[iter][i] for i in range(6)]
return any(q in (f or "").lower() for f in fields)
# Ausgewählten Dienst ermitteln
def get_selected_unit(self):
sel = self.view.get_selection()
model, treeiter = sel.get_selected()
if treeiter:
return model[treeiter][0], model[treeiter][4], model[treeiter][2]
return None, None, None
# Buttons je nach Status aktivieren/deaktivieren
def on_selection_changed(self, _selection):
unit, enabled, active = self.get_selected_unit()
has_selection = unit is not None
self.start_btn.set_sensitive(has_selection and active != "active")
self.stop_btn.set_sensitive(has_selection and active == "active")
self.restart_btn.set_sensitive(has_selection and active == "active")
self.enable_btn.set_sensitive(has_selection and enabled in {"disabled", "unknown"})
self.disable_btn.set_sensitive(has_selection and enabled == "enabled")
# Aktion ausführen
def action_clicked(self, _btn, action):
unit, _, _ = self.get_selected_unit()
if not unit:
return
self.status.set_text(f"{action} wird ausgeführt: {unit} …")
GObject.idle_add(self.perform_action, unit, action)
def perform_action(self, unit, action):
output = run_action(unit, action)
self.populate()
tail = "\n".join(output.strip().splitlines()[-3:])
if tail:
self.status.set_text(f"{action} {unit}: {tail}")
else:
self.status.set_text(f"{action} {unit}: erledigt.")
# Einstiegspunkt
def main():
try:
app = ServiceList()
app.connect("destroy", Gtk.main_quit)
app.show_all()
Gtk.main()
except Exception as e:
print(f"Fehler: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
main()
© 2025 MaDe-Online

