1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-07-13 01:20:24 +02:00

Merge pull request #4 from kellyjonbrazil/dev

Dev v0.9.1
This commit is contained in:
Kelly Brazil
2019-10-23 18:41:55 -07:00
committed by GitHub
8 changed files with 536 additions and 37 deletions

162
README.md
View File

@ -65,8 +65,11 @@ jc [parser] [options]
- `--free` enables the `free` parser - `--free` enables the `free` parser
- `--ifconfig` enables the `ifconfig` parser - `--ifconfig` enables the `ifconfig` parser
- `--iptables` enables the `iptables` parser - `--iptables` enables the `iptables` parser
- `--jobs` enables the `jobs` parser
- `--ls` enables the `ls` parser - `--ls` enables the `ls` parser
- `--lsblk` enables the `lsblk` parser - `--lsblk` enables the `lsblk` parser
- `--lsmod` enables the `lsmod` parser
- `--lsof` enables the `lsof` parser
- `--mount` enables the `mount` parser - `--mount` enables the `mount` parser
- `--netstat` enables the `netstat` parser - `--netstat` enables the `netstat` parser
- `--ps` enables the `ps` parser - `--ps` enables the `ps` parser
@ -549,6 +552,44 @@ $ sudo iptables -vnL -t filter | jc --iptables -p
... ...
] ]
``` ```
### jobs
```
$ jobs -l | jc --jobs -p
[
{
"job_number": 1,
"pid": 14798,
"status": "Running",
"command": "sleep 10000 &"
},
{
"job_number": 2,
"pid": 14799,
"status": "Running",
"command": "sleep 10001 &"
},
{
"job_number": 3,
"pid": 14800,
"status": "Running",
"command": "sleep 10002 &"
},
{
"job_number": 4,
"pid": 14814,
"history": "previous",
"status": "Running",
"command": "sleep 10003 &"
},
{
"job_number": 5,
"pid": 14815,
"history": "current",
"status": "Running",
"command": "sleep 10004 &"
}
]
```
### ls ### ls
``` ```
$ ls -l /bin | jc --ls -p $ ls -l /bin | jc --ls -p
@ -631,6 +672,127 @@ $ lsblk | jc --lsblk -p
} }
] ]
``` ```
### lsmod
```
$ lsmod | jc --lsmod -p
[
{
"Module": "nf_nat_ipv4",
"Size": "14115",
"Used": "1",
"By": [
"iptable_nat"
]
},
{
"Module": "nf_nat",
"Size": "26583",
"Used": "3",
"By": [
"nf_nat_ipv4",
"nf_nat_ipv6",
"nf_nat_masquerade_ipv4"
]
},
{
"Module": "iptable_mangle",
"Size": "12695",
"Used": "1"
},
{
"Module": "iptable_security",
"Size": "12705",
"Used": "1"
},
{
"Module": "iptable_raw",
"Size": "12678",
"Used": "1"
},
{
"Module": "nf_conntrack",
"Size": "139224",
"Used": "7",
"By": [
"nf_nat",
"nf_nat_ipv4",
"nf_nat_ipv6",
"xt_conntrack",
"nf_nat_masquerade_ipv4",
"nf_conntrack_ipv4",
"nf_conntrack_ipv6"
]
},
...
]
```
### lsof
```
$ sudo lsof | jc --lsof -p
[
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "cwd",
"TYPE": "DIR",
"DEVICE": "253,0",
"SIZE/OFF": "224",
"NODE": "64",
"NAME": "/"
},
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "rtd",
"TYPE": "DIR",
"DEVICE": "253,0",
"SIZE/OFF": "224",
"NODE": "64",
"NAME": "/"
},
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "txt",
"TYPE": "REG",
"DEVICE": "253,0",
"SIZE/OFF": "1624520",
"NODE": "50360451",
"NAME": "/usr/lib/systemd/systemd"
},
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "mem",
"TYPE": "REG",
"DEVICE": "253,0",
"SIZE/OFF": "20064",
"NODE": "8146",
"NAME": "/usr/lib64/libuuid.so.1.3.0"
},
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "mem",
"TYPE": "REG",
"DEVICE": "253,0",
"SIZE/OFF": "265600",
"NODE": "8147",
"NAME": "/usr/lib64/libblkid.so.1.1.0"
},
...
]
```
### mount ### mount
``` ```
$ mount | jc --mount -p $ mount | jc --mount -p

View File

