From 658e931aa6ada70a71c9fddb456380d43bb51ddf Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 18 Apr 2025 12:30:59 -0700 Subject: [PATCH 01/24] Initial commit --- installers/raspap-network-activity.sh | 53 +++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100755 installers/raspap-network-activity.sh diff --git a/installers/raspap-network-activity.sh b/installers/raspap-network-activity.sh new file mode 100755 index 00000000..ae42e1ee --- /dev/null +++ b/installers/raspap-network-activity.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# RaspAP network activity monitor +# Reads from /proc/net/dev to get TX/RX byte counters for a given interface (wlan0 by default), +# calculates the difference and writes to /dev/shm/net_activity. +# +# Author: @billz +# Author URI: https://github.com/billz/ +# License: GNU General Public License v3.0 +# License URI: https://github.com/raspap/raspap-webgui/blob/master/LICENSE +# +# /usr/local/bin/raspap-network-activity.sh + +# Exit on error +set -o errexit +# Exit on error inside functions +set -o errtrace +# Turn on traces, disabled by default +# set -o xtrace + +IFACE="${1:-wlan0}" # default to wlan0 if not specified +TMPFILE="/dev/shm/net_activity" # tmpfs that resides in RAM +INTERVAL=0.05 # 50 ms + +# Initialize +prev_rx=0 +prev_tx=0 + +get_bytes() { + awk -v iface="$IFACE" '$1 ~ iface":" { + gsub(":", "", $1); + print $2, $10 + }' /proc/net/dev +} + +read rx tx < <(get_bytes) +prev_rx=$rx +prev_tx=$tx + +while true; do + sleep $INTERVAL + read rx tx < <(get_bytes) + + rx_diff=$((rx - prev_rx)) + tx_diff=$((tx - prev_tx)) + total_diff=$((rx_diff + tx_diff)) + echo "$total_diff" + echo "$total_diff" > "$TMPFILE" + + prev_rx=$rx + prev_tx=$tx +done + From 1d80b5090d75e9a2ed13a0c853738d95666be20d Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 18 Apr 2025 12:32:02 -0700 Subject: [PATCH 02/24] Create updateActivityLED() --- app/js/custom.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/js/custom.js b/app/js/custom.js index ffd2fc39..bf376dc0 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -1022,6 +1022,30 @@ function disableValidation(form) { }); } +function updateActivityLED() { + fetch('/app/net_activity') + .then(res => res.text()) + .then(data => { + const activity = parseInt(data.trim()); + console.log(activity); + const led = document.getElementById('hostapd-led'); + + if (!isNaN(activity)) { + if (activity > 170) { + led.setAttribute('data-active', 'true'); + setTimeout(() => led.removeAttribute('data-active'), 50); + led.classList.add('led-pulse'); + } else { + led.classList.remove('led-pulse'); + } + } else { + console.warn('Unexpected data:', data); + } + }) + .catch(() => { /* ignore fetch errors */ }); +} +setInterval(updateActivityLED, 200); + $(document).ready(function() { const $htmlElement = $('html'); const $modeswitch = $('#night-mode'); From 6fe48d0e4e7b642b71e6ad7a2e631d8064ee138f Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 18 Apr 2025 12:33:14 -0700 Subject: [PATCH 03/24] Update status panel w/ hostapd-led ID --- includes/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/includes/functions.php b/includes/functions.php index 6ef53ea4..c000104a 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -954,7 +954,7 @@ function renderStatus($hostapd_led, $hostapd_status, $memused_led, $memused, $cp
Status
- +
% From 03e2b853be85193337068cee91d8507350ec167c Mon Sep 17 00:00:00 2001 From: billz Date: Fri, 18 Apr 2025 12:37:50 -0700 Subject: [PATCH 04/24] Remove debug output, format comments --- installers/raspap-network-activity.sh | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/installers/raspap-network-activity.sh b/installers/raspap-network-activity.sh index ae42e1ee..fb88f4a0 100755 --- a/installers/raspap-network-activity.sh +++ b/installers/raspap-network-activity.sh @@ -20,9 +20,9 @@ set -o errtrace IFACE="${1:-wlan0}" # default to wlan0 if not specified TMPFILE="/dev/shm/net_activity" # tmpfs that resides in RAM -INTERVAL=0.05 # 50 ms +INTERVAL=0.05 # 50 ms -# Initialize +# initialize prev_rx=0 prev_tx=0 @@ -44,7 +44,6 @@ while true; do rx_diff=$((rx - prev_rx)) tx_diff=$((tx - prev_tx)) total_diff=$((rx_diff + tx_diff)) - echo "$total_diff" echo "$total_diff" > "$TMPFILE" prev_rx=$rx From 12b343b5700346057225c144d4dd669e1755d9f0 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 19 Apr 2025 06:04:01 -0700 Subject: [PATCH 05/24] Added led-pulse animation + keyframes, #hostapd-led --- app/css/all.css | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/app/css/all.css b/app/css/all.css index b42a34e2..edd38bd3 100644 --- a/app/css/all.css +++ b/app/css/all.css @@ -661,3 +661,23 @@ a.inactive:focus { max-width: 250px; } +.led-pulse { + /* animation: pulse 0.25s infinite; */ + opacity: 0.3 !important; +} +@keyframes pulse { + 0% { opacity: 1; } + 50% { opacity: 0.3; } + 100% { opacity: 1; } +} + +#hostapd-led { + color: #28a745; + opacity: 1; + transition: opacity 0.05s; +} + +#hostapd-led[data-active="true"] { + opacity: 0.3; +} + From 97b846b2b66b288864719e3a5a86e3d553b97828 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 19 Apr 2025 06:04:59 -0700 Subject: [PATCH 06/24] Revise updateActivityLED() to handle multiple hostapd-led classes --- app/js/custom.js | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/app/js/custom.js b/app/js/custom.js index bf376dc0..6af1313e 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -1023,28 +1023,32 @@ function disableValidation(form) { } function updateActivityLED() { + const threshold_bytes = 300; fetch('/app/net_activity') .then(res => res.text()) .then(data => { const activity = parseInt(data.trim()); - console.log(activity); - const led = document.getElementById('hostapd-led'); + const leds = document.querySelectorAll('.hostapd-led'); if (!isNaN(activity)) { - if (activity > 170) { - led.setAttribute('data-active', 'true'); - setTimeout(() => led.removeAttribute('data-active'), 50); - led.classList.add('led-pulse'); - } else { - led.classList.remove('led-pulse'); - } - } else { - console.warn('Unexpected data:', data); + leds.forEach(led => { + if (activity > threshold_bytes) { + led.setAttribute('data-active', 'true'); + led.classList.add('led-pulse'); + setTimeout(() => { + led.removeAttribute('data-active'); + led.classList.remove('led-pulse'); + }, 50); + } else { + led.classList.remove('led-pulse'); + led.removeAttribute('data-active'); + } + }); } }) .catch(() => { /* ignore fetch errors */ }); } -setInterval(updateActivityLED, 200); +setInterval(updateActivityLED, 100); $(document).ready(function() { const $htmlElement = $('html'); From 73df916184ef661f15b365d41597095323d8bfc7 Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 19 Apr 2025 06:06:28 -0700 Subject: [PATCH 07/24] Add hostapd-led to fas class in template --- includes/functions.php | 2 +- templates/dashboard.php | 2 +- templates/hostapd.php | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/includes/functions.php b/includes/functions.php index c000104a..c31d16fd 100755 --- a/includes/functions.php +++ b/includes/functions.php @@ -954,7 +954,7 @@ function renderStatus($hostapd_led, $hostapd_status, $memused_led, $memused, $cp
Status
- +
% diff --git a/templates/dashboard.php b/templates/dashboard.php index c7db88f0..29b217b5 100755 --- a/templates/dashboard.php +++ b/templates/dashboard.php @@ -20,7 +20,7 @@
diff --git a/templates/hostapd.php b/templates/hostapd.php index ccdc1a62..746034b6 100755 --- a/templates/hostapd.php +++ b/templates/hostapd.php @@ -40,7 +40,7 @@
From 1ab39066322d2eb7fcd1b1e6390636836347838a Mon Sep 17 00:00:00 2001 From: billz Date: Sat, 19 Apr 2025 06:07:08 -0700 Subject: [PATCH 08/24] Initial commit --- ...etwork-activity.sh => network-activity.sh} | 31 ++++++++++++++++--- installers/raspap-network-activity.service | 15 +++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) rename installers/{raspap-network-activity.sh => network-activity.sh} (63%) create mode 100644 installers/raspap-network-activity.service diff --git a/installers/raspap-network-activity.sh b/installers/network-activity.sh similarity index 63% rename from installers/raspap-network-activity.sh rename to installers/network-activity.sh index fb88f4a0..9c8db6da 100755 --- a/installers/raspap-network-activity.sh +++ b/installers/network-activity.sh @@ -18,16 +18,34 @@ set -o errtrace # Turn on traces, disabled by default # set -o xtrace -IFACE="${1:-wlan0}" # default to wlan0 if not specified +# Default interface +interface="wlan0" + +# Parse arguments +while [[ $# -gt 0 ]]; do + key="$1" + case $key in + -i|--interface) + interface="$2" + shift # past argument + shift # past value + ;; + *) # unknown option + echo "Unknown option: $1" + exit 1 + ;; + esac +done + TMPFILE="/dev/shm/net_activity" # tmpfs that resides in RAM -INTERVAL=0.05 # 50 ms +INTERVAL=0.1 # 100 ms # initialize prev_rx=0 prev_tx=0 get_bytes() { - awk -v iface="$IFACE" '$1 ~ iface":" { + awk -v iface="$interface" '$1 ~ iface":" { gsub(":", "", $1); print $2, $10 }' /proc/net/dev @@ -44,7 +62,12 @@ while true; do rx_diff=$((rx - prev_rx)) tx_diff=$((tx - prev_tx)) total_diff=$((rx_diff + tx_diff)) - echo "$total_diff" > "$TMPFILE" + + # write to a temp file then atomically rename + tmpfile=$(mktemp /dev/shm/net_activity.XXXXXX) + echo "$total_diff" > "$tmpfile" + chmod 644 "$tmpfile" + mv "$tmpfile" "$TMPFILE" prev_rx=$rx prev_tx=$tx diff --git a/installers/raspap-network-activity.service b/installers/raspap-network-activity.service new file mode 100644 index 00000000..474c4306 --- /dev/null +++ b/installers/raspap-network-activity.service @@ -0,0 +1,15 @@ +# Author: BillZ + +[Unit] +Description=RaspAP Network Activity Monitor +After=network.target + +[Service] +ExecStart=/bin/bash /etc/raspap/hostapd/network-activity.sh --interface wlan0 +Restart=always +RestartSec=2 +User=root + +[Install] +WantedBy=multi-user.target + From 5c7ccfb64f5106bf80fcfc78141402e71ace38c8 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 20 Apr 2025 03:36:39 -0700 Subject: [PATCH 09/24] Remove --interface wlan0 arugment (set by default) --- installers/raspap-network-activity.service | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installers/raspap-network-activity.service b/installers/raspap-network-activity.service index 474c4306..fc74ebb6 100644 --- a/installers/raspap-network-activity.service +++ b/installers/raspap-network-activity.service @@ -5,7 +5,7 @@ Description=RaspAP Network Activity Monitor After=network.target [Service] -ExecStart=/bin/bash /etc/raspap/hostapd/network-activity.sh --interface wlan0 +ExecStart=/bin/bash /etc/raspap/hostapd/network-activity.sh Restart=always RestartSec=2 User=root From 3cb2e673876bfa8db4690996dd20bd1e677317ca Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 20 Apr 2025 03:38:45 -0700 Subject: [PATCH 10/24] Cleanup unused classes + setAttribute defs --- app/css/all.css | 12 +----------- app/js/custom.js | 3 --- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/app/css/all.css b/app/css/all.css index edd38bd3..6c054e18 100644 --- a/app/css/all.css +++ b/app/css/all.css @@ -662,22 +662,12 @@ a.inactive:focus { } .led-pulse { - /* animation: pulse 0.25s infinite; */ opacity: 0.3 !important; } -@keyframes pulse { - 0% { opacity: 1; } - 50% { opacity: 0.3; } - 100% { opacity: 1; } -} -#hostapd-led { +.hostapd-led { color: #28a745; opacity: 1; transition: opacity 0.05s; } -#hostapd-led[data-active="true"] { - opacity: 0.3; -} - diff --git a/app/js/custom.js b/app/js/custom.js index 6af1313e..fba8b8bf 100644 --- a/app/js/custom.js +++ b/app/js/custom.js @@ -1033,15 +1033,12 @@ function updateActivityLED() { if (!isNaN(activity)) { leds.forEach(led => { if (activity > threshold_bytes) { - led.setAttribute('data-active', 'true'); led.classList.add('led-pulse'); setTimeout(() => { - led.removeAttribute('data-active'); led.classList.remove('led-pulse'); }, 50); } else { led.classList.remove('led-pulse'); - led.removeAttribute('data-active'); } }); } From 1bf91277da2313a778a4bc03360ce2c50f567fb7 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 01:33:19 -0700 Subject: [PATCH 11/24] Rename systemd unit file template @.service --- ...work-activity.service => raspap-network-activity@.service} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename installers/{raspap-network-activity.service => raspap-network-activity@.service} (55%) diff --git a/installers/raspap-network-activity.service b/installers/raspap-network-activity@.service similarity index 55% rename from installers/raspap-network-activity.service rename to installers/raspap-network-activity@.service index fc74ebb6..dbca808d 100644 --- a/installers/raspap-network-activity.service +++ b/installers/raspap-network-activity@.service @@ -1,11 +1,11 @@ # Author: BillZ [Unit] -Description=RaspAP Network Activity Monitor +Description=RaspAP Network Activity Monitor for %i After=network.target [Service] -ExecStart=/bin/bash /etc/raspap/hostapd/network-activity.sh +ExecStart=/bin/bash /etc/raspap/hostapd/network-activity.sh --interface %i Restart=always RestartSec=2 User=root From 2d7d52150162088ba25a7c017429cfd06cf7b073 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 01:33:52 -0700 Subject: [PATCH 12/24] Add start/stop raspap-network-activity@*.service --- installers/raspap.sudoers | 2 ++ 1 file changed, 2 insertions(+) diff --git a/installers/raspap.sudoers b/installers/raspap.sudoers index c770a01b..9933cf18 100644 --- a/installers/raspap.sudoers +++ b/installers/raspap.sudoers @@ -80,3 +80,5 @@ www-data ALL=(ALL) NOPASSWD:/bin/truncate -s 0 /tmp/*.log,/bin/truncate -s 0 /va www-data ALL=(ALL) NOPASSWD:/usr/bin/vnstat * www-data ALL=(ALL) NOPASSWD:/usr/sbin/visudo -cf * www-data ALL=(ALL) NOPASSWD:/etc/raspap/plugins/plugin_helper.sh +www-data ALL=(ALL) NOPASSWD: /bin/systemctl start raspap-network-activity@*.service +www-data ALL=(ALL) NOPASSWD: /bin/systemctl stop raspap-network-activity@*.service From d7428e40e376d33ff129029fc406b75ac13f3343 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 01:34:22 -0700 Subject: [PATCH 13/24] Create _enable_network_activity_monitor() + _symlink_net_activity() --- installers/common.sh | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/installers/common.sh b/installers/common.sh index a6a56261..59642605 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -75,6 +75,7 @@ function _update_raspap() { _download_latest_files _change_file_ownership _patch_system_files + _symlink_net_activity _create_plugin_scripts _install_complete } @@ -818,9 +819,32 @@ function _configure_networking() { echo -e _enable_raspap_daemon fi + + # Enable RaspAP network activity monitor + _enable_network_activity_monitor + _install_status 0 } +# Install and enable RaspAP network activity monitor +function _enable_network_activity_monitor() { + _install_log "Enabling RaspAP network activity monitor" + sudo cp $webroot_dir/installers/network-activity.sh "$raspap_dir/hostapd" || _install_status 1 "Unable to copy network-activity.sh" + sudo cp $webroot_dir/installers/raspap-network-activity@.service /lib/systemd/system/ || _install_status 1 "Unable to move raspap-network-activity.service file" + sudo systemctl daemon-reload + sudo systemctl enable raspap-network-activity@wlan0.service || _install_status 1 "Failed to enable raspap-network-activity.service" + sudo systemctl start raspap-network-activity@wlan0.service || _install_status 1 "Failed to start raspap-network-activity.service" + sleep 0.5 + _symlink_net_activity + echo "Network activity monitor enabled" +} + +function _symlink_net_activity() { + echo "Symlinking /dev/shm/net_activity to $webroot_dir/app/net_activity" + sudo ln -sf /dev/shm/net_activity $webroot_dir/app/net_activity || _install_status 1 "Failed to link net_activity to ${webroot_dir}/app" + sudo chown -R $raspap_user:$raspap_user $webroot_dir/app/net_activity || _install_status 1 "Unable to set ownership of ${webroot_dir}/app/net_activity" +} + # Prompt to configure TCP BBR option function _prompt_configure_tcp_bbr() { _install_log "Configure TCP BBR congestion control" From cc215530d3149db4fee958136fdcdf80e4d62f6d Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 01:35:21 -0700 Subject: [PATCH 14/24] Stop raspap-network-activity@*.service, start service template w/ iface --- includes/hostapd.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/includes/hostapd.php b/includes/hostapd.php index 93386c5e..1c10084f 100755 --- a/includes/hostapd.php +++ b/includes/hostapd.php @@ -58,10 +58,19 @@ function DisplayHostAPDConfig() $status->addMessage('Attempting to start hotspot', 'info'); if ($arrHostapdConf['BridgedEnable'] == 1) { exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --interface br0 --seconds 2', $return); + exec('sudo systemctl stop "raspap-network-activity@*.service"'); + exec("sudo systemctl start raspap-network-activity@br0.service"); } elseif ($arrHostapdConf['WifiAPEnable'] == 1) { exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --interface uap0 --seconds 2', $return); + exec('sudo systemctl stop "raspap-network-activity@*.service"'); + exec("sudo systemctl start raspap-network-activity@uap0.service"); } else { exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --seconds 2', $return); + exec('sudo systemctl stop "raspap-network-activity@*.service"'); + if (!empty($_SESSION['ap_interface'])) { + $iface = escapeshellarg($_SESSION['ap_interface']); + exec("sudo systemctl start raspap-network-activity@{$iface}.service"); + } } foreach ($return as $line) { $status->addMessage($line, 'info'); @@ -69,6 +78,7 @@ function DisplayHostAPDConfig() } elseif (isset($_POST['StopHotspot'])) { $status->addMessage('Attempting to stop hotspot', 'info'); exec('sudo /bin/systemctl stop hostapd.service', $return); + exec('sudo systemctl stop "raspap-network-activity@*.service"'); foreach ($return as $line) { $status->addMessage($line, 'info'); } From de7e36cb8c055c7cb20d392fcfc9ab2ea726759d Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 01:36:01 -0700 Subject: [PATCH 15/24] Add app/net_activity to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index a0642d6e..71641545 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ rootCA.pem vendor .env locale/**/*.mo +app/net_activity From b900c401fef3838fef5c78e6b4527dc6b07e4ac6 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 09:07:58 -0700 Subject: [PATCH 16/24] Initial commit --- installers/raspap-network-monitor.c | 104 ++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 installers/raspap-network-monitor.c diff --git a/installers/raspap-network-monitor.c b/installers/raspap-network-monitor.c new file mode 100644 index 00000000..89acaf6e --- /dev/null +++ b/installers/raspap-network-monitor.c @@ -0,0 +1,104 @@ +// network-monitor.c + +/* +RaspAP Network Activity Monitor +Author: @billz +Author URI: https://github.com/billz/ +License: GNU General Public License v3.0 +License URI: https://github.com/raspap/raspap-webgui/blob/master/LICENSE +*/ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TMPFILE "/dev/shm/net_activity" +#define POLL_INTERVAL_MS 100 // 100 milliseconds + +unsigned long read_interface_bytes(const char *iface) { + FILE *fp = fopen("/proc/net/dev", "r"); + if (!fp) return 0; + + char line[512]; + unsigned long rx = 0, tx = 0; + + while (fgets(line, sizeof(line), fp)) { + if (strstr(line, iface)) { + char *ptr = strchr(line, ':'); + if (ptr) { + sscanf(ptr + 1, "%lu %*u %*u %*u %*u %*u %*u %*u %lu", &rx, &tx); + } + break; + } + } + + fclose(fp); + return rx + tx; +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + return EXIT_FAILURE; + } + + const char *iface = argv[1]; + unsigned long prev_total = read_interface_bytes(iface); + + // create a timerfd + int tfd = timerfd_create(CLOCK_MONOTONIC, 0); + if (tfd == -1) { + perror("timerfd_create"); + return EXIT_FAILURE; + } + + struct itimerspec timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_nsec = POLL_INTERVAL_MS * 1000000; // interval + timer.it_value.tv_sec = 0; + timer.it_value.tv_nsec = POLL_INTERVAL_MS * 1000000; // initial expiration + + if (timerfd_settime(tfd, 0, &timer, NULL) == -1) { + perror("timerfd_settime"); + close(tfd); + return EXIT_FAILURE; + } + + struct pollfd fds; + fds.fd = tfd; + fds.events = POLLIN; + + for (;;) { + int ret = poll(&fds, 1, -1); + if (ret == -1) { + perror("poll"); + break; + } + + if (fds.revents & POLLIN) { + uint64_t expirations; + read(tfd, &expirations, sizeof(expirations)); // clear timer + + unsigned long curr_total = read_interface_bytes(iface); + unsigned long diff = (curr_total >= prev_total) ? (curr_total - prev_total) : 0; + prev_total = curr_total; + + FILE *out = fopen(TMPFILE, "w"); + if (out) { + fprintf(out, "%lu\n", diff); + fclose(out); + } + } + } + + close(tfd); + return EXIT_SUCCESS; +} + From f30ff24ed49e8456b51599a1da8f5bd4472a2400 Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 09:08:51 -0700 Subject: [PATCH 17/24] Update _enable_network_activity_monitor() compile raspap-network-monitor.c --- installers/common.sh | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/installers/common.sh b/installers/common.sh index 59642605..ccb7e245 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -75,7 +75,7 @@ function _update_raspap() { _download_latest_files _change_file_ownership _patch_system_files - _symlink_net_activity + _enable_network_activity_monitor _create_plugin_scripts _install_complete } @@ -829,20 +829,21 @@ function _configure_networking() { # Install and enable RaspAP network activity monitor function _enable_network_activity_monitor() { _install_log "Enabling RaspAP network activity monitor" - sudo cp $webroot_dir/installers/network-activity.sh "$raspap_dir/hostapd" || _install_status 1 "Unable to copy network-activity.sh" + echo "Compiling raspap-network-monitor.c to /usr/local/bin/" + sudo gcc -O2 -o /usr/local/bin/raspap-network-monitor $webroot_dir/installers/raspap-network-monitor.c || _install_status 1 "Failed to compile raspap-network-monitor.c" + echo "Copying raspap-network-activity@.service to /lib/systemd/system/" sudo cp $webroot_dir/installers/raspap-network-activity@.service /lib/systemd/system/ || _install_status 1 "Unable to move raspap-network-activity.service file" sudo systemctl daemon-reload + echo "Enabling raspap-network-activity@wlan0.service" sudo systemctl enable raspap-network-activity@wlan0.service || _install_status 1 "Failed to enable raspap-network-activity.service" + echo "Starting raspap-network-activity@wlan0.service" sudo systemctl start raspap-network-activity@wlan0.service || _install_status 1 "Failed to start raspap-network-activity.service" sleep 0.5 - _symlink_net_activity - echo "Network activity monitor enabled" -} - -function _symlink_net_activity() { echo "Symlinking /dev/shm/net_activity to $webroot_dir/app/net_activity" sudo ln -sf /dev/shm/net_activity $webroot_dir/app/net_activity || _install_status 1 "Failed to link net_activity to ${webroot_dir}/app" + echo "Setting ownership for ${raspap_user} on ${webroot_dir}/app/net_activity" sudo chown -R $raspap_user:$raspap_user $webroot_dir/app/net_activity || _install_status 1 "Unable to set ownership of ${webroot_dir}/app/net_activity" + echo "Network activity monitor enabled" } # Prompt to configure TCP BBR option From b34f96d351a12bd7d9f414c5721c2504f9c1559f Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 09:09:43 -0700 Subject: [PATCH 18/24] Update ExecStart path to /usr/local/bin/raspap-network-monitor %i --- installers/network-activity.sh | 75 --------------------- installers/raspap-network-activity@.service | 4 +- 2 files changed, 2 insertions(+), 77 deletions(-) delete mode 100755 installers/network-activity.sh diff --git a/installers/network-activity.sh b/installers/network-activity.sh deleted file mode 100755 index 9c8db6da..00000000 --- a/installers/network-activity.sh +++ /dev/null @@ -1,75 +0,0 @@ -#!/bin/bash -# -# RaspAP network activity monitor -# Reads from /proc/net/dev to get TX/RX byte counters for a given interface (wlan0 by default), -# calculates the difference and writes to /dev/shm/net_activity. -# -# Author: @billz -# Author URI: https://github.com/billz/ -# License: GNU General Public License v3.0 -# License URI: https://github.com/raspap/raspap-webgui/blob/master/LICENSE -# -# /usr/local/bin/raspap-network-activity.sh - -# Exit on error -set -o errexit -# Exit on error inside functions -set -o errtrace -# Turn on traces, disabled by default -# set -o xtrace - -# Default interface -interface="wlan0" - -# Parse arguments -while [[ $# -gt 0 ]]; do - key="$1" - case $key in - -i|--interface) - interface="$2" - shift # past argument - shift # past value - ;; - *) # unknown option - echo "Unknown option: $1" - exit 1 - ;; - esac -done - -TMPFILE="/dev/shm/net_activity" # tmpfs that resides in RAM -INTERVAL=0.1 # 100 ms - -# initialize -prev_rx=0 -prev_tx=0 - -get_bytes() { - awk -v iface="$interface" '$1 ~ iface":" { - gsub(":", "", $1); - print $2, $10 - }' /proc/net/dev -} - -read rx tx < <(get_bytes) -prev_rx=$rx -prev_tx=$tx - -while true; do - sleep $INTERVAL - read rx tx < <(get_bytes) - - rx_diff=$((rx - prev_rx)) - tx_diff=$((tx - prev_tx)) - total_diff=$((rx_diff + tx_diff)) - - # write to a temp file then atomically rename - tmpfile=$(mktemp /dev/shm/net_activity.XXXXXX) - echo "$total_diff" > "$tmpfile" - chmod 644 "$tmpfile" - mv "$tmpfile" "$TMPFILE" - - prev_rx=$rx - prev_tx=$tx -done - diff --git a/installers/raspap-network-activity@.service b/installers/raspap-network-activity@.service index dbca808d..615a7547 100644 --- a/installers/raspap-network-activity@.service +++ b/installers/raspap-network-activity@.service @@ -1,11 +1,11 @@ # Author: BillZ [Unit] -Description=RaspAP Network Activity Monitor for %i +Description=RaspAP Network Activity Monitor for %I After=network.target [Service] -ExecStart=/bin/bash /etc/raspap/hostapd/network-activity.sh --interface %i +ExecStart=/usr/local/bin/raspap-network-monitor %i Restart=always RestartSec=2 User=root From 89dfa02d967c7e8e4e45f9fd93620ce7ab64b0ad Mon Sep 17 00:00:00 2001 From: billz Date: Sun, 27 Apr 2025 09:12:17 -0700 Subject: [PATCH 19/24] Minor: comments --- installers/raspap-network-monitor.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/installers/raspap-network-monitor.c b/installers/raspap-network-monitor.c index 89acaf6e..93a556cf 100644 --- a/installers/raspap-network-monitor.c +++ b/installers/raspap-network-monitor.c @@ -1,4 +1,4 @@ -// network-monitor.c +// raspap-network-monitor.c /* RaspAP Network Activity Monitor @@ -6,6 +6,8 @@ Author: @billz Author URI: https://github.com/billz/ License: GNU General Public License v3.0 License URI: https://github.com/raspap/raspap-webgui/blob/master/LICENSE + +Usage: raspap-network-monitor [interface] */ #define _GNU_SOURCE From 12c28f3f60c022e9a6663be78a6b4830ea05a12c Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 28 Apr 2025 00:12:59 -0700 Subject: [PATCH 20/24] Update to stop/start raspap-network-activity@.service --- installers/servicestart.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/installers/servicestart.sh b/installers/servicestart.sh index 377678ca..64e79143 100755 --- a/installers/servicestart.sh +++ b/installers/servicestart.sh @@ -42,6 +42,7 @@ systemctl stop systemd-networkd systemctl stop hostapd.service systemctl stop dnsmasq.service systemctl stop dhcpcd.service +systemctl stop 'raspap-network-activity@*.service' if [ "${action}" = "stop" ]; then echo "Services stopped. Exiting." @@ -114,6 +115,9 @@ sleep "${seconds}" systemctl start dnsmasq.service +echo "Starting raspap-network-activity@${interface}.service" +systemctl start raspap-network-activity@${interface}.service + if [ $OPENVPNENABLED -eq 1 ]; then systemctl start openvpn-client@client fi From 5d8b71b76857d36706cc608ff76ad69b6bda1142 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 28 Apr 2025 00:14:04 -0700 Subject: [PATCH 21/24] Delegate raspap-network-activity@*.service control to raspapd.service --- includes/hostapd.php | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/includes/hostapd.php b/includes/hostapd.php index 1c10084f..f9bd53bc 100755 --- a/includes/hostapd.php +++ b/includes/hostapd.php @@ -57,19 +57,16 @@ function DisplayHostAPDConfig() if (isset($_POST['StartHotspot']) || isset($_POST['RestartHotspot'])) { $status->addMessage('Attempting to start hotspot', 'info'); if ($arrHostapdConf['BridgedEnable'] == 1) { - exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --interface br0 --seconds 2', $return); - exec('sudo systemctl stop "raspap-network-activity@*.service"'); - exec("sudo systemctl start raspap-network-activity@br0.service"); + exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --interface br0 --seconds 1', $return); } elseif ($arrHostapdConf['WifiAPEnable'] == 1) { - exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --interface uap0 --seconds 2', $return); - exec('sudo systemctl stop "raspap-network-activity@*.service"'); - exec("sudo systemctl start raspap-network-activity@uap0.service"); + exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --interface uap0 --seconds 1', $return); } else { - exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --seconds 2', $return); - exec('sudo systemctl stop "raspap-network-activity@*.service"'); - if (!empty($_SESSION['ap_interface'])) { - $iface = escapeshellarg($_SESSION['ap_interface']); - exec("sudo systemctl start raspap-network-activity@{$iface}.service"); + // systemctl expects a unit name like raspap-network-activity@wlan0.service, no extra quotes + $iface_nonescaped = $_POST['interface']; + if (preg_match('/^[a-zA-Z0-9_-]+$/', $iface_nonescaped)) { // validate interface name + exec('sudo '.RASPI_CONFIG.'/hostapd/servicestart.sh --interface ' .$iface_nonescaped. ' --seconds 1', $return); + } else { + throw new \Exception('Invalid network interface'); } } foreach ($return as $line) { From 0ad82da51cbbac82b77950f1213cff0d7a583573 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 28 Apr 2025 00:47:16 -0700 Subject: [PATCH 22/24] Default to interface value in /etc/raspap/hostapd.ini --- installers/raspapd.service | 2 +- installers/servicestart.sh | 91 ++++++++++++++++++++------------------ 2 files changed, 49 insertions(+), 44 deletions(-) diff --git a/installers/raspapd.service b/installers/raspapd.service index c5738465..776cb16b 100644 --- a/installers/raspapd.service +++ b/installers/raspapd.service @@ -16,7 +16,7 @@ After=multi-user.target [Service] Type=oneshot -ExecStart=/bin/bash /etc/raspap/hostapd/servicestart.sh --interface uap0 --seconds 3 +ExecStart=/bin/bash /etc/raspap/hostapd/servicestart.sh --seconds 1 RemainAfterExit=no [Install] diff --git a/installers/servicestart.sh b/installers/servicestart.sh index 64e79143..6121e061 100755 --- a/installers/servicestart.sh +++ b/installers/servicestart.sh @@ -34,6 +34,20 @@ esac done set -- "${positional[@]}" +# Load config file into associative array +declare -A config +if [ -r "$CONFIGFILE" ]; then + while IFS=" = " read -r key value; do + config["$key"]="$value" + done < "$CONFIGFILE" +fi + +# Set interface from config if not set by parameter +if [ -z "$interface" ] && [ -n "${config[WifiInterface]}" ]; then + interface="${config[WifiInterface]}" + echo "Interface not provided. Using WifiInterface from config: $interface" +fi + echo "Stopping network services..." if [ $OPENVPNENABLED -eq 1 ]; then systemctl stop openvpn-client@client @@ -49,58 +63,51 @@ if [ "${action}" = "stop" ]; then exit 0 fi -if [ -f "$DAEMONPATH" ] && [ ! -z "$interface" ]; then +if [ -f "$DAEMONPATH" ] && [ -n "$interface" ]; then echo "Changing RaspAP Daemon --interface to $interface" sed -i "s/\(--interface \)[[:alnum:]]*/\1$interface/" "$DAEMONPATH" fi -if [ -r "$CONFIGFILE" ]; then - declare -A config - while IFS=" = " read -r key value; do - config["$key"]="$value" - done < "$CONFIGFILE" +if [ "${config[BridgedEnable]}" = 1 ]; then + if [ "${interface}" = "br0" ]; then + echo "Stopping systemd-networkd" + systemctl stop systemd-networkd - if [ "${config[BridgedEnable]}" = 1 ]; then - if [ "${interface}" = "br0" ]; then - echo "Stopping systemd-networkd" - systemctl stop systemd-networkd + echo "Restarting eth0 interface..." + ip link set down eth0 + ip link set up eth0 - echo "Restarting eth0 interface..." - ip link set down eth0 - ip link set up eth0 + echo "Removing uap0 interface..." + iw dev uap0 del - echo "Removing uap0 interface..." - iw dev uap0 del + echo "Enabling systemd-networkd" + systemctl start systemd-networkd + systemctl enable systemd-networkd + fi +else + echo "Disabling systemd-networkd" + systemctl disable systemd-networkd - echo "Enabling systemd-networkd" - systemctl start systemd-networkd - systemctl enable systemd-networkd - fi - else - echo "Disabling systemd-networkd" - systemctl disable systemd-networkd + ip link ls up | grep -q 'br0' &> /dev/null + if [ $? == 0 ]; then + echo "Removing br0 interface..." + ip link set down br0 + ip link del dev br0 + fi - ip link ls up | grep -q 'br0' &> /dev/null - if [ $? == 0 ]; then - echo "Removing br0 interface..." - ip link set down br0 - ip link del dev br0 - fi + if [ "${config[WifiAPEnable]}" = 1 ]; then + if [ "${interface}" = "uap0" ]; then - if [ "${config[WifiAPEnable]}" = 1 ]; then - if [ "${interface}" = "uap0" ]; then - - ip link ls up | grep -q 'uap0' &> /dev/null - if [ $? == 0 ]; then - echo "Removing uap0 interface..." - iw dev uap0 del - fi - - echo "Adding uap0 interface to ${config[WifiManaged]}" - iw dev ${config[WifiManaged]} interface add uap0 type __ap - # Bring up uap0 interface - ifconfig uap0 up + ip link ls up | grep -q 'uap0' &> /dev/null + if [ $? == 0 ]; then + echo "Removing uap0 interface..." + iw dev uap0 del fi + + echo "Adding uap0 interface to ${config[WifiManaged]}" + iw dev ${config[WifiManaged]} interface add uap0 type __ap + # Bring up uap0 interface + ifconfig uap0 up fi fi fi @@ -122,8 +129,6 @@ if [ $OPENVPNENABLED -eq 1 ]; then systemctl start openvpn-client@client fi -# @mp035 found that the wifi client interface would stop every 8 seconds -# for about 16 seconds. Reassociating seems to solve this if [ "${config[WifiAPEnable]}" = 1 ]; then echo "Reassociating wifi client interface..." sleep "${seconds}" From 384e1df1b7f78f6bfedf62d72c6ce4eb7f199007 Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 28 Apr 2025 00:58:24 -0700 Subject: [PATCH 23/24] Minor: echo output --- installers/servicestart.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installers/servicestart.sh b/installers/servicestart.sh index 6121e061..157a41a4 100755 --- a/installers/servicestart.sh +++ b/installers/servicestart.sh @@ -45,7 +45,7 @@ fi # Set interface from config if not set by parameter if [ -z "$interface" ] && [ -n "${config[WifiInterface]}" ]; then interface="${config[WifiInterface]}" - echo "Interface not provided. Using WifiInterface from config: $interface" + echo "Interface not provided. Using interface from config: $interface" fi echo "Stopping network services..." From 9528927ccad9c680ca5b590882ec4d2d7210a92f Mon Sep 17 00:00:00 2001 From: billz Date: Mon, 28 Apr 2025 01:44:22 -0700 Subject: [PATCH 24/24] Check for presence of gcc, install if needed w/ apt --- installers/common.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/installers/common.sh b/installers/common.sh index ccb7e245..2b619304 100755 --- a/installers/common.sh +++ b/installers/common.sh @@ -830,6 +830,11 @@ function _configure_networking() { function _enable_network_activity_monitor() { _install_log "Enabling RaspAP network activity monitor" echo "Compiling raspap-network-monitor.c to /usr/local/bin/" + if ! command -v gcc >/dev/null 2>&1; then + echo "gcc not found, installing..." + sudo apt-get update + sudo apt-get install -y gcc || _install_status 1 "Failed to install gcc" + fi sudo gcc -O2 -o /usr/local/bin/raspap-network-monitor $webroot_dir/installers/raspap-network-monitor.c || _install_status 1 "Failed to compile raspap-network-monitor.c" echo "Copying raspap-network-activity@.service to /lib/systemd/system/" sudo cp $webroot_dir/installers/raspap-network-activity@.service /lib/systemd/system/ || _install_status 1 "Unable to move raspap-network-activity.service file"