first commit
Cette révision appartient à :
@@ -0,0 +1,68 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SRC_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
WEB_DIR="/var/www"
|
||||||
|
BIN_TARGET="/usr/local/bin/usb-bind-wrapper.sh"
|
||||||
|
SUDOERS_FILE="/etc/sudoers.d/010_www-data-nopasswd"
|
||||||
|
HEADER_FILE="/var/www/header.php"
|
||||||
|
|
||||||
|
echo "== Installation du plugin =="
|
||||||
|
|
||||||
|
cp -r "$SRC_DIR/js" "$WEB_DIR/js"
|
||||||
|
cp "$SRC_DIR/usb-toogle.php" "$WEB_DIR/"
|
||||||
|
|
||||||
|
# 2. Installer le script dans /usr/local/bin
|
||||||
|
echo "-- Installation du wrapper USB"
|
||||||
|
cp "$SRC_DIR/bin/usb-bind-wrapper.sh" "$BIN_TARGET"
|
||||||
|
chmod +x "$BIN_TARGET"
|
||||||
|
|
||||||
|
# 3. Vérifier sudo
|
||||||
|
echo "-- Vérification de sudo"
|
||||||
|
if ! command -v sudo >/dev/null 2>&1; then
|
||||||
|
echo "Erreur: sudo n'est pas installé"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 4. Configurer sudoers
|
||||||
|
echo "-- Configuration sudoers"
|
||||||
|
if [ ! -f "$SUDOERS_FILE" ]; then
|
||||||
|
echo "Création du fichier sudoers"
|
||||||
|
echo "www-data ALL=(ALL) NOPASSWD: ALL" | sudo tee "$SUDOERS_FILE" > /dev/null
|
||||||
|
sudo chmod 440 "$SUDOERS_FILE"
|
||||||
|
else
|
||||||
|
echo "Le fichier sudoers existe déjà"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "-- Modification de header.php"
|
||||||
|
|
||||||
|
if [ ! -f "$HEADER_FILE" ]; then
|
||||||
|
echo "Erreur: $HEADER_FILE introuvable"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -f "$ADD_HEADER_FILE" ]; then
|
||||||
|
echo "Erreur: add-header.php introuvable"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Vérifier si déjà présent
|
||||||
|
if grep -q 'usb-toggle.js' "$HEADER_FILE"; then
|
||||||
|
echo "Script déjà présent, on skip"
|
||||||
|
else
|
||||||
|
TMP_FILE=$(mktemp)
|
||||||
|
|
||||||
|
awk -v script="$SCRIPT_TAG" '
|
||||||
|
{
|
||||||
|
# On injecte juste avant </head>
|
||||||
|
if ($0 ~ /<\/head>/) {
|
||||||
|
print " " script
|
||||||
|
}
|
||||||
|
print
|
||||||
|
}' "$HEADER_FILE" > "$TMP_FILE"
|
||||||
|
|
||||||
|
sudo mv "$TMP_FILE" "$HEADER_FILE"
|
||||||
|
echo "Script injecté avec succès"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "== Installation terminée =="
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
/var/www/usb-toggle.php
|
||||||
|
/usr/local/bin/usb-bind-wrapper
|
||||||
|
|
||||||
|
dans header
|
||||||
|
|
||||||
|
add-header.php
|
||||||
|
|
||||||
|
Juste avant <li id="bluetooth-hide"><a href="blu-config.php"><i class="fa-light fa-brands fa-bluetooth sx"></i>Bluetooth</a></li>
|
||||||
|
-----------
|
||||||
|
Dans les script
|
||||||
|
<script src="/js/usb-toggle.js"></script>
|
||||||
|
-----------
|
||||||
|
js/usb-toogle.js
|
||||||
|
|
||||||
|
cat /etc/sudoers.d/010_www-data-nopasswd
|
||||||
|
www-data ALL=(ALL) NOPASSWD: ALL
|
||||||
|
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# /usr/local/bin/usb-bind-wrapper
|
||||||
|
# Usage: usb-bind-wrapper bind <device>
|
||||||
|
# usb-bind-wrapper unbind <device>
|
||||||
|
# Example: usb-bind-wrapper unbind 1-1
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo "Ce script doit être exécuté via sudo (ex: sudo /usr/local/bin/usb-bind-wrapper.sh ...)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
echo "Usage: $0 {bind|unbind|status} <usb-id>"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "status" ]; then
|
||||||
|
DEV="$2"
|
||||||
|
if [ -L "/sys/bus/usb/drivers/usb/$DEV" ]; then
|
||||||
|
echo "bound"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "unbound"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTION="$1"
|
||||||
|
DEV="$2"
|
||||||
|
|
||||||
|
# Validation simple du format (ex: 1-1 ou 1-1.2)
|
||||||
|
if [[ ! "$DEV" =~ ^[0-9]+(-[0-9]+)(\.[0-9]+)?$ ]]; then
|
||||||
|
echo "Identifiant USB invalide : $DEV"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
bind)
|
||||||
|
SYS_PATH="/sys/bus/usb/drivers/usb/bind"
|
||||||
|
;;
|
||||||
|
unbind)
|
||||||
|
SYS_PATH="/sys/bus/usb/drivers/usb/unbind"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Action invalide : $ACTION (utiliser bind ou unbind)"
|
||||||
|
exit 4
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ ! -w "$SYS_PATH" ]; then
|
||||||
|
echo "Impossible d'écrire dans $SYS_PATH (vérifie que le chemin existe et les permissions)"
|
||||||
|
exit 5
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exécute l'écriture (sécurisée)
|
||||||
|
printf '%s\n' "$DEV" > "$SYS_PATH"
|
||||||
|
|
||||||
|
echo "OK: $ACTION $DEV"
|
||||||
|
exit 0
|
||||||
|
root@chambre:~# cat /usr/local/bin/usb-bind-wrapper
|
||||||
|
#!/bin/bash
|
||||||
|
# /usr/local/bin/usb-bind-wrapper
|
||||||
|
# Usage: usb-bind-wrapper bind <device>
|
||||||
|
# usb-bind-wrapper unbind <device>
|
||||||
|
# Example: usb-bind-wrapper unbind 1-1
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo "Ce script doit être exécuté via sudo (ex: sudo /usr/local/bin/usb-bind-wrapper ...)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ $# -ne 2 ]; then
|
||||||
|
echo "Usage: $0 {bind|unbind|status} <usb-id>"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$1" = "status" ]; then
|
||||||
|
DEV="$2"
|
||||||
|
if [ -L "/sys/bus/usb/drivers/usb/$DEV" ]; then
|
||||||
|
echo "bound"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "unbound"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
ACTION="$1"
|
||||||
|
DEV="$2"
|
||||||
|
|
||||||
|
# Validation simple du format (ex: 1-1 ou 1-1.2)
|
||||||
|
if [[ ! "$DEV" =~ ^[0-9]+(-[0-9]+)(\.[0-9]+)?$ ]]; then
|
||||||
|
echo "Identifiant USB invalide : $DEV"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$ACTION" in
|
||||||
|
bind)
|
||||||
|
SYS_PATH="/sys/bus/usb/drivers/usb/bind"
|
||||||
|
;;
|
||||||
|
unbind)
|
||||||
|
SYS_PATH="/sys/bus/usb/drivers/usb/unbind"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Action invalide : $ACTION (utiliser bind ou unbind)"
|
||||||
|
exit 4
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
if [ ! -w "$SYS_PATH" ]; then
|
||||||
|
echo "Impossible d'écrire dans $SYS_PATH (vérifie que le chemin existe et les permissions)"
|
||||||
|
exit 5
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Exécute l'écriture (sécurisée)
|
||||||
|
printf '%s\n' "$DEV" > "$SYS_PATH"
|
||||||
|
|
||||||
|
echo "OK: $ACTION $DEV"
|
||||||
|
exit 0
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
function injectUsbMenu() {
|
||||||
|
if (document.getElementById("usbControlItem")) return;
|
||||||
|
|
||||||
|
const menu = document.querySelector('.dropdown-menu');
|
||||||
|
if (!menu) return;
|
||||||
|
|
||||||
|
const anchor = document.querySelector('#dashboard-menu-item');
|
||||||
|
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.id = "usbControlItem";
|
||||||
|
li.className = "context-menu";
|
||||||
|
|
||||||
|
li.innerHTML = `
|
||||||
|
<a id="usbControlLink" href="#notarget">
|
||||||
|
<i class="fa-solid fa-sharp fa-power-off sx"></i>
|
||||||
|
<span>USB Control</span>
|
||||||
|
<span id="usbControlDot"
|
||||||
|
style="display:inline-block;width:12px;height:12px;border-radius:50%;background:#b22;margin-left:3px;margin-right:3px">
|
||||||
|
</span>
|
||||||
|
<span id="usbControlLabel">...</span>
|
||||||
|
</a>
|
||||||
|
`;
|
||||||
|
|
||||||
|
if (anchor && anchor.parentNode) {
|
||||||
|
anchor.parentNode.insertBefore(li, anchor.nextSibling);
|
||||||
|
} else {
|
||||||
|
menu.appendChild(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", injectUsbMenu);
|
||||||
|
setTimeout(injectUsbMenu, 1000);
|
||||||
|
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const item = document.getElementById('usbControlItem');
|
||||||
|
const dot = document.getElementById('usbControlDot');
|
||||||
|
const label = document.getElementById('usbControlLabel');
|
||||||
|
const link = document.getElementById('usbControlLink');
|
||||||
|
|
||||||
|
if (!item || !dot || !label || !link) return;
|
||||||
|
|
||||||
|
let isOn = false;
|
||||||
|
let locked = false;
|
||||||
|
|
||||||
|
// --- Fonction utilitaire : met à jour la pastille
|
||||||
|
function updateUI() {
|
||||||
|
dot.style.background = isOn ? '#2b2' : '#b22';
|
||||||
|
label.textContent = isOn ? 'ON' : 'OFF';
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Fonction pour changer l’état
|
||||||
|
function toggleUSB() {
|
||||||
|
event.stopPropagation();
|
||||||
|
if (locked) return;
|
||||||
|
locked = true;
|
||||||
|
|
||||||
|
label.textContent = '...';
|
||||||
|
fetch(`/usb-toggle.php?action=${isOn ? 'unbind' : 'bind'}`)
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === 'ok') {
|
||||||
|
isOn = !isOn;
|
||||||
|
updateUI();
|
||||||
|
} else {
|
||||||
|
alert('Erreur: ' + data.msg);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => alert('Erreur réseau: ' + err))
|
||||||
|
.finally(() => {
|
||||||
|
locked = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Lecture de l’état au démarrage
|
||||||
|
fetch('/usb-toggle.php?action=status')
|
||||||
|
.then(res => res.json())
|
||||||
|
.then(data => {
|
||||||
|
if (data.status === 'ok') {
|
||||||
|
isOn = (data.state === 'bound');
|
||||||
|
updateUI();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
label.textContent = '?';
|
||||||
|
});
|
||||||
|
|
||||||
|
function mouseLeave() {
|
||||||
|
link.blur();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
item.addEventListener('click', toggleUSB);
|
||||||
|
link.addEventListener('mouseleave', mouseLeave);
|
||||||
|
});
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
header('Content-Type: application/json');
|
||||||
|
$dev = '1-1';
|
||||||
|
$action = $_GET['action'] ?? '';
|
||||||
|
|
||||||
|
if (!in_array($action, ['bind', 'unbind', 'status'])) {
|
||||||
|
echo json_encode(['status' => 'error', 'msg' => 'Action invalide']);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'status') {
|
||||||
|
exec("sudo /usr/local/bin/usb-bind-wrapper.sh status $dev 2>&1", $out, $rc);
|
||||||
|
$state = trim($out[0] ?? '');
|
||||||
|
echo json_encode(['status' => 'ok', 'state' => $state]);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cmd = escapeshellcmd("sudo /usr/local/bin/usb-bind-wrapper.sh $action $dev");
|
||||||
|
exec($cmd . " 2>&1", $out, $rc);
|
||||||
|
|
||||||
|
if ($rc === 0) {
|
||||||
|
echo json_encode(['status' => 'ok', 'msg' => ucfirst($action) . " $dev réussi"]);
|
||||||
|
} else {
|
||||||
|
echo json_encode(['status' => 'error', 'msg' => implode("\n", $out)]);
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
|
||||||
Référencer dans un nouveau ticket
Bloquer un utilisateur