diff --git a/.cppcheck-suppressions b/.cppcheck-suppressions index 64cd289..524f796 100644 --- a/.cppcheck-suppressions +++ b/.cppcheck-suppressions @@ -9,5 +9,5 @@ unmatchedSuppression # Suppress preprocessor errors in kernel headers (configuration issues) preprocessorErrorDirective:*/linux/bitops.h preprocessorErrorDirective:*/linux/*.h -noValidConfiguration:src/elf_det.c -noValidConfiguration:src/elf_det.mod.c +noValidConfiguration:src/proclens_module.c +noValidConfiguration:src/proclens_module.mod.c diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 5408f02..1a7cffd 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -2,7 +2,7 @@ ## Project Overview -This is a Linux kernel module project that provides process information through `/proc/elf_det/`. The module displays detailed process information including memory mappings, CPU usage, memory pressure statistics, and ELF binary details. +This is a Linux kernel module project that provides process information through `/proc/proclens_module/`. The module displays detailed process information including memory mappings, CPU usage, memory pressure statistics, and ELF binary details. Current output includes memory pressure/layout, brief network stats, open sockets, per-process I/O statistics, and thread information. @@ -48,10 +48,10 @@ The pre-commit hook automatically runs format-check and cppcheck. ### Source Files (`src/`) -- `elf_det.c` / `elf_det.h` - Main kernel module and shared helpers -- `elf_det_tests.c` - Unit tests for elf_det helpers -- `proc_elf_ctrl.c` / `proc_elf_ctrl.h` - Control utility and helpers -- `proc_elf_ctrl_tests.c` - Unit tests for proc_elf_ctrl helpers +- `proclens_module.c` / `proclens_module.h` - Main kernel module and shared helpers +- `proclens_module_tests.c` - Unit tests for proclens_module helpers +- `proclens.c` / `proclens.h` - Control utility and helpers +- `proclens_tests.c` - Unit tests for proclens helpers - `test_multithread.c` - Multi-threaded test application ### Documentation (`docs/`) @@ -87,7 +87,7 @@ The pre-commit hook automatically runs format-check and cppcheck. ### Header Files -- `elf_det.h` contains helper functions that work in BOTH kernel and userspace +- `proclens_module.h` contains helper functions that work in BOTH kernel and userspace - Use `#ifdef __KERNEL__` to separate kernel-only code - Keep helpers pure (no side effects) for testability - Add comprehensive documentation comments for public functions @@ -119,7 +119,7 @@ When adding new helper functions to headers: ### Adding New Statistics -1. Add calculation helper to header file (e.g., `elf_det.h`) +1. Add calculation helper to header file (e.g., `proclens_module.h`) 2. Add unit tests for the helper 3. Call helper in kernel module print function 4. Update `e2e/qemu-test.sh` to validate output @@ -147,10 +147,10 @@ Current implementation includes: `avg_write_bytes_per_syscall`, `io_intensity`) When extending I/O output: -1. Add or update helper functions in `elf_det.h` +1. Add or update helper functions in `proclens_module.h` 2. Keep division-by-zero handling explicit in helper logic 3. Update `e2e/qemu-test.sh` checks for new fields -4. Keep `proc_elf_ctrl` view filtering in sync (section key `4` for I/O) +4. Keep `proclens` view filtering in sync (section key `4` for I/O) ## Build System (Makefile) @@ -234,7 +234,7 @@ Tests validate actual kernel module behavior in isolated environment. ## Key Functions Reference -### Kernel Module (`elf_det.c`) +### Kernel Module (`proclens_module.c`) - `print_memory_pressure()` - Display memory pressure metrics - `print_memory_layout()` - Show heap, stack, code segments @@ -242,10 +242,10 @@ Tests validate actual kernel module behavior in isolated environment. - `print_io_stats()` - Display per-process I/O counters and derived metrics - `print_network_stats()` - Display brief per-process network statistics - `print_sockets()` - Display open sockets and endpoint details -- `elfdet_show()` - Main process information output handler -- `elfdet_threads_show()` - Thread listing output handler +- `proclens_module_show()` - Main process information output handler +- `proclens_module_threads_show()` - Thread listing output handler -### Helpers (`elf_det.h`) +### Helpers (`proclens_module.h`) - `calculate_rss_pages()` - Compute resident set size from mm counters - `pages_to_kb()` - Convert pages to kilobytes diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1c3c095..a9e3299 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,8 +37,8 @@ jobs: - name: Verify build artifacts run: | ls -la build/ - file build/elf_det.ko - file build/proc_elf_ctrl + file build/proclens_module.ko + file build/proclens echo "Build artifacts verified successfully!" - name: Upload build artifacts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 22074eb..7b57390 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -147,17 +147,19 @@ jobs: printf '\n---\n' printf '**Version**: %s | **Type**: %s\n\n' "$VERSION" "$BUMP_TYPE" printf '## Binary package includes\n\n' - printf -- '- `amd64/elf_det.ko`\n' - printf -- '- `amd64/proc_elf_ctrl`\n' - printf -- '- `arm64/elf_det.ko`\n' - printf -- '- `arm64/proc_elf_ctrl`\n' + printf -- '- `amd64/proclens_module.ko`\n' + printf -- '- `amd64/proclens`\n' + printf -- '- `arm64/proclens_module.ko`\n' + printf -- '- `arm64/proclens`\n' + printf -- '- `install.sh`\n' + printf -- '- `uninstall.sh`\n' printf -- '- `README-QUICKSTART.md`\n\n' printf 'Quick Start summary:\n\n' printf -- '- Supports Linux kernel `%s.x`\n' "${TARGET_KERNEL_BASE}" printf -- '- Includes binaries for `amd64` (`x86_64`) and `arm64` (`aarch64`)\n' - printf -- '- Install module: `sudo insmod /elf_det.ko`\n' - printf -- '- Run user tool: `sudo .//proc_elf_ctrl`\n' - printf -- '- Unload module: `sudo rmmod elf_det`\n' + printf -- '- Install or update: `sudo ./install.sh`\n' + printf -- '- Run user tool: `sudo proclens`\n' + printf -- '- Uninstall: `sudo ./uninstall.sh`\n' } > /tmp/release_notes.md else # Manual workflow dispatch @@ -166,17 +168,19 @@ jobs: printf -- '- Manual release triggered via workflow dispatch.\n\n' printf '**Version**: %s | **Type**: %s\n\n' "$VERSION" "$BUMP_TYPE" printf '## Binary package includes\n\n' - printf -- '- `amd64/elf_det.ko`\n' - printf -- '- `amd64/proc_elf_ctrl`\n' - printf -- '- `arm64/elf_det.ko`\n' - printf -- '- `arm64/proc_elf_ctrl`\n' + printf -- '- `amd64/proclens_module.ko`\n' + printf -- '- `amd64/proclens`\n' + printf -- '- `arm64/proclens_module.ko`\n' + printf -- '- `arm64/proclens`\n' + printf -- '- `install.sh`\n' + printf -- '- `uninstall.sh`\n' printf -- '- `README-QUICKSTART.md`\n\n' printf 'Quick Start summary:\n\n' printf -- '- Supports Linux kernel `%s.x`\n' "${TARGET_KERNEL_BASE}" printf -- '- Includes binaries for `amd64` (`x86_64`) and `arm64` (`aarch64`)\n' - printf -- '- Install module: `sudo insmod /elf_det.ko`\n' - printf -- '- Run user tool: `sudo .//proc_elf_ctrl`\n' - printf -- '- Unload module: `sudo rmmod elf_det`\n' + printf -- '- Install or update: `sudo ./install.sh`\n' + printf -- '- Run user tool: `sudo proclens`\n' + printf -- '- Uninstall: `sudo ./uninstall.sh`\n' } > /tmp/release_notes.md fi @@ -215,6 +219,7 @@ jobs: - name: Build binaries for target kernel env: TARGET_ARCH: ${{ matrix.arch }} + RELEASE_TAG: ${{ needs.prepare_release.outputs.release_tag }} run: | set -euo pipefail @@ -228,13 +233,13 @@ jobs: echo "Using kernel headers: $KDIR" - make KDIR="$KDIR" clean - make KDIR="$KDIR" module - make user + make KDIR="$KDIR" PROCLENS_VERSION="$RELEASE_TAG" clean + make KDIR="$KDIR" PROCLENS_VERSION="$RELEASE_TAG" module + make PROCLENS_VERSION="$RELEASE_TAG" user mkdir -p "out/${TARGET_ARCH}" - cp build/elf_det.ko "out/${TARGET_ARCH}/elf_det.ko" - cp build/proc_elf_ctrl "out/${TARGET_ARCH}/proc_elf_ctrl" + cp build/proclens_module.ko "out/${TARGET_ARCH}/proclens_module.ko" + cp build/proclens "out/${TARGET_ARCH}/proclens" - name: Upload architecture binaries uses: actions/upload-artifact@v4 @@ -276,18 +281,152 @@ jobs: run: | set -euo pipefail - PKG_NAME="elf_det-binaries-${RELEASE_TAG}-linux-${TARGET_KERNEL_BASE}" + PKG_NAME="proclens_module-binaries-${RELEASE_TAG}-linux-${TARGET_KERNEL_BASE}" PKG_DIR="dist/${PKG_NAME}" ARCHIVE_PATH="dist/${PKG_NAME}.tar.gz" mkdir -p "$PKG_DIR/amd64" "$PKG_DIR/arm64" - cp dist/artifacts/binaries-amd64/elf_det.ko "$PKG_DIR/amd64/" - cp dist/artifacts/binaries-amd64/proc_elf_ctrl "$PKG_DIR/amd64/" - cp dist/artifacts/binaries-arm64/elf_det.ko "$PKG_DIR/arm64/" - cp dist/artifacts/binaries-arm64/proc_elf_ctrl "$PKG_DIR/arm64/" + cp dist/artifacts/binaries-amd64/proclens_module.ko "$PKG_DIR/amd64/" + cp dist/artifacts/binaries-amd64/proclens "$PKG_DIR/amd64/" + cp dist/artifacts/binaries-arm64/proclens_module.ko "$PKG_DIR/arm64/" + cp dist/artifacts/binaries-arm64/proclens "$PKG_DIR/arm64/" + + chmod +x "$PKG_DIR/amd64/proclens" "$PKG_DIR/arm64/proclens" + + cat > "$PKG_DIR/install.sh" << 'EOF' + #!/usr/bin/env bash + set -euo pipefail + + usage() { + cat << 'USAGE' + Usage: sudo ./install.sh [--arch amd64|arm64] + + Installs or updates both components: + - Kernel module: /lib/modules/$(uname -r)/extra/proclens_module.ko + - User binary: /usr/local/bin/proclens + + Behavior: + - Detects architecture automatically (x86_64 -> amd64, aarch64 -> arm64) + - If module is already loaded, unloads it first + - Replaces previously installed binaries (update-in-place) + - Runs depmod and loads module with modprobe (fallback: insmod) + USAGE + } + + if [[ "${EUID}" -ne 0 ]]; then + echo "Please run as root: sudo ./install.sh" + exit 1 + fi + + SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + + ARCH="" + if [[ $# -gt 0 ]]; then + case "$1" in + --arch) + ARCH="${2:-}" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "Unknown option: $1" + usage + exit 1 + ;; + esac + fi + + if [[ -z "$ARCH" ]]; then + case "$(uname -m)" in + x86_64) + ARCH="amd64" + ;; + aarch64|arm64) + ARCH="arm64" + ;; + *) + echo "Unsupported architecture: $(uname -m)" + echo "Use --arch amd64|arm64" + exit 1 + ;; + esac + fi + + if [[ "$ARCH" != "amd64" && "$ARCH" != "arm64" ]]; then + echo "Invalid --arch value: $ARCH" + echo "Use amd64 or arm64" + exit 1 + fi + + SRC_MOD="$SCRIPT_DIR/$ARCH/proclens_module.ko" + SRC_BIN="$SCRIPT_DIR/$ARCH/proclens" - chmod +x "$PKG_DIR/amd64/proc_elf_ctrl" "$PKG_DIR/arm64/proc_elf_ctrl" + if [[ ! -f "$SRC_MOD" || ! -f "$SRC_BIN" ]]; then + echo "Missing release binaries for arch '$ARCH'" + exit 1 + fi + + KREL="$(uname -r)" + MOD_DIR="/lib/modules/$KREL/extra" + MOD_DST="$MOD_DIR/proclens_module.ko" + BIN_DST="/usr/local/bin/proclens" + + echo "Installing ProcLens binaries for arch: $ARCH" + echo "Kernel release: $KREL" + + if lsmod | grep -q '^proclens_module '; then + echo "Module is currently loaded; unloading old version..." + modprobe -r proclens_module || rmmod proclens_module + fi + + mkdir -p "$MOD_DIR" + install -m 0644 "$SRC_MOD" "$MOD_DST" + install -m 0755 "$SRC_BIN" "$BIN_DST" + + depmod "$KREL" + + if ! insmod "$MOD_DST"; then + echo "insmod failed; trying modprobe..." + modprobe proclens_module + fi + + echo "Install/update complete." + echo "User binary: $BIN_DST" + echo "Module file: $MOD_DST" + echo "Check module version: cat /sys/module/proclens_module/version" + echo "Check user version: sudo proclens --version" + EOF + + cat > "$PKG_DIR/uninstall.sh" << 'EOF' + #!/usr/bin/env bash + set -euo pipefail + + if [[ "${EUID}" -ne 0 ]]; then + echo "Please run as root: sudo ./uninstall.sh" + exit 1 + fi + + KREL="$(uname -r)" + MOD_DST="/lib/modules/$KREL/extra/proclens_module.ko" + BIN_DST="/usr/local/bin/proclens" + + if lsmod | grep -q '^proclens_module '; then + echo "Unloading proclens_module module..." + modprobe -r proclens_module || rmmod proclens_module || true + fi + + rm -f "$MOD_DST" + rm -f "$BIN_DST" + depmod "$KREL" + + echo "Uninstall complete." + EOF + + chmod +x "$PKG_DIR/install.sh" "$PKG_DIR/uninstall.sh" cat > "$PKG_DIR/README-QUICKSTART.md" << 'EOF' # Quick Start (Linux kernel __TARGET_KERNEL_BASE__.x) @@ -310,11 +449,24 @@ jobs: - `amd64/` for `x86_64` - `arm64/` for `aarch64` - ## 2) Install kernel module + ## 2) Install or update + + ```bash + sudo ./install.sh + ``` + + The installer: + - Installs/updates `proclens` to `/usr/local/bin/proclens` + - Installs/updates `proclens_module.ko` to `/lib/modules/$(uname -r)/extra/proclens_module.ko` + - Unloads an already-loaded `proclens_module` before update + - Runs `depmod` and loads the module + + Optional (override architecture selection): ```bash - sudo insmod /elf_det.ko - lsmod | grep elf_det + sudo ./install.sh --arch amd64 + # or + sudo ./install.sh --arch arm64 ``` If Secure Boot is enabled, module signing may be required. @@ -322,13 +474,14 @@ jobs: ## 3) Run user program ```bash - sudo .//proc_elf_ctrl + sudo proclens --version + sudo proclens ``` - ## 4) Unload module + ## 4) Uninstall ```bash - sudo rmmod elf_det + sudo ./uninstall.sh ``` EOF diff --git a/.gitignore b/.gitignore index 8d84f82..e31c6d9 100644 --- a/.gitignore +++ b/.gitignore @@ -29,8 +29,7 @@ modules.order *.dSYM/ # Compiled user programs -proc_elf_ctrl -elf_det_ctrl +proclens # Core dumps core diff --git a/Makefile b/Makefile index fa5e6bd..72259a8 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ # Kernel Module and User Program Makefile # Kernel module name -obj-m := elf_det.o +obj-m := proclens_module.o # Kernel build directory (allow override via env: KDIR=/path make) KDIR ?= /lib/modules/$(shell uname -r)/build @@ -10,7 +10,7 @@ KDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) # User program -USER_PROG := proc_elf_ctrl +USER_PROG := proclens # Source directory SRC_DIR := src @@ -18,6 +18,12 @@ SRC_DIR := src # Build directory for user program BUILD_DIR := build +# Version: can be overridden by environment/CLI (used by release workflow). +# Default is derived from nearest git tag; falls back to "dev". +PROCLENS_VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev") +CFLAGS_VERSION := -DPROCLENS_VERSION='"$(PROCLENS_VERSION)"' +export PROCLENS_VERSION + # Static analysis tuning CPPCHECK_JOBS ?= $(shell nproc) CPPCHECK_SRCS := $(filter-out $(SRC_DIR)/%.mod.c, $(wildcard $(SRC_DIR)/*.c)) @@ -39,7 +45,7 @@ module: user: @echo "Building user program..." @mkdir -p $(BUILD_DIR) - gcc -Wall -o $(BUILD_DIR)/$(USER_PROG) $(SRC_DIR)/$(USER_PROG).c + gcc -Wall $(CFLAGS_VERSION) -o $(BUILD_DIR)/$(USER_PROG) $(SRC_DIR)/$(USER_PROG).c @echo "User program built successfully!" # Build multi-threaded test program (for E2E testing) @@ -53,23 +59,23 @@ build-multithread: unit: @echo "Building function-level unit tests..." @mkdir -p $(BUILD_DIR) - gcc -Wall -I$(SRC_DIR) -o $(BUILD_DIR)/elf_det_tests $(SRC_DIR)/elf_det_tests.c - gcc -Wall -I$(SRC_DIR) -o $(BUILD_DIR)/proc_elf_ctrl_tests $(SRC_DIR)/proc_elf_ctrl_tests.c + gcc -Wall -I$(SRC_DIR) $(CFLAGS_VERSION) -o $(BUILD_DIR)/proclens_module_tests $(SRC_DIR)/proclens_module_tests.c + gcc -Wall -I$(SRC_DIR) $(CFLAGS_VERSION) -o $(BUILD_DIR)/proclens_tests $(SRC_DIR)/proclens_tests.c @echo "Running unit tests..." - @$(BUILD_DIR)/elf_det_tests - @$(BUILD_DIR)/proc_elf_ctrl_tests + @$(BUILD_DIR)/proclens_module_tests + @$(BUILD_DIR)/proclens_tests @echo "All function-level unit tests passed!" # Install kernel module (requires root) install: module @echo "Installing kernel module..." - sudo insmod $(BUILD_DIR)/elf_det.ko - @echo "Module installed. Check with: lsmod | grep elf_det" + sudo insmod $(BUILD_DIR)/proclens_module.ko + @echo "Module installed. Check with: lsmod | grep proclens_module" # Uninstall kernel module (requires root) uninstall: @echo "Uninstalling kernel module..." - sudo rmmod elf_det 2>/dev/null || true + sudo rmmod proclens_module 2>/dev/null || true @echo "Module uninstalled." # Test: install module and run user program diff --git a/README.md b/README.md index 398caef..59778d0 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ - **Thread Information**: List all threads with TID, state, CPU usage, priority, and CPU affinity - **CPU Usage Tracking**: Real-time CPU percentage calculation per process and thread - **ELF Section Analysis**: Binary base address and section boundaries -- **Proc Interface**: Easy access through `/proc/elf_det/` +- **Proc Interface**: Easy access through `/proc/proclens_module/` - **Live Dashboard Mode**: No-arg mode refreshes every 1s with switchable Memory, Network, Threads, and I/O sections - **Comprehensive Testing**: Unit tests and QEMU-based E2E testing - **Code Quality**: Pre-configured static analysis (sparse, cppcheck, checkpatch) @@ -62,7 +62,7 @@ ``` 4. Run user program: ```bash - ./build/proc_elf_ctrl + sudo ./build/proclens ``` ### Live Mode (No Parameters) @@ -101,6 +101,7 @@ Running without arguments starts a live dashboard: sudo make uninstall ``` + ## Release Binaries Download prebuilt binaries from GitHub Releases: @@ -114,21 +115,32 @@ Download prebuilt binaries from GitHub Releases: > If your system runs any other kernel version, build from source instead. -After downloading, install/run with root privileges as needed: +After downloading, the preferred installation/update path is to use the bundled installer scripts: ```bash -sudo insmod ./elf_det.ko -./proc_elf_ctrl +sudo ./install.sh +sudo proclens --version +sudo proclens +sudo ./uninstall.sh +``` + +Manual install/run is still possible if needed: + +```bash +sudo insmod ./proclens_module.ko +sudo ./proclens ``` To unload the module: ```bash -sudo rmmod elf_det +sudo rmmod proclens_module ``` More detailed information is bundled with the release packages. + + ## Makefile Targets ```bash diff --git a/docs/RELEASE.md b/docs/RELEASE.md index 95b25ce..7c2e845 100644 --- a/docs/RELEASE.md +++ b/docs/RELEASE.md @@ -50,8 +50,8 @@ You can manually trigger a release from the GitHub Actions tab: - For PR releases, notes include a `Changelog` section with PR title/body - Notes include version/type metadata - Notes include a `Binary package includes` section summarizing packaged contents and quick-start commands -3. **Binary Builds**: Builds `elf_det.ko` and `proc_elf_ctrl` for both `amd64` and `arm64` -4. **Packaging**: Produces a bundled archive named `elf_det-binaries-vX.Y.Z-linux-6.8.0.tar.gz` plus a `.sha256` checksum file +3. **Binary Builds**: Builds `proclens_module.ko` and `proclens` for both `amd64` and `arm64` +4. **Packaging**: Produces a bundled archive named `proclens_module-binaries-vX.Y.Z-linux-6.8.0.tar.gz` plus a `.sha256` checksum file 5. **Git Tag Creation**: Creates an annotated git tag (e.g., `v1.4.0`) if it does not already exist 6. **GitHub Release**: Creates a release on GitHub using the generated release notes and uploads the archive + checksum @@ -74,7 +74,7 @@ MAJOR.MINOR.PATCH **Use MAJOR version when:** - Changing kernel module API -- Removing features or fields from /proc/elf_det/ +- Removing features or fields from /proc/proclens_module/ - Incompatible changes to output format **Use MINOR version when:** diff --git a/docs/TECHNICAL.md b/docs/TECHNICAL.md index 53b4d15..553ecd7 100644 --- a/docs/TECHNICAL.md +++ b/docs/TECHNICAL.md @@ -1,19 +1,19 @@ # Technical Details -## Kernel Module (`elf_det.c`) +## Kernel Module (`proclens_module.c`) -The kernel module creates entries in `/proc/elf_det/`: -- `/proc/elf_det/pid` - Read/write file to set target PID and read back current buffer value -- `/proc/elf_det/det` - Read-only file to retrieve process information -- `/proc/elf_det/threads` - Read-only file to retrieve thread information +The kernel module creates entries in `/proc/proclens_module/`: +- `/proc/proclens_module/pid` - Read/write file to set target PID and read back current buffer value +- `/proc/proclens_module/det` - Read-only file to retrieve process information +- `/proc/proclens_module/threads` - Read-only file to retrieve thread information ### Key Functions -- `elfdet_show()` - Main function to gather and format process information -- `elfdet_threads_show()` - Gathers thread information for all threads in a process +- `proclens_module_show()` - Main function to gather and format process information +- `proclens_module_threads_show()` - Gathers thread information for all threads in a process - `find_stack_vma_end()` - Finds stack VMA lower boundary by iterating VMAs - `procfile_write()` - Handles PID input from user space -- `procfile_read()` - Returns formatted `/proc/elf_det/pid` buffer content +- `procfile_read()` - Returns formatted `/proc/proclens_module/pid` buffer content ### Memory Information Extracted @@ -110,13 +110,13 @@ Shows both `start_stack` (top/base) and `stack_end` (current lower boundary). Th - `mm_types.h` - Memory management structures - Maple tree API - Modern VMA iteration (kernel 6.8+) -## User Program (`proc_elf_ctrl.c`) +## User Program (`proclens.c`) Simple C program that supports two modes: ### Live Mode (No Arguments) ```bash -./build/proc_elf_ctrl +./build/proclens ``` Prompts for a PID, then enters a 1-second refresh loop with section filtering. Each refresh prints start/end timestamps in `YY/MM/DD HH:MM:SS`. @@ -158,7 +158,7 @@ Notes: ### Argument Mode ```bash -./build/proc_elf_ctrl +./build/proclens ``` Non-interactive mode - write PID once and print both process and thread information. @@ -167,14 +167,14 @@ Non-interactive mode - write PID once and print both process and thread informat You can override the proc directory for testing: ```bash -ELF_DET_PROC_DIR=/tmp/fakeproc ./build/proc_elf_ctrl 12345 +PROCLENS_PROC_DIR=/tmp/fakeproc ./build/proclens 12345 ``` Internally, path construction is handled via `build_proc_path()`. ## Helper Libraries -### `src/elf_det.h` +### `src/proclens_module.h` Pure functions for CPU usage, BSS range, heap range, thread state, and address range checking: - `compute_usage_permyriad()` - CPU usage calculation - `compute_bss_range()` - BSS boundary validation @@ -183,13 +183,13 @@ Pure functions for CPU usage, BSS range, heap range, thread state, and address r Works in both kernel and user space contexts. -### `src/proc_elf_ctrl.h` +### `src/proclens.h` Path building with environment override: -- `build_proc_path()` - Constructs `/proc/elf_det/` paths with `ELF_DET_PROC_DIR` support +- `build_proc_path()` - Constructs `/proc/proclens_module/` paths with `PROCLENS_PROC_DIR` support ## Output Format -### Process Information (`/proc/elf_det/det`) +### Process Information (`/proc/proclens_module/det`) The output is human-readable and grouped into sections: @@ -200,7 +200,7 @@ The output is human-readable and grouped into sections: - Network stats (brief) - Open sockets (file descriptors, address families, connection states) -### Thread Information (`/proc/elf_det/threads`) +### Thread Information (`/proc/proclens_module/threads`) ``` TID NAME CPU(%) STATE PRIORITY NICE CPU_AFF ``` diff --git a/docs/TESTING.md b/docs/TESTING.md index 7ad93f3..37b4fbb 100644 --- a/docs/TESTING.md +++ b/docs/TESTING.md @@ -9,8 +9,8 @@ make unit ``` This builds and runs: -- `src/elf_det_tests.c` – verifies `compute_usage_permyriad()`, `compute_bss_range()`, `compute_heap_range()`, `is_address_in_range()`, `get_thread_state_char()`, `build_cpu_affinity_string()`, and memory-pressure helpers -- `src/proc_elf_ctrl_tests.c` – verifies `build_proc_path()`, PID argument bounds, +- `src/proclens_module_tests.c` – verifies `compute_usage_permyriad()`, `compute_bss_range()`, `compute_heap_range()`, `is_address_in_range()`, `get_thread_state_char()`, `build_cpu_affinity_string()`, and memory-pressure helpers +- `src/proclens_tests.c` – verifies `build_proc_path()`, PID argument bounds, process-info print path, cmdline formatting, and section filtering for memory/network/I/O live views Artifacts are created under `build/`. @@ -54,7 +54,7 @@ ssh -p 2222 ubuntu@localhost cd ProcLens make clean && make all sudo make install -./build/proc_elf_ctrl +./build/proclens # Multi-threaded module test make run-multithread @@ -132,27 +132,27 @@ sudo ./e2e/qemu-test.sh ```bash # Ensure module is loaded -lsmod | grep elf_det +lsmod | grep proclens_module # Check proc entries exist (should show det, pid, threads) -ls -la /proc/elf_det/ +ls -la /proc/proclens_module/ ``` ### Testing Thread Information ```bash # Test with single-threaded process -./build/proc_elf_ctrl $$ +./build/proclens $$ # Test with multi-threaded process # Start the bundled multi-threaded test program make build-multithread ./build/test_multithread & -./build/proc_elf_ctrl $! +./build/proclens $! # Manually check thread info -echo "" | sudo tee /proc/elf_det/pid -sudo cat /proc/elf_det/threads +echo "" | sudo tee /proc/proclens_module/pid +sudo cat /proc/proclens_module/threads ``` ### Verify Thread Features @@ -173,12 +173,12 @@ Multi-threaded processes (browsers, IDEs, servers) will show multiple threads. Verify no-argument live mode behavior: ```bash -./build/proc_elf_ctrl +./build/proclens ``` Expected behavior: - Program prints the startup logo once, then validates module/proc readiness -- If `elf_det` is not loaded, program exits immediately with status `-1` +- If `proclens_module` is not loaded, program exits immediately with status `-1` - If required proc files are missing (`pid`, `det`, `threads`), program exits immediately with status `-1` - Screen refreshes every 1 second - Header shows controls (`1` memory, `2` network, `3` threads, `4` I/O, `0` switch PID) @@ -201,7 +201,7 @@ mkdir -p /tmp/fake_proc : > /tmp/fake_proc/pid printf 'Memory Pressure Statistics:\n[network]\nsockets_total: 2\n' > /tmp/fake_proc/det printf 'tid header\nthread line\n' > /tmp/fake_proc/threads -ELF_DET_PROC_DIR=/tmp/fake_proc ./build/proc_elf_ctrl +PROCLENS_PROC_DIR=/tmp/fake_proc ./build/proclens ``` ### Testing Socket Information @@ -209,13 +209,13 @@ To verify socket functionality, test with processes that have open sockets: ```bash # Test with systemd (PID 1) - usually has several sockets -./build/proc_elf_ctrl 1 +./build/proclens 1 # Test with SSH daemon -pgrep sshd | head -1 | xargs ./build/proc_elf_ctrl +pgrep sshd | head -1 | xargs ./build/proclens # Test with current shell (will show SSH connection socket if remote) -./build/proc_elf_ctrl $$ +./build/proclens $$ ``` The socket output should include: @@ -235,7 +235,7 @@ Processes with no open sockets will display "No open sockets". The process output should include a short network section when sockets exist: ```bash -./build/proc_elf_ctrl $$ +./build/proclens $$ ``` Look for: @@ -262,7 +262,7 @@ In the open sockets section: The process output should include an I/O section: ```bash -./build/proc_elf_ctrl $$ +./build/proclens $$ ``` Look for: @@ -277,7 +277,7 @@ the section will show `status: unavailable` instead of per-field counters. ## Test Coverage -### Helper Function Tests (`elf_det_tests.c`) +### Helper Function Tests (`proclens_module_tests.c`) Complete unit test coverage for all pure helper functions: diff --git a/e2e/qemu-test.sh b/e2e/qemu-test.sh index 4adef08..970d933 100755 --- a/e2e/qemu-test.sh +++ b/e2e/qemu-test.sh @@ -70,8 +70,8 @@ echo "" echo "2. Copying files to QEMU VM..." ssh $SSH_OPTS ${SSH_USER}@${SSH_HOST} "mkdir -p ~/ProcLens" ssh $SSH_OPTS ${SSH_USER}@${SSH_HOST} "mkdir -p ~/ProcLens/src" -scp $SCP_OPTS src/elf_det.c src/elf_det.h src/elf_det_tests.c \ - src/proc_elf_ctrl.c src/proc_elf_ctrl.h src/proc_elf_ctrl_tests.c \ +scp $SCP_OPTS src/proclens_module.c src/proclens_module.h src/proclens_module_tests.c \ + src/proclens.c src/proclens.h src/proclens_tests.c \ src/test_multithread.c src/Kbuild ${SSH_USER}@${SSH_HOST}:~/ProcLens/src/ scp $SCP_OPTS Makefile ${SSH_USER}@${SSH_HOST}:~/ProcLens/ @@ -99,23 +99,23 @@ make KDIR=/lib/modules/$(uname -r)/build all make build-multithread echo "Installing kernel module..." -if lsmod | grep -q '^elf_det'; then +if lsmod | grep -q '^proclens_module'; then echo "Module already loaded; unloading first..." - sudo rmmod elf_det || true + sudo rmmod proclens_module || true fi -sudo insmod build/elf_det.ko +sudo insmod build/proclens_module.ko echo "Checking if module is loaded..." -lsmod | grep elf_det +lsmod | grep proclens_module echo "Checking /proc entries..." -ls -la /proc/elf_det/ +ls -la /proc/proclens_module/ echo "Expected files: det, pid, threads" echo "" echo "=== Testing Process Information (PID: $$) ===" -echo "$$" | sudo tee /proc/elf_det/pid > /dev/null -PROC_OUT=$(sudo cat /proc/elf_det/det) +echo "$$" | sudo tee /proc/proclens_module/pid > /dev/null +PROC_OUT=$(sudo cat /proc/proclens_module/det) echo "$PROC_OUT" if ! echo "$PROC_OUT" | grep -q "Memory Pressure Statistics"; then echo "[FAIL] Memory pressure stats missing for PID $$" @@ -167,13 +167,13 @@ fi echo "" echo "=== Testing Thread Information (PID: $$) ===" -sudo cat /proc/elf_det/threads +sudo cat /proc/proclens_module/threads echo "" echo "=== Testing with PID 1 (init/systemd) ===" -echo "1" | sudo tee /proc/elf_det/pid > /dev/null +echo "1" | sudo tee /proc/proclens_module/pid > /dev/null echo "Process info:" -PROC_OUT=$(sudo cat /proc/elf_det/det) +PROC_OUT=$(sudo cat /proc/proclens_module/det) echo "$PROC_OUT" if ! echo "$PROC_OUT" | grep -q "Memory Pressure Statistics"; then echo "[FAIL] Memory pressure stats missing for PID 1" @@ -224,11 +224,11 @@ if ! echo "$PROC_OUT" | grep -q "No open sockets"; then fi echo "" echo "Thread info:" -sudo cat /proc/elf_det/threads +sudo cat /proc/proclens_module/threads echo "" -echo "=== Testing with user program (proc_elf_ctrl, PID=1) ===" -sudo ./build/proc_elf_ctrl 1 || true +echo "=== Testing with user program (proclens, PID=1) ===" +sudo ./build/proclens 1 || true echo "" echo "=== Testing with multi-threaded application ===" @@ -243,10 +243,10 @@ sleep 1 # Test with the multi-threaded process echo "" echo "Testing thread detection with multi-threaded process:" -echo "$MULTITHREAD_PID" | sudo tee /proc/elf_det/pid > /dev/null +echo "$MULTITHREAD_PID" | sudo tee /proc/proclens_module/pid > /dev/null echo "" echo "Process info:" -PROC_OUT=$(sudo cat /proc/elf_det/det) +PROC_OUT=$(sudo cat /proc/proclens_module/det) echo "$PROC_OUT" if ! echo "$PROC_OUT" | grep -q "Memory Pressure Statistics"; then echo "[FAIL] Memory pressure stats missing for PID $MULTITHREAD_PID" @@ -297,14 +297,14 @@ if ! echo "$PROC_OUT" | grep -q "No open sockets"; then fi echo "" echo "Thread info (should show 5 threads: 1 main + 4 workers):" -sudo cat /proc/elf_det/threads +sudo cat /proc/proclens_module/threads sleep 1 # Use the user program to display formatted output echo "" -echo "Using proc_elf_ctrl with multi-threaded process:" -sudo ./build/proc_elf_ctrl $MULTITHREAD_PID || true +echo "Using proclens with multi-threaded process:" +sudo ./build/proclens $MULTITHREAD_PID || true # Wait for multi-threaded program to finish wait $MULTITHREAD_PID @@ -313,7 +313,7 @@ echo "[PASS] Multi-threaded application test completed" echo "" echo "=== Verifying all proc files are accessible ===" -if [ -r /proc/elf_det/det ] && [ -r /proc/elf_det/pid ] && [ -r /proc/elf_det/threads ]; then +if [ -r /proc/proclens_module/det ] && [ -r /proc/proclens_module/pid ] && [ -r /proc/proclens_module/threads ]; then echo "[PASS] All proc files exist and are readable" else echo "[FAIL] Some proc files are missing or not readable" @@ -326,11 +326,11 @@ sudo dmesg | tail -20 echo "" echo "Uninstalling module..." -sudo rmmod elf_det +sudo rmmod proclens_module echo "" echo "Verifying module unloaded..." -lsmod | grep elf_det || echo "Module successfully unloaded" +lsmod | grep proclens_module || echo "Module successfully unloaded" echo "" echo "===================================================" diff --git a/src/Kbuild b/src/Kbuild index 1a887e5..1f53724 100644 --- a/src/Kbuild +++ b/src/Kbuild @@ -1 +1,2 @@ -obj-m := elf_det.o +obj-m := proclens_module.o +ccflags-y += -DPROCLENS_VERSION='"$(PROCLENS_VERSION)"' diff --git a/src/proc_elf_ctrl.c b/src/proclens.c similarity index 92% rename from src/proc_elf_ctrl.c rename to src/proclens.c index 2c581aa..60d7f0e 100644 --- a/src/proc_elf_ctrl.c +++ b/src/proclens.c @@ -7,7 +7,11 @@ #include #include #include -#include "proc_elf_ctrl.h" +#include "proclens.h" + +#ifndef PROCLENS_VERSION +#define PROCLENS_VERSION "dev" +#endif #define C_RESET "\033[0m" #define C_BOLD "\033[1m" @@ -143,7 +147,7 @@ static int ensure_module_loaded(void) { int loaded; - loaded = is_module_loaded("elf_det"); + loaded = is_module_loaded("proclens_module"); if (loaded < 0) { perror("open /proc/modules"); return -1; @@ -151,10 +155,12 @@ static int ensure_module_loaded(void) if (loaded == 0) { fprintf(stderr, - "%serror:%s kernel module 'elf_det' is not loaded\n", + "%serror:%s kernel module 'proclens_module' is not " + "loaded\n", color_code(C_YELLOW), color_code(C_RESET)); - fprintf(stderr, "hint: run 'sudo insmod ./build/elf_det.ko' or " - "'sudo make install'\n"); + fprintf(stderr, + "hint: run 'sudo insmod ./build/proclens_module.ko' or " + "'sudo make install'\n"); return -1; } @@ -182,8 +188,9 @@ static int ensure_proc_files_present(void) "%s\n", color_code(C_YELLOW), color_code(C_RESET), path); - fprintf(stderr, "hint: confirm /proc/elf_det is " - "mounted and initialized\n"); + fprintf(stderr, + "hint: confirm /proc/proclens_module is " + "mounted and initialized\n"); free(path); return -1; } @@ -194,6 +201,17 @@ static int ensure_proc_files_present(void) return 0; } +static int ensure_root_privileges(void) +{ + if (geteuid() == 0) + return 0; + + fprintf(stderr, "%serror:%s proclens requires root privileges\n", + color_code(C_YELLOW), color_code(C_RESET)); + fprintf(stderr, "hint: run with sudo, e.g. 'sudo proclens'\n"); + return -1; +} + static void print_cmdline(const char *pid_str) { char path[64]; @@ -960,9 +978,49 @@ static void run_live_mode(void) restore_terminal(); } +static void print_usage(void) +{ + printf("Usage: proclens [OPTIONS] [PID]\n"); + printf("\n"); + printf("Options:\n"); + printf(" -h, --help Show this help message and exit\n"); + printf(" -v, --version Show version and exit\n"); + printf("\n"); + printf("Arguments:\n"); + printf(" PID Show one-shot process info for the given " + "PID\n"); + printf("\n"); + printf("Live mode (no arguments):\n"); + printf(" Auto-refreshes every 1s. Controls shown in-app:\n"); + printf(" 1 Memory section\n"); + printf(" 2 Network section\n"); + printf(" 3 Threads section\n"); + printf(" 4 I/O section\n"); + printf(" 0 Change PID\n"); + printf(" Up/k Older snapshot Down/j Newer snapshot\n"); + printf(" f Resume live follow\n"); + printf(" Ctrl+C Exit\n"); +} + int main(int argc, char **argv) { init_color_output(); + + if (argc > 1 && + (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)) { + print_usage(); + return 0; + } + + if (argc > 1 && + (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-v") == 0)) { + printf("proclens %s\n", PROCLENS_VERSION); + return 0; + } + + if (ensure_root_privileges() < 0) + return -1; + print_logo_text(); if (ensure_module_loaded() < 0) return -1; diff --git a/src/proc_elf_ctrl.h b/src/proclens.h similarity index 68% rename from src/proc_elf_ctrl.h rename to src/proclens.h index 4852e70..b42a220 100644 --- a/src/proc_elf_ctrl.h +++ b/src/proclens.h @@ -5,13 +5,13 @@ #include #include -/* Build path to /proc/elf_det/ with optional override via env. */ +/* Build path to /proc/proclens_module/ with optional override via env. */ static inline char *build_proc_path(const char *name) { - const char *base = getenv("ELF_DET_PROC_DIR"); + const char *base = getenv("PROCLENS_PROC_DIR"); if (!base || !*base) - base = "/proc/elf_det"; + base = "/proc/proclens_module"; size_t len = strlen(base) + 1 + strlen(name) + 1; char *p = (char *)malloc(len); diff --git a/src/elf_det.c b/src/proclens_module.c similarity index 90% rename from src/elf_det.c rename to src/proclens_module.c index 587c453..1ba9513 100644 --- a/src/elf_det.c +++ b/src/proclens_module.c @@ -22,9 +22,14 @@ #include //for sockaddr_in #include //for sockaddr_in6 #include //for inet_sock -#include "elf_det.h" +#include "proclens_module.h" + +#ifndef PROCLENS_VERSION +#define PROCLENS_VERSION "dev" +#endif MODULE_LICENSE("Dual MIT/GPL"); // module license +MODULE_VERSION(PROCLENS_VERSION); static char buff[20] = "1"; // the common(global) buffer between kernel and user space @@ -32,8 +37,8 @@ static int user_pid; // the desired pid that we get from user static int number_opens; // number of opens(writes) to the pid file // skip these instances (will be described bellow) -static struct proc_dir_entry *elfdet_dir, *elfdet_det_entry, *elfdet_pid_entry, - *elfdet_threads_entry; +static struct proc_dir_entry *proclens_module_dir, *proclens_module_det_entry, + *proclens_module_pid_entry, *proclens_module_threads_entry; static int procfile_open(struct inode *inode, struct file *file); static ssize_t procfile_read(struct file *, char __user *, size_t, loff_t *); @@ -347,9 +352,9 @@ static void print_network_stats(struct seq_file *m, struct task_struct *task) u64 socket_rx_bytes, socket_tx_bytes; u64 drops = 0; int socket_total = 0, tcp_count = 0, udp_count = 0, unix_count = 0; - struct netdev_count netdevs[ELF_DET_NETDEV_MAX]; + struct netdev_count netdevs[PROCLENS_MODULE_NETDEV_MAX]; int netdev_len = 0; - struct top_talker_entry top_talkers[ELF_DET_TOP_TALKERS_MAX]; + struct top_talker_entry top_talkers[PROCLENS_MODULE_TOP_TALKERS_MAX]; int top_talker_len = 0; int ifindex; struct net_device *dev; @@ -406,7 +411,7 @@ static void print_network_stats(struct seq_file *m, struct task_struct *task) unix_count++; try_insert_top_talker(top_talkers, &top_talker_len, - ELF_DET_TOP_TALKERS_MAX, fd, + PROCLENS_MODULE_TOP_TALKERS_MAX, fd, sk->sk_family, sk->sk_protocol, socket_rx_bytes, socket_tx_bytes); @@ -418,7 +423,8 @@ static void print_network_stats(struct seq_file *m, struct task_struct *task) dev = dev_get_by_index_rcu(sock_net(sk), ifindex); dev_name = dev ? dev->name : "unknown"; add_netdev_count(netdevs, &netdev_len, - ELF_DET_NETDEV_MAX, ifindex, dev_name); + PROCLENS_MODULE_NETDEV_MAX, ifindex, + dev_name); } } @@ -620,7 +626,7 @@ static void print_sockets(struct seq_file *m, struct task_struct *task) } // this function is the base function to gather information from kernel -static int elfdet_show(struct seq_file *m, void *v) +static int proclens_module_show(struct seq_file *m, void *v) { struct task_struct *task; unsigned long bss_start = 0, bss_end = 0; @@ -708,7 +714,7 @@ static int elfdet_show(struct seq_file *m, void *v) } // this function gathers thread information from kernel -static int elfdet_threads_show(struct seq_file *m, void *v) +static int proclens_module_threads_show(struct seq_file *m, void *v) { struct task_struct *task, *thread; int ret, thread_count = 0; @@ -751,28 +757,29 @@ static int elfdet_threads_show(struct seq_file *m, void *v) } // runs when opening file -static int elfdet_open(struct inode *inode, struct file *file) +static int proclens_module_open(struct inode *inode, struct file *file) { - return single_open(file, elfdet_show, NULL); // calling elfdet_show + return single_open(file, proclens_module_show, + NULL); // calling proclens_module_show } // runs when opening threads file -static int elfdet_threads_open(struct inode *inode, struct file *file) +static int proclens_module_threads_open(struct inode *inode, struct file *file) { - return single_open(file, elfdet_threads_show, NULL); + return single_open(file, proclens_module_threads_show, NULL); } // file operations of det proc (using proc_ops for kernel 5.6+) -static const struct proc_ops elfdet_det_ops = { - .proc_open = elfdet_open, +static const struct proc_ops proclens_module_det_ops = { + .proc_open = proclens_module_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, }; // file operations of threads proc -static const struct proc_ops elfdet_threads_ops = { - .proc_open = elfdet_threads_open, +static const struct proc_ops proclens_module_threads_ops = { + .proc_open = proclens_module_threads_open, .proc_read = seq_read, .proc_lseek = seq_lseek, .proc_release = single_release, @@ -845,46 +852,49 @@ static const struct proc_ops write_pops = { .proc_write = procfile_write, // this is the important part }; -static int elfdet_init(void) +static int proclens_module_init(void) { - elfdet_dir = proc_mkdir("elf_det", NULL); - // creating the directory: elf_det in proc + proclens_module_dir = proc_mkdir("proclens_module", NULL); + // creating the directory: proclens_module in proc - if (!elfdet_dir) + if (!proclens_module_dir) return -ENOMEM; // 0644 means owner read/write, others read-only - elfdet_det_entry = - proc_create("det", 0644, elfdet_dir, &elfdet_det_ops); - // create proc file det with elfdet_det_ops - pr_info("det initiated; /proc/elf_det/det created\n"); - elfdet_pid_entry = proc_create("pid", 0644, elfdet_dir, &write_pops); + proclens_module_det_entry = proc_create( + "det", 0644, proclens_module_dir, &proclens_module_det_ops); + // create proc file det with proclens_module_det_ops + pr_info("det initiated; /proc/proclens_module/det created\n"); + proclens_module_pid_entry = + proc_create("pid", 0644, proclens_module_dir, &write_pops); // create proc file pid with write_pops - pr_info("pid initiated; /proc/elf_det/pid created\n"); + pr_info("pid initiated; /proc/proclens_module/pid created\n"); - elfdet_threads_entry = - proc_create("threads", 0644, elfdet_dir, &elfdet_threads_ops); - // create proc file threads with elfdet_threads_ops - pr_info("threads initiated; /proc/elf_det/threads created\n"); + proclens_module_threads_entry = + proc_create("threads", 0644, proclens_module_dir, + &proclens_module_threads_ops); + // create proc file threads with proclens_module_threads_ops + pr_info("threads initiated; /proc/proclens_module/threads created\n"); - if (!elfdet_det_entry || !elfdet_threads_entry) + if (!proclens_module_det_entry || !proclens_module_threads_entry) return -ENOMEM; return 0; } // the remove operations done by module(cleaning up) -static void elfdet_exit(void) +static void proclens_module_exit(void) { - proc_remove(elfdet_det_entry); - pr_info("elf_det exited; /proc/elf_det/det deleted\n"); - proc_remove(elfdet_pid_entry); - pr_info("elf_det exited; /proc/elf_det/pid deleted\n"); - proc_remove(elfdet_threads_entry); - pr_info("elf_det exited; /proc/elf_det/threads deleted\n"); - proc_remove(elfdet_dir); + proc_remove(proclens_module_det_entry); + pr_info("proclens_module exited; /proc/proclens_module/det deleted\n"); + proc_remove(proclens_module_pid_entry); + pr_info("proclens_module exited; /proc/proclens_module/pid deleted\n"); + proc_remove(proclens_module_threads_entry); + pr_info("proclens_module exited; /proc/proclens_module/threads " + "deleted\n"); + proc_remove(proclens_module_dir); } // macros for init and exit -module_init(elfdet_init); -module_exit(elfdet_exit); +module_init(proclens_module_init); +module_exit(proclens_module_exit); diff --git a/src/elf_det.h b/src/proclens_module.h similarity index 98% rename from src/elf_det.h rename to src/proclens_module.h index 9246845..b8c724b 100644 --- a/src/elf_det.h +++ b/src/proclens_module.h @@ -211,14 +211,14 @@ struct memory_region { int exists; /* 1 if region should be displayed, 0 otherwise */ }; -#define ELF_DET_NETDEV_MAX 8 -#define ELF_DET_NETDEV_NAME_MAX IFNAMSIZ -#define ELF_DET_TOP_TALKERS_MAX 3 +#define PROCLENS_MODULE_NETDEV_MAX 8 +#define PROCLENS_MODULE_NETDEV_NAME_MAX IFNAMSIZ +#define PROCLENS_MODULE_TOP_TALKERS_MAX 3 struct netdev_count { int ifindex; int count; - char name[ELF_DET_NETDEV_NAME_MAX]; + char name[PROCLENS_MODULE_NETDEV_NAME_MAX]; }; struct top_talker_entry { diff --git a/src/elf_det_tests.c b/src/proclens_module_tests.c similarity index 93% rename from src/elf_det_tests.c rename to src/proclens_module_tests.c index 6df72dd..c7e4af2 100644 --- a/src/elf_det_tests.c +++ b/src/proclens_module_tests.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -#include "elf_det.h" +#include "proclens_module.h" #include #include #include @@ -503,21 +503,24 @@ int main(void) assert(len == 0); /* top talkers ranking tests */ - struct top_talker_entry talkers[ELF_DET_TOP_TALKERS_MAX]; + struct top_talker_entry talkers[PROCLENS_MODULE_TOP_TALKERS_MAX]; int talker_len = 0; memset(talkers, 0, sizeof(talkers)); - try_insert_top_talker(talkers, &talker_len, ELF_DET_TOP_TALKERS_MAX, 10, - 2, 6, 500, 500); + try_insert_top_talker(talkers, &talker_len, + PROCLENS_MODULE_TOP_TALKERS_MAX, 10, 2, 6, 500, + 500); assert(talker_len == 1); assert(talkers[0].fd == 10); assert(talkers[0].total_bytes == 1000); - try_insert_top_talker(talkers, &talker_len, ELF_DET_TOP_TALKERS_MAX, 20, - 2, 6, 100, 100); - try_insert_top_talker(talkers, &talker_len, ELF_DET_TOP_TALKERS_MAX, 30, - 1, 17, 700, 500); + try_insert_top_talker(talkers, &talker_len, + PROCLENS_MODULE_TOP_TALKERS_MAX, 20, 2, 6, 100, + 100); + try_insert_top_talker(talkers, &talker_len, + PROCLENS_MODULE_TOP_TALKERS_MAX, 30, 1, 17, 700, + 500); assert(talker_len == 3); assert(talkers[0].fd == 30); assert(talkers[0].total_bytes == 1200); @@ -525,15 +528,16 @@ int main(void) assert(talkers[2].fd == 20); /* Replaces the current lowest-ranked entry */ - try_insert_top_talker(talkers, &talker_len, ELF_DET_TOP_TALKERS_MAX, 40, - 10, 6, 300, 300); + try_insert_top_talker(talkers, &talker_len, + PROCLENS_MODULE_TOP_TALKERS_MAX, 40, 10, 6, 300, + 300); assert(talker_len == 3); assert(talkers[2].fd == 40); assert(talkers[2].total_bytes == 600); /* Zero-traffic entries are ignored */ - try_insert_top_talker(talkers, &talker_len, ELF_DET_TOP_TALKERS_MAX, 50, - 2, 6, 0, 0); + try_insert_top_talker(talkers, &talker_len, + PROCLENS_MODULE_TOP_TALKERS_MAX, 50, 2, 6, 0, 0); assert(talker_len == 3); assert(talkers[0].fd == 30); @@ -588,41 +592,42 @@ int main(void) assert(strcmp(state_str, "UNKNOWN") == 0); /* netdev_count tests */ - struct netdev_count devs[ELF_DET_NETDEV_MAX]; + struct netdev_count devs[PROCLENS_MODULE_NETDEV_MAX]; int dev_len = 0; int start_len; int i; memset(devs, 0, sizeof(devs)); - add_netdev_count(devs, &dev_len, ELF_DET_NETDEV_MAX, 2, "eth0"); + add_netdev_count(devs, &dev_len, PROCLENS_MODULE_NETDEV_MAX, 2, "eth0"); assert(dev_len == 1); assert(devs[0].ifindex == 2); assert(devs[0].count == 1); assert(strcmp(devs[0].name, "eth0") == 0); - add_netdev_count(devs, &dev_len, ELF_DET_NETDEV_MAX, 2, "eth1"); + add_netdev_count(devs, &dev_len, PROCLENS_MODULE_NETDEV_MAX, 2, "eth1"); assert(dev_len == 1); assert(devs[0].count == 2); assert(strcmp(devs[0].name, "eth0") == 0); - add_netdev_count(devs, &dev_len, ELF_DET_NETDEV_MAX, 1, "lo"); + add_netdev_count(devs, &dev_len, PROCLENS_MODULE_NETDEV_MAX, 1, "lo"); assert(dev_len == 2); assert(devs[1].ifindex == 1); assert(devs[1].count == 1); assert(strcmp(devs[1].name, "lo") == 0); start_len = dev_len; - for (i = start_len; i < ELF_DET_NETDEV_MAX; i++) { + for (i = start_len; i < PROCLENS_MODULE_NETDEV_MAX; i++) { char name_buf[16]; snprintf(name_buf, sizeof(name_buf), "d%d", i); - add_netdev_count(devs, &dev_len, ELF_DET_NETDEV_MAX, 100 + i, - name_buf); + add_netdev_count(devs, &dev_len, PROCLENS_MODULE_NETDEV_MAX, + 100 + i, name_buf); } - assert(dev_len == ELF_DET_NETDEV_MAX); + assert(dev_len == PROCLENS_MODULE_NETDEV_MAX); - add_netdev_count(devs, &dev_len, ELF_DET_NETDEV_MAX, 999, "extra"); - assert(dev_len == ELF_DET_NETDEV_MAX); + add_netdev_count(devs, &dev_len, PROCLENS_MODULE_NETDEV_MAX, 999, + "extra"); + assert(dev_len == PROCLENS_MODULE_NETDEV_MAX); /* procfile write/read logic tests */ { diff --git a/src/proc_elf_ctrl_tests.c b/src/proclens_tests.c similarity index 90% rename from src/proc_elf_ctrl_tests.c rename to src/proclens_tests.c index c71932f..f3dc33f 100644 --- a/src/proc_elf_ctrl_tests.c +++ b/src/proclens_tests.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: MIT #define _GNU_SOURCE -#include "proc_elf_ctrl.h" +#include "proclens.h" #include #include #include @@ -27,6 +27,7 @@ static int module_loaded; static int proc_pid_present; static int proc_det_present; static int proc_threads_present; +static uid_t mock_euid; static void append_output(const char *fmt, ...) { @@ -72,10 +73,11 @@ static void reset_mocks(void) proc_pid_present = 1; proc_det_present = 1; proc_threads_present = 1; + mock_euid = 0; memset(cmdline_content, 0, sizeof(cmdline_content)); cmdline_len = 0; - setenv("ELF_DET_PROC_DIR", "/fake_proc", 1); + setenv("PROCLENS_PROC_DIR", "/fake_proc", 1); } static FILE *mock_fopen(const char *path, const char *mode) @@ -112,8 +114,10 @@ static FILE *mock_fopen(const char *path, const char *mode) if (!strcmp(path, "/proc/modules") && strchr(mode, 'r')) { if (module_loaded) - return fmemopen((void *)"elf_det 0 0 - Live 0x0\n", 23, - "r"); + return fmemopen( + (void *)"proclens_module 0 0 - Live 0x0\n", + strlen("proclens_module 0 0 - Live 0x0\n"), + "r"); return fmemopen((void *)"", 0, "r"); } @@ -162,17 +166,24 @@ static void mock_perror(const char *s) append_output("%s\n", s); } -#define fopen mock_fopen -#define access mock_access -#define printf mock_printf -#define puts mock_puts -#define perror mock_perror -#define main proc_elf_ctrl_entry -#include "proc_elf_ctrl.c" +static uid_t mock_geteuid(void) +{ + return mock_euid; +} + +#define fopen mock_fopen +#define access mock_access +#define geteuid mock_geteuid +#define printf mock_printf +#define puts mock_puts +#define perror mock_perror +#define main proclens_entry +#include "proclens.c" #undef main #undef perror #undef puts #undef printf +#undef geteuid #undef access #undef fopen @@ -181,12 +192,12 @@ static void test_build_proc_path_helper(void) char *p1; char *p2; - unsetenv("ELF_DET_PROC_DIR"); + unsetenv("PROCLENS_PROC_DIR"); p1 = build_proc_path("pid"); - assert(p1 && strcmp(p1, "/proc/elf_det/pid") == 0); + assert(p1 && strcmp(p1, "/proc/proclens_module/pid") == 0); free(p1); - setenv("ELF_DET_PROC_DIR", "/tmp/fakeproc", 1); + setenv("PROCLENS_PROC_DIR", "/tmp/fakeproc", 1); p2 = build_proc_path("det"); assert(p2 && strcmp(p2, "/tmp/fakeproc/det") == 0); free(p2); @@ -249,7 +260,7 @@ static void test_main_argument_pid_is_bounded(void) reset_mocks(); cmdline_len = 0; - rc = proc_elf_ctrl_entry(2, (char **)argv); + rc = proclens_entry(2, (char **)argv); assert(rc == 0); assert(pid_stream_buf); @@ -264,7 +275,7 @@ static void test_main_exits_when_module_not_loaded(void) reset_mocks(); module_loaded = 0; - rc = proc_elf_ctrl_entry(2, (char **)argv); + rc = proclens_entry(2, (char **)argv); assert(rc == -1); assert(!pid_stream_buf); @@ -277,10 +288,24 @@ static void test_main_exits_when_proc_file_missing(void) reset_mocks(); proc_threads_present = 0; - rc = proc_elf_ctrl_entry(2, (char **)argv); + rc = proclens_entry(2, (char **)argv); + + assert(rc == -1); + assert(!pid_stream_buf); +} + +static void test_main_exits_without_root_privileges(void) +{ + static const char *const argv[] = {"prog", "1"}; + int rc; + + reset_mocks(); + mock_euid = 1000; + rc = proclens_entry(2, (char **)argv); assert(rc == -1); assert(!pid_stream_buf); + assert(!strstr(output_buf, "PROCESS INFORMATION")); } static void test_det_preamble_stops_before_sections(void) @@ -456,6 +481,7 @@ int main(void) test_main_argument_pid_is_bounded(); test_main_exits_when_module_not_loaded(); test_main_exits_when_proc_file_missing(); + test_main_exits_without_root_privileges(); test_det_preamble_stops_before_sections(); test_memory_view_filters_network_section(); test_network_view_starts_from_network_section(); @@ -463,7 +489,7 @@ int main(void) test_format_current_time_layout(); test_live_header_and_footer_show_timestamps(); test_snapshot_history_offset_navigation(); - puts("proc_elf_ctrl tests passed"); + puts("proclens tests passed"); reset_mocks(); return 0; }