Compare commits

...
Author SHA1 Message Date
Richard Alpe 0ae395cff8 hack: refactor software from cli-pretty to show
Signed-off-by: Richard Alpe <richard@bit42.se>
2025-05-06 16:33:48 +02:00
Richard Alpe ab8b174eff confd: augment new services container to ietf-system
This patch adds operational data support for system services. The
data is in a generic format but is intended to be able to represent
finit information (initctl) nicely.

The reason for augmenting this to ietf-system and not to
infix-services is that we consider this generic system information
which is totally disconnected from what ever services infix might
provide.

In this first state we only support pid, name, description and state.
Making the data look something like:

  "infix-system:services": {
    "service": [
      {
        "pid": 1185,
        "name": "udevd",
        "status": "running",
        "description": "Device event daemon (udev)"
      }]

Signed-off-by: Richard Alpe <richard@bit42.se>
2025-05-06 16:29:03 +02:00
12 changed files with 900 additions and 142 deletions
+1 -1
View File
@@ -30,7 +30,7 @@ MODULES=(
"infix-dhcp-client@2025-01-29.yang"
"infix-dhcp-server@2025-01-29.yang"
"infix-meta@2024-10-18.yang"
"infix-system@2025-01-25.yang"
"infix-system@2025-04-29.yang"
"infix-services@2024-12-03.yang"
"ieee802-ethernet-interface@2019-06-21.yang"
"infix-ethernet-interface@2024-02-27.yang"
+98
View File
@@ -28,6 +28,11 @@ module infix-system {
contact "kernelkit@googlegroups.com";
description "Infix augments and deviations to ietf-system.";
revision 2025-04-29 {
description "Add services status.";
reference "internal";
}
revision 2025-01-25 {
description "Add DNS resolver status.";
reference "internal";
@@ -412,6 +417,99 @@ module infix-system {
}
}
}
container services {
description "List of monitored system services (processes)";
config false;
list service {
key "pid";
description "Tracked system service processes.";
leaf pid {
type uint32;
description "Process ID (PID) of the service.";
}
leaf name {
type string;
description "Name of the service or process.";
}
leaf description {
type string;
description
"Short description of the service.";
}
leaf status {
type enumeration {
enum halted {
description "Service is halted.";
}
enum missing {
description "Service files or dependencies are missing.";
}
enum crashed {
description "Service has crashed.";
}
enum stopped {
description "Service has been manually stopped.";
}
enum busy {
description "Service is busy.";
}
enum restart {
description "Service is restarting.";
}
enum conflict {
description "Service has a conflict preventing start.";
}
enum unknown {
description "Service is in an unknown state.";
}
enum done {
description "Service task has completed.";
}
enum failed {
description "Service task has failed.";
}
enum active {
description "Run/task type service is active.";
}
enum stopping {
description "Service is in the process of stopping.";
}
enum teardown {
description "Service is performing teardown operations.";
}
enum setup {
description "Service is setting up.";
}
enum cleanup {
description "Service is cleaning up.";
}
enum paused {
description "Service is paused.";
}
enum waiting {
description "Service is waiting for conditions.";
}
enum starting {
description "Service is starting.";
}
enum running {
description "Service is running.";
}
enum dead {
description "Service process is dead.";
}
}
description
"Detailed current status of the process.";
}
}
}
}
deviation "/sys:system/sys:hostname" {
+45
View File
@@ -0,0 +1,45 @@
class JsonData:
@staticmethod
def get(default, indata, *args):
data = indata
for arg in args:
if arg in data:
data = data.get(arg)
else:
return default
return data
class Decore():
@staticmethod
def decorate(sgr, txt, restore="0"):
return f"\033[{sgr}m{txt}\033[{restore}m"
@staticmethod
def invert(txt):
return Decore.decorate("7", txt)
@staticmethod
def bold(txt):
return Decore.decorate("1", txt)
@staticmethod
def red(txt):
return Decore.decorate("31", txt, "39")
@staticmethod
def green(txt):
return Decore.decorate("32", txt, "39")
@staticmethod
def yellow(txt):
return Decore.decorate("33", txt, "39")
@staticmethod
def underline(txt):
return Decore.decorate("4", txt, "24")
@staticmethod
def gray_bg(txt):
return Decore.decorate("100", txt)
+111
View File
@@ -0,0 +1,111 @@
import json
from common import Decore
from common import JsonData
from dataclasses import dataclass
class System:
def __init__(self, cfg):
self.services = self.Services(cfg)
self.software = self.Software(cfg)
class Services:
def __init__(self, cfg):
self.cfg = cfg
def get(self):
return self.cfg.run("/ietf-system:system-state/infix-system:services")
def pretty(self):
data = self.get()
return data if data else {}
class Software:
class Slot:
@dataclass
class Pad:
name: int = 10
state: int = 10
version: int = 23
date: int = 25
hash: int = 64
def __init__(self, data, version, date): # TODO: why is slot version and date on top level?
self.pad = self.Pad()
self.name = data.get('bootname', '')
self.size = data.get('size', '')
self.type = data.get('class', '')
self.hash = data.get('sha256', '')
self.state = data.get('state', '')
self.version = version # TODO
self.date = version # TODO
def print_detailed(self):
"""Detailed information about one bundle"""
print(f"Name : {self.name}")
print(f"State : {self.state}")
print(f"Version : {self.version}")
print(f"Size : {self.size}")
print(f"SHA-256 : {self.hash}")
print(f"Installed : {self.date}")
@classmethod
def print_hdr(cls):
pad = cls.Pad()
hdr = (f"{'NAME':<{pad.name}}"
f"{'STATE':<{pad.state}}"
f"{'VERSION':<{pad.version}}"
f"{'DATE':<{pad.date}}")
print(Decore.invert(hdr))
def print_slot_row(self):
if self.type != "rootfs":
return
"""Brief information about one bundle"""
row = f"{self.name:<{self.pad.name}}"
row += f"{self.state:<{self.pad.state}}"
row += f"{self.version:<{self.pad.version}}"
row += f"{self.date:<{self.pad.date}}"
print(row)
def __init__(self, cfg):
self.cfg = cfg
self.data = self.get()
self.slots = self.data.get("slot", [])
self.version = JsonData.get('', self.data, 'bundle', 'version')
self.date = JsonData.get('', self.data, 'installed', 'datetime')
self.boot_order = self.data.get("boot-order", ["Unknown"])
def get(self):
return self.cfg.run("/ietf-system:system-state/infix-system:software")
def pprint(self):
software = self.data
print(Decore.invert("BOOT ORDER"))
order = " ".join(boot.strip() for boot in self.boot_order)
print(f"{order}\n")
self.Slot.print_hdr()
for _slot in reversed(self.slots):
slot = self.Slot(_slot, self.version, self.date)
slot.print_slot_row()
def pprint_slot(self, name):
match = next((slot for slot in self.slots if slot.get("bootname") == name), None)
if match:
slot = self.Slot(match, self.version, self.date)
slot.print_detailed()
def show(self, args):
if self.cfg.raw_output:
print(json.dumps(self.data, indent=2))
return
if args:
self.pprint_slot(args[0])
else:
self.pprint()
+23 -54
View File
@@ -1,35 +1,17 @@
#!/usr/bin/env python3
import re
import subprocess
import json
from typing import List
import os
import shlex
import argparse
import subprocess
from sysrepocfg import Sysrepocfg
from modules.system import System
RAW_OUTPUT = False
def run_sysrepocfg(xpath: str) -> dict:
if not isinstance(xpath, str) or not xpath.startswith("/"):
print("Invalid XPATH. It must be a valid string starting with '/'.")
return {}
safe_xpath = shlex.quote(xpath)
try:
result = subprocess.run([
"sysrepocfg", "-f", "json", "-X", "-d", "operational", "-x", safe_xpath
], capture_output=True, text=True, check=True)
json_data = json.loads(result.stdout)
return json_data
except subprocess.CalledProcessError as e:
print(f"Error running sysrepocfg: {e}")
return {}
except json.JSONDecodeError as e:
print(f"Error parsing JSON output: {e}")
return {}
def cli_pretty(json_data: dict, command: str, *args: str):
if not command or not all(isinstance(arg, str) for arg in args):
print("Invalid command or arguments. All arguments must be strings.")
@@ -47,7 +29,7 @@ def cli_pretty(json_data: dict, command: str, *args: str):
print(f"Error running cli-pretty: {e}")
def dhcp(args: List[str]) -> None:
data = run_sysrepocfg("/infix-dhcp-server:dhcp-server")
data = Sysrepocfg.run("/infix-dhcp-server:dhcp-server")
if not data:
print("No interface data retrieved.")
return
@@ -58,7 +40,7 @@ def dhcp(args: List[str]) -> None:
cli_pretty(data, "show-dhcp-server")
def hardware(args: List[str]) -> None:
data = run_sysrepocfg("/ietf-hardware:hardware")
data = Sysrepocfg.run("/ietf-hardware:hardware")
if not data:
print("No hardware data retrieved.")
return
@@ -69,7 +51,7 @@ def hardware(args: List[str]) -> None:
cli_pretty(data, "show-hardware")
def ntp(args: List[str]) -> None:
data = run_sysrepocfg("/system-state/ntp")
data = Sysrepocfg.run("/system-state/ntp")
if not data:
print("No ntp data retrieved.")
return
@@ -90,7 +72,7 @@ def is_valid_interface_name(interface_name: str) -> bool:
return bool(re.match(pattern, interface_name))
def interface(args: List[str]) -> None:
data = run_sysrepocfg("/ietf-interfaces:interfaces")
data = Sysrepocfg.run("/ietf-interfaces:interfaces")
if not data:
print("No interface data retrieved.")
return
@@ -111,7 +93,7 @@ def interface(args: List[str]) -> None:
print("Too many arguments provided. Only one interface name is expected.")
def stp(args: List[str]) -> None:
data = run_sysrepocfg("/ietf-interfaces:interfaces")
data = Sysrepocfg.run("/ietf-interfaces:interfaces")
if not data:
print("No interface data retrieved.")
return
@@ -121,31 +103,14 @@ def stp(args: List[str]) -> None:
return
cli_pretty(data, "show-bridge-stp")
def software(args: List[str]) -> None:
data = run_sysrepocfg("/ietf-system:system-state/infix-system:software")
if not data:
print("No software data retrieved.")
return
if RAW_OUTPUT:
print(json.dumps(data, indent=2))
return
if len(args) == 0 or not args[0]: # Treat "" as no arg.
cli_pretty(data, "show-software")
elif len(args) == 1:
name = args[0]
if name not in ("primary", "secondary"):
print(f"Invalid software name: {name}")
return
cli_pretty(data, f"show-software", "-n", name)
else:
print("Too many arguments provided. Only one name is expected.")
def software(cfg : Sysrepocfg, args: List[str]) -> None:
system = System(cfg)
system.software.show(args)
def routes(args: List[str]):
ip_version = args[0] if args and args[0] in ["ipv4", "ipv6"] else "ipv4"
data = run_sysrepocfg("/ietf-routing:routing/ribs")
data = Sysrepocfg.run("/ietf-routing:routing/ribs")
if not data:
print("No route data retrieved.")
return
@@ -155,14 +120,14 @@ def routes(args: List[str]):
cli_pretty(data, "show-routing-table", "-i", ip_version)
def execute_command(command: str, args: List[str]):
def execute_legacy_non_class_command(command: str, args: List[str]):
command_mapping = {
'dhcp': dhcp,
'hardware': hardware,
'interface': interface,
'ntp': ntp,
'routes': routes,
'software' : software,
'stp': stp,
}
@@ -171,7 +136,6 @@ def execute_command(command: str, args: List[str]):
else:
print(f"Unknown command: {command}")
def main():
global RAW_OUTPUT
@@ -179,11 +143,16 @@ def main():
parser.add_argument('command', help="Command to execute")
parser.add_argument('args', nargs=argparse.REMAINDER, help="Additional arguments for the command")
parser.add_argument('-r', '--raw', action='store_true', help="Print raw JSON output from Sysrepo")
parser.add_argument( "-f", "--datafile", default=None, type=str, help="Path to the input JSON data file")
args = parser.parse_args()
RAW_OUTPUT = args.raw
execute_command(args.command, args.args)
RAW_OUTPUT = args.raw
cfg = Sysrepocfg(args.datafile, args.raw)
if args.command == "software":
software(cfg, args.args)
else:
execute_legacy_non_class_command(args.command, args.args)
if __name__ == "__main__":
+59
View File
@@ -0,0 +1,59 @@
import shlex
import sys
import subprocess
import json
class Sysrepocfg:
def __init__(self, data_file=None, raw=False):
self.data_file = data_file
self.raw_output = raw
def _run(self, safe_xpath):
try:
result = subprocess.run([
"sysrepocfg", "-f", "json", "-X", "-d", "operational", "-x", safe_xpath
], capture_output=True, text=True, check=True)
data = json.loads(result.stdout)
if not data:
sys.exit(f"Error, no data from sysrepo")
return data
except subprocess.CalledProcessError as e:
sys.exit(f"Error, running sysrepocfg: {e}")
except json.JSONDecodeError as e:
sys.exit(f"Error, parsing JSON output: {e}")
def _run_emulator(self, xpath):
"""
Emulates `sysrepocfg -X -x <xpath>` by extracting a subtree from the JSON file.
:param json_file: Path to the local JSON file containing sysrepo-style data.
:param xpath: YANG-style XPath (e.g., '/ietf-interfaces:interfaces').
:return: Extracted subtree or None if not found.
"""
with open(self.data_file, 'r') as f:
data = json.load(f)
path_parts = xpath.strip('/').split('/')
node = data
for part in path_parts:
if isinstance(node, dict) and part in node:
node = node[part]
else:
return None # Path doesn't exist
return node
def run(self, xpath: str) -> dict:
if not isinstance(xpath, str) or not xpath.startswith("/"):
print("Invalid XPATH. It must be a valid string starting with '/'.")
return {}
safe_xpath = shlex.quote(xpath)
if self.data_file is not None:
return self._run_emulator(safe_xpath)
return self._run(safe_xpath)
-86
View File
@@ -49,14 +49,6 @@ class PadRoute:
else:
raise ValueError(f"unknown IP version: {ipv}")
class PadSoftware:
name = 10
date = 25
hash = 64
state = 10
version = 23
class PadDhcpServer:
ip = 17
mac = 19
@@ -287,40 +279,6 @@ class Route:
row += f"{hop:<{PadRoute.next_hop}} "
print(row)
class Software:
"""Software bundle class """
def __init__(self, data):
self.data = data
self.name = data.get('bootname', '')
self.size = data.get('size', '')
self.type = data.get('class', '')
self.hash = data.get('sha256', '')
self.state = data.get('state', '')
self.version = get_json_data('', self.data, 'bundle', 'version')
self.date = get_json_data('', self.data, 'installed', 'datetime')
def is_rootfs(self):
"""True if bundle type is 'rootfs'"""
return self.type == "rootfs"
def print(self):
"""Brief information about one bundle"""
row = f"{self.name:<{PadSoftware.name}}"
row += f"{self.state:<{PadSoftware.state}}"
row += f"{self.version:<{PadSoftware.version}}"
row += f"{self.date:<{PadSoftware.date}}"
print(row)
def detail(self):
"""Detailed information about one bundle"""
print(f"Name : {self.name}")
print(f"State : {self.state}")
print(f"Version : {self.version}")
print(f"Size : {self.size}")
print(f"SHA-256 : {self.hash}")
print(f"Installed : {self.date}")
class USBport:
def __init__(self, data):
self.data = data
@@ -1159,45 +1117,6 @@ def show_routing_table(json, ip):
route = Route(r, ip)
route.print()
def find_slot(_slots, name):
for _slot in [Software(data) for data in _slots]:
if _slot.name == name:
return _slot
return False
def show_software(json, name):
if not json.get("ietf-system:system-state", "infix-system:software"):
print("Error, cannot find infix-system:software")
sys.exit(1)
software = get_json_data({}, json, 'ietf-system:system-state', 'infix-system:software')
slots = software.get("slot")
boot_order = software.get("boot-order", ["Unknown"])
if name:
slot = find_slot(slots, name)
if slot:
slot.detail()
else:
print(Decore.invert("BOOT ORDER"))
order=""
for boot in boot_order:
order+=f"{boot.strip()} "
print(order)
print("")
hdr = (f"{'NAME':<{PadSoftware.name}}"
f"{'STATE':<{PadSoftware.state}}"
f"{'VERSION':<{PadSoftware.version}}"
f"{'DATE':<{PadSoftware.date}}")
print(Decore.invert(hdr))
for _s in reversed(slots):
slot = Software(_s)
if slot.is_rootfs():
slot.print()
def show_hardware(json):
if not json.get("ietf-hardware:hardware"):
print(f"Error, top level \"ietf-hardware:component\" missing")
@@ -1282,9 +1201,6 @@ def main():
parser_show_bridge_stp = subparsers.add_parser('show-bridge-stp',
help='Show spanning tree state')
parser_show_software = subparsers.add_parser('show-software', help='Show software versions')
parser_show_software.add_argument('-n', '--name', help='Slotname')
parser_show_hardware = subparsers.add_parser('show-hardware', help='Show USB ports')
parser_show_ntp_sources = subparsers.add_parser('show-ntp', help='Show NTP sources')
@@ -1301,8 +1217,6 @@ def main():
show_interfaces(json_data, args.name)
elif args.command == "show-routing-table":
show_routing_table(json_data, args.ip)
elif args.command == "show-software":
show_software(json_data, args.name)
elif args.command == "show-bridge-mdb":
show_bridge_mdb(json_data)
elif args.command == "show-bridge-stp":
+19
View File
@@ -177,6 +177,24 @@ def add_platform(out):
insert(out, "platform", platform)
def add_services(out):
data = HOST.run_json(["initctl", "-j"], [])
services = []
for d in data:
if "pid" not in d or "status" not in d or "identity" not in d or "description" not in d:
continue
entry = {
"pid": d["pid"],
"name": d["identity"],
"status": d["status"],
"description": d["description"]
}
services.append(entry)
insert(out, "infix-system:services", "service", services)
def add_software(out):
software = {}
try:
@@ -291,5 +309,6 @@ def operational():
add_dns(out_state)
add_clock(out_state)
add_platform(out_state)
add_services(out_state)
return out
+142
View File
@@ -128,6 +128,148 @@
"os-version": "v25.04.0-rc1-3-g8daf1571-dirty",
"os-release": "v25.04.0-rc1-3-g8daf1571-dirty",
"machine": "x86_64"
},
"infix-system:services": {
"service": [
{
"pid": 1185,
"name": "udevd",
"status": "running",
"description": "Device event daemon (udev)"
},
{
"pid": 2248,
"name": "dbus",
"status": "running",
"description": "D-Bus message bus daemon"
},
{
"pid": 3039,
"name": "confd",
"status": "running",
"description": "Configuration daemon"
},
{
"pid": 3548,
"name": "netopeer",
"status": "running",
"description": "NETCONF server"
},
{
"pid": 2249,
"name": "dnsmasq",
"status": "running",
"description": "DHCP/DNS proxy"
},
{
"pid": 3559,
"name": "tty:hvc0",
"status": "running",
"description": "Getty on hvc0"
},
{
"pid": 2340,
"name": "iitod",
"status": "running",
"description": "LED daemon"
},
{
"pid": 3560,
"name": "klishd",
"status": "running",
"description": "CLI backend daemon"
},
{
"pid": 3617,
"name": "mdns-alias",
"status": "running",
"description": "mDNS alias advertiser "
},
{
"pid": 0,
"name": "mstpd",
"status": "stopped",
"description": "Spanning Tree daemon"
},
{
"pid": 3564,
"name": "rauc",
"status": "running",
"description": "Software update service"
},
{
"pid": 0,
"name": "resolvconf",
"status": "done",
"description": "Update DNS configuration"
},
{
"pid": 3472,
"name": "statd",
"status": "running",
"description": "Status daemon"
},
{
"pid": 3653,
"name": "staticd",
"status": "running",
"description": "Static routing daemon"
},
{
"pid": 2241,
"name": "syslogd",
"status": "running",
"description": "System log daemon"
},
{
"pid": 2242,
"name": "watchdogd",
"status": "running",
"description": "System watchdog daemon"
},
{
"pid": 3587,
"name": "zebra",
"status": "running",
"description": "Zebra routing daemon"
},
{
"pid": 3616,
"name": "mdns",
"status": "running",
"description": "Avahi mDNS-SD daemon"
},
{
"pid": 3618,
"name": "chronyd",
"status": "running",
"description": "Chrony NTP v3/v4 daemon"
},
{
"pid": 3633,
"name": "lldpd",
"status": "running",
"description": "LLDP daemon (IEEE 802.1ab)"
},
{
"pid": 3635,
"name": "nginx",
"status": "running",
"description": "Web server"
},
{
"pid": 3636,
"name": "rousette",
"status": "running",
"description": "RESTCONF server"
},
{
"pid": 3641,
"name": "sshd",
"status": "running",
"description": "OpenSSH daemon"
}
]
}
}
}
@@ -0,0 +1,401 @@
[
{
"identity": "udevd",
"description": "Device event daemon (udev)",
"type": "service",
"forking": false,
"status": "running",
"origin": "/usr/lib/finit/system/10-hotplug.conf",
"command": "udevd $UDEVD_ARGS",
"environment": "-/etc/default/udevd",
"restarts": 0,
"pidfile": "/run/udevd.pid",
"pid": 1185,
"user": "root",
"group": "root",
"uptime": 1390,
"runlevels": [ "S", 1, 2, 3, 4, 5, 7, 8, 9 ]
},
{
"identity": "dbus",
"description": "D-Bus message bus daemon",
"type": "service",
"forking": true,
"status": "running",
"origin": "/etc/finit.d/dbus.conf",
"command": "/usr/bin/dbus-daemon --nofork --system --syslog-only",
"condition": [ "+pid/syslogd" ],
"restarts": 0,
"pidfile": "/run/messagebus.pid",
"pid": 2248,
"user": "root",
"group": "root",
"uptime": 1390,
"runlevels": [ "S", 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
},
{
"identity": "confd",
"description": "Configuration daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/confd.conf",
"command": "sysrepo-plugind -f -p /run/confd.pid -n -v warning",
"condition": [ "+run/bootstrap/success" ],
"restarts": 0,
"pidfile": "/run/confd.pid",
"pid": 3039,
"user": "root",
"group": "root",
"uptime": 1390,
"runlevels": [ "S", 1, 2, 3, 4, 5 ]
},
{
"identity": "netopeer",
"description": "NETCONF server",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/confd.conf",
"command": "netopeer2-server -F -t $CONFD_TIMEOUT -v 1",
"environment": "/etc/default/confd",
"condition": [ "+pid/confd" ],
"restarts": 0,
"pidfile": "/run/netopeer2-server.pid",
"pid": 3548,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 1, 2, 3, 4, 5 ]
},
{
"identity": "dnsmasq",
"description": "DHCP/DNS proxy",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/dnsmasq.conf",
"command": "dnsmasq -k -u root",
"condition": [ "+pid/syslogd" ],
"restarts": 0,
"pidfile": "/run/dnsmasq.pid",
"pid": 2249,
"user": "root",
"group": "root",
"uptime": 1390,
"runlevels": [ "S", 1, 2, 3, 4, 5 ]
},
{
"identity": "tty:hvc0",
"description": "Getty on hvc0",
"type": "tty",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/getty.conf",
"command": "/usr/libexec/finit/getty -p hvc0 0 xterm",
"restarts": 0,
"pidfile": "none",
"pid": 3559,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 1, 2, 3, 4, 5, 7, 8, 9 ]
},
{
"identity": "iitod",
"description": "LED daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/iitod.conf",
"command": "iitod",
"condition": [ "+usr/product", "+usr/led" ],
"restarts": 0,
"pidfile": "none",
"pid": 2340,
"user": "root",
"group": "root",
"uptime": 1390,
"runlevels": [ "S", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
},
{
"identity": "klishd",
"description": "CLI backend daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/klish.conf",
"command": "/usr/bin/klishd -d",
"restarts": 0,
"pidfile": "none",
"pid": 3560,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "mdns-alias",
"description": "mDNS alias advertiser ",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/mdns-alias.conf",
"command": "mdns-alias $MDNS_ALIAS_ARGS",
"environment": "-/etc/default/mdns-alias",
"condition": [ "+service/mdns/running" ],
"restarts": 0,
"pidfile": "none",
"pid": 3617,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "mstpd",
"description": "Spanning Tree daemon",
"type": "service",
"forking": false,
"status": "stopped",
"exit": { "code": 0 },
"origin": "/etc/finit.d/enabled/mstpd.conf",
"command": "mstpd $MSTPD_ARGS",
"environment": "-/etc/default/mstpd",
"starts": 0,
"restarts": 0,
"pidfile": "none",
"pid": 0,
"user": "root",
"group": "root",
"uptime": 0,
"runlevels": [ "S", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
},
{
"identity": "rauc",
"description": "Software update service",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/rauc.conf",
"command": "rauc service $RAUC_ARGS",
"environment": "-/etc/default/rauc",
"condition": [ "+service/dbus/running" ],
"restarts": 0,
"pidfile": "none",
"pid": 3564,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "resolvconf",
"description": "Update DNS configuration",
"type": "task",
"forking": false,
"status": "done",
"exit": { "code": 0 },
"origin": "/etc/finit.d/enabled/resolvconf.conf",
"command": "resolvconf -u",
"condition": [ "+usr/bootstrap" ],
"restarts": 2,
"pidfile": "none",
"pid": 0,
"user": "root",
"group": "root",
"uptime": 0,
"runlevels": [ "S", 1, 2, 3, 4, 5 ]
},
{
"identity": "statd",
"description": "Status daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/statd.conf",
"command": "statd -f -p /run/statd.pid -n",
"condition": [ "+pid/confd" ],
"restarts": 0,
"pidfile": "/run/statd.pid",
"pid": 3472,
"user": "root",
"group": "root",
"uptime": 1389,
"runlevels": [ "S", 1, 2, 3, 4, 5 ]
},
{
"identity": "staticd",
"description": "Static routing daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/staticd.conf",
"command": "staticd -A 127.0.0.1 -u frr -g frr -f /etc/frr/staticd.conf",
"condition": [ "+pid/zebra" ],
"restarts": 0,
"pidfile": "/run/frr/staticd.pid",
"pid": 3653,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "syslogd",
"description": "System log daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/sysklogd.conf",
"command": "syslogd -F $SYSLOGD_ARGS",
"environment": "-/etc/default/sysklogd",
"condition": [ "+run/udevadm:post/success" ],
"restarts": 0,
"pidfile": "/run/syslogd.pid",
"pid": 2241,
"user": "root",
"group": "root",
"uptime": 1390,
"runlevels": [ "S", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
},
{
"identity": "watchdogd",
"description": "System watchdog daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/watchdogd.conf",
"command": "watchdogd -xns $WATCHDOGD_ARGS",
"environment": "-/etc/default/watchdogd",
"restarts": 0,
"pidfile": "/run/watchdogd/pid",
"pid": 2242,
"user": "root",
"group": "root",
"uptime": 1390,
"runlevels": [ "S", 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
},
{
"identity": "zebra",
"description": "Zebra routing daemon",
"type": "service",
"forking": true,
"status": "running",
"origin": "/etc/finit.d/enabled/zebra.conf",
"command": "zebra $ZEBRA_ARGS",
"environment": "-/etc/default/zebra",
"restarts": 0,
"pidfile": "/run/frr/zebra.pid",
"pid": 3587,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "mdns",
"description": "Avahi mDNS-SD daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/avahi.conf",
"command": "avahi-daemon -s $AVAHI_ARGS",
"environment": "-/etc/default/avahi",
"restarts": 0,
"pidfile": "none",
"pid": 3616,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "chronyd",
"description": "Chrony NTP v3/v4 daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/chronyd.conf",
"command": "chronyd -n $CHRONY_ARGS",
"environment": "-/etc/default/chronyd",
"restarts": 0,
"pidfile": "/run/chrony/chronyd.pid",
"pid": 3618,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "lldpd",
"description": "LLDP daemon (IEEE 802.1ab)",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/lldpd.conf",
"command": "lldpd -d $LLDPD_ARGS",
"environment": "-/etc/default/lldpd",
"restarts": 0,
"pidfile": "none",
"pid": 3633,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "nginx",
"description": "Web server",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/nginx.conf",
"command": "nginx -g 'daemon off;' $NGINX_ARGS",
"environment": "-/etc/default/nginx",
"condition": [ "+usr/mkcert" ],
"restarts": 0,
"pidfile": "/run/nginx.pid",
"pid": 3635,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
},
{
"identity": "rousette",
"description": "RESTCONF server",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/restconf.conf",
"command": "rousette --syslog -t $CONFD_TIMEOUT",
"environment": "/etc/default/confd",
"condition": [ "+pid/confd" ],
"restarts": 0,
"pidfile": "none",
"pid": 3636,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 1, 2, 3, 4, 5 ]
},
{
"identity": "sshd",
"description": "OpenSSH daemon",
"type": "service",
"forking": false,
"status": "running",
"origin": "/etc/finit.d/enabled/sshd.conf",
"command": "/usr/sbin/sshd -D $SSHD_OPTS",
"environment": "-/etc/default/sshd",
"condition": [ "+pid/syslogd" ],
"restarts": 0,
"pidfile": "/run/sshd.pid",
"pid": 3641,
"user": "root",
"group": "root",
"uptime": 1388,
"runlevels": [ 2, 3, 4, 5 ]
}
]