@ -1,5 +1,13 @@
jc changelog jc changelog
20191023 v0.9.1
- Add jobs parser
- Add lsof parser
- Add lsmod parser
- No blocking if no piped data
- Better help text
- Clean up iptables parser code
20191022 v0.8.1 20191022 v0.8.1
- Add env parser - Add env parser
- Add df parser - Add df parser

View File

@ -11,8 +11,11 @@ import jc.parsers.env
import jc.parsers.free import jc.parsers.free
import jc.parsers.ifconfig import jc.parsers.ifconfig
import jc.parsers.iptables import jc.parsers.iptables
import jc.parsers.jobs
import jc.parsers.ls import jc.parsers.ls
import jc.parsers.lsblk import jc.parsers.lsblk
import jc.parsers.lsmod
import jc.parsers.lsof
import jc.parsers.mount import jc.parsers.mount
import jc.parsers.netstat import jc.parsers.netstat
import jc.parsers.ps import jc.parsers.ps
@ -20,13 +23,44 @@ import jc.parsers.route
import jc.parsers.uname import jc.parsers.uname
def helptext():
print('Usage: jc [parser] [options]\n', file=sys.stderr)
print('Parsers:', file=sys.stderr)
print(' --df df parser', file=sys.stderr)
print(' --env env parser', file=sys.stderr)
print(' --free free parser', file=sys.stderr)
print(' --ifconfig iconfig parser', file=sys.stderr)
print(' --iptables iptables parser', file=sys.stderr)
print(' --jobs jobs parser', file=sys.stderr)
print(' --ls ls parser', file=sys.stderr)
print(' --lsblk lsblk parser', file=sys.stderr)
print(' --lsmod lsmod parser', file=sys.stderr)
print(' --lsof lsof parser', file=sys.stderr)
print(' --mount mount parser', file=sys.stderr)
print(' --netstat netstat parser', file=sys.stderr)
print(' --ps ps parser', file=sys.stderr)
print(' --route route parser', file=sys.stderr)
print(' --uname uname parser\n', file=sys.stderr)
print('Options:', file=sys.stderr)
print(' -p pretty print output\n', file=sys.stderr)
print('Example:', file=sys.stderr)
print(' ls -al | jc --ls -p\n', file=sys.stderr)
def main(): def main():
if sys.stdin.isatty():
print('jc: missing piped data\n', file=sys.stderr)
helptext()
exit()
data = sys.stdin.read() data = sys.stdin.read()
pretty = False pretty = False
# options
if '-p' in sys.argv: if '-p' in sys.argv:
pretty = True pretty = True
# parsers
if '--df' in sys.argv: if '--df' in sys.argv:
result = jc.parsers.df.parse(data) result = jc.parsers.df.parse(data)
@ -42,12 +76,21 @@ def main():
elif '--iptables' in sys.argv: elif '--iptables' in sys.argv:
result = jc.parsers.iptables.parse(data) result = jc.parsers.iptables.parse(data)
elif '--jobs' in sys.argv:
result = jc.parsers.jobs.parse(data)
elif '--ls' in sys.argv: elif '--ls' in sys.argv:
result = jc.parsers.ls.parse(data) result = jc.parsers.ls.parse(data)
elif '--lsblk' in sys.argv: elif '--lsblk' in sys.argv:
result = jc.parsers.lsblk.parse(data) result = jc.parsers.lsblk.parse(data)
elif '--lsmod' in sys.argv:
result = jc.parsers.lsmod.parse(data)
elif '--lsof' in sys.argv:
result = jc.parsers.lsof.parse(data)
elif '--mount' in sys.argv: elif '--mount' in sys.argv:
result = jc.parsers.mount.parse(data) result = jc.parsers.mount.parse(data)
@ -64,25 +107,8 @@ def main():
result = jc.parsers.uname.parse(data) result = jc.parsers.uname.parse(data)
else: else:
print('jc: missing arguments\n', file=sys.stderr) print('jc: missing or incorrect arguments\n', file=sys.stderr)
print('Usage: jc [parser] [options]\n', file=sys.stderr) helptext()
print('Parsers:', file=sys.stderr)
print(' --df df parser', file=sys.stderr)
print(' --env env parser', file=sys.stderr)
print(' --free free parser', file=sys.stderr)
print(' --ifconfig iconfig parser', file=sys.stderr)
print(' --iptables iptables parser', file=sys.stderr)
print(' --ls ls parser', file=sys.stderr)
print(' --lsblk lsblk parser', file=sys.stderr)
print(' --mount mount parser', file=sys.stderr)
print(' --netstat netstat parser', file=sys.stderr)
print(' --ps ps parser', file=sys.stderr)
print(' --route route parser', file=sys.stderr)
print(' --uname uname parser\n', file=sys.stderr)
print('Options:', file=sys.stderr)
print(' -p pretty print output\n', file=sys.stderr)
print('Example:', file=sys.stderr)
print(' ls -al | jc --ls -p\n', file=sys.stderr)
exit() exit()
# output resulting dictionary as json # output resulting dictionary as json

