diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..cd4ce96
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,62 @@
+NAME=sysinfo
+IMAGE_NAME=sysinfo
+
+ifndef VERSION
+VERSION := $(shell python3 -c "from setup import find_version;find_version("src/sysinfo/__init__.py")" || echo 0.0.0)
+endif
+
+ifndef BRANCH
+BRANCH := $(shell git branch --show-current)
+endif
+
+ifndef COMMIT
+COMMIT := $(shell git log -n1 --format="%h")
+endif
+
+ifndef SRC
+SRC := src/sysinfo/*.py
+endif
+
+ifndef TESTS
+TESTS := src/tests
+endif
+
+BUILD_RUN=docker run --rm "$(IMAGE_NAME):$(COMMIT)"
+
+.PHONY: git black lint build test coverage security
+
+git:
+ @echo $(branch: [$(BRANCH)] commit: [$(COMMIT)])
+
+black:
+ isort $(SRC)
+ black $(SRC)
+ autoflake --remove-all-unused-imports --remove-duplicate-keys --expand-star-imports --recursive --in-place $(SRC)
+
+lint:
+ flake8 --max-line-length=120 --max-complexity 8 $(SRC)
+ interrogate $(SRC)
+ mypy $(SRC)
+ pylint -d C0301 -d R0902 $(SRC)
+
+build:
+ python setup.py build
+
+install:
+ python setup.py install
+
+build_docker:
+ docker build -t $(IMAGE_NAME):$(COMMIT) . -f docker/build.Dockerfile
+
+test_docker:
+ docker build -t $(IMAGE_NAME):$(COMMIT) . -f docker/test.Dockerfile
+
+test:
+ pytest $(TESTS)
+
+coverage:
+ pytest --cov-report term-missing --cov=sysinfo $(TESTS)
+
+security:
+ safety check
+ bandit -r $(SRC)
diff --git a/README.md b/README.md
index 9dea176..072fe82 100644
--- a/README.md
+++ b/README.md
@@ -24,6 +24,7 @@ positional arguments:
optional arguments:
-h, --help Show this help message and exit
--all, -a Execute all commands.
+ --camel-case, -c Convert keys to CamelCase.
--error, -e Show only error outputs from commands.
--export-only Export output from commands without processing.
--export-dir PATH Path to the directory for saving output from commands.
@@ -32,13 +33,14 @@ optional arguments:
--list, -l List all commands.
--output OUTPUT, -o OUTPUT Path to the output file.
--pool POOL, -p POOL Pool size for parallel execution of commands. (default value is 5)
+ --system SYSTEM, -s SYSTEM Execute or parse commands for selected system [linux, darwin, java, windows].
--verbose, -v Add more info to output - options, commands, raw command result.
```
## Examples
### Standart JSON output
```
-python2 sysinfo.py lscpu
+python sysinfo.py lscpu
```
```json
{
@@ -66,7 +68,7 @@ python2 sysinfo.py lscpu
### Get single value
```
-python2 sysinfo.py lscpu | jq -r ".lscpu.output.modelName"
+python sysinfo.py lscpu | jq -r ".lscpu.output.modelName"
```
```
ARM1176
@@ -74,7 +76,7 @@ ARM1176
### Output in CSV format
```
-python2 sysinfo.py lsblk | jq -r ".lsblk.output[] | [.name, .label, .size,
+python sysinfo.py lsblk | jq -r ".lsblk.output[] | [.name, .label, .size,
.mountpoint] | @csv"
```
@@ -101,12 +103,21 @@ sudo python sysinfo.py --import-dir ./out blkid
* [jq](https://stedolan.github.io/jq/)
## Available commands
+
+### Linux
+
```
+arp - System ARP cache
blkid - Block device attributes
blockdev - Block device ioctls
+blockdev_detail - Block device ioctls details
busctl - Introspect the bus
+busctl_status - Process information and credentials of a bus service
+busctl_tree - Object tree for services
+chage - Users password expiration information
chrt - Scheduling attributes of all the tasks (threads)
dev_disk - Disk devices mapping
+dev_input - Input devices mapping
df - Report file system disk space usage
dmidecode - Dumping all information from DMI (SMBIOS)
dmidecode_baseboard - Dumping BASEBOARD information from DMI (SMBIOS)
@@ -136,14 +147,14 @@ fbset_info - Show frame buffer device information
findmnt - List all mounted filesytems
free - Amount of free and used memory in the system
getconf - Configuration variables for the current system and their values
-groups - Group names
-hardware_platform - Hardware platform
+groups - Group names (compgen)
+hardware_platform - Hardware platform (uname)
hostnamectl - Current system hostname and related information
ifconfig - List all interfaces which are currently available, even if down
-jobs - Job names, if job control is active
-kernel_name - Kernel name
-kernel_release - Kernel release
-kernel_version - Kernel version
+jobs - Job names, if job control is active (compgen)
+kernel_name - Kernel name (uname)
+kernel_release - Kernel release (uname)
+kernel_version - Kernel version (uname)
lsblk - Lists information about all block devices
lscpu - Information about the CPU architecture
lsmod - Show the status of modules in the Linux Kernel
@@ -151,11 +162,14 @@ lsns - Block device ioctls
lsof - Information about files opened by processes
lspci - List all PCI devices
lsusb - List USB devices
-machine - Machine hardware name
+machine - Machine hardware name (uname)
modinfo - Information about a Linux Kernel modules
-nodename - Network node hostname
-operating_system - Operating system
+nodename - Network node hostname (uname)
+operating_system - Operating system (uname)
parted - Lists partition layout on all block devices
+proc_buddyinfo - Memory fragmentation
+proc_bus_input - Input devices
+proc_cgroups - Control groups
proc_cmdline - Parameters passed to the kernel at the time it is started
proc_consoles - Information about current consoles including tty
proc_cpuinfo - Type of processor used by your system
@@ -172,33 +186,47 @@ proc_locks - Files currently locked by the kernel
proc_meminfo - Reports a large amount of valuable information about the systems RAM usage
proc_modules - List of all modules loaded into the kernel
proc_mounts - List mounted filesystems (info provides from kernel)
+proc_net_arp - ARP
+proc_net_ax25_route - AX25 routing information
+proc_net_ipx_route - IPX routing information
+proc_net_route - IP routing information
+proc_net_tcp - TCP socket table
+proc_net_tcp6 - TCP6 socket table
+proc_net_udp - UDP socket table
+proc_net_udp6 - UDP6 socket table
proc_partitions - Partition block allocation information
proc_scsi - List of every recognized SCSI device
+proc_slabinfo - Kernel caches informations
+proc_stat - Kernel/system statistics
proc_swaps - Measures swap space and its utilization
proc_sys - Information about the system and kernel features
proc_uptime - Information detailing how long the system has been on since its last restart
proc_version - Version of the Linux kernel, the version of gcc used to compile the kernel, and the time of kernel compilation
+proc_version_signature - OS version signature
proc_vmstat - Detailed virtual memory statistics from the kernel
-processor - Processor type
+processor - Processor type (uname)
prtstat - Print statistics of a processes
ps - Report a snapshot of the current processes
python_pip_packages - List available python modules
python_platform - Probe the underlying platform's hardware, operating system, and Python interpreter version information
+route - IP routing table
rpm - Querying all RPM packages
-services - Service names
+services - Service names (compgen)
services_list - Displays services with status
-services_params - Displays services with status
-shell_alias - Shell alias names
-shell_all_commands - Shell command names
-shell_builtins - Names of shell builtin commands
-shell_exported_variables - Names of exported shell variables
-shell_variables - Names of all shell variables
+services_params - Displays services with params
+shell_alias - Shell alias names (compgen)
+shell_all_commands - Shell command names (compgen)
+shell_builtins - Names of shell builtin commands (compgen)
+shell_exported_variables - Names of exported shell variables (compgen)
+shell_variables - Names of all shell variables (compgen)
sysctl - Runtime kernel parameters
sysctl_system - Runtime kernel parameters from all system configuration files
timedatectl - System time and date
+timedatectl_timesync - Status of systemd-timesyncd.service
udevadm - Queries the udev database for device information stored in the udev database
udevadm_block_devices - Queries the udev database for block device information stored in the udev database
-users - User names
+update_alternatives - Symbolic links determining default commands
+users - User names (compgen)
vmstat_disk - Report disk statistics
vmstat_disk_sum - Report some summary statistics about disk activity
vmstat_forks - Displays the number of forks since boot
@@ -206,3 +234,16 @@ vmstat_stats - Displays a table of various event counters and memor
yum_installed - YUM - list installed packages
yum_repolist - YUM - defined repositories
```
+
+### Windows
+
+```
+arp - System ARP cache
+assoc - File associations
+driverquery - List of installed device drivers.
+driverquery_signed - List of installed device signed drivers.
+tasklist - Get currently running processes
+tasklist_apps - Get services hosted in each process
+tasklist_modules - Get modules loaded in each process
+tasklist_services - Get services hosted in each process
+```
diff --git a/docker/build.Dockerfile b/docker/build.Dockerfile
new file mode 100644
index 0000000..8f468d1
--- /dev/null
+++ b/docker/build.Dockerfile
@@ -0,0 +1,14 @@
+FROM python:3.10-alpine
+# LABEL instruction creates labels.
+LABEL "maintainer"="Petr Vavrin" "appname"="sysinfo"
+
+ENV applocation /usr/src
+COPY src/sysinfo $applocation/sysinfo
+ENV app $applocation/sysinfo
+WORKDIR $app/
+
+RUN apk add --update python py-pip
+RUN pip install --upgrade pip
+RUN pip install -r requirements.txt
+#EXPOSE 5000
+CMD ["python", "sysinfo.py"]
diff --git a/docker/test.Dockerfile b/docker/test.Dockerfile
new file mode 100644
index 0000000..40155e4
--- /dev/null
+++ b/docker/test.Dockerfile
@@ -0,0 +1,28 @@
+FROM python:3.10-alpine
+# LABEL instruction creates labels.
+LABEL "maintainer"="Petr Vavrin" "appname"="sysinfo"
+
+ENV applocation /usr/src
+COPY src/sysinfo $applocation/sysinfo
+COPY src/tests $applocation/tests
+ENV app $applocation/sysinfo
+ENV tests $applocation/tests
+WORKDIR $app/
+
+RUN apk add --update python py-pip && \
+ pip install --upgrade pip && \
+ pip install -r requirements-test.txt && \
+ isort $app && \
+ black $app && \
+ autoflake --remove-all-unused-imports --remove-duplicate-keys --expand-star-imports --recursive --in-place $app && \
+ flake8 --max-line-length=120 --max-complexity 8 $app && \
+ interrogate $app && \
+ mypy $app && \
+ pylint -d C0301 -d R0902 $app && \
+ pytest $tests && \
+ pytest --cov-report term-missing --cov=sysinfo $tests && \
+ safety check && \
+ bandit -r $app
+
+#EXPOSE 5000
+CMD ["python", "sysinfo.py"]
diff --git a/modules/blkid.py b/modules/blkid.py
deleted file mode 100644
index 349a59e..0000000
--- a/modules/blkid.py
+++ /dev/null
@@ -1,33 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-
-def parser(stdout, stderr):
- output = {}
- ignoredLines = []
- if stdout:
- device = ''
- for line in stdout.splitlines():
- dev = re.search(r'^>>> Device: (\S+)', line)
- kv = re.search(r'^(\w[^=]+)=(.*)$', line)
- if dev:
- device = dev.group(1)
- output[device] = {}
- elif kv:
- output[device][camelCase(kv.group(1))] = kv.group(2)
- else:
- ignoredLines.append(line)
- pass
-
- return {
- 'output': output,
- 'ignored': ignoredLines
- }
-
-def register(main):
- main['blkid'] = {
- 'cmd': """blkid -o device | xargs -n 1 -I {} sh -c "echo '>>> Device: {}'; blkid -o export -p {}; blkid -o export -i {}" """,
- 'description': 'Block device attributes',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/blockdev.py b/modules/blockdev.py
deleted file mode 100644
index 8768ee3..0000000
--- a/modules/blockdev.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-from sysinfo_lib import parseTable
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- output = parseTable(stdout)
-
- return {'output': output}
-
-def register(main):
- main['blockdev'] = {
- 'cmd': 'blockdev --report | column -t',
- 'description': 'Block device ioctls',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/busctl.py b/modules/busctl.py
deleted file mode 100644
index 4258610..0000000
--- a/modules/busctl.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-from sysinfo_lib import parseTable
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- output = parseTable(stdout)
-
- return {'output': output}
-
-def register(main):
- main['busctl'] = {
- 'cmd': 'busctl --no-pager | column -t',
- 'description': 'Introspect the bus',
- 'parser': parser
- }
diff --git a/modules/chrt.py b/modules/chrt.py
deleted file mode 100644
index 1d2e306..0000000
--- a/modules/chrt.py
+++ /dev/null
@@ -1,29 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- pid = None
- if stdout:
- for line in stdout.splitlines():
- pidSearch = re.search(r'^>>>\s+PID:\s+(\S+)', line)
- if pidSearch:
- pid = pidSearch.group(1)
- output[pid] = {'pid': pid, 'scheduling': {}, 'current': {}}
-
- if pid:
- sched = re.search(r'^SCHED_(\S+)[^:]+:\s*(\S+)', line)
- if sched:
- output[pid]['scheduling'][sched.group(1).strip()] = sched.group(2).strip()
-
- current = re.search(r'current scheduling (\S+).*:\s*(\S+)', line)
- if current:
- output[pid]['current'][current.group(1).strip()] = current.group(2).strip()
- return {'output': output}
-
-def register(main):
- main['chrt'] = {
- 'cmd': """ps -eo pid | grep -vi pid | xargs -I {} sh -c "echo '>>> PID: {}'; chrt -a --pid {}; echo '----'; chrt -m --pid {};" """,
- 'description': 'Scheduling attributes of all the tasks (threads)',
- 'parser': parser
- }
diff --git a/modules/compgen.py b/modules/compgen.py
deleted file mode 100644
index f3f92fa..0000000
--- a/modules/compgen.py
+++ /dev/null
@@ -1,63 +0,0 @@
-
-from sysinfo_lib import sortedList
-
-def parser(stdout, stderr):
- if stdout:
- return {'output': sortedList(stdout)}
- else:
- return {}
-
-def register(main):
- main['shell_alias'] = {
- 'cmd': '$(which bash) -c "compgen -a"',
- 'description': 'Shell alias names',
- 'parser': parser
- }
-
- main['shell_builtins'] = {
- 'cmd': '$(which bash) -c "compgen -b"',
- 'description': 'Names of shell builtin commands',
- 'parser': parser
- }
-
- main['shell_all_commands'] = {
- 'cmd': '$(which bash) -c "compgen -c"',
- 'description': 'Shell command names',
- 'parser': parser
- }
-
- main['shell_exported_variables'] = {
- 'cmd': '$(which bash) -c "compgen -e"',
- 'description': 'Names of exported shell variables',
- 'parser': parser
- }
-
- main['groups'] = {
- 'cmd': '$(which bash) -c "compgen -g"',
- 'description': 'Group names',
- 'parser': parser
- }
-
- main['jobs'] = {
- 'cmd': '$(which bash) -c "compgen -j"',
- 'description': 'Job names, if job control is active',
- 'parser': parser
- }
-
- main['services'] = {
- 'cmd': '$(which bash) -c "compgen -s"',
- 'description': 'Service names',
- 'parser': parser
- }
-
- main['users'] = {
- 'cmd': '$(which bash) -c "compgen -u"',
- 'description': 'User names',
- 'parser': parser
- }
-
- main['shell_variables'] = {
- 'cmd': '$(which bash) -c "compgen -v"',
- 'description': 'Names of all shell variables',
- 'parser': parser
- }
diff --git a/modules/dev_disk.py b/modules/dev_disk.py
deleted file mode 100644
index be13a0f..0000000
--- a/modules/dev_disk.py
+++ /dev/null
@@ -1,32 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {'all': {}}
- if stdout:
- typeName = ''
- for line in stdout.splitlines():
- matchType = re.search(r'^\/dev\/disk\/by-(.*):\s*$', line)
- if matchType:
- typeName = matchType.group(1)
- output[typeName] = {}
-
- matchEntry = re.search(r'\s(\S+)\s+->\s+[\.\/]+(.*)$', line)
- if matchEntry and typeName:
- key = matchEntry.group(1).strip()
- value = matchEntry.group(2).strip()
- output[typeName][key] = value
-
- if not value in output['all']:
- output['all'][value] = {}
-
- output['all'][value][typeName] = key
-
- return {'output': output}
-
-def register(main):
- main['dev_disk'] = {
- 'cmd': 'ls -l /dev/disk/by-*',
- 'description': 'Disk devices mapping',
- 'parser': parser
- }
diff --git a/modules/df.py b/modules/df.py
deleted file mode 100644
index 7ad9fa4..0000000
--- a/modules/df.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import re
-
-def parser(stdout, stderr):
- output = {}
- ignoredLines = []
- if stdout:
- reSplit = re.compile(r'\s+')
- for line in stdout.splitlines():
- cols = reSplit.split(line)
- if len(cols) > 11 and cols[11].lower() != 'mounted':
- output[cols[11]] = {
- 'source': cols[0],
- 'fstype': cols[1],
- 'itotal': cols[2],
- 'iused': cols[3],
- 'iavail': cols[4],
- 'ipcent': cols[5],
- 'size': cols[6],
- 'used': cols[7],
- 'avail': cols[8],
- 'pcent': cols[9],
- 'file': cols[10],
- 'target': cols[11]
- }
- else:
- ignoredLines.append(line)
-
- return {
- 'output': output,
- 'ignored': ignoredLines
- }
-
-def register(main):
- main['df'] = {
- 'cmd': 'df -a --output=source,fstype,itotal,iused,iavail,ipcent,size,used,avail,pcent,file,target',
- 'description': 'Report file system disk space usage',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/dmidecode.py b/modules/dmidecode.py
deleted file mode 100644
index 8ebff83..0000000
--- a/modules/dmidecode.py
+++ /dev/null
@@ -1,132 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- dmiSections = {
- '0': 'BIOS',
- '1': 'System',
- '2': 'Base Board',
- '3': 'Chassis',
- '4': 'Processor',
- '5': 'Memory Controller',
- '6': 'Memory Module',
- '7': 'Cache',
- '8': 'Port Connector',
- '9': 'System Slots',
- '10': 'On Board Devices',
- '11': 'OEM Strings',
- '12': 'System Configuration Options',
- '13': 'BIOS Language',
- '14': 'Group Associations',
- '15': 'System Event Log',
- '16': 'Physical Memory Array',
- '17': 'Memory Device',
- '18': '32-bit Memory Error',
- '19': 'Memory Array Mapped Address',
- '20': 'Memory Device Mapped Address',
- '21': 'Built-in Pointing Device',
- '22': 'Portable Battery',
- '23': 'System Reset',
- '24': 'Hardware Security',
- '25': 'System Power Controls',
- '26': 'Voltage Probe',
- '27': 'Cooling Device',
- '28': 'Temperature Probe',
- '29': 'Electrical Current Probe',
- '30': 'Out-of-band Remote Access',
- '31': 'Boot Integrity Services',
- '32': 'System Boot',
- '33': '64-bit Memory Error',
- '34': 'Management Device',
- '35': 'Management Device Component',
- '36': 'Management Device Threshold Data',
- '37': 'Memory Channel',
- '38': 'IPMI Device',
- '39': 'Power Supply'
- }
- section = None
- output = {}
-
- if stdout:
- for line in stdout.splitlines():
- if line.strip() == '':
- section = None
-
- handleSearch = re.search(r'^Handle\s+([^,]+),\s+DMI type\s+([^,]+),', line, re.IGNORECASE)
- if handleSearch:
- handle = handleSearch.group(1)
- dmiType = handleSearch.group(2)
- if dmiType in dmiSections:
- section = camelCase(dmiSections[dmiType])
- output[section] = {
- '__handle': handle,
- '__dmiType': dmiType
- }
-
- entry = re.search(r'^\s+([^:]+):\s+(.*)$', line)
- if section and entry:
- output[section][camelCase(entry.group(1))] = entry.group(2).strip()
-
- return {'output': output}
-
-def register(main):
- main['dmidecode'] = {
- 'cmd': 'dmidecode',
- 'description': 'Dumping all information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_bios'] = {
- 'cmd': 'dmidecode -t bios',
- 'description': 'Dumping BIOS information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_system'] = {
- 'cmd': 'dmidecode -t system',
- 'description': 'Dumping SYSTEM information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_baseboard'] = {
- 'cmd': 'dmidecode -t baseboard',
- 'description': 'Dumping BASEBOARD information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_chassis'] = {
- 'cmd': 'dmidecode -t chassis',
- 'description': 'Dumping CHASSIS information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_processor'] = {
- 'cmd': 'dmidecode -t processor',
- 'description': 'Dumping CHASSIS information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_memory'] = {
- 'cmd': 'dmidecode -t memory',
- 'description': 'Dumping MEMORY information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_cache'] = {
- 'cmd': 'dmidecode -t cache',
- 'description': 'Dumping CACHE information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_connector'] = {
- 'cmd': 'dmidecode -t connector',
- 'description': 'Dumping CONNECTOR information from DMI (SMBIOS)',
- 'parser': parser
- }
-
- main['dmidecode_slot'] = {
- 'cmd': 'dmidecode -t slot',
- 'description': 'Dumping SLOT information from DMI (SMBIOS)',
- 'parser': parser
- }
diff --git a/modules/dnf.py b/modules/dnf.py
deleted file mode 100644
index 7b72a0e..0000000
--- a/modules/dnf.py
+++ /dev/null
@@ -1,72 +0,0 @@
-
-import re
-
-def parser_repolist(stdout, stderr):
- output = {'repos': [], 'errors': []}
- insideTable = False
- col1 = None
- col2 = None
- if stdout:
- for line in stdout.splitlines():
- tableHeader = re.search(r'^(repo id\s+)(repo name\s+)status', line, re.IGNORECASE)
-
- if col1 and col2:
- tableRow = re.search(r'^(.{%s})(.{%s})(.*)$' % (col1, col2), line)
-
- if insideTable and tableRow:
- output['repos'].append({
- 'repo': tableRow.group(1).strip(),
- 'repo_name': tableRow.group(2).strip(),
- 'status': tableRow.group(3).strip()
- })
- continue
-
- if tableHeader:
- insideTable = True
- col1 = len(tableHeader.group(1))
- col2 = len(tableHeader.group(2))
-
- else:
- insideTable = False
-
- if stderr:
- for line in stderr.splitlines():
- if re.search(r'http.*error .*', line, re.IGNORECASE):
- if not line in output['errors']:
- output['errors'].append(line)
-
- return {'output': output}
-
-def parser_installed(stdout, stderr):
- output = {'packages': [], 'errors': []}
- if stdout:
- for line in stdout.splitlines():
- package = re.search(r'^([\S\.]+)\.(\S+)\s+(\S+\.\S+)\s+(\S+.*)$', line)
- if package:
- output['packages'].append({
- 'name': package.group(1).strip(),
- 'arch': package.group(2).strip(),
- 'version': package.group(3).strip(),
- 'status': package.group(4).strip()
- })
-
- if stderr:
- for line in stderr.splitlines():
- if re.search(r'http.*error .*', line, re.IGNORECASE):
- if not line in output['errors']:
- output['errors'].append(line)
-
- return {'output': output}
-
-def register(main):
- main['dnf_repolist'] = {
- 'cmd': 'dnf repolist --all',
- 'description': 'DNF - defined repositories',
- 'parser': parser_repolist
- }
-
- main['dnf_installed'] = {
- 'cmd': 'dnf list installed',
- 'description': 'DNF - list installed packages',
- 'parser': parser_installed
- }
diff --git a/modules/env.py b/modules/env.py
deleted file mode 100644
index d6fc230..0000000
--- a/modules/env.py
+++ /dev/null
@@ -1,19 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineMatch = re.search(r'^([^=]+)=(.*)$', line)
- if lineMatch:
- output[lineMatch.group(1)] = lineMatch.group(2)
-
- return {'output': output}
-
-def register(main):
- main['env'] = {
- 'cmd': 'env',
- 'description': 'Environment variables',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/etc_default.py b/modules/etc_default.py
deleted file mode 100644
index b0912f0..0000000
--- a/modules/etc_default.py
+++ /dev/null
@@ -1,41 +0,0 @@
-
-import re
-
-def setPathValue(data, path, value):
- pathRest = None
- pathParts = re.search(r'^([^\/]+)\/?(.*)$', path)
- if pathParts:
- path = pathParts.group(1)
- pathRest = pathParts.group(2)
-
- if not path in data:
- data[path] = {}
-
- if pathRest:
- setPathValue(data[path], pathRest, value)
- else:
- if isinstance(data[path], dict):
- data[path] = []
- data[path].append(value)
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- pathValue = re.search(r'^\/etc\/default\/([^:]+):(.*)$', line)
- if pathValue:
- path = pathValue.group(1)
- value = pathValue.group(2)
- if value.strip() == '':
- continue
- if not re.search(r'^\s*#', value):
- setPathValue(output, path, value)
-
- return {'output': output}
-
-def register(main):
- main['etc_default'] = {
- 'cmd': 'find /etc/default -type f -follow -print | xargs grep ""',
- 'description': 'Default configuration for programs',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/etc_fstab.py b/modules/etc_fstab.py
deleted file mode 100644
index a43c3c4..0000000
--- a/modules/etc_fstab.py
+++ /dev/null
@@ -1,28 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- if re.match(r'^\s*#', line):
- continue
- lineMatch = re.split(r'\s+', line)
- if lineMatch and len(lineMatch) > 5:
- output[lineMatch[0]] = {
- 'location': lineMatch[0],
- 'mountPoint': lineMatch[1],
- 'type': lineMatch[2],
- 'security': lineMatch[3],
- 'dump': lineMatch[4],
- 'fsckOrder': lineMatch[5]
- }
-
- return {'output': output}
-
-def register(main):
- main['etc_fstab'] = {
- 'cmd': 'cat /etc/fstab',
- 'description': 'Filesystems mounted on boot',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/etc_group.py b/modules/etc_group.py
deleted file mode 100644
index 099cc8b..0000000
--- a/modules/etc_group.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-from sysinfo_lib import parseCharDelimitedTable, tableToDict
-
-def parser(stdout, stderr):
- columnsNames = ['groupName', 'password', 'gid', 'groupList']
- output = parseCharDelimitedTable(stdout, ':', columnsNames)
- output = tableToDict(output, 'groupName')
- return {'output': output}
-
-def register(main):
- main['etc_group'] = {
- 'cmd': 'cat /etc/group',
- 'description': 'Groups essential information',
- 'parser': parser
- }
-
diff --git a/modules/etc_hosts.py b/modules/etc_hosts.py
deleted file mode 100644
index c2c86e9..0000000
--- a/modules/etc_hosts.py
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- values = re.search(r'^\/etc\/([^\:]+):\s*(.*)$', line)
- if not values:
- continue
- group = values.group(1)
- value = re.sub(r'#.*$', '', values.group(2)).strip()
- if group not in output:
- output[group] = []
- if value != '':
- output[group].append(value.split('\t'))
-
- return {'output': output}
-
-def register(main):
- main['etc_hosts'] = {
- 'cmd': """grep "" /etc/hosts*""",
- 'description': 'Maps hostnames to IP addresses',
- 'parser': parser
- }
diff --git a/modules/etc_locale_gen.py b/modules/etc_locale_gen.py
deleted file mode 100644
index 1a7c09b..0000000
--- a/modules/etc_locale_gen.py
+++ /dev/null
@@ -1,22 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = []
- if stdout:
- for line in stdout.splitlines():
- values = re.search(r'^\s*([^#]+)', line)
- if not values:
- continue
- value = values.group(1).strip()
- if value != '':
- output.append(value)
-
- return {'output': output}
-
-def register(main):
- main['etc_locale_gen'] = {
- 'cmd': 'cat /etc/locale.gen',
- 'description': 'Configuration file for locale-gen',
- 'parser': parser
- }
diff --git a/modules/etc_mtab.py b/modules/etc_mtab.py
deleted file mode 100644
index 0b8ae00..0000000
--- a/modules/etc_mtab.py
+++ /dev/null
@@ -1,26 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- values = re.split(r'\s+', line)
- if len(values) > 5:
- output[values[1]] = {
- 'partition': values[0],
- 'mountPoint': values[1],
- 'fileSystem': values[2],
- 'mountOptions': re.split(r',', values[3]),
- 'dump': values[4],
- 'fsckOrder': values[5]
- }
-
- return {'output': output}
-
-def register(main):
- main['etc_mtab'] = {
- 'cmd': 'cat /etc/mtab',
- 'description': 'Currently mounted filesystems',
- 'parser': parser
- }
diff --git a/modules/etc_passwd.py b/modules/etc_passwd.py
deleted file mode 100644
index 6ca092c..0000000
--- a/modules/etc_passwd.py
+++ /dev/null
@@ -1,15 +0,0 @@
-
-from sysinfo_lib import parseCharDelimitedTable, tableToDict
-
-def parser(stdout, stderr):
- columnsNames = ['username', 'password', 'uid', 'gid', 'idInfo', 'homeDir', 'shell']
- output = parseCharDelimitedTable(stdout, ':', columnsNames)
- output = tableToDict(output, 'username')
- return {'output': output}
-
-def register(main):
- main['etc_passwd'] = {
- 'cmd': 'cat /etc/passwd',
- 'description': 'Attributes of each user or account on a computer',
- 'parser': parser
- }
diff --git a/modules/etc_release.py b/modules/etc_release.py
deleted file mode 100644
index 70f1b90..0000000
--- a/modules/etc_release.py
+++ /dev/null
@@ -1,19 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- values = re.search(r'^([^=]+)=(.*)$', line)
- if values:
- output[values.group(1)] = values.group(2).strip().strip('"')
-
- return {'output': output}
-
-def register(main):
- main['etc_release'] = {
- 'cmd': 'cat /etc/*release',
- 'description': 'OS release info',
- 'parser': parser
- }
diff --git a/modules/etc_shadow.py b/modules/etc_shadow.py
deleted file mode 100644
index 767b9f3..0000000
--- a/modules/etc_shadow.py
+++ /dev/null
@@ -1,15 +0,0 @@
-
-from sysinfo_lib import parseCharDelimitedTable, tableToDict
-
-def parser(stdout, stderr):
- columnsNames = ['username', 'password', 'lastPasswordChange', 'minimum', 'maximum', 'warn', 'inactive', 'expire']
- output = parseCharDelimitedTable(stdout, ':', columnsNames)
- output = tableToDict(output, 'username')
- return {'output': output}
-
-def register(main):
- main['etc_shadow'] = {
- 'cmd': 'cat /etc/shadow',
- 'description': 'Shadow database of the passwd file',
- 'parser': parser
- }
diff --git a/modules/etc_timezone.py b/modules/etc_timezone.py
deleted file mode 100644
index 10cf6ca..0000000
--- a/modules/etc_timezone.py
+++ /dev/null
@@ -1,13 +0,0 @@
-
-def parser(stdout, stderr):
- output = ''
- if stdout:
- output = stdout.strip()
- return {'output': output}
-
-def register(main):
- main['etc_timezone'] = {
- 'cmd': 'cat /etc/timezone',
- 'description': 'Timezone settings',
- 'parser': parser
- }
diff --git a/modules/fbset.py b/modules/fbset.py
deleted file mode 100644
index 5d28910..0000000
--- a/modules/fbset.py
+++ /dev/null
@@ -1,93 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- modeName = ''
- if stdout:
- for line in stdout.splitlines():
- mode = re.search(r'^\s*mode "([^"]+)\s*"$', line)
- if mode:
- modeName = mode.group(1)
- output[modeName] = {}
-
- endmode = re.search(r'^\s*endmode', line)
- if endmode:
- modeName = ''
-
- if modeName:
- geometry = re.search(r'^\s*geometry\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*$', line)
- if geometry:
- output[modeName]['geometry'] = {
- 'xres': geometry.group(1),
- 'yres': geometry.group(2),
- 'xresVirtual': geometry.group(3),
- 'yresVirtual': geometry.group(4),
- 'bitsPerPixel': geometry.group(5)
- }
-
- timings = re.search(r'^\s*timings\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*$', line)
- if timings:
- output[modeName]['timings'] = {
- 'pixclock': timings.group(1),
- 'leftMargin': timings.group(2),
- 'rightMargin': timings.group(3),
- 'upperMargin': timings.group(4),
- 'lowerMargin': timings.group(5),
- 'hsyncLen': timings.group(6),
- 'vsyncLen': timings.group(7)
- }
-
- rgba = re.search(r'^\s*rgba\s*(\d+)\/(\d+),(\d+)\/(\d+),(\d+)\/(\d+),(\d+)\/(\d+)\s*$', line)
- if rgba:
- output[modeName]['rgba'] = {
- 'redLength': rgba.group(1),
- 'redOffset': rgba.group(2),
- 'greenLength': rgba.group(3),
- 'greenOffset': rgba.group(4),
- 'blueLength': rgba.group(5),
- 'blueOffset': rgba.group(6),
- 'transpLength': rgba.group(7),
- 'transpOffset': rgba.group(8)
- }
-
- state = re.search(r'^\s*(interlaced|double|vsync|hsync|csync|extsync)\s+(\S+)\s*$', line)
- if state:
- output[modeName][state.group(1)] = state.group(2)
-
- return {
- 'output': {
- 'mode': output
- }
- }
-
-def parserInfo(stdout, stderr):
- output = parser(stdout, stderr)
- output['output']['info'] = {}
-
- informationBlock = False
- if stdout:
- for line in stdout.splitlines():
- info = re.search(r'^\s*Frame buffer device information:\s*$', line)
- if info:
- informationBlock = True
-
- if informationBlock:
- keyValue = re.search(r'^\s+(\S+)\s*:\s+(.*)\s*$', line)
- if keyValue:
- output['output']['info'][keyValue.group(1)] = keyValue.group(2)
-
- return output
-
-def register(main):
- main['fbset'] = {
- 'cmd': 'fbset -a',
- 'description': 'Show frame buffer device settings',
- 'parser': parser
- }
-
- main['fbset_info'] = {
- 'cmd': 'fbset -i',
- 'description': 'Show frame buffer device information',
- 'parser': parserInfo
- }
\ No newline at end of file
diff --git a/modules/findmnt.py b/modules/findmnt.py
deleted file mode 100644
index ce9f9af..0000000
--- a/modules/findmnt.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-from sysinfo_lib import parseTable
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- output = parseTable(stdout)
-
- return {'output': output}
-
-def register(main):
- main['findmnt'] = {
- 'cmd': 'findmnt -Al | column -t',
- 'description': 'List all mounted filesytems',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/free.py b/modules/free.py
deleted file mode 100644
index 5edf159..0000000
--- a/modules/free.py
+++ /dev/null
@@ -1,29 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- columns = None
- if stdout:
- typeName = ''
- for line in stdout.splitlines():
- if re.search(r'total\s+used', line, re.IGNORECASE):
- columns = re.split(r'\s+', line.strip())
-
- entrySearch = re.search(r'^([^:]+):\s+(.*)$', line)
- if columns and entrySearch:
- type = camelCase(entrySearch.group(1))
- output[type] = {}
- for idx, value in enumerate(re.split(r'\s+', entrySearch.group(2).strip())):
- if idx < len(columns):
- output[type][columns[idx]] = value
-
- return {'output': output}
-
-def register(main):
- main['free'] = {
- 'cmd': 'free -b -l -w',
- 'description': 'Amount of free and used memory in the system',
- 'parser': parser
- }
diff --git a/modules/getconf.py b/modules/getconf.py
deleted file mode 100644
index 03a97e9..0000000
--- a/modules/getconf.py
+++ /dev/null
@@ -1,21 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- kv = re.search(r'^(\S+)\s*(.*)$', line)
- if kv:
- output[kv.group(1)] = kv.group(2).strip()
-
- return {
- 'output': output,
- }
-
-def register(main):
- main['getconf'] = {
- 'cmd': 'getconf -a',
- 'description': 'Configuration variables for the current system and their values',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/hostnamectl.py b/modules/hostnamectl.py
deleted file mode 100644
index 3403da7..0000000
--- a/modules/hostnamectl.py
+++ /dev/null
@@ -1,20 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineMatch = re.search(r'^([^:]+):\s*(.*)', line)
- if lineMatch:
- output[camelCase(lineMatch.group(1))] = lineMatch.group(2)
-
- return {'output': output}
-
-def register(main):
- main['hostnamectl'] = {
- 'cmd': 'hostnamectl status',
- 'description': 'Current system hostname and related information',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/ifconfig.py b/modules/ifconfig.py
deleted file mode 100644
index ba26809..0000000
--- a/modules/ifconfig.py
+++ /dev/null
@@ -1,74 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def extractValue(data, line, key, regExp):
- search = re.search(regExp, line, re.IGNORECASE)
- if search:
- data[key] = search.group(1)
- return data
-
-def extractValues(data, line):
- for pair in re.split(r'\s\s+', line):
- desc = re.search(r'^(.*)\((.*)\)$', pair.strip())
- if desc:
- data['description'] = desc.group(2)
- if desc.group(1):
- pair = desc.group(1).strip()
- else:
- continue
-
- kv = re.search(r'^([^\s]+)\s(\S.*)$', pair)
- if kv:
- value = kv.group(2)
- valueFix = re.search(r'^(\S+)\s+(\S+)\s(\S+)$', value)
- if valueFix:
- data[kv.group(1)] = valueFix.group(1)
- data[valueFix.group(2)] = valueFix.group(3)
- else:
- data[kv.group(1)] = value
-
- return extractValues
-
-def parser(stdout, stderr):
- output = {}
- blockData = {}
- if stdout:
- for block in re.split(r'\r\r|\n\n|\r\n\r\n', stdout):
- blockData = {'entries': [], 'rx': {}, 'tx': {}}
- for line in block.splitlines():
- header = re.search(r'^(\S[^:]+):\s*(.*)$', line)
- if header:
- name = header.group(1)
- blockData['name'] = name
- extractValue(blockData, line, 'flags', r'flags=(\S+)')
- extractValues(blockData, header.group(2))
- output[name] = blockData
-
- rxTx = re.search(r'^\s+([rt]x)\s+(.*)$', line, re.IGNORECASE)
- if rxTx:
- type = rxTx.group(1).lower()
- extractValues(blockData[type], rxTx.group(2))
- continue
-
- sub = re.search(r'^\s+(\S+)\s\s(.*)$', line, re.IGNORECASE)
- if sub:
- subData = {'type': sub.group(1)}
- extractValues(subData, sub.group(2))
- blockData['entries'].append(subData)
- continue
-
- sub = re.search(r'^\s+(\S+)\s(\S+)\s\s(.*)$', line, re.IGNORECASE)
- if sub:
- subData = {'type': sub.group(1), 'value': sub.group(2)}
- extractValues(subData, sub.group(3))
- blockData['entries'].append(subData)
-
- return {'output': output}
-
-def register(main):
- main['ifconfig'] = {
- 'cmd': 'ifconfig -a -v',
- 'description': 'List all interfaces which are currently available, even if down',
- 'parser': parser
- }
diff --git a/modules/lsblk.py b/modules/lsblk.py
deleted file mode 100644
index ab06997..0000000
--- a/modules/lsblk.py
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- entry = {}
- kv = re.findall(r'(\S[^=]+)=\"([^"]*)\"', line)
- if kv:
- for pair in kv:
- entry[camelCase(pair[0])] = pair[1].strip()
-
- if 'name' in entry:
- output[entry['name']] = entry
-
- return {'output': output}
-
-def register(main):
- main['lsblk'] = {
- 'cmd': 'lsblk -P -o NAME,KNAME,MAJ:MIN,FSTYPE,MOUNTPOINT,LABEL,UUID,PARTLABEL,PARTUUID,RA,RO,RM,MODEL,SERIAL,SIZE,STATE,OWNER,GROUP,MODE,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,ROTA,SCHED,RQ-SIZE,TYPE,DISC-ALN,DISC-GRAN,DISC-MAX,DISC-ZERO,WSAME,WWN,RAND,PKNAME,HCTL,TRAN,REV,VENDOR',
- 'description': 'Lists information about all block devices',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/lscpu.py b/modules/lscpu.py
deleted file mode 100644
index 4c40546..0000000
--- a/modules/lscpu.py
+++ /dev/null
@@ -1,21 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- line = re.sub(r'\(s\)', 's', line)
- lineMatch = re.search(r'^([^:]+):\s*(.*)', line)
- if lineMatch:
- output[camelCase(lineMatch.group(1))] = lineMatch.group(2)
-
- return {'output': output}
-
-def register(main):
- main['lscpu'] = {
- 'cmd': 'lscpu',
- 'description': 'Information about the CPU architecture',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/lsmod.py b/modules/lsmod.py
deleted file mode 100644
index 9e1e131..0000000
--- a/modules/lsmod.py
+++ /dev/null
@@ -1,24 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineMatch = re.search(r'^(^\S+)\s*(\d+)\s*(\d+)\s*(.*)$', line)
- if lineMatch:
- output[lineMatch.group(1)] = {
- 'module': lineMatch.group(1),
- 'size': lineMatch.group(2),
- 'usedNumber': lineMatch.group(3),
- 'usedBy': lineMatch.group(4)
- }
-
- return {'output': output}
-
-def register(main):
- main['lsmod'] = {
- 'cmd': 'lsmod',
- 'description': 'Show the status of modules in the Linux Kernel',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/lsns.py b/modules/lsns.py
deleted file mode 100644
index cc4339e..0000000
--- a/modules/lsns.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-from sysinfo_lib import parseTable
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- output = parseTable(stdout, r'^(\s*NS)(\sTYPE\s+)(\sPATH\s*)(\s\s*NPROCS)(\s*\sPID)(\s*\sPPID)(\s*\sUID)(\sUSER\s*)(\sCOMMAND\s*)')
-
- return {'output': output}
-
-def register(main):
- main['lsns'] = {
- 'cmd': 'lsns -o NS,TYPE,PATH,NPROCS,PID,PPID,UID,USER,COMMAND',
- 'description': 'Block device ioctls',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/lsof.py b/modules/lsof.py
deleted file mode 100644
index 6118248..0000000
--- a/modules/lsof.py
+++ /dev/null
@@ -1,98 +0,0 @@
-
-import re
-
-opField = {
- 'a': 'accessMode',
- 'c': 'commandName',
- 'C': 'structureShareCount',
- 'd': 'deviceCharacterCode',
- 'D': 'majorMinorDeviceNumber',
- 'f': 'fileDescriptor',
- 'F': 'structureAddress',
- 'g': 'processGroupId',
- 'G': 'flags',
- 'i': 'inodeNumber',
- 'k': 'linkCount',
- 'K': 'taskId',
- 'l': 'lockStatus',
- 'L': 'loginName',
- 'm': 'markerBetweenRepeatedOutput',
- 'n': 'name',
- 'N': 'nodeIdentifier',
- 'o': 'fileOffset',
- 'p': 'processId',
- 'P': 'protocolName',
- 'r': 'rawDeviceNumber',
- 'R': 'parentPid',
- 's': 'fileSize',
- 'S': 'streamModuleAndDeviceNames',
- 't': 'fileType',
- 'T': 'tcpTpiInfo',
- 'u': 'userId',
- 'z': 'zoneName',
- 'Z': 'selinuxSecurityContext'
-}
-
-tcptpiField = {
- 'QR': 'readQueueSize',
- 'QS': 'sendQueueSize',
- 'SO': 'socketOptionsAndValues',
- 'SS': 'socketStates',
- 'ST': 'connectionState',
- 'TF': 'tcpFlagsAndValues',
- 'WR': 'windowReadSize',
- 'WW': 'windowWriteSize'
-}
-
-def parseElements(elements):
- global opField
- global tcptpiField
- output = {}
- for el in elements:
- ident = el[0:1]
- content = el[1:]
- identType = opField.get(ident, ident)
- if identType:
- if not identType in output:
- output[identType] = {}
- if ident == 'T':
- fifc = (content + '=').split('=')
- fifcType = tcptpiField.get(fifc[0], fifc[0])
- output[identType][fifcType] = fifc[1]
-
- else:
- output[identType] = content
-
- return output
-
-def parser(stdout, stderr):
- output = {}
- pid = None
- if stdout:
- for line in re.split(r'\x00\n', stdout):
- line = re.sub(r'^[\s\x00]*', '', line)
- elements = re.split(r'\x00', line)
- if not elements:
- continue
-
- first = elements.pop(0)
- if first:
- ident = first[0:1]
- content = first[1:]
- if ident == 'p':
- pid = content
- output[pid] = parseElements(elements)
- output[pid]['pid'] = pid
- output[pid]['files'] = []
-
- if pid and ident == 'f':
- output[pid]['files'].append(parseElements(elements))
-
- return {'output': output}
-
-def register(main):
- main['lsof'] = {
- 'cmd': 'lsof -F0',
- 'description': 'Information about files opened by processes',
- 'parser': parser
- }
diff --git a/modules/lspci.py b/modules/lspci.py
deleted file mode 100644
index ddbed32..0000000
--- a/modules/lspci.py
+++ /dev/null
@@ -1,26 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- slot = None
- if stdout:
- for line in stdout.splitlines():
- slotSearch = re.search(r'^Slot:\s+(.*)$', line, re.IGNORECASE)
- if slotSearch:
- slot = slotSearch.group(1).strip()
- output[slot] = {}
-
- keyValueSearch = re.search(r'^(\S[^:]+):\s+(.*)$', line)
- if slot and keyValueSearch:
- output[slot][camelCase(keyValueSearch.group(1))] = keyValueSearch.group(2).strip()
-
- return {'output': output}
-
-def register(main):
- main['lspci'] = {
- 'cmd': 'lspci -mm -vvv',
- 'description': 'List all PCI devices',
- 'parser': parser
- }
diff --git a/modules/lsusb.py b/modules/lsusb.py
deleted file mode 100644
index d70afab..0000000
--- a/modules/lsusb.py
+++ /dev/null
@@ -1,93 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def extractDescriptors(data, lineNumber, offset):
- output = {}
- ln = lineNumber
- descOffset = 0
- while ln < len(data):
- line = data[ln]
- searchDesc = re.search(r'^(\s*)(.*)Descriptors?:\s*$', line, re.IGNORECASE)
- searchStatus = re.search(r'^(\s*)(.*Status)?:\s*(.*)$', line, re.IGNORECASE)
- searchKeyValue = re.search(r'^\s*(\S+)\s+([0-9].*)$', line)
- searchOffset = re.search(r'^(\s*)', line)
- if searchOffset:
- lineOffset = len(searchOffset.group(1))
-
- if lineOffset < offset:
- ln -= 1
- break
-
- if searchDesc:
- descOffset = len(searchDesc.group(1)) + 2
- desc, ln = extractDescriptors(data, ln + 1, descOffset)
- name = camelCase(searchDesc.group(2).strip())
- output[name] = desc
-
- elif searchStatus:
- statusOffset = len(searchStatus.group(1)) + 2
- desc, ln = extractDescriptors(data, ln + 1, statusOffset)
- name = camelCase(searchStatus.group(2).strip())
- if searchStatus.group(3).strip() != '':
- desc['value'] = searchStatus.group(3).strip()
- output[name] = desc
-
- elif searchKeyValue:
- key = searchKeyValue.group(1).strip(':')
- value = searchKeyValue.group(2).strip()
- valueSplit = re.search(r'^(\S+)\s+(\S.*)$', value)
- if valueSplit:
- value = [valueSplit.group(1), valueSplit.group(2)]
- if key in output:
- output[key] = [output[key], value]
- else:
- output[key] = value
-
- else:
- if not 'data' in output:
- output['data'] = []
- output['data'].append(line.strip())
-
- ln += 1
- return output, ln
-
-def parseBlock(data):
- output = {}
- lines = data.split('\n')
-
- while lines[0].strip() == '':
- lines.pop(0)
-
- firstLine = lines.pop(0)
- busDevice = re.search(r'Bus\s+(\S+)\s+Device\s+([^:]+):\s+ID\s+([^:]+):(\S+)\s*(.*)$', firstLine, re.IGNORECASE)
- if busDevice:
- output['bus'] = busDevice.group(1)
- output['device'] = busDevice.group(2)
- output['idVendor'] = busDevice.group(3)
- output['idProduct'] = busDevice.group(4)
- output['vendorProduct'] = busDevice.group(5)
-
- output['desc'], tmp = extractDescriptors(lines, 0, 0)
-
- return output
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- delimiter = '-' * 20
- blocks = re.split(delimiter, re.sub(r'\n\nBus', '\n\n' + delimiter + 'Bus', stdout))
- if blocks:
- for block in blocks:
- blockData = parseBlock(block)
- if 'bus' in blockData and 'device' in blockData:
- id = blockData['bus'] + '/' + blockData['device']
- output[id] = blockData
- return {'output': output}
-
-def register(main):
- main['lsusb'] = {
- 'cmd': 'lsusb -v',
- 'description': 'List USB devices',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/modinfo.py b/modules/modinfo.py
deleted file mode 100644
index 16a90fe..0000000
--- a/modules/modinfo.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- moduleName = None
- if stdout:
- for line in stdout.splitlines():
- values = re.search(r'^([^:]+):\s+(.*)$', line)
- if not values:
- continue
-
- key = values.group(1)
- value = values.group(2)
-
- if key == 'moduleName':
- moduleName = value
- output[moduleName] = {}
-
- if moduleName:
- output[moduleName][key] = value.strip()
-
-
- return {'output': output}
-
-def register(main):
- main['modinfo'] = {
- 'cmd': """lsmod | grep -v "Module" | sed 's/ .*//g' | xargs -I {} -n 1 sh -c "echo 'moduleName: {}'; modinfo {}" """,
- 'description': 'Information about a Linux Kernel modules',
- 'parser': parser
- }
diff --git a/modules/parted.py b/modules/parted.py
deleted file mode 100644
index 477bd88..0000000
--- a/modules/parted.py
+++ /dev/null
@@ -1,52 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- defaultUnit = None
- path = None
- if stdout:
- for line in stdout.splitlines():
- if line.strip() == '':
- defaultUnit = None
- path = None
-
- unitSearch = re.search(r'^(\S+);$', line)
- if unitSearch:
- defaultUnit = unitSearch.group(1)
-
- lineSplit = (line.strip(';') + (':' * 10)).split(':')
-
- if defaultUnit and re.search(r'^(\/[^:]+)', line):
- path = lineSplit[0]
- output[path] = {
- 'path': lineSplit[0],
- 'defaultUnit': defaultUnit,
- 'end': lineSplit[1],
- 'devType': lineSplit[2],
- 'sectorSize': lineSplit[3],
- 'physSectorSize': lineSplit[4],
- 'ptName': lineSplit[5],
- 'model': lineSplit[6],
- 'diskFlags': lineSplit[7],
- 'table': {}
- }
-
- if path and re.search(r'^(\d+):', line):
- output[path]['table'][lineSplit[0]] = {
- 'start': lineSplit[1],
- 'end': lineSplit[2],
- 'size': lineSplit[3],
- 'fileSystem': lineSplit[4],
- 'flags': lineSplit[5]
- }
-
- return {'output': output}
-
-def register(main):
- main['parted'] = {
- 'cmd': 'parted -m -l print',
- 'description': 'Lists partition layout on all block devices',
- 'parser': parser
- }
-
\ No newline at end of file
diff --git a/modules/proc_cmdline.py b/modules/proc_cmdline.py
deleted file mode 100644
index e248f95..0000000
--- a/modules/proc_cmdline.py
+++ /dev/null
@@ -1,30 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for kv in re.split(r'[\s\t]+', stdout.strip()):
- splitted = re.search(r'^([^=]+)=(.*)$', kv)
- if splitted:
- key = splitted.group(1)
- value = splitted.group(2)
- else:
- key = kv
- value = ''
- if key in output:
- if isinstance(output[key], str):
- output[key] = [output[key]]
-
- output[key].append(value)
- else:
- output[key] = value
-
- return {'output': output}
-
-def register(main):
- main['proc_cmdline'] = {
- 'cmd': 'cat /proc/cmdline',
- 'description': 'Parameters passed to the kernel at the time it is started',
- 'parser': parser
- }
diff --git a/modules/proc_consoles.py b/modules/proc_consoles.py
deleted file mode 100644
index dbd0434..0000000
--- a/modules/proc_consoles.py
+++ /dev/null
@@ -1,52 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- """
- The columns are:
- device name of the device
- operations R = can do read operations
- W = can do write operations
- U = can do unblank
- flags E = it is enabled
- C = it is preferred console
- B = it is primary boot console
- p = it is used for printk buffer
- b = it is not a TTY but a Braille device
- a = it is safe to use when cpu is offline
- major:minor major and minor number of the device separated by a colon
- """
-
- output = {}
- if stdout:
- for line in stdout.splitlines():
- values = re.search(r'^(\S+)\s+(.*)\s+(\S+):(\S+)', line)
- if values:
- params = values.group(2).strip()
- output[values.group(1)] = {
- 'device': values.group(1),
- 'operations': {
- 'read': 'R' in params,
- 'write': 'W' in params,
- 'unblank': 'U' in params,
- },
- 'flags': {
- 'enabled': 'E' in params,
- 'preferred': 'C' in params,
- 'primaryBoot': 'B' in params,
- 'printkBuffer': 'p' in params,
- 'braile': 'b' in params,
- 'safeCpuOffline': 'a' in params,
- },
- 'major': values.group(3),
- 'minor': values.group(4)
- }
-
- return {'output': output}
-
-def register(main):
- main['proc_consoles'] = {
- 'cmd': 'cat /proc/consoles',
- 'description': 'Information about current consoles including tty',
- 'parser': parser
- }
diff --git a/modules/proc_cpuinfo.py b/modules/proc_cpuinfo.py
deleted file mode 100644
index 1b8e920..0000000
--- a/modules/proc_cpuinfo.py
+++ /dev/null
@@ -1,36 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {
- 'processor': {},
- 'hardware': {},
- 'oth': {}
- }
-
- if stdout:
- for block in re.split(r'\r\r|\n\n|\r\n\r\n', stdout):
- sub = {}
- for line in block.splitlines():
- values = re.search(r'([^\t]+)\s*:\s*(.*)$', line)
- if values:
- sub[camelCase(values.group(1).strip())] = values.group(2).strip()
-
- if 'processor' in sub:
- output['processor'][sub['processor']] = sub
-
- elif 'hardware' in sub:
- output['hardware'] = sub
-
- else:
- output['oth'] = sub
-
- return {'output': output}
-
-def register(main):
- main['proc_cpuinfo'] = {
- 'cmd': 'cat /proc/cpuinfo',
- 'description': 'Type of processor used by your system',
- 'parser': parser
- }
diff --git a/modules/proc_crypto.py b/modules/proc_crypto.py
deleted file mode 100644
index 442f82c..0000000
--- a/modules/proc_crypto.py
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for block in re.split(r'\r\r|\n\n|\r\n\r\n', stdout):
- sub = {}
- for line in block.splitlines():
- values = re.search(r'([^\t]+)\s*:\s*(.*)$', line)
- if values:
- sub[camelCase(values.group(1).strip())] = values.group(2).strip()
-
- if 'name' in sub:
- output[sub['name']] = sub
-
- return {'output': output}
-
-def register(main):
- main['proc_crypto'] = {
- 'cmd': 'cat /proc/crypto',
- 'description': 'Installed cryptographic ciphers used by the Linux kernel',
- 'parser': parser
- }
diff --git a/modules/proc_devices.py b/modules/proc_devices.py
deleted file mode 100644
index 7d11094..0000000
--- a/modules/proc_devices.py
+++ /dev/null
@@ -1,31 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- deviceType = None
- if stdout:
- for line in stdout.splitlines():
- dt = re.search(r'^([^:]+):', line)
- if dt:
- deviceType = camelCase(dt.group(1))
- output[deviceType] = {}
-
- dv = re.search(r'^\s*(\d+)\s*(.*)$', line)
- if dv and deviceType:
- id = dv.group(1)
- name = dv.group(2)
- if not name in output[deviceType]:
- output[deviceType][name] = []
-
- output[deviceType][name].append(id)
-
- return {'output': output}
-
-def register(main):
- main['proc_devices'] = {
- 'cmd': 'cat /proc/devices',
- 'description': 'Installed cryptographic ciphers used by the Linux kernel',
- 'parser': parser
- }
diff --git a/modules/proc_diskstats.py b/modules/proc_diskstats.py
deleted file mode 100644
index d3d29cd..0000000
--- a/modules/proc_diskstats.py
+++ /dev/null
@@ -1,44 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- columnNames = [
- 'majorNumber',
- 'minorNumber',
- 'deviceName',
- 'readsCompletedSuccessfully',
- 'readsMerged',
- 'sectorsRead',
- 'timeSpentReading',
- 'writesCompleted',
- 'writesMerged',
- 'sectorsWritten',
- 'timeSpentWriting',
- 'IOsCurrentlyInProgress',
- 'timeSpentDoingIOs',
- 'weightedTimeSpentDoingIOs'
- ]
- lenColumnNames = len(columnNames)
-
- if stdout:
- for line in stdout.splitlines():
- line = line.strip()
- columns = re.split(r'\s+', line)
- if not columns:
- continue
-
- output[columns[2]] = {}
- for num, val in enumerate(columns, start=0):
- if num < lenColumnNames:
- output[columns[2]][columnNames[num]] = val
-
- return {'output': output}
-
-def register(main):
- main['proc_diskstats'] = {
- 'cmd': 'cat /proc/diskstats',
- 'description': 'I/O statistics of block devices',
- 'parser': parser
- }
diff --git a/modules/proc_dma.py b/modules/proc_dma.py
deleted file mode 100644
index da73728..0000000
--- a/modules/proc_dma.py
+++ /dev/null
@@ -1,19 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- keyValueSearch = re.search(r'^\s*([^:]+):\s*(.*)$', line)
- if keyValueSearch:
- output[keyValueSearch.group(1)] = keyValueSearch.group(2).strip()
-
- return {'output': output}
-
-def register(main):
- main['proc_dma'] = {
- 'cmd': 'cat /proc/dma',
- 'description': 'List of the registered ISA DMA channels in use',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_filesystems.py b/modules/proc_filesystems.py
deleted file mode 100644
index 6a2d22f..0000000
--- a/modules/proc_filesystems.py
+++ /dev/null
@@ -1,23 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineMatch = re.search(r'^(\S+)\s+(\S+)', line)
- if lineMatch:
- output[lineMatch.group(2).strip()] = lineMatch.group(1).strip()
-
- lineMatch = re.search(r'^\s+(\S+)$', line)
- if lineMatch:
- output[lineMatch.group(1).strip()] = ''
-
- return {'output': output}
-
-def register(main):
- main['proc_filesystems'] = {
- 'cmd': 'cat /proc/filesystems',
- 'description': 'List of the file system types currently supported by the kernel',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_fs.py b/modules/proc_fs.py
deleted file mode 100644
index 2c38911..0000000
--- a/modules/proc_fs.py
+++ /dev/null
@@ -1,39 +0,0 @@
-
-import re
-
-def setPathValue(data, path, value):
- pathRest = None
- pathParts = re.search(r'^([^\/]+)\/?(.*)$', path)
- if pathParts:
- path = pathParts.group(1)
- pathRest = pathParts.group(2)
-
- if not path in data:
- data[path] = {}
-
- if pathRest:
- setPathValue(data[path], pathRest, value)
- else:
- if isinstance(data[path], dict):
- data[path] = []
- data[path].append(value)
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- pathValue = re.search(r'^\/proc\/fs\/([^:]+):(.*)$', line)
- if pathValue:
- path = pathValue.group(1)
- value = pathValue.group(2)
- if not re.search(r'^\s*#', value):
- setPathValue(output, path, value)
-
- return {'output': output}
-
-def register(main):
- main['proc_fs'] = {
- 'cmd': 'find /proc/fs -type f -follow -print | xargs grep ""',
- 'description': 'File system parameters',
- 'parser': parser
- }
diff --git a/modules/proc_iomem.py b/modules/proc_iomem.py
deleted file mode 100644
index 9781a58..0000000
--- a/modules/proc_iomem.py
+++ /dev/null
@@ -1,23 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = []
- if stdout:
- for line in stdout.splitlines():
- values = re.search(r'^\s*([^-]+)-(\S+)\s*:\s*(.*)$', line)
- if values:
- output.append({
- 'from': values.group(1),
- 'to': values.group(2),
- 'device': values.group(3)
- })
-
- return {'output': output}
-
-def register(main):
- main['proc_iomem'] = {
- 'cmd': 'cat /proc/iomem',
- 'description': """Map of the system's memory for each physical device""",
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_ioports.py b/modules/proc_ioports.py
deleted file mode 100644
index 5d02a71..0000000
--- a/modules/proc_ioports.py
+++ /dev/null
@@ -1,23 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = []
- if stdout:
- for line in stdout.splitlines():
- entrySearch = re.search(r'^\s*([^-]+)-(\S+)\s*:\s*(.*)$', line, re.IGNORECASE)
- if entrySearch:
- output.append({
- 'from': entrySearch.group(1),
- 'to': entrySearch.group(2),
- 'device': entrySearch.group(3)
- })
-
- return {'output': output}
-
-def register(main):
- main['proc_ioports'] = {
- 'cmd': 'cat /proc/ioports',
- 'description': 'List of currently registered port regions used for input or output communication with a device',
- 'parser': parser
- }
diff --git a/modules/proc_loadavg.py b/modules/proc_loadavg.py
deleted file mode 100644
index 7f7929e..0000000
--- a/modules/proc_loadavg.py
+++ /dev/null
@@ -1,24 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- values = re.search(r'^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)', stdout.strip())
- if values:
- output = {
- 'periodLast': values.group(1),
- 'period5minute': values.group(2),
- 'period15minute': values.group(3),
- 'processes': values.group(4),
- 'lastPid': values.group(5)
- }
-
- return {'output': output}
-
-def register(main):
- main['proc_loadavg'] = {
- 'cmd': 'cat /proc/loadavg',
- 'description': 'Load average in regard to both the CPU and IO over time',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_locks.py b/modules/proc_locks.py
deleted file mode 100644
index 82a8eb4..0000000
--- a/modules/proc_locks.py
+++ /dev/null
@@ -1,27 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineSplit = re.split(r'[\s\t]+', line)
- if lineSplit and len(lineSplit) > 5:
- output[lineSplit[0]] = {
- 'uid': lineSplit[0],
- 'class': lineSplit[1],
- 'lockType': lineSplit[2],
- 'allowAccessType': lineSplit[3],
- 'pid': lineSplit[4],
- 'fileID': lineSplit[5],
- 'lockedRegion': lineSplit[6]
- }
-
- return {'output': output}
-
-def register(main):
- main['proc_locks'] = {
- 'cmd': 'cat /proc/locks',
- 'description': 'Files currently locked by the kernel',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_meminfo.py b/modules/proc_meminfo.py
deleted file mode 100644
index f9bd14d..0000000
--- a/modules/proc_meminfo.py
+++ /dev/null
@@ -1,33 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- keyValueSearch = re.search(r'^([^:]+):\s*(.*)$', line, re.IGNORECASE)
- if keyValueSearch:
- key = keyValueSearch.group(1).strip(':')
- value = keyValueSearch.group(2).strip()
-
- valueSearch = re.search(r'(.*)\s+(.*)$', value)
- if valueSearch:
- output[camelCase(key)] = {
- 'value': valueSearch.group(1),
- 'type': valueSearch.group(2)
- }
- else:
- output[camelCase(key)] = {
- 'value': value,
- 'type': ''
- }
-
- return {'output': output}
-
-def register(main):
- main['proc_meminfo'] = {
- 'cmd': 'cat /proc/meminfo',
- 'description': 'Reports a large amount of valuable information about the systems RAM usage',
- 'parser': parser
- }
diff --git a/modules/proc_modules.py b/modules/proc_modules.py
deleted file mode 100644
index 4c5e7ea..0000000
--- a/modules/proc_modules.py
+++ /dev/null
@@ -1,26 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineSplit = re.split(r'[\s\t]+', line)
- if lineSplit and len(lineSplit) > 5:
- output[lineSplit[0]] = {
- 'moduleName': lineSplit[0],
- 'moduleMemorySize': lineSplit[1],
- 'numInstancesLoaded': lineSplit[2],
- 'depends': lineSplit[3].strip(',').split(','),
- 'state': lineSplit[4],
- 'kernelMemoryOffset': lineSplit[5]
- }
-
- return {'output': output}
-
-def register(main):
- main['proc_modules'] = {
- 'cmd': 'cat /proc/modules',
- 'description': 'List of all modules loaded into the kernel',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_mounts.py b/modules/proc_mounts.py
deleted file mode 100644
index a67d195..0000000
--- a/modules/proc_mounts.py
+++ /dev/null
@@ -1,29 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineSplit = re.split(r'[\s\t]+', line)
- if lineSplit and len(lineSplit) > 4:
- accessValues = {}
- for access in re.split(r',', lineSplit[3]):
- accessSplit = re.split(r'=', access + '=')
- accessValues[accessSplit[0]] = accessSplit[1]
-
- output[lineSplit[1]] = {
- 'device': lineSplit[0],
- 'mountPoint': lineSplit[1],
- 'type': lineSplit[2],
- 'access': accessValues
- }
-
- return {'output': output}
-
-def register(main):
- main['proc_mounts'] = {
- 'cmd': 'cat /proc/mounts',
- 'description': 'List mounted filesystems (info provides from kernel)',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_partitions.py b/modules/proc_partitions.py
deleted file mode 100644
index 1a28ee9..0000000
--- a/modules/proc_partitions.py
+++ /dev/null
@@ -1,17 +0,0 @@
-
-from sysinfo_lib import parseSpaceTable, tableToDict
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- output = parseSpaceTable(stdout)
- output = tableToDict(output, 'name')
-
- return {'output': output}
-
-def register(main):
- main['proc_partitions'] = {
- 'cmd': 'cat /proc/partitions',
- 'description': 'Partition block allocation information',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_scsi.py b/modules/proc_scsi.py
deleted file mode 100644
index a77bdbb..0000000
--- a/modules/proc_scsi.py
+++ /dev/null
@@ -1,42 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- path = None
- if stdout:
- for line in stdout.splitlines():
- hostSearch = re.search(r'^Host:\s+(\S+)\s+Channel:\s+(\S+)\s+Id:\s+(\S+)\s+Lun:\s+(\S+)$', line, re.IGNORECASE)
- if hostSearch:
- host = hostSearch.group(1)
- channel = hostSearch.group(2)
- id = hostSearch.group(3)
- lun = hostSearch.group(4)
- path = '%s:%s:%s:%s' % (host.replace('scsi', ''), channel, id, lun)
- output[path] = {
- 'host': host,
- 'channel': channel,
- 'id': id,
- 'lun': lun
- }
-
- if path:
- vendorSearch = re.search(r'^\s+Vendor:\s+(.*)\s+Model:\s+(.*)\s+Rev:\s+(.*)$', line, re.IGNORECASE)
- if vendorSearch:
- output[path]['vendor'] = vendorSearch.group(1).strip()
- output[path]['model'] = vendorSearch.group(2).strip()
- output[path]['rev'] = vendorSearch.group(3).strip()
-
- typeSearch = re.search(r'^\s+Type:\s+(.*)\s+ANSI\s+SCSI\s+revision:\s+(.*)$', line, re.IGNORECASE)
- if typeSearch:
- output[path]['type'] = typeSearch.group(1).strip()
- output[path]['revision'] = typeSearch.group(2).strip()
-
- return {'output': output}
-
-def register(main):
- main['proc_scsi'] = {
- 'cmd': 'cat /proc/scsi/scsi',
- 'description': 'List of every recognized SCSI device',
- 'parser': parser
- }
diff --git a/modules/proc_swaps.py b/modules/proc_swaps.py
deleted file mode 100644
index 8726e37..0000000
--- a/modules/proc_swaps.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-from sysinfo_lib import parseSpaceTable
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- output = parseSpaceTable(stdout)
-
- return {'output': output}
-
-def register(main):
- main['proc_swaps'] = {
- 'cmd': 'cat /proc/swaps',
- 'description': 'Measures swap space and its utilization',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_sys.py b/modules/proc_sys.py
deleted file mode 100644
index 33944a3..0000000
--- a/modules/proc_sys.py
+++ /dev/null
@@ -1,39 +0,0 @@
-
-import re
-
-def setPathValue(data, path, value):
- pathRest = None
- pathParts = re.search(r'^([^\/]+)\/?(.*)$', path)
- if pathParts:
- path = pathParts.group(1)
- pathRest = pathParts.group(2)
-
- if not path in data:
- data[path] = {}
-
- if pathRest:
- setPathValue(data[path], pathRest, value)
- else:
- data[path] = value
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- pathValue = re.search(r'^\/proc\/sys\/([^:]+):(.*)$', line)
- if pathValue:
- path = pathValue.group(1)
- value = pathValue.group(2)
- if value.strip() == '':
- continue
- if not re.search(r'^\s*#', value):
- setPathValue(output, path, value)
-
- return {'output': output}
-
-def register(main):
- main['proc_sys'] = {
- 'cmd': """find /proc/sys -type f -follow -print 2>/dev/null | xargs -n 1 -I {} sh -c 'VAL=$(cat {} 2>/dev/null); echo {}:$VAL;'""",
- 'description': 'Information about the system and kernel features',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_uptime.py b/modules/proc_uptime.py
deleted file mode 100644
index 76eff29..0000000
--- a/modules/proc_uptime.py
+++ /dev/null
@@ -1,20 +0,0 @@
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- splitted = re.split(r'\s+', stdout.strip())
- if len(splitted) > 0:
- output['systemUp'] = splitted[0]
-
- if len(splitted) > 1:
- output['sumCoresIdle'] = splitted[1]
-
- return {'output': output}
-
-def register(main):
- main['proc_uptime'] = {
- 'cmd': 'cat /proc/uptime',
- 'description': 'Information detailing how long the system has been on since its last restart',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_version.py b/modules/proc_version.py
deleted file mode 100644
index 174f919..0000000
--- a/modules/proc_version.py
+++ /dev/null
@@ -1,14 +0,0 @@
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- output = stdout.strip()
-
- return {'output': output}
-
-def register(main):
- main['proc_version'] = {
- 'cmd': 'cat /proc/version',
- 'description': 'Version of the Linux kernel, the version of gcc used to compile the kernel, and the time of kernel compilation',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/proc_vmstat.py b/modules/proc_vmstat.py
deleted file mode 100644
index bdf2c31..0000000
--- a/modules/proc_vmstat.py
+++ /dev/null
@@ -1,18 +0,0 @@
-import re
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineMatch = re.search(r'^([^\s+]+)\s(.*)$', line)
- if lineMatch:
- output[lineMatch.group(1)] = lineMatch.group(2)
-
- return {'output': output}
-
-def register(main):
- main['proc_vmstat'] = {
- 'cmd': 'cat /proc/vmstat',
- 'description': 'Detailed virtual memory statistics from the kernel',
- 'parser': parser
- }
diff --git a/modules/prtstat.py b/modules/prtstat.py
deleted file mode 100644
index 9a476dc..0000000
--- a/modules/prtstat.py
+++ /dev/null
@@ -1,25 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- pid = ''
- if stdout:
- for line in stdout.splitlines():
- lineMatch = re.findall(r'(\S+):\s(\S+)', line)
- for kv in lineMatch:
- if kv[0] == 'pid':
- pid = kv[1]
- output[pid] = {}
-
- if pid != '':
- output[pid][kv[0]] = kv[1]
-
- return {'output': output}
-
-def register(main):
- main['prtstat'] = {
- 'cmd': 'ps -eo pid | xargs -I {} prtstat -r {}',
- 'description': 'Print statistics of a processes',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/ps.py b/modules/ps.py
deleted file mode 100644
index 9d62fe7..0000000
--- a/modules/ps.py
+++ /dev/null
@@ -1,51 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = {}
- columnsNames = [
- 'user',
- 'ruser',
- 'group',
- 'rgroup',
- 'pid',
- 'ppid',
- 'pgid',
- 'cpu',
- 'size',
- 'bytes',
- 'nice',
- 'time',
- 'stime',
- 'tty',
- 'args'
- ]
- columnsCount = len(columnsNames)
- if stdout:
- for line in stdout.splitlines():
- if re.search(r'ps --cols 2048 -eo', line) or re.search(r'USER.*RUSER.*GROUP', line):
- continue
-
- cols = re.split(r'\s+', line)
- if cols:
- entry = {}
- for num, val in enumerate(cols, start=0):
- if num < columnsCount:
- name = columnsNames[num]
- entry[name] = val
- elif 'args' in entry:
- entry['args'] += ' ' + val
-
- if 'pid' in entry:
- output[entry['pid']] = entry
-
- return {'output': output}
-
-def register(main):
- main['ps'] = {
- 'cmd': 'ps --cols 2048 -eo user:80,ruser:80,group:80,rgroup:80,pid,ppid,pgid,pcpu,vsz,nice,etime,time,stime,tty,args 2>/dev/null',
- 'description': 'Report a snapshot of the current processes',
- 'parser': parser
- }
-
-
diff --git a/modules/python.py b/modules/python.py
deleted file mode 100644
index a665ef2..0000000
--- a/modules/python.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import platform
-
-loaded_pkg_resources = False
-
-try:
- import pkg_resources
- loaded_pkg_resources = True
-except:
- pass
-
-def python_pip_packages():
- output = {}
- for pkg in pkg_resources.working_set:
- package = {}
- for key in ['location', 'project_name', 'key', 'version', 'parsed_version', 'py_version', 'platform', 'precedence']:
- if hasattr(pkg, key):
- package[key] = str(getattr(pkg, key))
-
- if 'key' in package:
- output[package['key']] = package
-
- return output
-
-def python_platform():
- output = {
- 'architecture': platform.architecture(),
- 'machine': platform.machine(),
- 'node': platform.node(),
- 'platform': {
- 'normal': platform.platform(),
- 'aliased': platform.platform(aliased=True),
- 'terse': platform.platform(terse=True)
- },
- 'processor': platform.processor(),
- 'python': {
- 'branch': platform.python_branch(),
- 'build': platform.python_build(),
- 'compiler': platform.python_compiler(),
- 'implementation': platform.python_implementation(),
- 'revision': platform.python_revision(),
- 'version': platform.python_version(),
- 'versionTuple': platform.python_version_tuple(),
- },
- 'release': platform.release(),
- 'system': platform.system(),
- 'version': platform.version(),
- 'uname': platform.uname(),
- }
- return output
-
-def register(main):
- if loaded_pkg_resources:
- main['python_pip_packages'] = {
- 'function': python_pip_packages,
- 'description': 'List available python modules',
- }
-
- main['python_platform'] = {
- 'function': python_platform,
- 'description': 'Probe the underlying platform\'s hardware, operating system, and Python interpreter version information',
- }
diff --git a/modules/rpm.py b/modules/rpm.py
deleted file mode 100644
index 0f2c4f2..0000000
--- a/modules/rpm.py
+++ /dev/null
@@ -1,18 +0,0 @@
-
-from sysinfo_lib import parseCharDelimitedTable, tableToDict
-
-def parser(stdout, stderr):
- output = {}
- columns = ['installtime', 'buildtime', 'name', 'version', 'release', 'arch', 'vendor', 'packager', 'distribution', 'disttag']
- if stdout:
- output = parseCharDelimitedTable(stdout, '|', columns)
- output = tableToDict(output, 'name')
-
- return {'output': output}
-
-def register(main):
- main['rpm'] = {
- 'cmd': 'rpm -q -a --queryformat "%{INSTALLTIME}|%{BUILDTIME}|%{NAME}|%{VERSION}|%{RELEASE}|%{arch}|%{VENDOR}|%{PACKAGER}|%{DISTRIBUTION}|%{DISTTAG}\n"',
- 'description': 'Querying all RPM packages',
- 'parser': parser
- }
diff --git a/modules/services_status.py b/modules/services_status.py
deleted file mode 100644
index cb9057d..0000000
--- a/modules/services_status.py
+++ /dev/null
@@ -1,39 +0,0 @@
-
-import re
-from sysinfo_lib import parseTable, tableToDict
-
-def parser_services(stdout, stderr):
- output = parseTable(stdout)
- output = tableToDict(output, 'unit')
- return {'output': output}
-
-def parser_services_params(stdout, stderr):
- output = {}
- service = None
- if stdout:
- for line in stdout.splitlines():
- serviceSearch = re.search(r'^>>>\s*Service:\s*(.*)$', line)
- if serviceSearch:
- service = serviceSearch.group(1).strip()
- output[service] = {}
-
- if service:
- keyValueSearch = re.search(r'^([^=]+)=(.*)$', line)
- if keyValueSearch:
- output[service][keyValueSearch.group(1)] = keyValueSearch.group(2).strip()
-
- return {'output': output}
-
-def register(main):
- main['services_list'] = {
- 'cmd': """systemctl -l --type service --all --plain | grep -i -e ".service\|description" | sed 's/^\s*//g' """,
- 'description': 'Displays services with status',
- 'parser': parser_services
- }
-
- main['services_params'] = {
- 'cmd': """systemctl -l --type service --all --plain | sed -E 's/^\s*(\\S+.service).*$/\\1/g' | grep -i -e ".service" | xargs -I '{}' sh -c "echo '>>> Service: {}'; systemctl show {} --no-page" """,
- 'description': 'Displays services with status',
- 'parser': parser_services_params
- }
-
diff --git a/modules/sysctl.py b/modules/sysctl.py
deleted file mode 100644
index 8b1bd2e..0000000
--- a/modules/sysctl.py
+++ /dev/null
@@ -1,41 +0,0 @@
-
-import re
-
-def setPathValue(data, path, value):
- pathRest = None
- pathParts = re.search(r'^([^\.]+)\.?(.*)$', path)
- if pathParts:
- path = pathParts.group(1)
- pathRest = pathParts.group(2)
-
- if not path in data:
- data[path] = {}
-
- if pathRest:
- setPathValue(data[path], pathRest, value)
- else:
- data[path] = value
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- kv = re.search(r'^([^=]+)=(.*)$', line)
- if kv:
- key = kv.group(1).strip()
- value = kv.group(2).strip()
- setPathValue(output, key, value)
-
- return {'output': output}
-
-def register(main):
- main['sysctl'] = {
- 'cmd': 'sysctl -a -e',
- 'description': 'Runtime kernel parameters',
- 'parser': parser
- }
- main['sysctl_system'] = {
- 'cmd': 'sysctl -a -e --system',
- 'description': 'Runtime kernel parameters from all system configuration files',
- 'parser': parser
- }
diff --git a/modules/timedatectl.py b/modules/timedatectl.py
deleted file mode 100644
index 84d286e..0000000
--- a/modules/timedatectl.py
+++ /dev/null
@@ -1,20 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- lineMatch = re.search(r'^([^:]+):\s*(.*)', line)
- if lineMatch:
- output[camelCase(lineMatch.group(1))] = lineMatch.group(2)
-
- return {'output': output}
-
-def register(main):
- main['timedatectl'] = {
- 'cmd': 'timedatectl status',
- 'description': 'System time and date',
- 'parser': parser
- }
\ No newline at end of file
diff --git a/modules/udevadm.py b/modules/udevadm.py
deleted file mode 100644
index f10dc53..0000000
--- a/modules/udevadm.py
+++ /dev/null
@@ -1,97 +0,0 @@
-
-import re
-from sysinfo_lib import camelCase
-
-def parser(stdout, stderr):
- output = {'devices': {}, 'parents': {}}
- types = {
- 'P': 'path',
- 'N': 'node',
- 'L': 'linkPriority',
- 'E': 'entry',
- 'S': 'link'
- }
- device = None
- parent = None
- if stdout:
- for line in stdout.splitlines():
- deviceSearch = re.search(r'^>>> Device: (\S+)', line)
- if deviceSearch:
- device = deviceSearch.group(1)
- output['devices'][device] = {'parents': [], 'entry': {}, 'link': []}
- parent = None
-
- if not device:
- continue
-
- keyValue = re.search(r'^(\S):\s+(.*)$', line)
- if keyValue:
- key = keyValue.group(1)
- value = keyValue.group(2).strip()
- if key in types:
- key = types[key]
-
- if key == 'entry':
- valueSearch = re.search(r'^([^=]+)=(.*)$', value)
- if valueSearch:
- subkey = valueSearch.group(1).lower()
- subvalue = valueSearch.group(2).strip()
- output['devices'][device]['entry'][subkey] = subvalue
-
- elif key == 'link':
- output['devices'][device]['link'].append(value)
-
- else:
- output['devices'][device][key] = value
-
- deviceLook = re.search(r'^\s+looking at device \'([^\']+)', line)
- if deviceLook:
- parent = deviceLook.group(1)
- if not parent in output['parents']:
- output['parents'][parent] = {}
- output['devices'][device]['parents'].append(parent)
-
- parentDeviceLook = re.search(r'^\s+looking at device \'([^\']+)', line)
- if parentDeviceLook:
- parent = parentDeviceLook.group(1)
- if not parent in output['parents']:
- output['parents'][parent] = {}
- output['devices'][device]['parents'].append(parent)
-
- if parent:
- parentKeyValue = re.search(r'^\s+([^=]+)=="([^"]+)"', line)
- if parentKeyValue:
- key = parentKeyValue.group(1).lower()
- value = parentKeyValue.group(2)
-
- multipleValues = re.match(r'^(\s+\d+)+$', value)
- if multipleValues:
- value = re.split(r'\s+', value.strip())
-
- keyAttr = re.match(r'^(\S+){([^}]+)}', key, re.IGNORECASE)
- if keyAttr:
- attrType = keyAttr.group(1).lower()
- if not attrType in output['parents'][parent]:
- output['parents'][parent][attrType] = {}
-
- attrKey = camelCase(keyAttr.group(2))
- output['parents'][parent][attrType][attrKey] = value
-
- else:
- output['parents'][parent][key] = value
-
- return {'output': output}
-
-
-def register(main):
- main['udevadm'] = {
- 'cmd': """udevadm info --export-db | grep "DEVNAME" | cut -d "=" -f2 | xargs -n 1 -I {} sh -c "echo '>>> Device: {}'; udevadm info --query=all --name={}; udevadm info --attribute-walk --name={}" """,
- 'description': 'Queries the udev database for device information stored in the udev database',
- 'parser': parser
- }
-
- main['udevadm_block_devices'] = {
- 'cmd': """find /dev/ -type b | xargs -n 1 -I {} sh -c "echo '>>> Device: {}'; udevadm info --query=all --name={}; udevadm info --attribute-walk --name={}" """,
- 'description': 'Queries the udev database for block device information stored in the udev database',
- 'parser': parser
- }
diff --git a/modules/uname.py b/modules/uname.py
deleted file mode 100644
index 26d5d3a..0000000
--- a/modules/uname.py
+++ /dev/null
@@ -1,59 +0,0 @@
-
-import re
-
-def parser(stdout, stderr):
- output = ''
- if stdout:
- output = re.sub(r'\n|\r|\r\n', '', stdout)
- output = stdout.strip()
-
- return {'output': output}
-
-def register(main):
- main['kernel_name'] = {
- 'cmd': 'uname -s',
- 'description': 'Kernel name',
- 'parser': parser
- }
-
- main['kernel_release'] = {
- 'cmd': 'uname -r',
- 'description': 'Kernel release',
- 'parser': parser
- }
-
- main['kernel_version'] = {
- 'cmd': 'uname -v',
- 'description': 'Kernel version',
- 'parser': parser
- }
-
- main['nodename'] = {
- 'cmd': 'uname -n',
- 'description': 'Network node hostname',
- 'parser': parser
- }
-
- main['machine'] = {
- 'cmd': 'uname -m',
- 'description': 'Machine hardware name',
- 'parser': parser
- }
-
- main['processor'] = {
- 'cmd': 'uname -p',
- 'description': 'Processor type',
- 'parser': parser
- }
-
- main['hardware_platform'] = {
- 'cmd': 'uname -i',
- 'description': 'Hardware platform',
- 'parser': parser
- }
-
- main['operating_system'] = {
- 'cmd': 'uname -o',
- 'description': 'Operating system',
- 'parser': parser
- }
diff --git a/modules/vmstat.py b/modules/vmstat.py
deleted file mode 100644
index 2b0c2c5..0000000
--- a/modules/vmstat.py
+++ /dev/null
@@ -1,116 +0,0 @@
-
-import re
-import sys
-from struct import pack, unpack
-from sysinfo_lib import camelCase
-
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-
-def parser_stats(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- entry = re.search(r'^\s*(\d+)\s+(.*)$', line)
- if entry:
- output[camelCase(entry.group(2).strip())] = entry.group(1).strip()
-
- return {'output': output}
-
-def parser_disk(stdout, stderr):
- output = {}
- sectionsNames = []
- sectionsMask = ''
- totalLength = 0
- columnPaths = []
- if stdout:
- for line in stdout.splitlines():
- if re.match(r'disk.*reads', line, re.IGNORECASE):
- topHeader = re.split(r'\s+', line)
- if topHeader:
- for value in topHeader:
- sectionsNames.append(value.strip().strip('-').lower())
- totalLength += len(value) + 1
- sectionsMask += str(len(value) + 1) + 's'
-
- elif not sectionsMask:
- continue
-
- if re.match(r'.*total.*merged.*sectors.*', line):
- lineFix = line + (' ' * (totalLength - len(line)))
- if sys.version_info[0] != 2:
- lineFix = bytes(lineFix, 'utf-8')
-
- sectionData = unpack(sectionsMask, lineFix)
- if sectionData:
- index = 0
-
- for sec in sectionData:
- if PY2:
- secStrip = sec.strip()
- else:
- secStrip = str(sec.strip(), 'utf-8')
-
- topColumns = re.split(r'\s+', secStrip)
- if topColumns:
-
- for column in topColumns:
- if column:
- columnPaths.append(camelCase('%s %s' %(sectionsNames[index], column, )))
- else:
- columnPaths.append('%s' %(sectionsNames[index], ))
- index += 1
- else:
- entry = {}
- columns = re.split(r'\s+', line)
- for ci, cv in enumerate(columns):
- if ci < len(columnPaths):
- entry[columnPaths[ci]] = cv
- if 'disk' in entry:
- output[entry['disk']] = entry
-
- return {'output': output}
-
-def parser_disk_sum(stdout, stderr):
- output = {}
- if stdout:
- for line in stdout.splitlines():
- entry = re.search(r'^\s*(\d+)\s+(.*)$', line)
- if entry:
- output[camelCase(entry.group(2).strip())] = entry.group(1).strip()
-
- return {'output': output}
-
-def parser_forks(stdout, stderr):
- output = {}
- if stdout:
- forks = re.search(r'\s*(\d+)\s*forks', stdout)
- if forks:
- output['forks'] = forks.group(1)
-
- return {'output': output}
-
-def register(main):
- main['vmstat_stats'] = {
- 'cmd': 'vmstat -s',
- 'description': 'Displays a table of various event counters and memory statistics',
- 'parser': parser_stats
- }
-
- main['vmstat_disk'] = {
- 'cmd': 'vmstat -dwn',
- 'description': 'Report disk statistics',
- 'parser': parser_disk
- }
-
- main['vmstat_disk_sum'] = {
- 'cmd': 'vmstat -D',
- 'description': 'Report some summary statistics about disk activity',
- 'parser': parser_disk_sum
- }
-
- main['vmstat_forks'] = {
- 'cmd': 'vmstat -f',
- 'description': 'Displays the number of forks since boot',
- 'parser': parser_forks
- }
diff --git a/modules/yum.py b/modules/yum.py
deleted file mode 100644
index c3fa9a6..0000000
--- a/modules/yum.py
+++ /dev/null
@@ -1,73 +0,0 @@
-
-import re
-
-def parser_repolist(stdout, stderr):
- output = {'mirrors': [], 'repos': [], 'errors': []}
- insideTable = False
- if stdout:
- for line in stdout.splitlines():
- mirrors = re.search(r'^\s*\*\s*([^:]+):\s*(.*)$', line)
- if mirrors:
- output['mirrors'].append({
- 'repo': mirrors.group(1).strip(),
- 'host': mirrors.group(2).strip()
- })
-
- tableHeader = re.search(r'^repo id\s+repo name\s+status', line, re.IGNORECASE)
- tableRow = re.search(r'^([^\/]+)\/([^\/]+)\/(.*)\s\s+(\S+.*)\s\s+(\S+.*)$', line)
- if tableHeader:
- insideTable = True
-
- elif insideTable and tableRow:
- output['repos'].append({
- 'repo': tableRow.group(1).strip(),
- 'version': tableRow.group(2).strip(),
- 'arch': tableRow.group(3).strip(),
- 'repo_name': tableRow.group(4).strip(),
- 'status': tableRow.group(5).strip()
- })
-
- else:
- insideTable = False
-
- if stderr:
- for line in stderr.splitlines():
- if re.search(r'http.*error .*', line, re.IGNORECASE):
- if not line in output['errors']:
- output['errors'].append(line)
-
- return {'output': output}
-
-def parser_installed(stdout, stderr):
- output = {'packages': [], 'errors': []}
- if stdout:
- for line in stdout.splitlines():
- package = re.search(r'^([\S\.]+)\.(\S+)\s+(\S+\.\S+)\s+(\S+.*)$', line)
- if package:
- output['packages'].append({
- 'name': package.group(1).strip(),
- 'arch': package.group(2).strip(),
- 'version': package.group(3).strip(),
- 'status': package.group(4).strip()
- })
-
- if stderr:
- for line in stderr.splitlines():
- if re.search(r'http.*error .*', line, re.IGNORECASE):
- if not line in output['errors']:
- output['errors'].append(line)
-
- return {'output': output}
-
-def register(main):
- main['yum_repolist'] = {
- 'cmd': 'yum repolist all',
- 'description': 'YUM - defined repositories',
- 'parser': parser_repolist
- }
-
- main['yum_installed'] = {
- 'cmd': 'yum list installed',
- 'description': 'YUM - list installed packages',
- 'parser': parser_installed
- }
diff --git a/requirements-test.txt b/requirements-test.txt
index e69de29..3fca4d8 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -0,0 +1,29 @@
+coverage
+pytest
+pytest-aiohttp
+pytest-asyncio
+pytest-cov
+pytest-coverage
+pytest-datafiles
+autoflake
+black
+pylint
+pylint-exit
+bandit
+certifi
+flake8
+flake8-polyfill
+isort
+lazy-object-proxy
+mccabe
+mypy
+mypy-extensions
+pre-commit
+py
+pycodestyle
+pyflakes
+pyparsing
+safety
+types-requests
+typing-extensions
+interrogate
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..546845a
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,16 @@
+[flake8]
+extend-ignore = E203
+
+[mypy]
+follow_imports = silent
+strict_optional = True
+warn_redundant_casts = True
+warn_unused_ignores = True
+disallow_any_generics = True
+check_untyped_defs = True
+no_implicit_reexport = True
+disallow_untyped_defs = True
+ignore_missing_imports = True
+
+[mypy-tests.*]
+ignore_errors = True
diff --git a/setup.py b/setup.py
index 87243fd..e2c5284 100644
--- a/setup.py
+++ b/setup.py
@@ -3,11 +3,14 @@
import os
import re
+import sys
from distutils.core import setup
from pathlib import Path
from setuptools import find_packages, setup
+sys.path.append("src")
+
def find_version(fname):
"""Attempts to find the version number in the file names fname.
Raises RuntimeError if not found.
@@ -35,7 +38,7 @@ def requirements(fname):
# We use the version to construct the DOWNLOAD_URL.
-VERSION = find_version("__init__.py")
+VERSION = find_version("src/sysinfo/__init__.py")
# URL to the repository on Github.
REPO_URL = 'https://github.com/peterbay/sysinfo'
@@ -46,7 +49,7 @@ def requirements(fname):
INSTALL_REQUIRES = requirements("requirements.txt")
EXTRAS_REQUIRE = {
- "test": requirements("requirements-dev.txt"),
+ "test": requirements("requirements-test.txt"),
}
try:
@@ -74,7 +77,7 @@ def requirements(fname):
'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: 2.7',
],
- package_dir={"": "."},
+ package_dir={"": "src"},
packages=find_packages(),
install_requires=INSTALL_REQUIRES,
include_package_data=True,
diff --git a/__init__.py b/src/sysinfo/__init__.py
similarity index 91%
rename from __init__.py
rename to src/sysinfo/__init__.py
index 62dae51..04bdaf8 100644
--- a/__init__.py
+++ b/src/sysinfo/__init__.py
@@ -26,7 +26,9 @@
__program__ = "sysinfo"
__executable__ = "sysinfo"
-__description__ = "sysinfo - Python based scripts for obtaining system information from Linux."
+__description__ = (
+ "sysinfo - Python based scripts for obtaining system information from Linux."
+)
__author__ = "Petr Vavrin"
__email__ = "pvavrin@gmail.com"
__version__ = "0.0.1"
diff --git a/modules/__init__.py b/src/sysinfo/modules/__init__.py
similarity index 100%
rename from modules/__init__.py
rename to src/sysinfo/modules/__init__.py
diff --git a/src/sysinfo/modules/arp.py b/src/sysinfo/modules/arp.py
new file mode 100644
index 0000000..4463ec9
--- /dev/null
+++ b/src/sysinfo/modules/arp.py
@@ -0,0 +1,70 @@
+import re
+from sysinfo_lib import parseTable, tableToDict, camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = parseTable(
+ stdout,
+ header_pattern=r"^(Address\s+)\s(HWtype\s+)\s(HWaddress\s+)\s(Flags Mask\s+)\s(Iface\s*)",
+ to_camelcase=to_camelcase,
+ )
+
+ return {"output": output, "unprocessed": []}
+
+
+def parser_win(stdout, stderr, to_camelcase):
+ output = []
+ unprocessed = []
+ interface = None
+
+ if stdout:
+ for line in stdout.splitlines():
+ interface_match = re.search(r"^Interface:\s*(\S+)\s*---\s*(\S+)$", line)
+ if interface_match:
+ interface = {
+ "ip": interface_match.group(1).strip(),
+ "id": interface_match.group(2).strip(),
+ "entries": [],
+ }
+ output.append(interface)
+ continue
+
+ if re.match(r"^.*Physical\s*Address", line, re.IGNORECASE):
+ continue
+
+ entry_match = re.search(r"^\s+(\S+)\s+(\S+)\s+(\S+)", line)
+ if entry_match and interface:
+ interface["entries"].append(
+ {
+ "intenetAddress": entry_match.group(1).strip(),
+ "physicalAddress": entry_match.group(2).strip(),
+ "type": entry_match.group(3).strip(),
+ }
+ )
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "arp",
+ "system": ["linux"],
+ "cmd": "arp",
+ "description": "System ARP cache",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "arp",
+ "system": ["windows"],
+ "cmd": "%SystemRoot%\\system32\\arp -a",
+ "description": "System ARP cache",
+ "parser": parser_win,
+ }
+ )
diff --git a/src/sysinfo/modules/assoc.py b/src/sysinfo/modules/assoc.py
new file mode 100644
index 0000000..869ee03
--- /dev/null
+++ b/src/sysinfo/modules/assoc.py
@@ -0,0 +1,33 @@
+import re
+from sysinfo_lib import parseTable, tableToDict, camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ device = ""
+ for line in stdout.splitlines():
+ kv = re.search(r"^([^=]+)=(.*)$", line)
+ if kv:
+ key = kv.group(1)
+ value = kv.group(2)
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "assoc",
+ "system": ["windows"],
+ "cmd": "assoc",
+ "description": "File associations",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/blkid.py b/src/sysinfo/modules/blkid.py
new file mode 100644
index 0000000..d2334af
--- /dev/null
+++ b/src/sysinfo/modules/blkid.py
@@ -0,0 +1,39 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ device = ""
+ for line in stdout.splitlines():
+ dev = re.search(r"^>>> Device: (\S+)", line)
+ if dev:
+ device = dev.group(1)
+ output[device] = {}
+ continue
+
+ kv = re.search(r"^(\w[^=]+)=(.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2)
+ output[device][key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "blkid",
+ "system": ["linux"],
+ "cmd": """blkid -o device | xargs -n 1 -I {} sh -c "echo '>>> Device: {}'; blkid -o export -p {}; blkid -o export -i {}" """,
+ "description": "Block device attributes",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/blockdev.py b/src/sysinfo/modules/blockdev.py
new file mode 100644
index 0000000..bae3b54
--- /dev/null
+++ b/src/sysinfo/modules/blockdev.py
@@ -0,0 +1,57 @@
+from sysinfo_lib import parseTable, camelCase
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ if stdout:
+ output = parseTable(stdout, to_camelcase=to_camelcase)
+
+ return {"output": output, "unprocessed": []}
+
+
+def parser_detail(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ device = ""
+ for line in stdout.splitlines():
+ dev = re.search(r"^>>> Device: (\S+)", line)
+ if dev:
+ device = dev.group(1)
+ output[device] = {}
+ continue
+
+ kv = re.search(r"^get (\w[^:]+): (.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2)
+ output[device][key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "blockdev",
+ "system": ["linux"],
+ "cmd": "blockdev --report | column -t",
+ "description": "Block device ioctls",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "blockdev_detail",
+ "system": ["linux"],
+ "cmd": """blkid -o device | xargs -n 1 -I {} sh -c "echo '>>> Device: {}'; blockdev -v --getalignoff --getbsz --getdiscardzeroes --getfra --getiomin --getioopt --getmaxsect --getpbsz --getra --getro --getsize64 --getss {}" """,
+ "description": "Block device ioctls details",
+ "parser": parser_detail,
+ }
+ )
diff --git a/src/sysinfo/modules/busctl.py b/src/sysinfo/modules/busctl.py
new file mode 100644
index 0000000..7d3a107
--- /dev/null
+++ b/src/sysinfo/modules/busctl.py
@@ -0,0 +1,112 @@
+import re
+from sysinfo_lib import parseTable, camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ if stdout:
+ output = parseTable(stdout, to_camelcase=to_camelcase)
+
+ return {"output": output, "unprocessed": []}
+
+
+def parser_tree(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ name = ""
+ for line in stdout.splitlines():
+ header = re.search(r"^(\S+) (.+):", line)
+
+ if header:
+ name = header.group(2)
+ output[name] = []
+ continue
+
+ tree = re.search(r"^\/(.+)$", line)
+ if tree and name:
+ output[name].append(tree.group(1))
+ continue
+
+ if line == "" or line == "/":
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def parser_status(stdout, stderr, to_camelcase):
+ output = {}
+ key = ""
+ is_array = False
+ unprocessed = []
+
+ if stdout:
+ device = ""
+ for line in stdout.splitlines():
+ dev = re.search(r"^>>> Device: (\S+)", line)
+ if dev:
+ device = dev.group(1)
+ output[device] = {}
+ key = ""
+ is_array = False
+ continue
+
+ kv = re.search(r"^(\w[^=]+)=(.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2).strip()
+
+ if re.match(r"^cap_", value):
+ is_array = True
+ output[device][key] = value.split(r" ")
+ print(value.split(r" "))
+
+ else:
+ output[device][key] = value
+
+ continue
+
+ cont = re.search(r"^\s\s+(.*)$", line)
+ if cont and key and is_array:
+ key = camelCase(key, to_camelcase)
+ output[device][key] += cont.group(1).strip().split(r" ")
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "busctl",
+ "system": ["linux"],
+ "cmd": "busctl --no-pager | column -t",
+ "description": "Introspect the bus",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "busctl_tree",
+ "system": ["linux"],
+ "cmd": "busctl --no-pager --list tree",
+ "description": "Object tree for services",
+ "parser": parser_tree,
+ }
+ )
+
+ main.register(
+ {
+ "name": "busctl_status",
+ "system": ["linux"],
+ "cmd": """busctl list | awk '!/^(NAME)/ {print $1}' | xargs -n 1 -I {} sh -c "echo '>>> Device: {}'; busctl --no-pager status {}" """,
+ "description": "Process information and credentials of a bus service",
+ "parser": parser_status,
+ }
+ )
diff --git a/src/sysinfo/modules/chage.py b/src/sysinfo/modules/chage.py
new file mode 100644
index 0000000..d766867
--- /dev/null
+++ b/src/sysinfo/modules/chage.py
@@ -0,0 +1,40 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+ user = None
+
+ if stdout:
+ for line in stdout.splitlines():
+ user_match = re.search(r"^>>> User:\s+(.*)$", line)
+ if user_match:
+ user = user_match.group(1)
+ output[user] = {"name": user}
+ continue
+
+ kv = re.search(r"^([^:]+):\s*(.*)$", line)
+ if user and kv:
+ key = camelCase(kv.group(1).strip(), to_camelcase)
+ value = kv.group(2).strip()
+
+ output[user][key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "chage",
+ "system": ["linux"],
+ "cmd": """cat /etc/passwd | awk '{split($0,a,":"); print a[1]}' | xargs -I {} sh -c "echo '>>> User: {}'; chage -l {}" """,
+ "description": "Users password expiration information",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/chrt.py b/src/sysinfo/modules/chrt.py
new file mode 100644
index 0000000..b27e22e
--- /dev/null
+++ b/src/sysinfo/modules/chrt.py
@@ -0,0 +1,56 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ pid = None
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+
+ pidSearch = re.search(r"^>>>\s+PID:\s+(\S+)", line)
+ if pidSearch:
+ pid = pidSearch.group(1)
+ output[pid] = {"pid": pid, "scheduling": {}, "current": {}}
+ continue
+
+ if pid:
+ sched = re.search(r"^SCHED_(\S+)[^:]+:\s*(\S+)", line)
+ if sched:
+ key = camelCase(sched.group(1).strip(), to_camelcase)
+ value = sched.group(2).strip()
+
+ output[pid]["scheduling"][key] = value
+ continue
+
+ current = re.search(r"current scheduling (\S+).*:\s*(\S+)", line)
+ if current:
+ key = camelCase(current.group(1).strip(), to_camelcase)
+ value = current.group(2).strip()
+
+ if re.match(r"^SCHED_", value):
+ value = camelCase(value.replace("SCHED_", ""), to_camelcase)
+
+ output[pid]["current"][key] = value
+ continue
+
+ if line.strip("-") == "":
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "chrt",
+ "system": ["linux"],
+ "cmd": """ps -eo pid | grep -vi pid | xargs -I {} sh -c "echo '>>> PID: {}'; chrt -a --pid {}; echo '----'; chrt -m --pid {};" """,
+ "description": "Scheduling attributes of all the tasks (threads)",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/compgen.py b/src/sysinfo/modules/compgen.py
new file mode 100644
index 0000000..89454f2
--- /dev/null
+++ b/src/sysinfo/modules/compgen.py
@@ -0,0 +1,101 @@
+from sysinfo_lib import sortedList
+
+
+def parser(stdout, stderr, to_camelcase):
+ if stdout:
+ return {"output": sortedList(stdout), "unprocessed": []}
+
+ else:
+ return {}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "shell_alias",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -a"',
+ "description": "Shell alias names (compgen)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "shell_builtins",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -b"',
+ "description": "Names of shell builtin commands (compgen)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "shell_all_commands",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -c"',
+ "description": "Shell command names (compgen)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "shell_exported_variables",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -e"',
+ "description": "Names of exported shell variables (compgen)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "shell_variables",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -v"',
+ "description": "Names of all shell variables (compgen)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "groups",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -g"',
+ "description": "Group names (compgen)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "jobs",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -j"',
+ "description": "Job names, if job control is active (compgen)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "services",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -s"',
+ "description": "Service names (compgen)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "users",
+ "system": ["linux"],
+ "cmd": '$(which bash) -c "compgen -u"',
+ "description": "User names (compgen)",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/dev_disk.py b/src/sysinfo/modules/dev_disk.py
new file mode 100644
index 0000000..9d16c4f
--- /dev/null
+++ b/src/sysinfo/modules/dev_disk.py
@@ -0,0 +1,46 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {"all": {}}
+ unprocessed = []
+
+ if stdout:
+ typeName = ""
+ for line in stdout.splitlines():
+ matchType = re.search(r"^\/dev\/disk\/by-(.*):\s*$", line)
+ if matchType:
+ typeName = matchType.group(1)
+ output[typeName] = {}
+ continue
+
+ matchEntry = re.search(r"\s(\S+)\s+->\s+[\.\/]+(.*)$", line)
+ if matchEntry and typeName:
+ key = matchEntry.group(1).strip()
+ value = matchEntry.group(2).strip()
+ output[typeName][key] = value
+
+ if not value in output["all"]:
+ output["all"][value] = {}
+
+ output["all"][value][typeName] = key
+ continue
+
+ if line == "" or re.match(r"^total", line):
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "dev_disk",
+ "system": ["linux"],
+ "cmd": "ls -l /dev/disk/by-*",
+ "description": "Disk devices mapping",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/dev_input.py b/src/sysinfo/modules/dev_input.py
new file mode 100644
index 0000000..8d37464
--- /dev/null
+++ b/src/sysinfo/modules/dev_input.py
@@ -0,0 +1,46 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {"all": {}}
+ unprocessed = []
+
+ if stdout:
+ typeName = ""
+ for line in stdout.splitlines():
+ matchType = re.search(r"^\/dev\/input\/by-(.*):\s*$", line)
+ if matchType:
+ typeName = matchType.group(1)
+ output[typeName] = {}
+ continue
+
+ matchEntry = re.search(r"\s(\S+)\s+->\s+[\.\/]+(.*)$", line)
+ if matchEntry and typeName:
+ key = matchEntry.group(1).strip()
+ value = matchEntry.group(2).strip()
+ output[typeName][key] = value
+
+ if not value in output["all"]:
+ output["all"][value] = {}
+
+ output["all"][value][typeName] = key
+ continue
+
+ if line == "" or re.match(r"^total", line):
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "dev_input",
+ "system": ["linux"],
+ "cmd": "ls -l /dev/input/by-*",
+ "description": "Input devices mapping",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/df.py b/src/sysinfo/modules/df.py
new file mode 100644
index 0000000..800eeb5
--- /dev/null
+++ b/src/sysinfo/modules/df.py
@@ -0,0 +1,49 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ reSplit = re.compile(r"\s+")
+ for line in stdout.splitlines():
+ cols = reSplit.split(line)
+ if len(cols) > 11:
+ if cols[11].lower() == "mounted":
+ continue
+
+ output[cols[11]] = {
+ "source": cols[0],
+ "fstype": cols[1],
+ "itotal": cols[2],
+ "iused": cols[3],
+ "iavail": cols[4],
+ "ipcent": cols[5],
+ "size": cols[6],
+ "used": cols[7],
+ "avail": cols[8],
+ "pcent": cols[9],
+ "file": cols[10],
+ "target": cols[11],
+ }
+ continue
+
+ if re.match(r"Filesystem", line):
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "df",
+ "system": ["linux"],
+ "cmd": "df -a --output=source,fstype,itotal,iused,iavail,ipcent,size,used,avail,pcent,file,target",
+ "description": "Report file system disk space usage",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/dmidecode.py b/src/sysinfo/modules/dmidecode.py
new file mode 100644
index 0000000..fc78adb
--- /dev/null
+++ b/src/sysinfo/modules/dmidecode.py
@@ -0,0 +1,207 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ dmiSections = {
+ "0": "BIOS",
+ "1": "System",
+ "2": "Base Board",
+ "3": "Chassis",
+ "4": "Processor",
+ "5": "Memory Controller",
+ "6": "Memory Module",
+ "7": "Cache",
+ "8": "Port Connector",
+ "9": "System Slots",
+ "10": "On Board Devices",
+ "11": "OEM Strings",
+ "12": "System Configuration Options",
+ "13": "BIOS Language",
+ "14": "Group Associations",
+ "15": "System Event Log",
+ "16": "Physical Memory Array",
+ "17": "Memory Device",
+ "18": "32-bit Memory Error",
+ "19": "Memory Array Mapped Address",
+ "20": "Memory Device Mapped Address",
+ "21": "Built-in Pointing Device",
+ "22": "Portable Battery",
+ "23": "System Reset",
+ "24": "Hardware Security",
+ "25": "System Power Controls",
+ "26": "Voltage Probe",
+ "27": "Cooling Device",
+ "28": "Temperature Probe",
+ "29": "Electrical Current Probe",
+ "30": "Out-of-band Remote Access",
+ "31": "Boot Integrity Services",
+ "32": "System Boot",
+ "33": "64-bit Memory Error",
+ "34": "Management Device",
+ "35": "Management Device Component",
+ "36": "Management Device Threshold Data",
+ "37": "Memory Channel",
+ "38": "IPMI Device",
+ "39": "Power Supply",
+ "40": "Additional Information",
+ "41": "Onboard Devices Extended Information",
+ "42": "Management Controller Host Interface",
+ "126": "Disabled entry",
+ "127": "End of table",
+ }
+ handle = None
+ output = {}
+ unprocessed = []
+
+ if stdout:
+
+ # fix multiline
+ stdout = re.sub(r"\n\t\t", " ", stdout)
+
+ for line in stdout.splitlines():
+ if line.strip() == "":
+ handle = None
+
+ handleSearch = re.search(
+ r"^Handle\s+([^,]+),\s+DMI type\s+([^,]+),", line, re.IGNORECASE
+ )
+ if handleSearch:
+ handle = handleSearch.group(1)
+ dmiType = handleSearch.group(2)
+ intDmiType = int(dmiType)
+
+ if intDmiType > 127 and intDmiType < 256:
+ output[handle] = {"__dmiType": dmiType, "__section": "OEM Specific"}
+ continue
+
+ if dmiType in dmiSections:
+ output[handle] = {
+ "__dmiType": dmiType,
+ "__section": dmiSections[dmiType],
+ }
+ continue
+
+ output[handle] = {"__dmiType": dmiType, "__section": "Unknown"}
+ continue
+
+ if handle and re.match(r"^\S", line):
+ output[handle]["__type"] = line
+ continue
+
+ entry = re.search(r"^\s+([^:]+):\s+(.*)$", line)
+ if handle and entry:
+ key = camelCase(entry.group(1), to_camelcase)
+ value = entry.group(2).strip()
+
+ output[handle][key] = value
+ continue
+
+ if line == "":
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "dmidecode",
+ "system": ["linux"],
+ "cmd": "dmidecode",
+ "description": "Dumping all information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_bios",
+ "system": ["linux"],
+ "cmd": "dmidecode -t bios",
+ "description": "Dumping BIOS information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_system",
+ "system": ["linux"],
+ "cmd": "dmidecode -t system",
+ "description": "Dumping SYSTEM information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_baseboard",
+ "system": ["linux"],
+ "cmd": "dmidecode -t baseboard",
+ "description": "Dumping BASEBOARD information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_chassis",
+ "system": ["linux"],
+ "cmd": "dmidecode -t chassis",
+ "description": "Dumping CHASSIS information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_processor",
+ "system": ["linux"],
+ "cmd": "dmidecode -t processor",
+ "description": "Dumping CHASSIS information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_memory",
+ "system": ["linux"],
+ "cmd": "dmidecode -t memory",
+ "description": "Dumping MEMORY information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_cache",
+ "system": ["linux"],
+ "cmd": "dmidecode -t cache",
+ "description": "Dumping CACHE information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_connector",
+ "system": ["linux"],
+ "cmd": "dmidecode -t connector",
+ "description": "Dumping CONNECTOR information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dmidecode_slot",
+ "system": ["linux"],
+ "cmd": "dmidecode -t slot",
+ "description": "Dumping SLOT information from DMI (SMBIOS)",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/dnf.py b/src/sysinfo/modules/dnf.py
new file mode 100644
index 0000000..fb71b2c
--- /dev/null
+++ b/src/sysinfo/modules/dnf.py
@@ -0,0 +1,89 @@
+import re
+
+
+def parser_repolist(stdout, stderr, to_camelcase):
+ output = {"repos": [], "errors": []}
+
+ insideTable = False
+ col1 = None
+ col2 = None
+ if stdout:
+ for line in stdout.splitlines():
+ tableHeader = re.search(
+ r"^(repo id\s+)(repo name\s+)status", line, re.IGNORECASE
+ )
+
+ if col1 and col2:
+ tableRow = re.search(r"^(.{%s})(.{%s})(.*)$" % (col1, col2), line)
+
+ if insideTable and tableRow:
+ output["repos"].append(
+ {
+ "repo": tableRow.group(1).strip(),
+ "repo_name": tableRow.group(2).strip(),
+ "status": tableRow.group(3).strip(),
+ }
+ )
+ continue
+
+ if tableHeader:
+ insideTable = True
+ col1 = len(tableHeader.group(1))
+ col2 = len(tableHeader.group(2))
+
+ else:
+ insideTable = False
+
+ if stderr:
+ for line in stderr.splitlines():
+ if re.search(r"http.*error .*", line, re.IGNORECASE):
+ if not line in output["errors"]:
+ output["errors"].append(line)
+
+ return {"output": output}
+
+
+def parser_installed(stdout, stderr, to_camelcase):
+ output = {"packages": [], "errors": []}
+ if stdout:
+ for line in stdout.splitlines():
+ package = re.search(r"^([\S\.]+)\.(\S+)\s+(\S+\.\S+)\s+(\S+.*)$", line)
+ if package:
+ output["packages"].append(
+ {
+ "name": package.group(1).strip(),
+ "arch": package.group(2).strip(),
+ "version": package.group(3).strip(),
+ "status": package.group(4).strip(),
+ }
+ )
+
+ if stderr:
+ for line in stderr.splitlines():
+ if re.search(r"http.*error .*", line, re.IGNORECASE):
+ if not line in output["errors"]:
+ output["errors"].append(line)
+
+ return {"output": output}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "dnf_repolist",
+ "system": ["linux"],
+ "cmd": "dnf repolist --all",
+ "description": "DNF - defined repositories",
+ "parser": parser_repolist,
+ }
+ )
+
+ main.register(
+ {
+ "name": "dnf_installed",
+ "system": ["linux"],
+ "cmd": "dnf list installed",
+ "description": "DNF - list installed packages",
+ "parser": parser_installed,
+ }
+ )
diff --git a/src/sysinfo/modules/driverquery.py b/src/sysinfo/modules/driverquery.py
new file mode 100644
index 0000000..e6dc62f
--- /dev/null
+++ b/src/sysinfo/modules/driverquery.py
@@ -0,0 +1,59 @@
+import re
+from sysinfo_lib import camelCase, fixMultilineAndSplit
+
+
+def parser_fo(stdout, stderr, to_camelcase):
+ output = []
+ unprocessed = []
+ image = {}
+
+ if stdout:
+ data = fixMultilineAndSplit(stdout, r"^\s+", ", ")
+
+ for line in data:
+ if line.strip() == "":
+ continue
+
+ kv = re.search(r"^([^:]+):\s*(.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1).strip(), to_camelcase)
+ value = kv.group(2).strip().replace("\u00a0", "")
+
+ if (
+ key == "moduleName"
+ or key == "Module Name"
+ or key == "deviceName"
+ or key == "DeviceName"
+ ):
+ image = {}
+ output.append(image)
+
+ image[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+
+ main.register(
+ {
+ "name": "driverquery",
+ "system": ["windows"],
+ "cmd": "%SystemRoot%\\system32\\driverquery.exe /v /fo list",
+ "description": "List of installed device drivers.",
+ "parser": parser_fo,
+ }
+ )
+
+ main.register(
+ {
+ "name": "driverquery_signed",
+ "system": ["windows"],
+ "cmd": "%SystemRoot%\\system32\\driverquery.exe /si /fo list",
+ "description": "List of installed device signed drivers.",
+ "parser": parser_fo,
+ }
+ )
diff --git a/src/sysinfo/modules/env.py b/src/sysinfo/modules/env.py
new file mode 100644
index 0000000..55f17f8
--- /dev/null
+++ b/src/sysinfo/modules/env.py
@@ -0,0 +1,29 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ lineMatch = re.search(r"^([^=]+)=(.*)$", line)
+ if lineMatch:
+ output[lineMatch.group(1)] = lineMatch.group(2)
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "env",
+ "system": ["linux"],
+ "cmd": "env",
+ "description": "Environment variables",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_default.py b/src/sysinfo/modules/etc_default.py
new file mode 100644
index 0000000..0f28803
--- /dev/null
+++ b/src/sysinfo/modules/etc_default.py
@@ -0,0 +1,58 @@
+import re
+
+
+def setPathValue(data, path, value):
+ pathRest = None
+ pathParts = re.search(r"^([^\/]+)\/?(.*)$", path)
+ if pathParts:
+ path = pathParts.group(1)
+ pathRest = pathParts.group(2)
+
+ if not path in data:
+ data[path] = {}
+
+ if pathRest:
+ setPathValue(data[path], pathRest, value)
+
+ else:
+ if isinstance(data[path], dict):
+ data[path] = []
+ data[path].append(value)
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ pathValue = re.search(r"^\/etc\/default\/([^:]+):(.*)$", line)
+ if pathValue:
+ path = pathValue.group(1)
+ value = pathValue.group(2)
+ if value.strip() == "":
+ continue
+
+ if not re.search(r"^\s*#", value):
+ setPathValue(output, path, value)
+ continue
+
+ if re.match(r"^\s*#", value):
+ # ignore line with comment
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_default",
+ "system": ["linux"],
+ "cmd": 'find /etc/default -type f -follow -print | xargs grep ""',
+ "description": "Default configuration for programs",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_fstab.py b/src/sysinfo/modules/etc_fstab.py
new file mode 100644
index 0000000..a7fbde0
--- /dev/null
+++ b/src/sysinfo/modules/etc_fstab.py
@@ -0,0 +1,39 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ if re.match(r"^\s*#", line):
+ continue
+
+ lineMatch = re.split(r"\s+", line)
+ if lineMatch and len(lineMatch) > 5:
+ output[lineMatch[0]] = {
+ "location": lineMatch[0],
+ "mountPoint": lineMatch[1],
+ "type": lineMatch[2],
+ "security": lineMatch[3],
+ "dump": lineMatch[4],
+ "fsckOrder": lineMatch[5],
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_fstab",
+ "system": ["linux"],
+ "cmd": "cat /etc/fstab",
+ "description": "Filesystems mounted on boot",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_group.py b/src/sysinfo/modules/etc_group.py
new file mode 100644
index 0000000..0f4e3e4
--- /dev/null
+++ b/src/sysinfo/modules/etc_group.py
@@ -0,0 +1,21 @@
+from sysinfo_lib import parseCharDelimitedTable, tableToDict
+
+
+def parser(stdout, stderr, to_camelcase):
+ columnsNames = ["groupName", "password", "gid", "groupList"]
+ output = parseCharDelimitedTable(stdout, ":", columnsNames)
+ output = tableToDict(output, "groupName")
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_group",
+ "system": ["linux"],
+ "cmd": "cat /etc/group",
+ "description": "Groups essential information",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_hosts.py b/src/sysinfo/modules/etc_hosts.py
new file mode 100644
index 0000000..1c07c48
--- /dev/null
+++ b/src/sysinfo/modules/etc_hosts.py
@@ -0,0 +1,74 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ values = re.search(r"^\/etc\/([^\:]+):\s*(.*)$", line)
+ if values:
+ group = values.group(1)
+ value = values.group(2).strip()
+
+ if value == "":
+ continue
+
+ if re.match(r"\s*#", value):
+ # ignore line with comment
+ continue
+
+ if group not in output:
+ output[group] = []
+
+ if group in ["hosts"]:
+ parts = re.search(r"^(\S+)\s+([^#]+)#?(.*)$", value)
+ if parts:
+ ip = parts.group(1).strip()
+ hostnames = parts.group(2).strip()
+ comment = parts.group(3).strip()
+
+ output[group].append(
+ {
+ "ip": ip,
+ "hostnames": re.split(r"\s+", hostnames),
+ "comment": comment,
+ }
+ )
+ continue
+
+ if group in ["hosts.allow", "hosts.deny"]:
+ print("value", value)
+ parts = re.search(r"^([^:]+):\s*([^:]+):?([^#]+)#?(.*)$", value)
+ if parts:
+ daemon_list = parts.group(1).strip().split(",")
+ client_list = parts.group(2).strip().split(",")
+ command = parts.group(3).strip()
+ comment = parts.group(4).strip()
+
+ output[group].append(
+ {
+ "daemonList": daemon_list,
+ "clientList": client_list,
+ "command": command,
+ "comment": comment,
+ }
+ )
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_hosts",
+ "system": ["linux"],
+ "cmd": """grep "" /etc/hosts*""",
+ "description": "Maps hostnames to IP addresses",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_locale_gen.py b/src/sysinfo/modules/etc_locale_gen.py
new file mode 100644
index 0000000..6792e8f
--- /dev/null
+++ b/src/sysinfo/modules/etc_locale_gen.py
@@ -0,0 +1,30 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = []
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ values = re.search(r"^\s*([^#]+)", line)
+ if not values:
+ continue
+
+ value = values.group(1).strip()
+ if value != "":
+ output.append(value)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_locale_gen",
+ "system": ["linux"],
+ "cmd": "cat /etc/locale.gen",
+ "description": "Configuration file for locale-gen",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_mtab.py b/src/sysinfo/modules/etc_mtab.py
new file mode 100644
index 0000000..f08ed57
--- /dev/null
+++ b/src/sysinfo/modules/etc_mtab.py
@@ -0,0 +1,50 @@
+import re
+
+
+def parse_mount_options(value):
+ output = {}
+ for option in re.split(r"\s*,\s*", value):
+ dir = re.search(r"^([^=]+)=(.*)$", option)
+
+ if dir:
+ output[dir.group(1)] = dir.group(2).split(":")
+
+ else:
+ output[option] = True
+
+ return output
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ values = re.split(r"\s+", line)
+ if len(values) > 5:
+ output[values[1]] = {
+ "partition": values[0],
+ "mountPoint": values[1],
+ "fileSystem": values[2],
+ "mountOptions": parse_mount_options(values[3]),
+ "dump": values[4],
+ "fsckOrder": values[5],
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_mtab",
+ "system": ["linux"],
+ "cmd": "cat /etc/mtab",
+ "description": "Currently mounted filesystems",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_passwd.py b/src/sysinfo/modules/etc_passwd.py
new file mode 100644
index 0000000..e053172
--- /dev/null
+++ b/src/sysinfo/modules/etc_passwd.py
@@ -0,0 +1,20 @@
+from sysinfo_lib import parseCharDelimitedTable, tableToDict
+
+
+def parser(stdout, stderr, to_camelcase):
+ columnsNames = ["username", "password", "uid", "gid", "idInfo", "homeDir", "shell"]
+ output = parseCharDelimitedTable(stdout, ":", columnsNames)
+ output = tableToDict(output, "username")
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_passwd",
+ "system": ["linux"],
+ "cmd": "cat /etc/passwd",
+ "description": "Attributes of each user or account on a computer",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_release.py b/src/sysinfo/modules/etc_release.py
new file mode 100644
index 0000000..91cb598
--- /dev/null
+++ b/src/sysinfo/modules/etc_release.py
@@ -0,0 +1,32 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ values = re.search(r"^([^=]+)=(.*)$", line)
+ if values:
+ key = camelCase(values.group(1), to_camelcase)
+ value = values.group(2).strip().strip('"')
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_release",
+ "system": ["linux"],
+ "cmd": "cat /etc/*release",
+ "description": "OS release info",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_shadow.py b/src/sysinfo/modules/etc_shadow.py
new file mode 100644
index 0000000..fd7ba57
--- /dev/null
+++ b/src/sysinfo/modules/etc_shadow.py
@@ -0,0 +1,30 @@
+from sysinfo_lib import parseCharDelimitedTable, tableToDict
+
+
+def parser(stdout, stderr, to_camelcase):
+ columnsNames = [
+ "username",
+ "password",
+ "lastPasswordChange",
+ "minimum",
+ "maximum",
+ "warn",
+ "inactive",
+ "expire",
+ ]
+ output = parseCharDelimitedTable(stdout, ":", columnsNames)
+ output = tableToDict(output, "username")
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_shadow",
+ "system": ["linux"],
+ "cmd": "cat /etc/shadow",
+ "description": "Shadow database of the passwd file",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/etc_timezone.py b/src/sysinfo/modules/etc_timezone.py
new file mode 100644
index 0000000..49f009c
--- /dev/null
+++ b/src/sysinfo/modules/etc_timezone.py
@@ -0,0 +1,19 @@
+def parser(stdout, stderr, to_camelcase):
+ output = ""
+
+ if stdout:
+ output = stdout.strip()
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "etc_timezone",
+ "system": ["linux"],
+ "cmd": "cat /etc/timezone",
+ "description": "Timezone settings",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/ethtool.py b/src/sysinfo/modules/ethtool.py
new file mode 100644
index 0000000..a795321
--- /dev/null
+++ b/src/sysinfo/modules/ethtool.py
@@ -0,0 +1,40 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+ user = None
+
+ if stdout:
+ for line in stdout.splitlines():
+ user_match = re.search(r"^>>> User:\s+(.*)$", line)
+ if user_match:
+ user = user_match.group(1)
+ output[user] = {"name": user}
+ continue
+
+ kv = re.search(r"^([^:]+):\s*(.*)$", line)
+ if user and kv:
+ key = camelCase(kv.group(1).strip(), to_camelcase)
+ value = kv.group(2).strip()
+
+ output[user][key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "chage",
+ "system": ["linux"],
+ "cmd": """ifconfig -a -s | grep -v "Iface" | awk '{split($0,a," "); print a[1]}' | xargs -I {} sh -c "echo '>>> Device: {}'; ethtool -i {}" """,
+ "description": "Users password expiration information",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/fbset.py b/src/sysinfo/modules/fbset.py
new file mode 100644
index 0000000..9f44b7a
--- /dev/null
+++ b/src/sysinfo/modules/fbset.py
@@ -0,0 +1,127 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ modeName = ""
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ mode = re.search(r'^\s*mode "([^"]+)\s*"$', line)
+ if mode:
+ modeName = mode.group(1)
+ output[modeName] = {}
+ continue
+
+ endmode = re.search(r"^\s*endmode", line)
+ if endmode:
+ modeName = ""
+ continue
+
+ if modeName:
+ geometry = re.search(
+ r"^\s+geometry\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*$", line
+ )
+ if geometry:
+ output[modeName]["geometry"] = {
+ "xres": geometry.group(1),
+ "yres": geometry.group(2),
+ "xresVirtual": geometry.group(3),
+ "yresVirtual": geometry.group(4),
+ "bitsPerPixel": geometry.group(5),
+ }
+ continue
+
+ timings = re.search(
+ r"^\s+timings\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*(\d+)\s*$",
+ line,
+ )
+ if timings:
+ output[modeName]["timings"] = {
+ "pixclock": timings.group(1),
+ "leftMargin": timings.group(2),
+ "rightMargin": timings.group(3),
+ "upperMargin": timings.group(4),
+ "lowerMargin": timings.group(5),
+ "hsyncLen": timings.group(6),
+ "vsyncLen": timings.group(7),
+ }
+ continue
+
+ rgba = re.search(
+ r"^\s+rgba\s*(\d+)\/(\d+),(\d+)\/(\d+),(\d+)\/(\d+),(\d+)\/(\d+)\s*$",
+ line,
+ )
+ if rgba:
+ output[modeName]["rgba"] = {
+ "redLength": rgba.group(1),
+ "redOffset": rgba.group(2),
+ "greenLength": rgba.group(3),
+ "greenOffset": rgba.group(4),
+ "blueLength": rgba.group(5),
+ "blueOffset": rgba.group(6),
+ "transpLength": rgba.group(7),
+ "transpOffset": rgba.group(8),
+ }
+ continue
+
+ state = re.search(
+ r"^\s+(interlaced|double|vsync|hsync|csync|extsync)\s+(\S+)\s*$",
+ line,
+ )
+ if state:
+ output[modeName][state.group(1)] = state.group(2)
+ continue
+
+ kv = re.search(r"^\s+(\S+)\s+(.*)$", line)
+ if kv:
+ output[modeName][kv.group(1)] = kv.group(2)
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def parserInfo(stdout, stderr, to_camelcase):
+ output = parser(stdout, stderr, to_camelcase)
+ output["output"]["info"] = {}
+
+ informationBlock = False
+ if stdout:
+ for line in stdout.splitlines():
+ info = re.search(r"^\s*Frame buffer device information:\s*$", line)
+ if info:
+ informationBlock = True
+ continue
+
+ if informationBlock:
+ keyValue = re.search(r"^\s+(\S+)\s*:\s+(.*)\s*$", line)
+ if keyValue:
+ output["output"]["info"][keyValue.group(1)] = keyValue.group(2)
+ continue
+
+ return output
+
+
+def register(main):
+ main.register(
+ {
+ "name": "fbset",
+ "system": ["linux"],
+ "cmd": "fbset -a",
+ "description": "Show frame buffer device settings",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "fbset_info",
+ "system": ["linux"],
+ "cmd": "fbset -i",
+ "description": "Show frame buffer device information",
+ "parser": parserInfo,
+ }
+ )
diff --git a/src/sysinfo/modules/findmnt.py b/src/sysinfo/modules/findmnt.py
new file mode 100644
index 0000000..66790ae
--- /dev/null
+++ b/src/sysinfo/modules/findmnt.py
@@ -0,0 +1,40 @@
+from sysinfo_lib import parseTable
+import re
+
+
+def parse_mount_options(value):
+ output = {}
+ for option in re.split(r"\s*,\s*", value):
+ dir = re.search(r"^([^=]+)=(.*)$", option)
+
+ if dir:
+ output[dir.group(1)] = dir.group(2).split(":")
+
+ else:
+ output[option] = True
+
+ return output
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ if stdout:
+ output = parseTable(stdout, to_camelcase=to_camelcase)
+
+ for entry in output:
+ if "options" in entry:
+ entry["options"] = parse_mount_options(entry["options"])
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "findmnt",
+ "system": ["linux"],
+ "cmd": "findmnt -Al | column -t",
+ "description": "List all mounted filesytems",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/free.py b/src/sysinfo/modules/free.py
new file mode 100644
index 0000000..684f284
--- /dev/null
+++ b/src/sysinfo/modules/free.py
@@ -0,0 +1,43 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ columns = None
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ if re.search(r"total\s+used", line, re.IGNORECASE):
+ columns = re.split(r"\s+", line.strip())
+ continue
+
+ entrySearch = re.search(r"^([^:]+):\s+(.*)$", line)
+ if columns and entrySearch:
+ type = camelCase(entrySearch.group(1), to_camelcase)
+ output[type] = {}
+ for idx, value in enumerate(
+ re.split(r"\s+", entrySearch.group(2).strip())
+ ):
+ if idx < len(columns):
+ key = camelCase(columns[idx], to_camelcase)
+ output[type][key] = value
+
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "free",
+ "system": ["linux"],
+ "cmd": "free -b -l -w",
+ "description": "Amount of free and used memory in the system",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/getconf.py b/src/sysinfo/modules/getconf.py
new file mode 100644
index 0000000..2c2c00a
--- /dev/null
+++ b/src/sysinfo/modules/getconf.py
@@ -0,0 +1,33 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ kv = re.search(r"^(\S+)\s*(.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2).strip()
+
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "getconf",
+ "system": ["linux"],
+ "cmd": "getconf -a",
+ "description": "Configuration variables for the current system and their values",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/hostnamectl.py b/src/sysinfo/modules/hostnamectl.py
new file mode 100644
index 0000000..fb95070
--- /dev/null
+++ b/src/sysinfo/modules/hostnamectl.py
@@ -0,0 +1,33 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ kv = re.search(r"^([^:]+):\s*(.*)", line)
+ if kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2)
+
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "hostnamectl",
+ "system": ["linux"],
+ "cmd": "hostnamectl status",
+ "description": "Current system hostname and related information",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/ifconfig.py b/src/sysinfo/modules/ifconfig.py
new file mode 100644
index 0000000..7f4d9ea
--- /dev/null
+++ b/src/sysinfo/modules/ifconfig.py
@@ -0,0 +1,90 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def extractValue(data, line, key, regExp):
+ search = re.search(regExp, line, re.IGNORECASE)
+ if search:
+ data[key] = search.group(1)
+ return data
+
+
+def extractValues(data, line, to_camelcase):
+ for pair in re.split(r"\s\s+", line):
+ desc = re.search(r"^(.*)\((.*)\)$", pair.strip())
+ if desc:
+ data["description"] = desc.group(2)
+ if desc.group(1):
+ pair = desc.group(1).strip()
+ else:
+ continue
+
+ kv = re.search(r"^([^\s]+)\s(\S.*)$", pair)
+ if kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2).strip()
+ valueFix = re.search(r"^(\S+)\s+(\S+)\s(\S+)$", value)
+
+ if valueFix:
+ data[key] = valueFix.group(1)
+ data[valueFix.group(2)] = valueFix.group(3)
+ else:
+ data[key] = value
+
+ return extractValues
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ blockData = {}
+ unprocessed = []
+
+ if stdout:
+ for block in re.split(r"\r\r|\n\n|\r\n\r\n", stdout):
+ blockData = {"entries": [], "rx": {}, "tx": {}}
+
+ for line in block.splitlines():
+ header = re.search(r"^(\S[^:]+):\s*(.*)$", line)
+ if header:
+ name = header.group(1)
+ blockData["name"] = name
+ extractValue(blockData, line, "flags", r"flags=(\S+)")
+ extractValues(blockData, header.group(2), to_camelcase)
+ output[name] = blockData
+ continue
+
+ rxTx = re.search(r"^\s+([rt]x)\s+(.*)$", line, re.IGNORECASE)
+ if rxTx:
+ type = rxTx.group(1).lower()
+ extractValues(blockData[type], rxTx.group(2), to_camelcase)
+ continue
+
+ sub = re.search(r"^\s+(\S+)\s\s(.*)$", line, re.IGNORECASE)
+ if sub:
+ subData = {"type": sub.group(1)}
+ extractValues(subData, sub.group(2), to_camelcase)
+ blockData["entries"].append(subData)
+ continue
+
+ sub = re.search(r"^\s+(\S+)\s(\S+)\s\s(.*)$", line, re.IGNORECASE)
+ if sub:
+ subData = {"type": sub.group(1), "value": sub.group(2)}
+ extractValues(subData, sub.group(3), to_camelcase)
+ blockData["entries"].append(subData)
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "ifconfig",
+ "system": ["linux"],
+ "cmd": "ifconfig -a -v",
+ "description": "List all interfaces which are currently available, even if down",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/lsblk.py b/src/sysinfo/modules/lsblk.py
new file mode 100644
index 0000000..ab07659
--- /dev/null
+++ b/src/sysinfo/modules/lsblk.py
@@ -0,0 +1,42 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ entry = {}
+ kv = re.findall(r'(\S[^=]+)=\"([^"]*)\"', line)
+ if kv:
+ for pair in kv:
+ key = camelCase(pair[0], to_camelcase)
+ value = pair[1].strip()
+
+ entry[key] = value
+
+ if "name" in entry:
+ output[entry["name"]] = entry
+ continue
+
+ elif "NAME" in entry:
+ output[entry["NAME"]] = entry
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "lsblk",
+ "system": ["linux"],
+ "cmd": "lsblk -P -o NAME,KNAME,MAJ:MIN,FSTYPE,MOUNTPOINT,LABEL,UUID,PARTLABEL,PARTUUID,RA,RO,RM,MODEL,SERIAL,SIZE,STATE,OWNER,GROUP,MODE,ALIGNMENT,MIN-IO,OPT-IO,PHY-SEC,LOG-SEC,ROTA,SCHED,RQ-SIZE,TYPE,DISC-ALN,DISC-GRAN,DISC-MAX,DISC-ZERO,WSAME,WWN,RAND,PKNAME,HCTL,TRAN,REV,VENDOR",
+ "description": "Lists information about all block devices",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/lscpu.py b/src/sysinfo/modules/lscpu.py
new file mode 100644
index 0000000..a079b13
--- /dev/null
+++ b/src/sysinfo/modules/lscpu.py
@@ -0,0 +1,34 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ line = re.sub(r"\(s\)", "s", line)
+ kv = re.search(r"^([^:]+):\s*(.*)", line)
+ if kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2)
+
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "lscpu",
+ "system": ["linux"],
+ "cmd": "lscpu",
+ "description": "Information about the CPU architecture",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/lsmod.py b/src/sysinfo/modules/lsmod.py
new file mode 100644
index 0000000..d86416e
--- /dev/null
+++ b/src/sysinfo/modules/lsmod.py
@@ -0,0 +1,42 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ if re.match(r"Module.*Size", line):
+ continue
+
+ lineMatch = re.search(r"^(^\S+)\s*(\d+)\s*(\d+)\s*(.*)$", line)
+ if lineMatch:
+ used_by = lineMatch.group(4).split(",")
+ if len(used_by) == 1:
+ if used_by[0] == "":
+ used_by = []
+
+ output[lineMatch.group(1)] = {
+ "module": lineMatch.group(1),
+ "size": lineMatch.group(2),
+ "usedNumber": lineMatch.group(3),
+ "usedBy": used_by,
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "lsmod",
+ "system": ["linux"],
+ "cmd": "lsmod",
+ "description": "Show the status of modules in the Linux Kernel",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/lsns.py b/src/sysinfo/modules/lsns.py
new file mode 100644
index 0000000..55964cb
--- /dev/null
+++ b/src/sysinfo/modules/lsns.py
@@ -0,0 +1,26 @@
+from sysinfo_lib import parseTable
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = parseTable(
+ stdout,
+ header_pattern=r"^(\s*NS)(\sTYPE\s+)(\sPATH\s*)(\s\s*NPROCS)(\s*\sPID)(\s*\sPPID)(\s*\sUID)(\sUSER\s*)(\sCOMMAND\s*)",
+ to_camelcase=to_camelcase,
+ )
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "lsns",
+ "system": ["linux"],
+ "cmd": "lsns -o NS,TYPE,PATH,NPROCS,PID,PPID,UID,USER,COMMAND",
+ "description": "Block device ioctls",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/lsof.py b/src/sysinfo/modules/lsof.py
new file mode 100644
index 0000000..64e3f69
--- /dev/null
+++ b/src/sysinfo/modules/lsof.py
@@ -0,0 +1,109 @@
+import re
+
+opField = {
+ "a": "accessMode",
+ "c": "commandName",
+ "C": "structureShareCount",
+ "d": "deviceCharacterCode",
+ "D": "majorMinorDeviceNumber",
+ "f": "fileDescriptor",
+ "F": "structureAddress",
+ "g": "processGroupId",
+ "G": "flags",
+ "i": "inodeNumber",
+ "k": "linkCount",
+ "K": "taskId",
+ "l": "lockStatus",
+ "L": "loginName",
+ "m": "markerBetweenRepeatedOutput",
+ "n": "name",
+ "N": "nodeIdentifier",
+ "o": "fileOffset",
+ "p": "processId",
+ "P": "protocolName",
+ "r": "rawDeviceNumber",
+ "R": "parentPid",
+ "s": "fileSize",
+ "S": "streamModuleAndDeviceNames",
+ "t": "fileType",
+ "T": "tcpTpiInfo",
+ "u": "userId",
+ "z": "zoneName",
+ "Z": "selinuxSecurityContext",
+}
+
+tcptpiField = {
+ "QR": "readQueueSize",
+ "QS": "sendQueueSize",
+ "SO": "socketOptionsAndValues",
+ "SS": "socketStates",
+ "ST": "connectionState",
+ "TF": "tcpFlagsAndValues",
+ "WR": "windowReadSize",
+ "WW": "windowWriteSize",
+}
+
+
+def parseElements(elements):
+ global opField
+ global tcptpiField
+ output = {}
+ for el in elements:
+ ident = el[0:1]
+ content = el[1:].strip()
+
+ identType = opField.get(ident, ident)
+ if identType:
+ if not identType in output:
+ output[identType] = {}
+
+ if ident == "T":
+ fifc = (content + "=").split("=")
+ fifcType = tcptpiField.get(fifc[0], fifc[0])
+
+ output[identType][fifcType] = fifc[1]
+
+ else:
+ output[identType] = content
+
+ return output
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ pid = None
+
+ if stdout:
+ for line in re.split(r"\x00\n", stdout):
+ line = re.sub(r"^[\s\x00]*", "", line)
+ elements = re.split(r"\x00", line)
+ if not elements:
+ continue
+
+ first = elements.pop(0)
+ if first:
+ ident = first[0:1]
+ content = first[1:]
+
+ if ident == "p":
+ pid = content
+ output[pid] = parseElements(elements)
+ output[pid]["pid"] = pid
+ output[pid]["files"] = []
+
+ if pid and ident == "f":
+ output[pid]["files"].append(parseElements(elements))
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "lsof",
+ "system": ["linux"],
+ "cmd": "lsof -F0",
+ "description": "Information about files opened by processes",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/lspci.py b/src/sysinfo/modules/lspci.py
new file mode 100644
index 0000000..69292ef
--- /dev/null
+++ b/src/sysinfo/modules/lspci.py
@@ -0,0 +1,43 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ slot = None
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ if line.strip() == "":
+ continue
+
+ slotSearch = re.search(r"^Slot:\s+(.*)$", line, re.IGNORECASE)
+ if slotSearch:
+ slot = slotSearch.group(1).strip()
+ output[slot] = {}
+ continue
+
+ kv = re.search(r"^(\S[^:]+):\s+(.*)$", line)
+ if slot and kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2).strip()
+
+ output[slot][key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "lspci",
+ "system": ["linux"],
+ "cmd": "lspci -mm -vvv",
+ "description": "List all PCI devices",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/lsusb.py b/src/sysinfo/modules/lsusb.py
new file mode 100644
index 0000000..611559e
--- /dev/null
+++ b/src/sysinfo/modules/lsusb.py
@@ -0,0 +1,140 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def extractDescriptors(data, lineNumber, offset, to_camelcase):
+ output = {}
+ ln = lineNumber
+ descOffset = 0
+
+ while ln < len(data):
+ line = data[ln]
+
+ searchOffset = re.search(r"^(\s*)", line)
+ if searchOffset:
+ lineOffset = len(searchOffset.group(1))
+
+ if lineOffset < offset:
+ ln -= 1
+ break
+
+ searchDesc = re.search(r"^(\s*)(.*Descriptor):\s*$", line, re.IGNORECASE)
+ if searchDesc:
+ descOffset = len(searchDesc.group(1)) + 2
+ desc, ln = extractDescriptors(data, ln + 1, descOffset, to_camelcase)
+ name = camelCase(searchDesc.group(2).strip(), to_camelcase)
+ output[name] = desc
+ ln += 1
+ continue
+
+ searchStatus = re.search(r"^(\s*)(.*Status):\s*(.*)$", line, re.IGNORECASE)
+ if searchStatus:
+ statusOffset = len(searchStatus.group(1)) + 2
+ desc, ln = extractDescriptors(data, ln + 1, statusOffset, to_camelcase)
+ name = camelCase(searchStatus.group(2).strip(), to_camelcase)
+ if searchStatus.group(3).strip() != "":
+ desc["value"] = searchStatus.group(3).strip()
+
+ if name == "hubPortStatus":
+ print("desc", desc)
+ output[name] = desc
+ ln += 1
+ continue
+
+ searchKeyIndexValue = re.search(r"^\s*(\S+)\s+([0-9]+):\s+(.*)$", line)
+ if searchKeyIndexValue:
+ key = camelCase(searchKeyIndexValue.group(1), to_camelcase)
+ number = searchKeyIndexValue.group(2)
+ value = searchKeyIndexValue.group(3).strip()
+
+ valueSplit = re.search(r"^(\S+)\s+(\S.*)$", value)
+ if valueSplit:
+ value = [valueSplit.group(1), valueSplit.group(2)]
+
+ if not key in output:
+ output[key] = {}
+
+ output[key][number] = value
+
+ ln += 1
+ continue
+
+ searchKeyValue = re.search(r"^\s*(\S+)\s+([0-9].*)$", line)
+ if searchKeyValue:
+ key = camelCase(searchKeyValue.group(1).strip(":"), to_camelcase)
+ value = searchKeyValue.group(2).strip()
+
+ valueSplit = re.search(r"^(\S+)\s+(\S.*)$", value)
+ if valueSplit:
+ value = [valueSplit.group(1), valueSplit.group(2)]
+
+ if key in output:
+ output[key] = [output[key], value]
+ else:
+ output[key] = value
+
+ ln += 1
+ continue
+
+ if not "data" in output:
+ output["data"] = []
+
+ output["data"].append(line.strip())
+
+ ln += 1
+ return output, ln
+
+
+def parseBlock(data, to_camelcase):
+ output = {}
+ lines = data.split("\n")
+
+ while lines[0].strip() == "":
+ lines.pop(0)
+
+ firstLine = lines.pop(0)
+ busDevice = re.search(
+ r"Bus\s+(\S+)\s+Device\s+([^:]+):\s+ID\s+([^:]+):(\S+)\s*(.*)$",
+ firstLine,
+ re.IGNORECASE,
+ )
+ if busDevice:
+ output["bus"] = busDevice.group(1)
+ output["device"] = busDevice.group(2)
+ output["idVendor"] = busDevice.group(3)
+ output["idProduct"] = busDevice.group(4)
+ output["vendorProduct"] = busDevice.group(5)
+
+ output["desc"], tmp = extractDescriptors(lines, 0, 0, to_camelcase)
+
+ return output
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ delimiter = "-" * 20
+ blocks = re.split(
+ delimiter, re.sub(r"\n\nBus", "\n\n" + delimiter + "Bus", stdout)
+ )
+ if blocks:
+ for block in blocks:
+ blockData = parseBlock(block, to_camelcase)
+ if "bus" in blockData and "device" in blockData:
+ id = blockData["bus"] + "/" + blockData["device"]
+ output[id] = blockData
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "lsusb",
+ "system": ["linux"],
+ "cmd": "lsusb -v",
+ "description": "List USB devices",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/modinfo.py b/src/sysinfo/modules/modinfo.py
new file mode 100644
index 0000000..02bdfa2
--- /dev/null
+++ b/src/sysinfo/modules/modinfo.py
@@ -0,0 +1,43 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ moduleName = None
+ unprocessed = []
+
+ if stdout:
+ stdout_fix = re.sub(r"\n[\t]+", " ", stdout)
+
+ for line in stdout_fix.splitlines():
+ kv = re.search(r"^([^:]+):\s+(.*)$", line)
+ if kv:
+ key = kv.group(1)
+ value = kv.group(2)
+
+ if key == ">>> moduleName":
+ moduleName = value
+ output[moduleName] = {}
+ continue
+
+ if moduleName:
+ key = camelCase(key, to_camelcase)
+ output[moduleName][key] = value.strip()
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "modinfo",
+ "system": ["linux"],
+ "cmd": """lsmod | grep -v "Module" | sed 's/ .*//g' | xargs -I {} -n 1 sh -c "echo '>>> moduleName: {}'; modinfo {}" """,
+ "description": "Information about a Linux Kernel modules",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/parted.py b/src/sysinfo/modules/parted.py
new file mode 100644
index 0000000..8267ece
--- /dev/null
+++ b/src/sysinfo/modules/parted.py
@@ -0,0 +1,64 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ defaultUnit = None
+ path = None
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ if line.strip() == "":
+ defaultUnit = None
+ path = None
+ continue
+
+ unitSearch = re.search(r"^(\S+);$", line)
+ if unitSearch:
+ defaultUnit = unitSearch.group(1)
+ continue
+
+ lineSplit = (line.strip(";") + (":" * 10)).split(":")
+
+ if defaultUnit and re.search(r"^(\/[^:]+)", line):
+ path = lineSplit[0]
+ output[path] = {
+ "path": lineSplit[0],
+ "defaultUnit": defaultUnit,
+ "end": lineSplit[1],
+ "devType": lineSplit[2],
+ "sectorSize": lineSplit[3],
+ "physSectorSize": lineSplit[4],
+ "ptName": lineSplit[5],
+ "model": lineSplit[6],
+ "diskFlags": lineSplit[7],
+ "table": {},
+ }
+ continue
+
+ if path and re.search(r"^(\d+):", line):
+ output[path]["table"][lineSplit[0]] = {
+ "start": lineSplit[1],
+ "end": lineSplit[2],
+ "size": lineSplit[3],
+ "fileSystem": lineSplit[4],
+ "flags": lineSplit[5],
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "parted",
+ "system": ["linux"],
+ "cmd": "parted -m -l print",
+ "description": "Lists partition layout on all block devices",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_buddyinfo.py b/src/sysinfo/modules/proc_buddyinfo.py
new file mode 100644
index 0000000..13826f7
--- /dev/null
+++ b/src/sysinfo/modules/proc_buddyinfo.py
@@ -0,0 +1,37 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {"nodes": {}}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ row = re.search(r"Node\s([^,]+),\szone\s+(\S+)\s*(.*)$", line)
+ if row:
+ node = row.group(1)
+ zone = row.group(2)
+ value = re.split(r"\s+", row.group(3).strip())
+
+ if not node in output["nodes"]:
+ output["nodes"][node] = {}
+
+ output["nodes"][node][zone] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_buddyinfo",
+ "system": ["linux"],
+ "cmd": "cat /proc/buddyinfo",
+ "description": "Memory fragmentation",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_bus_input.py b/src/sysinfo/modules/proc_bus_input.py
new file mode 100644
index 0000000..1b77207
--- /dev/null
+++ b/src/sysinfo/modules/proc_bus_input.py
@@ -0,0 +1,86 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def extract_params(entry, params, to_camelcase):
+ patterns = [
+ r"(\S[^=]+)=(\S+)",
+ r'(\S[^=]+)=\"([^"]*)\"',
+ r"(\S[^=]+)=(\s|$)",
+ ]
+
+ for pattern in patterns:
+ kv = re.findall(pattern, params)
+ if kv:
+ for pair in kv:
+ key = camelCase(pair[0], to_camelcase)
+ value = pair[1]
+ entry[key] = value
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {"devices": [], "handlers": {}}
+ unprocessed = []
+ types = {
+ "I": "deviceId",
+ "N": "name",
+ "P": "physicalPath",
+ "S": "sysfsPath",
+ "U": "uid",
+ "H": "inputHandlers",
+ "B": "bitmaps",
+ }
+
+ if stdout:
+ [devices, handlers] = stdout.split(">>> handlers")
+
+ print(devices)
+
+ for block in re.split(r"\r\r|\n\n|\r\n\r\n", devices):
+ blockData = {}
+
+ for line in block.splitlines():
+ parts = re.search(r"^(\w):\s+(.*)$", line)
+ if parts:
+ type = parts.group(1).strip()
+ params = parts.group(2).strip()
+
+ if type in types:
+ type_label = types[type]
+ if not type_label in blockData:
+ blockData[type_label] = {}
+
+ extract_params(blockData[type_label], params, to_camelcase)
+
+ output["devices"].append(blockData)
+
+ for line in handlers.splitlines():
+ line = re.sub(r"^N: ", "", line)
+
+ kv = re.findall(r"(\S[^=]+)=(\S+)", line)
+ if kv:
+ entry = {}
+ for pair in kv:
+ key = camelCase(pair[0], to_camelcase)
+ value = pair[1]
+ entry[key] = value
+
+ if "name" in entry:
+ output["handlers"][entry["name"]] = entry
+
+ if "Name" in entry:
+ output["handlers"][entry["Name"]] = entry
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_bus_input",
+ "system": ["linux"],
+ "cmd": 'cat /proc/bus/input/devices; echo ">>> handlers"; cat /proc/bus/input/handlers;',
+ "description": "Input devices",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_cgroups.py b/src/sysinfo/modules/proc_cgroups.py
new file mode 100644
index 0000000..bd63d3d
--- /dev/null
+++ b/src/sysinfo/modules/proc_cgroups.py
@@ -0,0 +1,28 @@
+from sysinfo_lib import parseSpaceTable, tableToDict
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ stdout = stdout.replace("#subsys_name", "subsys_name")
+
+ output = parseSpaceTable(stdout, to_camelcase=to_camelcase)
+ if to_camelcase:
+ output = tableToDict(output, "subsysName")
+ else:
+ output = tableToDict(output, "subsys_name")
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_cgroups",
+ "system": ["linux"],
+ "cmd": "cat /proc/cgroups",
+ "description": "Control groups",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_cmdline.py b/src/sysinfo/modules/proc_cmdline.py
new file mode 100644
index 0000000..8331c4c
--- /dev/null
+++ b/src/sysinfo/modules/proc_cmdline.py
@@ -0,0 +1,40 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ for kv in re.split(r"[\s\t]+", stdout.strip()):
+ splitted = re.search(r"^([^=]+)=(.*)$", kv)
+ if splitted:
+ key = camelCase(splitted.group(1), to_camelcase)
+ value = splitted.group(2)
+
+ else:
+ key = kv
+ value = ""
+
+ if key in output:
+ if isinstance(output[key], str):
+ output[key] = [output[key]]
+
+ output[key].append(value)
+
+ else:
+ output[key] = value
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_cmdline",
+ "system": ["linux"],
+ "cmd": "cat /proc/cmdline",
+ "description": "Parameters passed to the kernel at the time it is started",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_consoles.py b/src/sysinfo/modules/proc_consoles.py
new file mode 100644
index 0000000..5b0e4ad
--- /dev/null
+++ b/src/sysinfo/modules/proc_consoles.py
@@ -0,0 +1,62 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ """
+ The columns are:
+ device name of the device
+ operations R = can do read operations
+ W = can do write operations
+ U = can do unblank
+ flags E = it is enabled
+ C = it is preferred console
+ B = it is primary boot console
+ p = it is used for printk buffer
+ b = it is not a TTY but a Braille device
+ a = it is safe to use when cpu is offline
+ major:minor major and minor number of the device separated by a colon
+ """
+
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ values = re.search(r"^(\S+)\s+(.*)\s+(\S+):(\S+)", line)
+ if values:
+ params = values.group(2).strip()
+ output[values.group(1)] = {
+ "device": values.group(1),
+ "operations": {
+ "read": "R" in params,
+ "write": "W" in params,
+ "unblank": "U" in params,
+ },
+ "flags": {
+ "enabled": "E" in params,
+ "preferred": "C" in params,
+ "primaryBoot": "B" in params,
+ "printkBuffer": "p" in params,
+ "braile": "b" in params,
+ "safeCpuOffline": "a" in params,
+ },
+ "major": values.group(3),
+ "minor": values.group(4),
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_consoles",
+ "system": ["linux"],
+ "cmd": "cat /proc/consoles",
+ "description": "Information about current consoles including tty",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_cpuinfo.py b/src/sysinfo/modules/proc_cpuinfo.py
new file mode 100644
index 0000000..b992895
--- /dev/null
+++ b/src/sysinfo/modules/proc_cpuinfo.py
@@ -0,0 +1,44 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {"processor": {}, "hardware": {}, "oth": {}}
+ unprocessed = []
+
+ if stdout:
+ for block in re.split(r"\r\r|\n\n|\r\n\r\n", stdout):
+ sub = {}
+ for line in block.splitlines():
+ kv = re.search(r"([^\t]+)\s*:\s*(.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1).strip(), to_camelcase)
+ value = kv.group(2).strip()
+
+ sub[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ if "processor" in sub:
+ output["processor"][sub["processor"]] = sub
+
+ elif "hardware" in sub:
+ output["hardware"] = sub
+
+ else:
+ output["oth"] = sub
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_cpuinfo",
+ "system": ["linux"],
+ "cmd": "cat /proc/cpuinfo",
+ "description": "Type of processor used by your system",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_crypto.py b/src/sysinfo/modules/proc_crypto.py
new file mode 100644
index 0000000..3734989
--- /dev/null
+++ b/src/sysinfo/modules/proc_crypto.py
@@ -0,0 +1,38 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for block in re.split(r"\r\r|\n\n|\r\n\r\n", stdout):
+ sub = {}
+ for line in block.splitlines():
+ kv = re.search(r"([^\t]+)\s*:\s*(.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1).strip(), to_camelcase)
+ value = kv.group(2).strip()
+
+ sub[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ if "name" in sub:
+ output[sub["name"]] = sub
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_crypto",
+ "system": ["linux"],
+ "cmd": "cat /proc/crypto",
+ "description": "Installed cryptographic ciphers used by the Linux kernel",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_devices.py b/src/sysinfo/modules/proc_devices.py
new file mode 100644
index 0000000..4c629e2
--- /dev/null
+++ b/src/sysinfo/modules/proc_devices.py
@@ -0,0 +1,43 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ deviceType = None
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ dt = re.search(r"^([^:]+):", line)
+ if dt:
+ deviceType = camelCase(dt.group(1), to_camelcase)
+ output[deviceType] = {}
+ continue
+
+ dv = re.search(r"^\s*(\d+)\s*(.*)$", line)
+ if dv and deviceType:
+ id = dv.group(1)
+ name = dv.group(2)
+
+ if not name in output[deviceType]:
+ output[deviceType][name] = []
+
+ output[deviceType][name].append(id)
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_devices",
+ "system": ["linux"],
+ "cmd": "cat /proc/devices",
+ "description": "Installed cryptographic ciphers used by the Linux kernel",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_diskstats.py b/src/sysinfo/modules/proc_diskstats.py
new file mode 100644
index 0000000..1555471
--- /dev/null
+++ b/src/sysinfo/modules/proc_diskstats.py
@@ -0,0 +1,51 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ columnNames = [
+ "majorNumber",
+ "minorNumber",
+ "deviceName",
+ "readsCompletedSuccessfully",
+ "readsMerged",
+ "sectorsRead",
+ "timeSpentReading",
+ "writesCompleted",
+ "writesMerged",
+ "sectorsWritten",
+ "timeSpentWriting",
+ "IOsCurrentlyInProgress",
+ "timeSpentDoingIOs",
+ "weightedTimeSpentDoingIOs",
+ ]
+ lenColumnNames = len(columnNames)
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ line = line.strip()
+ columns = re.split(r"\s+", line)
+ if columns:
+ output[columns[2]] = {}
+ for num, val in enumerate(columns, start=0):
+ if num < lenColumnNames:
+ output[columns[2]][columnNames[num]] = val
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_diskstats",
+ "system": ["linux"],
+ "cmd": "cat /proc/diskstats",
+ "description": "I/O statistics of block devices",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_dma.py b/src/sysinfo/modules/proc_dma.py
new file mode 100644
index 0000000..be7745a
--- /dev/null
+++ b/src/sysinfo/modules/proc_dma.py
@@ -0,0 +1,32 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ kv = re.search(r"^\s*([^:]+):\s*(.*)$", line)
+ if kv:
+ key = kv.group(1).strip()
+ value = kv.group(2).strip()
+
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_dma",
+ "system": ["linux"],
+ "cmd": "cat /proc/dma",
+ "description": "List of the registered ISA DMA channels in use",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_filesystems.py b/src/sysinfo/modules/proc_filesystems.py
new file mode 100644
index 0000000..b72bfd9
--- /dev/null
+++ b/src/sysinfo/modules/proc_filesystems.py
@@ -0,0 +1,37 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ kv = re.search(r"^(\S+)\s+(\S+)", line)
+ if kv:
+ key = kv.group(2).strip()
+ value = kv.group(1).strip()
+
+ output[key] = value
+ continue
+
+ k = re.search(r"^\s+(\S+)$", line)
+ if k:
+ output[k.group(1).strip()] = ""
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_filesystems",
+ "system": ["linux"],
+ "cmd": "cat /proc/filesystems",
+ "description": "List of the file system types currently supported by the kernel",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_fs.py b/src/sysinfo/modules/proc_fs.py
new file mode 100644
index 0000000..a53e5c1
--- /dev/null
+++ b/src/sysinfo/modules/proc_fs.py
@@ -0,0 +1,60 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def setPathValue(data, path, value):
+ pathRest = None
+ pathParts = re.search(r"^([^\/]+)\/?(.*)$", path)
+ if pathParts:
+ path = camelCase(pathParts.group(1))
+ pathRest = pathParts.group(2)
+
+ if not path in data:
+ data[path] = {}
+
+ if pathRest:
+ setPathValue(data[path], pathRest, value)
+
+ else:
+ path = camelCase(path)
+ if isinstance(data[path], dict):
+ data[path] = []
+
+ data[path].append(value)
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ pathValue = re.search(r"^\/proc\/fs\/([^:]+):(.*)$", line)
+ if pathValue:
+ path = pathValue.group(1)
+ value = pathValue.group(2)
+
+ print(path, value)
+ # if not re.search(r"^\s*#", value):
+ # setPathValue(output, path, value)
+
+ # continue
+
+ # else:
+ # print(line)
+
+ # unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_fs",
+ "system": ["linux"],
+ "cmd": """find /proc/fs -type f -follow -print | xargs grep "" """,
+ "description": "File system parameters",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_iomem.py b/src/sysinfo/modules/proc_iomem.py
new file mode 100644
index 0000000..e98c4b6
--- /dev/null
+++ b/src/sysinfo/modules/proc_iomem.py
@@ -0,0 +1,35 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = []
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ values = re.search(r"^\s*([^-]+)-(\S+)\s*:\s*(.*)$", line)
+ if values:
+ output.append(
+ {
+ "from": values.group(1),
+ "to": values.group(2),
+ "device": values.group(3),
+ }
+ )
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_iomem",
+ "system": ["linux"],
+ "cmd": "cat /proc/iomem",
+ "description": """Map of the system's memory for each physical device""",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_ioports.py b/src/sysinfo/modules/proc_ioports.py
new file mode 100644
index 0000000..5ec0abd
--- /dev/null
+++ b/src/sysinfo/modules/proc_ioports.py
@@ -0,0 +1,37 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = []
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ entrySearch = re.search(
+ r"^\s*([^-]+)-(\S+)\s*:\s*(.*)$", line, re.IGNORECASE
+ )
+ if entrySearch:
+ output.append(
+ {
+ "from": entrySearch.group(1),
+ "to": entrySearch.group(2),
+ "device": entrySearch.group(3),
+ }
+ )
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_ioports",
+ "system": ["linux"],
+ "cmd": "cat /proc/ioports",
+ "description": "List of currently registered port regions used for input or output communication with a device",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_loadavg.py b/src/sysinfo/modules/proc_loadavg.py
new file mode 100644
index 0000000..67afe9e
--- /dev/null
+++ b/src/sysinfo/modules/proc_loadavg.py
@@ -0,0 +1,34 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ values = re.search(r"^(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)", stdout.strip())
+ if values:
+ output = {
+ "periodLast": values.group(1),
+ "period5minute": values.group(2),
+ "period15minute": values.group(3),
+ "processes": values.group(4),
+ "lastPid": values.group(5),
+ }
+
+ else:
+ unprocessed.append(stdout)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_loadavg",
+ "system": ["linux"],
+ "cmd": "cat /proc/loadavg",
+ "description": "Load average in regard to both the CPU and IO over time",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_locks.py b/src/sysinfo/modules/proc_locks.py
new file mode 100644
index 0000000..46483d9
--- /dev/null
+++ b/src/sysinfo/modules/proc_locks.py
@@ -0,0 +1,37 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ lineSplit = re.split(r"[\s\t]+", line)
+ if lineSplit and len(lineSplit) > 5:
+ output[lineSplit[0]] = {
+ "uid": lineSplit[0],
+ "class": lineSplit[1],
+ "lockType": lineSplit[2],
+ "allowAccessType": lineSplit[3],
+ "pid": lineSplit[4],
+ "fileID": lineSplit[5],
+ "lockedRegion": lineSplit[6],
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_locks",
+ "system": ["linux"],
+ "cmd": "cat /proc/locks",
+ "description": "Files currently locked by the kernel",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_meminfo.py b/src/sysinfo/modules/proc_meminfo.py
new file mode 100644
index 0000000..f0ece86
--- /dev/null
+++ b/src/sysinfo/modules/proc_meminfo.py
@@ -0,0 +1,41 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ kv = re.search(r"^([^:]+):\s*(.*)$", line, re.IGNORECASE)
+ if kv:
+ key = camelCase(kv.group(1).strip(":"), to_camelcase)
+ value = kv.group(2).strip()
+
+ valueSearch = re.search(r"(.*)\s+(.*)$", value)
+ if valueSearch:
+ output[key] = {
+ "value": valueSearch.group(1),
+ "type": valueSearch.group(2),
+ }
+ else:
+ output[key] = {"value": value, "type": ""}
+
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_meminfo",
+ "system": ["linux"],
+ "cmd": "cat /proc/meminfo",
+ "description": "Reports a large amount of valuable information about the systems RAM usage",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_modules.py b/src/sysinfo/modules/proc_modules.py
new file mode 100644
index 0000000..8ea2431
--- /dev/null
+++ b/src/sysinfo/modules/proc_modules.py
@@ -0,0 +1,36 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ lineSplit = re.split(r"[\s\t]+", line)
+ if lineSplit and len(lineSplit) > 5:
+ output[lineSplit[0]] = {
+ "moduleName": lineSplit[0],
+ "moduleMemorySize": lineSplit[1],
+ "numInstancesLoaded": lineSplit[2],
+ "depends": lineSplit[3].strip(",").split(","),
+ "state": lineSplit[4],
+ "kernelMemoryOffset": lineSplit[5],
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_modules",
+ "system": ["linux"],
+ "cmd": "cat /proc/modules",
+ "description": "List of all modules loaded into the kernel",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_mounts.py b/src/sysinfo/modules/proc_mounts.py
new file mode 100644
index 0000000..5835a84
--- /dev/null
+++ b/src/sysinfo/modules/proc_mounts.py
@@ -0,0 +1,39 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ lineSplit = re.split(r"[\s\t]+", line)
+ if lineSplit and len(lineSplit) > 4:
+ accessValues = {}
+ for access in re.split(r",", lineSplit[3]):
+ accessSplit = re.split(r"=", access + "=")
+ accessValues[accessSplit[0]] = accessSplit[1]
+
+ output[lineSplit[1]] = {
+ "device": lineSplit[0],
+ "mountPoint": lineSplit[1],
+ "type": lineSplit[2],
+ "access": accessValues,
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_mounts",
+ "system": ["linux"],
+ "cmd": "cat /proc/mounts",
+ "description": "List mounted filesystems (info provides from kernel)",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_net.py b/src/sysinfo/modules/proc_net.py
new file mode 100644
index 0000000..759e658
--- /dev/null
+++ b/src/sysinfo/modules/proc_net.py
@@ -0,0 +1,178 @@
+import struct
+import socket
+from sysinfo_lib import parseSpaceTable, tableToDict
+
+
+def parser_route(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = parseSpaceTable(stdout, to_camelcase=to_camelcase)
+ if to_camelcase:
+ output = tableToDict(output, "iface")
+ else:
+ output = tableToDict(output, "Iface")
+
+ return {"output": output, "unprocessed": []}
+
+
+def split_every_n(data, n):
+ return [data[i : i + n] for i in range(0, len(data), n)]
+
+
+def parse_ipv4_address(address):
+ hex_addr, hex_port = address.split(":")
+
+ addr_list = split_every_n(hex_addr, 2)
+ addr_list.reverse()
+ addr = ".".join(map(lambda x: str(int(x, 16)), addr_list))
+ port = str(int(hex_port, 16))
+
+ return addr, port
+
+
+def parse_ipv6_address(address):
+ hex_addr, hex_port = address.split(":")
+
+ addr = bytes.fromhex(hex_addr)
+ addr = struct.unpack(">IIII", addr)
+ addr = struct.pack("@IIII", *addr)
+ addr = socket.inet_ntop(socket.AF_INET6, addr)
+ port = str(int(hex_port, 16))
+
+ return addr, port
+
+
+def extend_address4(entry, name, name_addr, name_port):
+ if name in entry:
+ address, port = parse_ipv4_address(entry[name])
+ entry[name_addr] = address
+ entry[name_port] = port
+
+
+def extend_address6(entry, name, name_addr, name_port):
+ if name in entry:
+ address, port = parse_ipv6_address(entry[name])
+ entry[name_addr] = address
+ entry[name_port] = port
+
+
+def parser_tcp_udp(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = parseSpaceTable(stdout, to_camelcase=to_camelcase)
+
+ for entry in output:
+ extend_address4(entry, "local_address", "local_addr", "local_port")
+ extend_address4(entry, "rem_address", "rem_addr", "rem_port")
+ extend_address4(entry, "localAddress", "localAddr", "localPort")
+ extend_address4(entry, "remAddress", "remAddr", "remPort")
+
+ return {"output": output, "unprocessed": []}
+
+
+def parser_tcp_udp_6(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = parseSpaceTable(stdout, to_camelcase=to_camelcase)
+
+ for entry in output:
+ extend_address6(entry, "local_address", "local_addr", "local_port")
+ extend_address6(entry, "rem_address", "rem_addr", "rem_port")
+ extend_address6(entry, "localAddress", "localAddr", "localPort")
+ extend_address6(entry, "remAddress", "remAddr", "remPort")
+
+ return {"output": output, "unprocessed": []}
+
+
+def parser_arp(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = parseSpaceTable(stdout, to_camelcase=to_camelcase)
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_net_route",
+ "system": ["linux"],
+ "cmd": "cat /proc/net/route",
+ "description": "IP routing information",
+ "parser": parser_route,
+ }
+ )
+
+ main.register(
+ {
+ "name": "proc_net_ax25_route",
+ "system": ["linux"],
+ "cmd": "cat /proc/net/ax25_route",
+ "description": "AX25 routing information",
+ "parser": parser_route,
+ }
+ )
+
+ main.register(
+ {
+ "name": "proc_net_ipx_route",
+ "system": ["linux"],
+ "cmd": "cat /proc/net/ipx_route",
+ "description": "IPX routing information",
+ "parser": parser_route,
+ }
+ )
+
+ main.register(
+ {
+ "name": "proc_net_tcp",
+ "system": ["linux"],
+ "cmd": "cat /proc/net/tcp",
+ "description": "TCP socket table",
+ "parser": parser_tcp_udp,
+ }
+ )
+
+ main.register(
+ {
+ "name": "proc_net_udp",
+ "system": ["linux"],
+ "cmd": "cat /proc/net/udp",
+ "description": "UDP socket table",
+ "parser": parser_tcp_udp,
+ }
+ )
+
+ main.register(
+ {
+ "name": "proc_net_tcp6",
+ "system": ["linux"],
+ "cmd": "cat /proc/net/tcp6",
+ "description": "TCP6 socket table",
+ "parser": parser_tcp_udp_6,
+ }
+ )
+
+ main.register(
+ {
+ "name": "proc_net_udp6",
+ "system": ["linux"],
+ "cmd": "cat /proc/net/udp6",
+ "description": "UDP6 socket table",
+ "parser": parser_tcp_udp_6,
+ }
+ )
+
+ main.register(
+ {
+ "name": "proc_net_arp",
+ "system": ["linux"],
+ "cmd": "cat /proc/net/arp",
+ "description": "ARP ",
+ "parser": parser_arp,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_partitions.py b/src/sysinfo/modules/proc_partitions.py
new file mode 100644
index 0000000..ef93029
--- /dev/null
+++ b/src/sysinfo/modules/proc_partitions.py
@@ -0,0 +1,23 @@
+from sysinfo_lib import parseSpaceTable, tableToDict
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = parseSpaceTable(stdout, to_camelcase=to_camelcase)
+ output = tableToDict(output, "name")
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_partitions",
+ "system": ["linux"],
+ "cmd": "cat /proc/partitions",
+ "description": "Partition block allocation information",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_scsi.py b/src/sysinfo/modules/proc_scsi.py
new file mode 100644
index 0000000..a69b8e1
--- /dev/null
+++ b/src/sysinfo/modules/proc_scsi.py
@@ -0,0 +1,64 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ path = None
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ hostSearch = re.search(
+ r"^Host:\s+(\S+)\s+Channel:\s+(\S+)\s+Id:\s+(\S+)\s+Lun:\s+(\S+)$",
+ line,
+ re.IGNORECASE,
+ )
+ if hostSearch:
+ host = hostSearch.group(1)
+ channel = hostSearch.group(2)
+ id = hostSearch.group(3)
+ lun = hostSearch.group(4)
+ path = "%s:%s:%s:%s" % (host.replace("scsi", ""), channel, id, lun)
+ output[path] = {"host": host, "channel": channel, "id": id, "lun": lun}
+ continue
+
+ if path:
+ vendorSearch = re.search(
+ r"^\s+Vendor:\s+(.*)\s+Model:\s+(.*)\s+Rev:\s+(.*)$",
+ line,
+ re.IGNORECASE,
+ )
+ if vendorSearch:
+ output[path]["vendor"] = vendorSearch.group(1).strip()
+ output[path]["model"] = vendorSearch.group(2).strip()
+ output[path]["rev"] = vendorSearch.group(3).strip()
+ continue
+
+ typeSearch = re.search(
+ r"^\s+Type:\s+(.*)\s+ANSI\s+SCSI\s+revision:\s+(.*)$",
+ line,
+ re.IGNORECASE,
+ )
+ if typeSearch:
+ output[path]["type"] = typeSearch.group(1).strip()
+ output[path]["revision"] = typeSearch.group(2).strip()
+ continue
+
+ if re.match(r"Attached devices", line):
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_scsi",
+ "system": ["linux"],
+ "cmd": "cat /proc/scsi/scsi",
+ "description": "List of every recognized SCSI device",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_slabinfo.py b/src/sysinfo/modules/proc_slabinfo.py
new file mode 100644
index 0000000..9558a37
--- /dev/null
+++ b/src/sysinfo/modules/proc_slabinfo.py
@@ -0,0 +1,75 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def extract_key_value(keys, data):
+ entry = {}
+
+ if len(keys) == len(data):
+ for key_index, key in enumerate(keys):
+ entry[key] = data[key_index]
+
+ return entry
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ key_names = []
+ has_keys = False
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ if re.match(r"^slabinfo - version", line):
+ continue
+
+ header = re.search(r"^# name\s+(.*)$", line)
+ if header:
+ parts = header.group(1).strip().split(":")
+ for part in parts:
+ part = part.strip()
+ part = re.sub(r"(tunables|slabdata)\s+", "", part)
+ part = part.replace("<", "").replace(">", "")
+ part_columns = re.split(r"\s+", part)
+ part_columns = [
+ camelCase(key, to_camelcase) for key in part_columns
+ ]
+
+ key_names.append(part_columns)
+
+ has_keys = True
+ continue
+
+ row = re.search(r"^(\S+)\s+(.*)\s:\stunables(.*)\s:\sslabdata(.*)$", line)
+ if has_keys and row:
+ name = row.group(1)
+ statistics_data = re.split(r"\s+", row.group(2).strip())
+ tunables_data = re.split(r"\s+", row.group(3).strip())
+ slabdata_data = re.split(r"\s+", row.group(4).strip())
+
+ statistics = extract_key_value(key_names[0], statistics_data)
+ tunables = extract_key_value(key_names[1], tunables_data)
+ slabdata = extract_key_value(key_names[2], slabdata_data)
+
+ output[name] = {
+ "statistics": statistics,
+ "tunables": tunables,
+ "slabdata": slabdata,
+ }
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_slabinfo",
+ "system": ["linux"],
+ "cmd": "cat /proc/slabinfo",
+ "description": "Kernel caches informations",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_stat.py b/src/sysinfo/modules/proc_stat.py
new file mode 100644
index 0000000..705f418
--- /dev/null
+++ b/src/sysinfo/modules/proc_stat.py
@@ -0,0 +1,42 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ for line in stdout.splitlines():
+ parts = re.split(r"\s+", line)
+ key = parts.pop(0)
+
+ if re.match(r"^cpu", key) and len(parts) == 10:
+ output[key] = {
+ "user": parts[0],
+ "nice": parts[1],
+ "system": parts[2],
+ "idle": parts[3],
+ "iowait": parts[4],
+ "irq": parts[5],
+ "softirq": parts[6],
+ "steal": parts[7],
+ "guest": parts[8],
+ "guest_nice": parts[9],
+ }
+ continue
+
+ output[key] = " ".join(parts)
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_stat",
+ "system": ["linux"],
+ "cmd": "cat /proc/stat",
+ "description": "Kernel/system statistics",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_swaps.py b/src/sysinfo/modules/proc_swaps.py
new file mode 100644
index 0000000..032bd7b
--- /dev/null
+++ b/src/sysinfo/modules/proc_swaps.py
@@ -0,0 +1,22 @@
+from sysinfo_lib import parseSpaceTable
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = parseSpaceTable(stdout, to_camelcase=to_camelcase)
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_swaps",
+ "system": ["linux"],
+ "cmd": "cat /proc/swaps",
+ "description": "Measures swap space and its utilization",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_sys.py b/src/sysinfo/modules/proc_sys.py
new file mode 100644
index 0000000..8619a23
--- /dev/null
+++ b/src/sysinfo/modules/proc_sys.py
@@ -0,0 +1,52 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def setPathValue(data, path, value, to_camelcase):
+ pathRest = None
+ pathParts = re.search(r"^([^\/]+)\/?(.*)$", path)
+ if pathParts:
+ path = camelCase(pathParts.group(1), to_camelcase)
+ pathRest = pathParts.group(2)
+
+ else:
+ path = camelCase(path)
+
+ if not path in data:
+ data[path] = {}
+
+ if pathRest:
+ setPathValue(data[path], pathRest, value, to_camelcase)
+ else:
+ key = camelCase(path, to_camelcase)
+ data[key] = value
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ pathValue = re.search(r"^\/proc\/sys\/([^:]+):(.*)$", line)
+ if pathValue:
+ path = pathValue.group(1)
+ value = pathValue.group(2)
+ setPathValue(output, path, value, to_camelcase)
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_sys",
+ "system": ["linux"],
+ "cmd": """find /proc/sys -type f -follow -print 2>/dev/null | xargs -n 1 -I {} sh -c 'VAL=$(cat {} 2>/dev/null); echo {}:$VAL;'""",
+ "description": "Information about the system and kernel features",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_uptime.py b/src/sysinfo/modules/proc_uptime.py
new file mode 100644
index 0000000..f05445c
--- /dev/null
+++ b/src/sysinfo/modules/proc_uptime.py
@@ -0,0 +1,27 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ splitted = re.split(r"\s+", stdout.strip())
+ if len(splitted) > 0:
+ output["systemUp"] = splitted[0]
+
+ if len(splitted) > 1:
+ output["sumCoresIdle"] = splitted[1]
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_uptime",
+ "system": ["linux"],
+ "cmd": "cat /proc/uptime",
+ "description": "Information detailing how long the system has been on since its last restart",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_version.py b/src/sysinfo/modules/proc_version.py
new file mode 100644
index 0000000..c068d69
--- /dev/null
+++ b/src/sysinfo/modules/proc_version.py
@@ -0,0 +1,19 @@
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = stdout.strip()
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_version",
+ "system": ["linux"],
+ "cmd": "cat /proc/version",
+ "description": "Version of the Linux kernel, the version of gcc used to compile the kernel, and the time of kernel compilation",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_version_signature.py b/src/sysinfo/modules/proc_version_signature.py
new file mode 100644
index 0000000..382fccd
--- /dev/null
+++ b/src/sysinfo/modules/proc_version_signature.py
@@ -0,0 +1,23 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = ""
+
+ if stdout:
+ output = re.sub(r"\n|\r|\r\n", "", stdout)
+ output = stdout.strip()
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_version_signature",
+ "system": ["linux"],
+ "cmd": "cat /proc/version_signature",
+ "description": "OS version signature",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/proc_vmstat.py b/src/sysinfo/modules/proc_vmstat.py
new file mode 100644
index 0000000..cba3d2d
--- /dev/null
+++ b/src/sysinfo/modules/proc_vmstat.py
@@ -0,0 +1,33 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ lineMatch = re.search(r"^([^\s+]+)\s(.*)$", line)
+ if lineMatch:
+ key = camelCase(lineMatch.group(1), to_camelcase)
+ value = lineMatch.group(2)
+
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "proc_vmstat",
+ "system": ["linux"],
+ "cmd": "cat /proc/vmstat",
+ "description": "Detailed virtual memory statistics from the kernel",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/prtstat.py b/src/sysinfo/modules/prtstat.py
new file mode 100644
index 0000000..fc96362
--- /dev/null
+++ b/src/sysinfo/modules/prtstat.py
@@ -0,0 +1,41 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ pid = ""
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ processed = False
+ pairs = re.findall(r"(\S+):\s(\S+)", line)
+ for kv in pairs:
+ if kv[0] == "pid":
+ pid = kv[1]
+ output[pid] = {}
+
+ if pid != "":
+ key = camelCase(kv[0], to_camelcase)
+ value = kv[1].strip()
+
+ output[pid][key] = value
+ processed = True
+
+ if not processed:
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "prtstat",
+ "system": ["linux"],
+ "cmd": "ps -eo pid | xargs -I {} prtstat -r {}",
+ "description": "Print statistics of a processes",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/ps.py b/src/sysinfo/modules/ps.py
new file mode 100644
index 0000000..76d830d
--- /dev/null
+++ b/src/sysinfo/modules/ps.py
@@ -0,0 +1,62 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+ columnsNames = [
+ "user",
+ "ruser",
+ "group",
+ "rgroup",
+ "pid",
+ "ppid",
+ "pgid",
+ "cpu",
+ "size",
+ "bytes",
+ "nice",
+ "time",
+ "stime",
+ "tty",
+ "args",
+ ]
+ columnsCount = len(columnsNames)
+
+ if stdout:
+ for line in stdout.splitlines():
+ if re.search(r"ps --cols 12288 -eo", line) or re.search(
+ r"USER.*RUSER.*GROUP", line
+ ):
+ continue
+
+ cols = re.split(r"\s+", line)
+ if cols:
+ entry = {}
+ for num, val in enumerate(cols, start=0):
+ if num < columnsCount:
+ name = columnsNames[num]
+ entry[name] = val
+
+ elif "args" in entry:
+ entry["args"] += " " + val
+
+ if "pid" in entry:
+ output[entry["pid"]] = entry
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "ps",
+ "system": ["linux"],
+ "cmd": "ps --cols 12288 -eo user:256,ruser:256,group:256,rgroup:256,pid,ppid,pgid,pcpu,vsz,nice,etime,time,stime,tty,args 2>/dev/null",
+ "description": "Report a snapshot of the current processes",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/python.py b/src/sysinfo/modules/python.py
new file mode 100644
index 0000000..feeca6a
--- /dev/null
+++ b/src/sysinfo/modules/python.py
@@ -0,0 +1,86 @@
+import platform
+
+from modules.sysinfo_lib import camelCase
+
+
+try:
+ import pkg_resources
+
+ loaded_pkg_resources = True
+except:
+ loaded_pkg_resources = False
+
+
+def python_pip_packages(to_camelcase):
+ output = {}
+
+ if loaded_pkg_resources:
+ for pkg in pkg_resources.working_set:
+ package = {}
+ for key in [
+ "location",
+ "project_name",
+ "key",
+ "version",
+ "parsed_version",
+ "py_version",
+ "platform",
+ "precedence",
+ ]:
+ if hasattr(pkg, key):
+ key_case = camelCase(key, to_camelcase)
+ package[key_case] = str(getattr(pkg, key))
+
+ if "key" in package:
+ output[package["key"]] = package
+
+ return {"output": output, "unprocessed": []}
+
+
+def python_platform(to_camelcase):
+ output = {
+ "architecture": platform.architecture(),
+ "machine": platform.machine(),
+ "node": platform.node(),
+ "platform": {
+ "normal": platform.platform(),
+ "aliased": platform.platform(aliased=True),
+ "terse": platform.platform(terse=True),
+ },
+ "processor": platform.processor(),
+ "python": {
+ "branch": platform.python_branch(),
+ "build": platform.python_build(),
+ "compiler": platform.python_compiler(),
+ "implementation": platform.python_implementation(),
+ "revision": platform.python_revision(),
+ "version": platform.python_version(),
+ "versionTuple": platform.python_version_tuple(),
+ },
+ "release": platform.release(),
+ "system": platform.system(),
+ "version": platform.version(),
+ "uname": platform.uname(),
+ }
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ if loaded_pkg_resources:
+ main.register(
+ {
+ "name": "python_pip_packages",
+ "system": ["linux"],
+ "function": python_pip_packages,
+ "description": "List available python modules",
+ }
+ )
+
+ main.register(
+ {
+ "name": "python_platform",
+ "system": ["linux"],
+ "function": python_platform,
+ "description": "Probe the underlying platform's hardware, operating system, and Python interpreter version information",
+ }
+ )
diff --git a/src/sysinfo/modules/route.py b/src/sysinfo/modules/route.py
new file mode 100644
index 0000000..ba9fe07
--- /dev/null
+++ b/src/sysinfo/modules/route.py
@@ -0,0 +1,26 @@
+from sysinfo_lib import parseTable
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ output = parseTable(
+ stdout,
+ header_pattern=r"^(Destination\s*)(\sGateway\s*)(\sGenmask\s*)(\sFlags\s*)(\sMetric\s*)(\sRef\s)(\s*Use)(\sIface\s*)(\sMSS\s*)(\sWindow\s*)(\sirtt\s*)",
+ to_camelcase=to_camelcase,
+ )
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "route",
+ "system": ["linux"],
+ "cmd": "route -ee",
+ "description": "IP routing table",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/rpm.py b/src/sysinfo/modules/rpm.py
new file mode 100644
index 0000000..dc968d0
--- /dev/null
+++ b/src/sysinfo/modules/rpm.py
@@ -0,0 +1,35 @@
+from sysinfo_lib import parseCharDelimitedTable, tableToDict
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ columns = [
+ "installtime",
+ "buildtime",
+ "name",
+ "version",
+ "release",
+ "arch",
+ "vendor",
+ "packager",
+ "distribution",
+ "disttag",
+ ]
+
+ if stdout:
+ output = parseCharDelimitedTable(stdout, "|", columns)
+ output = tableToDict(output, "name")
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "rpm",
+ "system": ["linux"],
+ "cmd": 'rpm -q -a --queryformat "%{INSTALLTIME}|%{BUILDTIME}|%{NAME}|%{VERSION}|%{RELEASE}|%{arch}|%{VENDOR}|%{PACKAGER}|%{DISTRIBUTION}|%{DISTTAG}\n"',
+ "description": "Querying all RPM packages",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/services_status.py b/src/sysinfo/modules/services_status.py
new file mode 100644
index 0000000..d0bc2ab
--- /dev/null
+++ b/src/sysinfo/modules/services_status.py
@@ -0,0 +1,58 @@
+import re
+from sysinfo_lib import parseTable, tableToDict, camelCase
+
+
+def parser_services(stdout, stderr, to_camelcase):
+ output = parseTable(stdout, to_camelcase=to_camelcase)
+ output = tableToDict(output, "unit")
+
+ return {"output": output, "unprocessed": []}
+
+
+def parser_services_params(stdout, stderr, to_camelcase):
+ output = {}
+ service = None
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ service_search = re.search(r"^>>>\s*Service:\s*(.*)$", line)
+ if service_search:
+ service = service_search.group(1).strip()
+ output[service] = {}
+ continue
+
+ if service:
+ kv = re.search(r"^([^=]+)=(.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1), to_camelcase)
+ value = kv.group(2).strip()
+
+ output[service][key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "services_list",
+ "system": ["linux"],
+ "cmd": """systemctl -l --type service --all --plain | grep -i -e ".service\|description" | sed 's/^\s*//g' """,
+ "description": "Displays services with status",
+ "parser": parser_services,
+ }
+ )
+
+ main.register(
+ {
+ "name": "services_params",
+ "system": ["linux"],
+ "cmd": """systemctl -l --type service --all --plain | sed -E 's/^\s*(\\S+.service).*$/\\1/g' | grep -i -e ".service" | xargs -I '{}' sh -c "echo '>>> Service: {}'; systemctl show {} --no-page" """,
+ "description": "Displays services with params",
+ "parser": parser_services_params,
+ }
+ )
diff --git a/src/sysinfo/modules/sysctl.py b/src/sysinfo/modules/sysctl.py
new file mode 100644
index 0000000..0d482e6
--- /dev/null
+++ b/src/sysinfo/modules/sysctl.py
@@ -0,0 +1,59 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def set_path_value(data, path, value, to_camelcase):
+ pathRest = None
+ pathParts = re.search(r"^([^\.]+)\.?(.*)$", path)
+ if pathParts:
+ path = camelCase(pathParts.group(1), to_camelcase)
+ pathRest = pathParts.group(2)
+
+ if not path in data:
+ data[path] = {}
+
+ if pathRest:
+ set_path_value(data[path], pathRest, value, to_camelcase)
+ else:
+ data[path] = value
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ kv = re.search(r"^([^=]+)=(.*)$", line)
+ if kv:
+ key = kv.group(1).strip()
+ value = kv.group(2).strip()
+
+ set_path_value(output, key, value, to_camelcase)
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "sysctl",
+ "system": ["linux"],
+ "cmd": "sysctl -a -e",
+ "description": "Runtime kernel parameters",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "sysctl_system",
+ "system": ["linux"],
+ "cmd": "sysctl -a -e --system",
+ "description": "Runtime kernel parameters from all system configuration files",
+ "parser": parser,
+ }
+ )
diff --git a/modules/sysinfo_lib.py b/src/sysinfo/modules/sysinfo_lib.py
similarity index 52%
rename from modules/sysinfo_lib.py
rename to src/sysinfo/modules/sysinfo_lib.py
index 2bdf4cd..ace01be 100644
--- a/modules/sysinfo_lib.py
+++ b/src/sysinfo/modules/sysinfo_lib.py
@@ -3,23 +3,39 @@
from struct import pack, unpack
PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
+PY3 = sys.version_info[0] == 3
+
def sortedList(st):
values = list(set(st.splitlines()))
values.sort()
return values
-def camelCase(st):
- output = ''.join(x for x in st.title() if x.isalnum())
+
+def camelCase_cb(matchobj):
+ return " ".join([matchobj.group(1), matchobj.group(2)])
+
+
+def camelCase(st, to_camelcase):
+ if not to_camelcase:
+ return st
+
+ st = re.sub(r"([a-z])([A-Z])", camelCase_cb, st)
+
+ output = "".join(x for x in st.title() if x.isalnum())
if len(output) == 1:
return output.lower()
+
elif len(output) > 1:
return output[0].lower() + output[1:]
+
else:
- return ''
+ return ""
+
-def parseTable(input, headerPattern = None, endPattern = None, ignoreEmpty = True):
+def parseTable(
+ input, header_pattern=None, end_pattern=None, ignore_empty=True, to_camelcase=False
+):
output = []
colNames = []
colLengths = []
@@ -29,53 +45,61 @@ def parseTable(input, headerPattern = None, endPattern = None, ignoreEmpty = Tru
if len(lines) == 0:
return output
- while len(lines) > 0 and lines[0].strip() == '':
+ while len(lines) > 0 and lines[0].strip() == "":
lines.pop(0)
- if headerPattern:
- while len(lines) > 0 and not re.search(headerPattern, lines[0], re.IGNORECASE):
+ if header_pattern:
+ while len(lines) > 0 and not re.search(header_pattern, lines[0], re.IGNORECASE):
lines.pop(0)
if len(lines) == 0:
return output
- header = re.search(headerPattern, lines.pop(0), re.IGNORECASE)
+ header = re.search(header_pattern, lines.pop(0), re.IGNORECASE)
if header:
header = header.groups()
else:
- header = re.findall(r'(\S+\s*)', lines.pop(0), re.IGNORECASE)
+ header = re.findall(r"(\S+\s*)", lines.pop(0), re.IGNORECASE)
if header:
for value in header:
- colNames.append(camelCase(value.strip()))
+ colNames.append(camelCase(value.strip(), to_camelcase))
colLengths.append(len(value))
if len(colNames) > 0:
colLengths[-1] = 8192
totalLength = sum(colLengths)
- packTemplate = ''.join([str(s) + 's' for s in colLengths])
+ packTemplate = "".join([str(s) + "s" for s in colLengths])
for line in lines:
- if ignoreEmpty == True and line.strip() == '':
+ if ignore_empty == True and line.strip() == "":
continue
+
row = {}
- if endPattern and re.match(endPattern, line):
+ if end_pattern and re.match(end_pattern, line):
break
+
if PY2:
- cols = unpack(packTemplate, line + (' ' * (totalLength - len(line))))
+ cols = unpack(packTemplate, line + (" " * (totalLength - len(line))))
else:
- cols = unpack(packTemplate, bytes(line + (' ' * (totalLength - len(line))), 'utf-8'))
+ cols = unpack(
+ packTemplate,
+ bytes(line + (" " * (totalLength - len(line))), "utf-8"),
+ )
+
for num, val in enumerate(cols, start=0):
if PY2:
row[colNames[num]] = val.strip()
else:
- row[colNames[num]] = str(val.strip(), 'utf-8')
+ row[colNames[num]] = str(val.strip(), "utf-8")
+
output.append(row)
return output
-def parseSpaceTable(input, ignoreEmpty = True):
+
+def parseSpaceTable(input, ignore_empty=True, to_camelcase=False):
output = []
colNames = []
lines = input.splitlines()
@@ -83,27 +107,29 @@ def parseSpaceTable(input, ignoreEmpty = True):
if len(lines) == 0:
return output
- while len(lines) > 0 and lines[0].strip() == '':
+ while len(lines) > 0 and lines[0].strip() == "":
lines.pop(0)
- header = re.findall(r'(\S+[\s\t]*)', lines.pop(0), re.IGNORECASE)
+ header = re.findall(r"(\S+[\s\t]*)", lines.pop(0), re.IGNORECASE)
if header:
for value in header:
- colNames.append(camelCase(value.strip()))
+ colNames.append(camelCase(value.strip(), to_camelcase))
if len(colNames) > 0:
for line in lines:
- if ignoreEmpty == True and line.strip() == '':
+ if ignore_empty == True and line.strip() == "":
continue
row = {}
- cols = re.split(r'\s+', line.strip())
+ cols = re.split(r"\s+", line.strip())
for num, val in enumerate(cols, start=0):
if num < len(colNames):
row[colNames[num]] = val.strip()
+
output.append(row)
return output
+
def parseCharDelimitedTable(input, delimiter, columnsNames):
output = []
if input:
@@ -117,6 +143,7 @@ def parseCharDelimitedTable(input, delimiter, columnsNames):
output.append(row)
return output
+
def tableToDict(input, key):
output = {}
@@ -126,5 +153,31 @@ def tableToDict(input, key):
output[row[key]] = row
else:
return input
-
+
+ return output
+
+
+def fixMultilineAndSplit(data, match, delimiter):
+ output = []
+ last_line = None
+
+ for line in data.splitlines():
+ if last_line == None:
+ last_line = line
+ continue
+
+ if line.strip() == "":
+ continue
+
+ if re.match(r"^\s+", line):
+ line = line.strip()
+ last_line = f"{last_line}{delimiter}{line}"
+
+ else:
+ output.append(last_line)
+ last_line = line
+
+ if not last_line == None:
+ output.append(last_line)
+
return output
diff --git a/src/sysinfo/modules/tasklist.py b/src/sysinfo/modules/tasklist.py
new file mode 100644
index 0000000..55bee38
--- /dev/null
+++ b/src/sysinfo/modules/tasklist.py
@@ -0,0 +1,82 @@
+import re
+from sysinfo_lib import camelCase, fixMultilineAndSplit
+
+
+def parser_fo(stdout, stderr, to_camelcase):
+ output = []
+ unprocessed = []
+ image = {}
+
+ if stdout:
+ data = fixMultilineAndSplit(stdout, r"^\s+", ", ")
+
+ for line in data:
+ if line.strip() == "":
+ continue
+
+ kv = re.search(r"^([^:]+):\s*(.*)$", line)
+ if kv:
+ key = camelCase(kv.group(1).strip(), to_camelcase)
+ value = kv.group(2).strip().replace("\u00a0", "")
+
+ if key == "imageName" or key == "Image Name":
+ image = {}
+ output.append(image)
+
+ if (
+ key == "modules"
+ or key == "Modules"
+ or key == "services"
+ or key == "Services"
+ ):
+ value = value.split(", ")
+
+ image[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+
+ main.register(
+ {
+ "name": "tasklist",
+ "system": ["windows"],
+ "cmd": "%SystemRoot%\\system32\\tasklist.exe /fo list",
+ "description": "Get currently running processes",
+ "parser": parser_fo,
+ }
+ )
+
+ main.register(
+ {
+ "name": "tasklist_services",
+ "system": ["windows"],
+ "cmd": "%SystemRoot%\\system32\\tasklist.exe /svc /fo list",
+ "description": "Get services hosted in each process",
+ "parser": parser_fo,
+ }
+ )
+
+ main.register(
+ {
+ "name": "tasklist_apps",
+ "system": ["windows"],
+ "cmd": "%SystemRoot%\\system32\\tasklist.exe /apps /fo list",
+ "description": "Get services hosted in each process",
+ "parser": parser_fo,
+ }
+ )
+
+ main.register(
+ {
+ "name": "tasklist_modules",
+ "system": ["windows"],
+ "cmd": "%SystemRoot%\\system32\\tasklist.exe /m /fo list",
+ "description": "Get modules loaded in each process",
+ "parser": parser_fo,
+ }
+ )
diff --git a/src/sysinfo/modules/timedatectl.py b/src/sysinfo/modules/timedatectl.py
new file mode 100644
index 0000000..7e3f338
--- /dev/null
+++ b/src/sysinfo/modules/timedatectl.py
@@ -0,0 +1,43 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ lineMatch = re.search(r"^([^:]+):\s*(.*)", line)
+ if lineMatch:
+ key = camelCase(lineMatch.group(1).strip(), to_camelcase)
+ value = lineMatch.group(2).strip()
+
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "timedatectl",
+ "system": ["linux"],
+ "cmd": "timedatectl status",
+ "description": "System time and date",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "timedatectl_timesync",
+ "system": ["linux"],
+ "cmd": "timedatectl timesync-status",
+ "description": "Status of systemd-timesyncd.service",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/udevadm.py b/src/sysinfo/modules/udevadm.py
new file mode 100644
index 0000000..a9e24bf
--- /dev/null
+++ b/src/sysinfo/modules/udevadm.py
@@ -0,0 +1,149 @@
+import re
+from sysinfo_lib import camelCase
+
+
+def parse_looking_entry(line, entry, to_camelcase):
+ key_value = re.search(r'^\s+([^=]+)=="([^"]*)"', line)
+ if key_value:
+ key = key_value.group(1).lower()
+ value = key_value.group(2)
+
+ multiple_values = re.match(r"^(\s+\d+)+$", value)
+ if multiple_values:
+ value = re.split(r"\s+", value.strip())
+
+ key_attr = re.match(r"^(\S+){([^}]+)}", key, re.IGNORECASE)
+ if key_attr:
+ attrType = camelCase(key_attr.group(1), to_camelcase)
+
+ if not attrType in entry:
+ entry[attrType] = {}
+
+ path = key_attr.group(2).split("/")
+
+ if len(path) == 1:
+ key_case = camelCase(path[0], to_camelcase)
+ entry[attrType][key_case] = value
+
+ else:
+ sub_entry = entry[attrType]
+
+ for part in path[0:-1]:
+ part_case = camelCase(part, to_camelcase)
+
+ if not part_case in sub_entry:
+ sub_entry[part_case] = {}
+
+ sub_entry = sub_entry[part_case]
+
+ key_case = camelCase(path[-1], to_camelcase)
+ sub_entry[key_case] = value
+
+ else:
+ entry[key] = value
+
+ return True
+
+ return False
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = {"devices": {}, "parents": {}}
+ types = {"P": "path", "N": "node", "L": "linkPriority", "E": "entry", "S": "link"}
+ device = None
+ parent = None
+ looking_entry = None
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ deviceSearch = re.search(r"^>>> Device: (\S+)", line)
+ if deviceSearch:
+ device = deviceSearch.group(1)
+ output["devices"][device] = {"parents": [], "entry": {}, "link": []}
+ parent = None
+ continue
+
+ if not device:
+ continue
+
+ if line.strip() == "":
+ looking_entry = None
+ continue
+
+ if re.match(
+ r"^.*(Udevadm info starts|chain of parent|the udev rules|rule to match|single parent device)",
+ line,
+ ):
+ continue
+
+ keyValue = re.search(r"^(\S):\s+(.*)$", line)
+ if keyValue:
+ key = keyValue.group(1)
+ value = keyValue.group(2).strip()
+ if key in types:
+ key = types[key]
+
+ if key == "entry":
+ valueSearch = re.search(r"^([^=]+)=(.*)$", value)
+ if valueSearch:
+ subkey = camelCase(valueSearch.group(1), to_camelcase)
+ subvalue = valueSearch.group(2).strip()
+ output["devices"][device]["entry"][subkey] = subvalue
+
+ elif key == "link":
+ output["devices"][device]["link"].append(value)
+
+ else:
+ output["devices"][device][key] = value
+
+ continue
+
+ deviceLook = re.search(r"^\s+looking at device \'([^\']+)", line)
+ if deviceLook:
+ looking_entry = output["devices"][device]
+ continue
+
+ parentDeviceLook = re.search(
+ r"^\s+looking at parent device \'([^\']+)", line
+ )
+ if parentDeviceLook:
+ parent = parentDeviceLook.group(1)
+ output["devices"][device]["parents"].append(parent)
+
+ if not parent in output["parents"]:
+ output["parents"][parent] = {}
+
+ looking_entry = output["parents"][parent]
+ continue
+
+ if isinstance(looking_entry, dict):
+ processed = parse_looking_entry(line, looking_entry, to_camelcase)
+ if processed:
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "udevadm",
+ "system": ["linux"],
+ "cmd": """udevadm info --export-db | grep "DEVNAME" | cut -d "=" -f2 | xargs -n 1 -I {} sh -c "echo '>>> Device: {}'; udevadm info --query=all --name={}; udevadm info --attribute-walk --name={}" """,
+ "description": "Queries the udev database for device information stored in the udev database",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "udevadm_block_devices",
+ "system": ["linux"],
+ "cmd": """find /dev/ -type b | xargs -n 1 -I {} sh -c "echo '>>> Device: {}'; udevadm info --query=all --name={}; udevadm info --attribute-walk --name={}" """,
+ "description": "Queries the udev database for block device information stored in the udev database",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/uname.py b/src/sysinfo/modules/uname.py
new file mode 100644
index 0000000..c58e4d9
--- /dev/null
+++ b/src/sysinfo/modules/uname.py
@@ -0,0 +1,93 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = ""
+
+ if stdout:
+ output = re.sub(r"\n|\r|\r\n", "", stdout)
+ output = stdout.strip()
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "kernel_name",
+ "system": ["linux"],
+ "cmd": "uname -s",
+ "description": "Kernel name (uname)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "kernel_release",
+ "system": ["linux"],
+ "cmd": "uname -r",
+ "description": "Kernel release (uname)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "kernel_version",
+ "system": ["linux"],
+ "cmd": "uname -v",
+ "description": "Kernel version (uname)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "nodename",
+ "system": ["linux"],
+ "cmd": "uname -n",
+ "description": "Network node hostname (uname)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "machine",
+ "system": ["linux"],
+ "cmd": "uname -m",
+ "description": "Machine hardware name (uname)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "processor",
+ "system": ["linux"],
+ "cmd": "uname -p",
+ "description": "Processor type (uname)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "hardware_platform",
+ "system": ["linux"],
+ "cmd": "uname -i",
+ "description": "Hardware platform (uname)",
+ "parser": parser,
+ }
+ )
+
+ main.register(
+ {
+ "name": "operating_system",
+ "system": ["linux"],
+ "cmd": "uname -o",
+ "description": "Operating system (uname)",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/update_alternatives.py b/src/sysinfo/modules/update_alternatives.py
new file mode 100644
index 0000000..60d8dc0
--- /dev/null
+++ b/src/sysinfo/modules/update_alternatives.py
@@ -0,0 +1,35 @@
+import re
+
+
+def parser(stdout, stderr, to_camelcase):
+ output = []
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ lineMatch = re.search(r"^(\S+)\s+(.*)\s+(\S+)$", line)
+ if lineMatch:
+ output.append(
+ {
+ "name": lineMatch.group(1).strip(),
+ "mode": lineMatch.group(2).strip(),
+ "link": lineMatch.group(3).strip(),
+ }
+ )
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "update_alternatives",
+ "system": ["linux"],
+ "cmd": "update-alternatives --get-selections",
+ "description": "Symbolic links determining default commands",
+ "parser": parser,
+ }
+ )
diff --git a/src/sysinfo/modules/vmstat.py b/src/sysinfo/modules/vmstat.py
new file mode 100644
index 0000000..c556f89
--- /dev/null
+++ b/src/sysinfo/modules/vmstat.py
@@ -0,0 +1,160 @@
+import re
+import sys
+from struct import pack, unpack
+from sysinfo_lib import camelCase
+
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+
+def parser_stats(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ entry = re.search(r"^\s*(\d+)\s+(.*)$", line)
+ if entry:
+ key = camelCase(entry.group(2).strip(), to_camelcase)
+ value = entry.group(1).strip()
+ output[key] = value
+
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def parser_disk(stdout, stderr, to_camelcase):
+ output = {}
+ sectionsNames = []
+ sectionsMask = ""
+ totalLength = 0
+ columnPaths = []
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ if re.match(r"disk.*reads", line, re.IGNORECASE):
+ topHeader = re.split(r"\s+", line)
+ if topHeader:
+ for value in topHeader:
+ sectionsNames.append(value.strip().strip("-").lower())
+ totalLength += len(value) + 1
+ sectionsMask += str(len(value) + 1) + "s"
+ continue
+
+ if sectionsMask:
+ if re.match(r".*total.*merged.*sectors.*", line):
+ lineFix = line + (" " * (totalLength - len(line)))
+ if PY3:
+ lineFix = bytes(lineFix, "utf-8")
+
+ sectionData = unpack(sectionsMask, lineFix)
+ if sectionData:
+ for index, sec in enumerate(sectionData):
+ if PY2:
+ secStrip = sec.strip()
+ else:
+ secStrip = str(sec.strip(), "utf-8")
+
+ topColumns = re.split(r"\s+", secStrip)
+ for column in topColumns:
+ if column:
+ columnPaths.append(
+ camelCase(
+ "%s %s"
+ % (
+ sectionsNames[index],
+ column,
+ ),
+ to_camelcase,
+ )
+ )
+ else:
+ columnPaths.append("%s" % (sectionsNames[index],))
+
+ else:
+ entry = {}
+ columns = re.split(r"\s+", line)
+ for ci, cv in enumerate(columns):
+ if ci < len(columnPaths):
+ entry[columnPaths[ci]] = cv
+
+ if "disk" in entry:
+ output[entry["disk"]] = entry
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def parser_disk_sum(stdout, stderr, to_camelcase):
+ output = {}
+ unprocessed = []
+
+ if stdout:
+ for line in stdout.splitlines():
+ entry = re.search(r"^\s*(\d+)\s+(.*)$", line)
+ if entry:
+ key = camelCase(entry.group(2).strip(), to_camelcase)
+ value = entry.group(1).strip()
+
+ output[key] = value
+ continue
+
+ unprocessed.append(line)
+
+ return {"output": output, "unprocessed": unprocessed}
+
+
+def parser_forks(stdout, stderr, to_camelcase):
+ output = {}
+
+ if stdout:
+ forks = re.search(r"\s*(\d+)\s*forks", stdout)
+ if forks:
+ output["forks"] = forks.group(1)
+
+ return {"output": output, "unprocessed": []}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "vmstat_stats",
+ "system": ["linux"],
+ "cmd": "vmstat -s",
+ "description": "Displays a table of various event counters and memory statistics",
+ "parser": parser_stats,
+ }
+ )
+
+ main.register(
+ {
+ "name": "vmstat_disk",
+ "system": ["linux"],
+ "cmd": "vmstat -dwn",
+ "description": "Report disk statistics",
+ "parser": parser_disk,
+ }
+ )
+
+ main.register(
+ {
+ "name": "vmstat_disk_sum",
+ "system": ["linux"],
+ "cmd": "vmstat -D",
+ "description": "Report some summary statistics about disk activity",
+ "parser": parser_disk_sum,
+ }
+ )
+
+ main.register(
+ {
+ "name": "vmstat_forks",
+ "system": ["linux"],
+ "cmd": "vmstat -f",
+ "description": "Displays the number of forks since boot",
+ "parser": parser_forks,
+ }
+ )
diff --git a/src/sysinfo/modules/yum.py b/src/sysinfo/modules/yum.py
new file mode 100644
index 0000000..03c62b1
--- /dev/null
+++ b/src/sysinfo/modules/yum.py
@@ -0,0 +1,90 @@
+import re
+
+
+def parser_repolist(stdout, stderr, to_camelcase):
+ output = {"mirrors": [], "repos": [], "errors": []}
+ insideTable = False
+ if stdout:
+ for line in stdout.splitlines():
+ mirrors = re.search(r"^\s*\*\s*([^:]+):\s*(.*)$", line)
+ if mirrors:
+ output["mirrors"].append(
+ {"repo": mirrors.group(1).strip(), "host": mirrors.group(2).strip()}
+ )
+
+ tableHeader = re.search(
+ r"^repo id\s+repo name\s+status", line, re.IGNORECASE
+ )
+ tableRow = re.search(
+ r"^([^\/]+)\/([^\/]+)\/(.*)\s\s+(\S+.*)\s\s+(\S+.*)$", line
+ )
+ if tableHeader:
+ insideTable = True
+
+ elif insideTable and tableRow:
+ output["repos"].append(
+ {
+ "repo": tableRow.group(1).strip(),
+ "version": tableRow.group(2).strip(),
+ "arch": tableRow.group(3).strip(),
+ "repo_name": tableRow.group(4).strip(),
+ "status": tableRow.group(5).strip(),
+ }
+ )
+
+ else:
+ insideTable = False
+
+ if stderr:
+ for line in stderr.splitlines():
+ if re.search(r"http.*error .*", line, re.IGNORECASE):
+ if not line in output["errors"]:
+ output["errors"].append(line)
+
+ return {"output": output}
+
+
+def parser_installed(stdout, stderr, to_camelcase):
+ output = {"packages": [], "errors": []}
+ if stdout:
+ for line in stdout.splitlines():
+ package = re.search(r"^([\S\.]+)\.(\S+)\s+(\S+\.\S+)\s+(\S+.*)$", line)
+ if package:
+ output["packages"].append(
+ {
+ "name": package.group(1).strip(),
+ "arch": package.group(2).strip(),
+ "version": package.group(3).strip(),
+ "status": package.group(4).strip(),
+ }
+ )
+
+ if stderr:
+ for line in stderr.splitlines():
+ if re.search(r"http.*error .*", line, re.IGNORECASE):
+ if not line in output["errors"]:
+ output["errors"].append(line)
+
+ return {"output": output}
+
+
+def register(main):
+ main.register(
+ {
+ "name": "yum_repolist",
+ "system": ["linux"],
+ "cmd": "yum repolist all",
+ "description": "YUM - defined repositories",
+ "parser": parser_repolist,
+ }
+ )
+
+ main.register(
+ {
+ "name": "yum_installed",
+ "system": ["linux"],
+ "cmd": "yum list installed",
+ "description": "YUM - list installed packages",
+ "parser": parser_installed,
+ }
+ )
diff --git a/src/sysinfo/sysinfo.py b/src/sysinfo/sysinfo.py
new file mode 100644
index 0000000..e324243
--- /dev/null
+++ b/src/sysinfo/sysinfo.py
@@ -0,0 +1,422 @@
+#!/usr/bin/python3
+
+"""
+ *
+ * sysinfo - Python based scripts for obtaining system information from Linux.
+ *
+ * Petr Vavrin (peterbay) pvavrin@gmail.com
+ * https://github.com/peterbay
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+"""
+
+import os
+import sys
+import glob
+import json
+import locale
+import argparse
+import platform
+import subprocess
+from threading import Timer
+from multiprocessing import Pool
+from os.path import dirname, basename, isfile, join
+
+sys.path.append(join(dirname(__file__), "modules"))
+
+
+class systemInfoModules:
+ modules = {}
+
+ def __init__(self, system):
+ self.system = system
+
+ def register(self, definition):
+ if "system" in definition:
+ if not self.system in definition["system"]:
+ return
+
+ if not "name" in definition:
+ return
+
+ self.modules[definition["name"]] = definition
+
+ def items(self):
+ return self.modules.items()
+
+
+siModules = None
+PY2 = sys.version_info[0] == 2
+PY3 = sys.version_info[0] == 3
+
+
+def loadModules():
+ global siModules
+ global PY2
+ global PY3
+ modules = glob.glob(join(dirname(__file__), "modules", "*.py"))
+ for f in modules:
+ if isfile(f) and not f.endswith("__init__.py"):
+ if PY2:
+ import imp
+
+ lib = imp.load_source(basename(f)[:-3], f)
+ if hasattr(lib, "register"):
+ lib.register(siModules)
+ else:
+ from importlib import import_module
+
+ lib = __import__(basename(f)[:-3])
+ if hasattr(lib, "register"):
+ lib.register(siModules)
+
+
+def readFile(pathToFile):
+ try:
+ f = open(pathToFile, "r")
+ content = f.read()
+ f.close()
+ return content, None
+
+ except Exception as err:
+ return None, err
+
+
+def writeToFile(pathToFile, content):
+ try:
+ f = open(pathToFile, "w")
+ f.write(content)
+ f.close()
+
+ except Exception as err:
+ sys.stdout.write("ERROR: Can't write to file '%s': %s\n" % (pathToFile, err))
+
+
+def pathCheck(args):
+ if args.export_dir:
+ if not os.access(args.export_dir, os.W_OK):
+ sys.stdout.write(
+ "ERROR: Export directory '%s' not exist or is not writable\n"
+ % (args.export_dir,)
+ )
+ exit(1)
+
+ if args.import_dir:
+ if not os.access(args.import_dir, os.R_OK):
+ sys.stdout.write(
+ "ERROR: Import directory '%s' not exist or is not readable\n"
+ % (args.import_dir,)
+ )
+ exit(1)
+
+
+def kill(process):
+ return process.kill()
+
+
+def executeCmd(cmd):
+ proc = None
+ outs = None
+ errs = None
+
+ try:
+ command = cmd.get("cmd", None)
+ if not command:
+ cmd["error"] = "Empty command"
+ return
+
+ system = cmd.get("system", None)
+ if system:
+ if system == "windows":
+ subprocess.call(
+ ["chcp", "65001"],
+ shell=True,
+ stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL,
+ )
+
+ proc = subprocess.Popen(
+ command,
+ shell=True,
+ # executable="/usr/bin/bash",
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ )
+ cmdTimer = Timer(int(cmd.get("timeout", 30)), kill, [proc])
+
+ try:
+ cmdTimer.start()
+ outs, errs = proc.communicate()
+
+ except Exception as err:
+ proc.kill()
+ outs, errs = proc.communicate()
+ cmd["error"] = str(err)
+
+ finally:
+ cmdTimer.cancel()
+
+ except Exception as err:
+ cmd["error"] = str(err)
+
+ if proc:
+ procPoll = proc.poll()
+ if procPoll != 0:
+ cmd["rc"] = procPoll
+ if not "error" in cmd:
+ cmd["error"] = "Unknown error"
+
+ if PY2:
+ cmd["stdout"] = outs
+ cmd["stderr"] = errs
+ else:
+ try:
+ cmd["stdout"] = str(outs, "utf-8") if outs else None
+ cmd["stderr"] = str(errs, "utf-8") if errs else None
+ except Exception as e:
+ cmd["error"] = str(e)
+
+
+def execute(cmd):
+ if not isinstance(cmd, dict):
+ return {}
+
+ if "import_dir" in cmd:
+ commandImportPath = join(cmd.get("import_dir", ""), cmd.get("name", ""))
+ cmd["stdout"], cmd["stderr"] = readFile(commandImportPath)
+
+ else:
+ outs, errs = "", ""
+ to_camelcase = cmd.get("to_camelcase", None)
+
+ if "function" in cmd:
+ moduleFunction = cmd.get("function", None)
+ if moduleFunction and callable(moduleFunction):
+ outs = moduleFunction(to_camelcase)
+
+ cmd["stdout"] = outs
+ cmd["stderr"] = errs
+ return cmd
+
+ elif "cmd" in cmd:
+ executeCmd(cmd)
+
+ return cmd
+
+
+def run(args):
+ poolSize = int(args.pool)
+ loadModules()
+ p = Pool(poolSize)
+ selectedModules = []
+ executeModules = False
+ results = {}
+
+ to_camelcase = args.camel_case
+
+ for name, settings in sorted(siModules.items()):
+ if args.list:
+ sys.stdout.write(
+ "%-25s - %s\n"
+ % (
+ name,
+ settings.get("description", ""),
+ )
+ )
+
+ elif args.info:
+ if "function" in settings:
+ info = "built-in function"
+ else:
+ info = settings.get("cmd", "")
+
+ sys.stdout.write(
+ "%-25s - %s\n"
+ % (
+ name,
+ info,
+ )
+ )
+
+ elif args.all or name in args.commands:
+ settings["name"] = name
+ if args.import_dir:
+ settings["import_dir"] = args.import_dir
+
+ settings["to_camelcase"] = to_camelcase
+ settings["system"] = args.system
+ selectedModules.append(settings)
+ executeModules = True
+
+ if executeModules:
+ for result in p.map(execute, selectedModules):
+ name = result.get("name", None)
+ if not name:
+ continue
+
+ if args.error and not result.get("error", None):
+ continue
+
+ if args.export_dir:
+ commandExportPath = join(args.export_dir, name)
+ writeToFile(commandExportPath, result.get("stdout", None))
+
+ if args.export_only:
+ continue
+
+ parser = result.get("parser", None)
+ if parser and callable(parser):
+ try:
+ parsed = parser(
+ result.get("stdout", None),
+ result.get("stderr", None),
+ to_camelcase,
+ )
+
+ if isinstance(parsed, dict):
+ result["output"] = parsed.get("output", None)
+ result["unprocessed"] = parsed.get("unprocessed", None)
+
+ except Exception as err:
+ result["parser_error"] = str(err)
+
+ else:
+ stdout = result.get("stdout", None)
+ if isinstance(stdout, dict):
+ result["output"] = stdout.get("output", None)
+ result["unprocessed"] = stdout.get("unprocessed", None)
+
+ else:
+ result["output"] = stdout
+ result["unprocessed"] = []
+
+ result.pop("parser", None)
+ result.pop("function", None)
+
+ if not args.verbose and not args.error:
+ result.pop("stdout", None)
+ result.pop("stderr", None)
+ result.pop("rc", None)
+ result.pop("cmd", None)
+ result.pop("description", None)
+ result.pop("name", None)
+ result.pop("unprocessed", None)
+ result.pop("import_dir", None)
+ result.pop("system", None)
+
+ results[name] = result
+
+ if not args.export_only:
+ if args.output:
+ writeToFile(args.output, json.dumps(results, indent=4, sort_keys=True))
+
+ else:
+ sys.stdout.write(json.dumps(results, indent=4, sort_keys=True) + "\n")
+
+ elif not args.list and not args.info:
+ sys.stdout.write("No commands to execute\n")
+
+
+def argsError(error):
+ pass
+
+
+def main(argv):
+ global siModules
+ parser = argparse.ArgumentParser()
+ parser.error = argsError
+
+ parser.add_argument(
+ "--all", "-a", action="store_true", default=False, help="Execute all commands."
+ )
+
+ parser.add_argument(
+ "--camel-case",
+ "-c",
+ action="store_true",
+ default=False,
+ help="Convert keys to CamelCase.",
+ )
+
+ parser.add_argument(
+ "--error",
+ "-e",
+ action="store_true",
+ default=False,
+ help="Show only error outputs from commands.",
+ )
+
+ parser.add_argument(
+ "--export-only",
+ action="store_true",
+ default=False,
+ help="Export output from commands without processing.",
+ )
+
+ parser.add_argument(
+ "--export-dir", help="Path to the directory for saving output from commands."
+ )
+
+ parser.add_argument(
+ "--import-dir",
+ help="Path to the directory for reading the stored outputs of commands.",
+ )
+
+ parser.add_argument(
+ "--info",
+ "-i",
+ action="store_true",
+ default=False,
+ help="List all commands with command line arguments.",
+ )
+
+ parser.add_argument(
+ "--list", "-l", action="store_true", default=False, help="List all commands."
+ )
+
+ parser.add_argument("--output", "-o", help="Path to the output file.")
+
+ parser.add_argument(
+ "--pool",
+ "-p",
+ default="5",
+ type=int,
+ help="Pool size for parallel execution of commands. (default value is 5)",
+ )
+
+ parser.add_argument(
+ "--system",
+ "-s",
+ default=platform.system().lower(),
+ help="Execute or parse commands for selected system [linux, darwin, java, windows].",
+ )
+
+ parser.add_argument(
+ "--verbose",
+ "-v",
+ action="store_true",
+ default=False,
+ help="Add more info to output - options, commands, raw command result.",
+ )
+
+ parser.add_argument("commands", nargs="*", help="Commands")
+ args = parser.parse_args()
+ pathCheck(args)
+ siModules = systemInfoModules(args.system)
+ run(args)
+
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/src/tests/__init__.py b/src/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/sysinfo.py b/sysinfo.py
deleted file mode 100644
index d8bfa98..0000000
--- a/sysinfo.py
+++ /dev/null
@@ -1,255 +0,0 @@
-"""
- *
- * sysinfo - Python based scripts for obtaining system information from Linux.
- *
- * Petr Vavrin (peterbay) pvavrin@gmail.com
- * https://github.com/peterbay
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- *
-"""
-
-import os
-import sys
-import glob
-import json
-import argparse
-import subprocess
-from threading import Timer
-from multiprocessing import Pool
-from os.path import dirname, basename, isfile, join
-
-sys.path.append(join(dirname(__file__), 'modules'))
-
-siModules = {}
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-
-def loadModules():
- global siModules
- global PY2
- global PY3
- modules = glob.glob(join(dirname(__file__), 'modules', '*.py'))
- for f in modules:
- if isfile(f) and not f.endswith('__init__.py'):
- if PY2:
- import imp
- lib = imp.load_source(basename(f)[:-3], f)
- if hasattr(lib, 'register'):
- lib.register(siModules)
- else:
- from importlib import import_module
- lib = __import__(basename(f)[:-3])
- if hasattr(lib, 'register'):
- lib.register(siModules)
-
-def readFile(pathToFile):
- try:
- f = open(pathToFile, 'r')
- content = f.read()
- f.close()
- return content, None
-
- except Exception as err:
- return None, err
-
-def writeToFile(pathToFile, content):
- try:
- f = open(pathToFile, 'w')
- f.write(content)
- f.close()
-
- except Exception as err:
- sys.stdout.write('ERROR: Can\'t write to file \'%s\': %s\n' % (pathToFile, err))
-
-def pathCheck(args):
- if args.export_dir:
- if not os.access(args.export_dir, os.W_OK):
- sys.stdout.write('ERROR: Export directory \'%s\' not exist or is not writable\n' % (args.export_dir, ))
- exit(1)
-
- if args.import_dir:
- if not os.access(args.import_dir, os.R_OK):
- sys.stdout.write('ERROR: Import directory \'%s\' not exist or is not readable\n' % (args.import_dir, ))
- exit(1)
-
-def kill(process):
- return process.kill()
-
-def executeCmd(cmd):
- try:
- proc = subprocess.Popen(cmd.get('cmd', ''), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
- cmdTimer = Timer(int(cmd.get('timeout', 30)), kill, [proc])
-
- try:
- cmdTimer.start()
- outs, errs = proc.communicate()
-
- except Exception as err:
- proc.kill()
- outs, errs = proc.communicate()
- cmd['error'] = err
-
- finally:
- cmdTimer.cancel()
-
- except Exception as err:
- cmd['error'] = err
-
- if proc:
- procPoll = proc.poll()
- if procPoll != 0:
- cmd['rc'] = procPoll
- if not cmd.get('error', None):
- cmd['error'] = 'error'
-
- if PY2:
- cmd['stdout'] = outs
- cmd['stderr'] = errs
- else:
- cmd['stdout'] = str(outs, 'utf-8')
- cmd['stderr'] = str(errs, 'utf-8')
-
-def execute(cmd):
- if not isinstance(cmd, dict):
- return {}
-
- if 'import_dir' in cmd:
- commandImportPath = join(cmd.get('import_dir', ''), cmd.get('name', ''))
- cmd['stdout'], cmd['stderr'] = readFile(commandImportPath)
-
- else:
- outs, errs = '', ''
-
- if 'function' in cmd:
- moduleFunction = cmd.get('function', None)
- if moduleFunction and callable(moduleFunction):
- outs = moduleFunction()
-
- cmd['stdout'] = outs
- cmd['stderr'] = errs
- return cmd
-
- elif 'cmd' in cmd:
- executeCmd(cmd)
-
- return cmd
-
-def run(args):
- poolSize = int(args.pool)
- loadModules()
- p = Pool(poolSize)
- selectedModules = []
- executeModules = False
- results = {}
-
- for name, settings in sorted(siModules.items()):
- if args.list:
- sys.stdout.write('%-25s - %s\n' % (name, settings.get('description', ''), ))
-
- elif args.info:
- if 'function' in settings:
- info = 'built-in function'
- else:
- info = settings.get('cmd', '')
-
- sys.stdout.write('%-25s - %s\n' % (name, info, ))
-
- elif args.all or name in args.commands:
- settings['name'] = name
- if args.import_dir:
- settings['import_dir'] = args.import_dir
- selectedModules.append(settings)
- executeModules = True
-
- if executeModules:
- for result in p.map(execute, selectedModules):
- name = result.get('name', None)
- if not name:
- continue
-
- if args.error and not result.get('error', None):
- continue
-
- if args.export_dir:
- commandExportPath = join(args.export_dir, name)
- writeToFile(commandExportPath, result.get('stdout', None))
-
- if args.export_only:
- continue
-
- parser = result.get('parser', None)
- if parser and callable(parser):
- try:
- parsed = parser(result.get('stdout', None), result.get('stderr', None))
-
- if isinstance(parsed, dict):
- result['output'] = parsed.get('output', None)
- result['ignored'] = parsed.get('ignored', None)
- except Exception as err:
- result['parser_error'] = str(err)
-
- else:
- result['output'] = result.get('stdout', None)
-
- result.pop('parser', None)
- result.pop('function', None)
-
- if not args.verbose and not args.error:
- result.pop('stdout', None)
- result.pop('stderr', None)
- result.pop('rc', None)
- result.pop('cmd', None)
- result.pop('description', None)
- result.pop('name', None)
- result.pop('ignored', None)
- result.pop('import_dir', None)
-
- results[name] = result
-
- if not args.export_only:
- if args.output:
- writeToFile(args.output, json.dumps(results, indent=4, sort_keys=True))
-
- else:
- sys.stdout.write(json.dumps(results, indent=4, sort_keys=True) + '\n')
-
- elif not args.list and not args.info:
- sys.stdout.write('No commands to execute\n')
-
-def argsError(error):
- pass
-
-def main(argv):
- parser = argparse.ArgumentParser()
- parser.error = argsError
-
- parser.add_argument('--all', '-a', action='store_true', default=False, help='Execute all commands.')
- parser.add_argument('--error', '-e', action='store_true', default=False, help='Show only error outputs from commands.')
- parser.add_argument('--export-only', action='store_true', default=False, help='Export output from commands without processing.')
- parser.add_argument('--export-dir', help='Path to the directory for saving output from commands.')
- parser.add_argument('--import-dir', help='Path to the directory for reading the stored outputs of commands.')
- parser.add_argument('--info', '-i', action='store_true', default=False, help='List all commands with command line arguments.')
- parser.add_argument('--list', '-l', action='store_true', default=False, help='List all commands.')
- parser.add_argument('--output', '-o', help='Path to the output file.')
- parser.add_argument('--pool', '-p', default='5', type=int, help='Pool size for parallel execution of commands. (default value is 5)')
- parser.add_argument('--verbose', '-v', action='store_true', default=False, help='Add more info to output - options, commands, raw command result.')
- parser.add_argument('commands', nargs='*', help='Commands')
-
- args = parser.parse_args()
- pathCheck(args)
- run (args)
-
-if __name__ == '__main__':
- main(sys.argv)