feat: fleet ops, KEV scan, tunnels, beacon fallback, persistence
Extend owned-fleet control with scheduled tasks, audit log, file browser, HTTPS beacon when WS drops, protocol tunnels, registry/autostart forge options, KEV exposure in full sys check with Telegram alerts, and UI/tests.
This commit is contained in:
105
agent/client/camera_linux.go
Normal file
105
agent/client/camera_linux.go
Normal file
@@ -0,0 +1,105 @@
|
||||
//go:build linux
|
||||
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func handleCameraAction(action string) (handled bool, success bool, message string) {
|
||||
switch action {
|
||||
case "camera_snapshot":
|
||||
raw, err := captureLinuxCameraJPEG()
|
||||
if err != nil {
|
||||
return true, false, err.Error()
|
||||
}
|
||||
b64, err := encodeCameraSnapshotJPEG(raw)
|
||||
if err != nil {
|
||||
return true, false, err.Error()
|
||||
}
|
||||
return true, true, b64
|
||||
case "camera_list":
|
||||
devs, err := listLinuxCameraDevices()
|
||||
if err != nil {
|
||||
return true, false, err.Error()
|
||||
}
|
||||
if len(devs) == 0 {
|
||||
return true, false, "no V4L2 devices found under /dev/video*"
|
||||
}
|
||||
return true, true, strings.Join(devs, "\n")
|
||||
default:
|
||||
return false, false, ""
|
||||
}
|
||||
}
|
||||
|
||||
func listLinuxCameraDevices() ([]string, error) {
|
||||
matches, err := filepath.Glob("/dev/video*")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var devs []string
|
||||
for _, p := range matches {
|
||||
if fi, err := os.Stat(p); err == nil && (fi.Mode()&os.ModeCharDevice) != 0 {
|
||||
devs = append(devs, p)
|
||||
}
|
||||
}
|
||||
return devs, nil
|
||||
}
|
||||
|
||||
func captureLinuxCameraJPEG() ([]byte, error) {
|
||||
devs, err := listLinuxCameraDevices()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(devs) == 0 {
|
||||
return nil, fmt.Errorf("no /dev/video* devices — connect a USB camera or install v4l2 drivers")
|
||||
}
|
||||
device := devs[0]
|
||||
|
||||
if ff, err := exec.LookPath("ffmpeg"); err == nil {
|
||||
out, runErr := exec.Command(ff,
|
||||
"-hide_banner", "-loglevel", "error",
|
||||
"-f", "v4l2",
|
||||
"-i", device,
|
||||
"-frames:v", "1",
|
||||
"-q:v", "2",
|
||||
"-f", "image2",
|
||||
"pipe:1",
|
||||
).CombinedOutput()
|
||||
if runErr == nil && len(out) >= 100 {
|
||||
return out, nil
|
||||
}
|
||||
if runErr != nil {
|
||||
hint := strings.TrimSpace(string(out))
|
||||
if hint != "" {
|
||||
return nil, fmt.Errorf("ffmpeg v4l2 capture failed: %v (%s)", runErr, hint)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if fw, err := exec.LookPath("fswebcam"); err == nil {
|
||||
tmp, err := os.CreateTemp("", "af-cam-*.jpg")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tmpPath := tmp.Name()
|
||||
_ = tmp.Close()
|
||||
defer os.Remove(tmpPath)
|
||||
|
||||
out, runErr := exec.Command(fw, "-q", "-d", device, "-r", "1280x720", "--no-banner", tmpPath).CombinedOutput()
|
||||
if runErr != nil {
|
||||
return nil, fmt.Errorf("fswebcam failed: %v (%s)", runErr, strings.TrimSpace(string(out)))
|
||||
}
|
||||
raw, err := os.ReadFile(tmpPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("camera capture unsupported: install ffmpeg or fswebcam on the agent host")
|
||||
}
|
||||
Reference in New Issue
Block a user