View File

@ -325,42 +325,40 @@ $ sudo iptables -vnL -t filter | jc --iptables -p
""" """
class state(): def parse(data):
output = [] output = []
chain = {} chain = {}
headers = [] headers = []
def parse(data):
cleandata = data.splitlines() cleandata = data.splitlines()
for line in cleandata: for line in cleandata:
if line.find('Chain') == 0: if line.find('Chain') == 0:
state.output.append(state.chain) output.append(chain)
state.chain = {} chain = {}
state.headers = [] headers = []
parsed_line = line.split() parsed_line = line.split()
state.chain['chain'] = parsed_line[1] chain['chain'] = parsed_line[1]
state.chain['rules'] = [] chain['rules'] = []
continue continue
if line.find('target') == 0 or line.find('pkts') == 1: elif line.find('target') == 0 or line.find('pkts') == 1:
state.headers = [] headers = []
state.headers = [h for h in ' '.join(line.strip().split()).split() if h] headers = [h for h in ' '.join(line.strip().split()).split() if h]
state.headers.append("options") headers.append("options")
continue continue
else: else:
rule = line.split(maxsplit=len(state.headers) - 1) rule = line.split(maxsplit=len(headers) - 1)
temp_rule = dict(zip(state.headers, rule)) temp_rule = dict(zip(headers, rule))
if temp_rule: if temp_rule:
state.chain['rules'].append(temp_rule) chain['rules'].append(temp_rule)
state.output = list(filter(None, state.output)) output = list(filter(None, output))
return state.output return output

108
jc/parsers/jobs.py Normal file
View File

@ -0,0 +1,108 @@
"""jc - JSON CLI output utility jobs Parser
Usage:
specify --jobs as the first argument if the piped input is coming from jobs
Also supports the -l option
Example:
$ jobs -l | jc --jobs -p
[
{
"job_number": 1,
"pid": 14798,
"status": "Running",
"command": "sleep 10000 &"
},
{
"job_number": 2,
"pid": 14799,
"status": "Running",
"command": "sleep 10001 &"
},
{
"job_number": 3,
"pid": 14800,
"status": "Running",
"command": "sleep 10002 &"
},
{
"job_number": 4,
"pid": 14814,
"history": "previous",
"status": "Running",
"command": "sleep 10003 &"
},
{
"job_number": 5,
"pid": 14815,
"history": "current",
"status": "Running",
"command": "sleep 10004 &"
}
]
"""
import string
def parse(data):
output = []
linedata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, linedata))
if cleandata:
for entry in cleandata:
output_line = {}
remainder = []
job_number = ''
pid = ''
job_history = ''
parsed_line = entry.split(maxsplit=2)
# check if -l was used
if parsed_line[1][0] in string.digits:
pid = parsed_line.pop(1)
remainder = parsed_line.pop(1)
job_number = parsed_line.pop(0)
remainder = remainder.split(maxsplit=1)
# rebuild parsed_line
parsed_line = []
for r in remainder:
parsed_line.append(r)
parsed_line.insert(0, job_number)
# check for + or - in first field
if parsed_line[0].find('+') != -1:
job_history = 'current'
parsed_line[0] = parsed_line[0].rstrip('+')
if parsed_line[0].find('-') != -1:
job_history = 'previous'
parsed_line[0] = parsed_line[0].rstrip('-')
# clean up first field
parsed_line[0] = parsed_line[0].lstrip('[').rstrip(']')
# create list of dictionaries
output_line['job_number'] = int(parsed_line[0])
if pid:
output_line['pid'] = int(pid)
if job_history:
output_line['history'] = job_history
output_line['status'] = parsed_line[1]
output_line['command'] = parsed_line[2]
output.append(output_line)
return output

80
jc/parsers/lsmod.py Normal file
View File

