first commit
Cette révision appartient à :
@@ -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