diff --git a/README.md b/README.md index fada4961..f8c91714 100755 --- a/README.md +++ b/README.md @@ -86,6 +86,7 @@ jc PARSER [OPTIONS] - `--route` enables the `route` parser - `--ss` enables the `ss` parser - `--stat` enables the `stat` parser +- `--systemctl` enables the `systemctl` parser - `--uname` enables the `uname -a` parser - `--uptime` enables the `uptime` parser - `--w` enables the `w` parser @@ -1271,6 +1272,34 @@ $ stat /bin/* | jc --stat -p ... ] ``` +### systemctl +``` +$ systemctl -a | jc --systemctl -p +[ + { + "unit": "proc-sys-fs-binfmt_misc.automount", + "load": "loaded", + "active": "active", + "sub": "waiting", + "description": "Arbitrary Executable File Formats File System Automount Point" + }, + { + "unit": "dev-block-8:2.device", + "load": "loaded", + "active": "active", + "sub": "plugged", + "description": "LVM PV 3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM on /dev/sda2 2" + }, + { + "unit": "dev-cdrom.device", + "load": "loaded", + "active": "active", + "sub": "plugged", + "description": "VMware_Virtual_IDE_CDROM_Drive" + }, + ... +] +``` ### uname -a ``` $ uname -a | jc --uname -p diff --git a/changelog.txt b/changelog.txt index 468491ff..8db8205f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -4,6 +4,7 @@ jc changelog - Add ss parser - Add stat parser - Add /etc/hosts parser +- Add systemctl parser - Add -r and raw=True options. By default, jc will now convert numbers and boolean, if possible, and add other semantic information, while the raw output will keep all values as text and provide a more literal JSON output - Add -q and quiet=True options to suppress warnings to stderr - Add -d option to debug parsing issues diff --git a/docgen.sh b/docgen.sh index a4528626..7f5709fe 100755 --- a/docgen.sh +++ b/docgen.sh @@ -25,6 +25,7 @@ pydocmd simple jc.parsers.ps+ > ../docs/parsers/ps.md pydocmd simple jc.parsers.route+ > ../docs/parsers/route.md pydocmd simple jc.parsers.ss+ > ../docs/parsers/ss.md pydocmd simple jc.parsers.stat+ > ../docs/parsers/stat.md +pydocmd simple jc.parsers.systemctl+ > ../docs/parsers/systemctl.md pydocmd simple jc.parsers.uname+ > ../docs/parsers/uname.md pydocmd simple jc.parsers.uptime+ > ../docs/parsers/uptime.md pydocmd simple jc.parsers.w+ > ../docs/parsers/w.md diff --git a/docs/parsers/systemctl.md b/docs/parsers/systemctl.md new file mode 100644 index 00000000..32682f5f --- /dev/null +++ b/docs/parsers/systemctl.md @@ -0,0 +1,76 @@ +# jc.parsers.systemctl +jc - JSON CLI output utility systemctl Parser + +Usage: + specify --systemctl as the first argument if the piped input is coming from systemctl + +Examples: + + $ systemctl -a | jc --systemctl -p + [ + { + "unit": "proc-sys-fs-binfmt_misc.automount", + "load": "loaded", + "active": "active", + "sub": "waiting", + "description": "Arbitrary Executable File Formats File System Automount Point" + }, + { + "unit": "dev-block-8:2.device", + "load": "loaded", + "active": "active", + "sub": "plugged", + "description": "LVM PV 3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM on /dev/sda2 2" + }, + { + "unit": "dev-cdrom.device", + "load": "loaded", + "active": "active", + "sub": "plugged", + "description": "VMware_Virtual_IDE_CDROM_Drive" + }, + ... + ] + +## process +```python +process(proc_data) +``` + +Final processing to conform to the schema. + +Parameters: + + proc_data: (dictionary) raw structured data to process + +Returns: + + dictionary structured data with the following schema: + + [ + { + "unit": string, + "load": string, + "active": string, + "sub": string, + "description": string + } + ] + +## parse +```python +parse(data, raw=False, quiet=False) +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + +Returns: + + dictionary raw or processed structured data + diff --git a/jc/cli.py b/jc/cli.py index b5f6e40d..70d4b1fb 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -29,6 +29,7 @@ import jc.parsers.ps import jc.parsers.route import jc.parsers.ss import jc.parsers.stat +import jc.parsers.systemctl import jc.parsers.uname import jc.parsers.uptime import jc.parsers.w @@ -66,6 +67,7 @@ def helptext(message): --route route parser --ss ss parser --stat stat parser + --systemctl systemctl parser --uname uname -a parser --uptime uptime parser --w w parser @@ -131,6 +133,7 @@ def main(): '--route': jc.parsers.route.parse, '--ss': jc.parsers.ss.parse, '--stat': jc.parsers.stat.parse, + '--systemctl': jc.parsers.systemctl.parse, '--uname': jc.parsers.uname.parse, '--uptime': jc.parsers.uptime.parse, '--w': jc.parsers.w.parse diff --git a/jc/parsers/systemctl.py b/jc/parsers/systemctl.py new file mode 100644 index 00000000..e3f91c9c --- /dev/null +++ b/jc/parsers/systemctl.py @@ -0,0 +1,109 @@ +"""jc - JSON CLI output utility systemctl Parser + +Usage: + specify --systemctl as the first argument if the piped input is coming from systemctl + +Examples: + + $ systemctl -a | jc --systemctl -p + [ + { + "unit": "proc-sys-fs-binfmt_misc.automount", + "load": "loaded", + "active": "active", + "sub": "waiting", + "description": "Arbitrary Executable File Formats File System Automount Point" + }, + { + "unit": "dev-block-8:2.device", + "load": "loaded", + "active": "active", + "sub": "plugged", + "description": "LVM PV 3klkIj-w1qk-DkJi-0XBJ-y3o7-i2Ac-vHqWBM on /dev/sda2 2" + }, + { + "unit": "dev-cdrom.device", + "load": "loaded", + "active": "active", + "sub": "plugged", + "description": "VMware_Virtual_IDE_CDROM_Drive" + }, + ... + ] +""" +import jc.utils + + +def process(proc_data): + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (dictionary) raw structured data to process + + Returns: + + dictionary structured data with the following schema: + + [ + { + "unit": string, + "load": string, + "active": string, + "sub": string, + "description": string + } + ] + """ + # nothing more to process + return proc_data + + +def parse(data, raw=False, quiet=False): + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) output preprocessed JSON if True + quiet: (boolean) suppress warning messages if True + + Returns: + + dictionary raw or processed structured data + """ + + # compatible options: linux, darwin, cygwin, win32, aix, systemctlbsd + compatible = ['linux'] + + if not quiet: + jc.utils.compatibility(__name__, compatible) + + linedata = data.splitlines() + # Clear any blank lines + linedata = list(filter(None, linedata)) + # clean up non-ascii characters, if any + cleandata = [] + for entry in linedata: + cleandata.append(entry.encode('ascii', errors='ignore').decode()) + + header_text = cleandata[0] + header_list = header_text.lower().split() + + raw_output = [] + + for entry in cleandata[1:]: + if entry.find('LOAD = ') != -1: + break + + else: + entry_list = entry.split(maxsplit=4) + output_line = dict(zip(header_list, entry_list)) + raw_output.append(output_line) + + if raw: + return raw_output + else: + return process(raw_output)