@ -0,0 +1,80 @@
"""jc - JSON CLI output utility lsmod Parser
Usage:
specify --lsmod as the first argument if the piped input is coming from lsmod
Example:
$ lsmod | jc --lsmod -p
[
{
"Module": "nf_nat_ipv4",
"Size": "14115",
"Used": "1",
"By": [
"iptable_nat"
]
},
{
"Module": "nf_nat",
"Size": "26583",
"Used": "3",
"By": [
"nf_nat_ipv4",
"nf_nat_ipv6",
"nf_nat_masquerade_ipv4"
]
},
{
"Module": "iptable_mangle",
"Size": "12695",
"Used": "1"
},
{
"Module": "iptable_security",
"Size": "12705",
"Used": "1"
},
{
"Module": "iptable_raw",
"Size": "12678",
"Used": "1"
},
{
"Module": "nf_conntrack",
"Size": "139224",
"Used": "7",
"By": [
"nf_nat",
"nf_nat_ipv4",
"nf_nat_ipv6",
"xt_conntrack",
"nf_nat_masquerade_ipv4",
"nf_conntrack_ipv4",
"nf_conntrack_ipv6"
]
},
...
]
"""
def parse(data):
# code adapted from Conor Heine at:
# https://gist.github.com/cahna/43a1a3ff4d075bcd71f9d7120037a501
cleandata = data.splitlines()
headers = [h for h in ' '.join(cleandata[0].strip().split()).split() if h]
headers.pop(-1)
headers.append('By')
raw_data = map(lambda s: s.strip().split(None, len(headers) - 1), cleandata[1:])
output = [dict(zip(headers, r)) for r in raw_data]
for mod in output:
if 'By' in mod:
mod['By'] = mod['By'].split(',')
return output

117
jc/parsers/lsof.py Normal file
View File

@ -0,0 +1,117 @@
"""jc - JSON CLI output utility lsof Parser
Usage:
specify --lsof as the first argument if the piped input is coming from lsof
Limitations:
No additional columns are supported
Example:
$ sudo lsof | jc --lsof -p
[
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "cwd",
"TYPE": "DIR",
"DEVICE": "253,0",
"SIZE/OFF": "224",
"NODE": "64",
"NAME": "/"
},
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "rtd",
"TYPE": "DIR",
"DEVICE": "253,0",
"SIZE/OFF": "224",
"NODE": "64",
"NAME": "/"
},
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "txt",
"TYPE": "REG",
"DEVICE": "253,0",
"SIZE/OFF": "1624520",
"NODE": "50360451",
"NAME": "/usr/lib/systemd/systemd"
},
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "mem",
"TYPE": "REG",
"DEVICE": "253,0",
"SIZE/OFF": "20064",
"NODE": "8146",
"NAME": "/usr/lib64/libuuid.so.1.3.0"
},
{
"COMMAND": "systemd",
"PID": "1",
"TID": null,
"USER": "root",
"FD": "mem",
"TYPE": "REG",
"DEVICE": "253,0",
"SIZE/OFF": "265600",
"NODE": "8147",
"NAME": "/usr/lib64/libblkid.so.1.1.0"
},
...
]
"""
def parse(data):
output = []
linedata = data.splitlines()
# Clear any blank lines
cleandata = list(filter(None, linedata))
if cleandata:
# find column value of last character of each header
header_row = cleandata.pop(0)
headers = header_row.split()
header_spec = []
for i, h in enumerate(headers):
# header tuple is (index, header_name, col)
header_spec.append((i, h, header_row.find(h) + len(h)))
# parse lines
for entry in cleandata:
output_line = {}
# normalize data by inserting Null for missing data
temp_line = entry.split(maxsplit=len(headers) - 1)
for spec in header_spec:
if spec[1] == 'COMMAND' or spec[1] == 'NAME':
continue
if entry[spec[2] - 1] == ' ':
temp_line.insert(spec[0], None)
name = ' '.join(temp_line[9:])
fixed_line = temp_line[0:9]
fixed_line.append(name)
output_line = dict(zip(headers, fixed_line))
output.append(output_line)
return output

View File

@ -5,7 +5,7 @@ with open('README.md', 'r') as f:
setuptools.setup( setuptools.setup(
name='jc', name='jc',
version='0.8.1', version='0.9.1',
author='Kelly Brazil', author='Kelly Brazil',
author_email='kellyjonbrazil@gmail.com', author_email='kellyjonbrazil@gmail.com',
description='This tool serializes the output of popular command line tools to structured JSON output.', description='This tool serializes the output of popular command line tools to structured JSON output.',