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.
106 lines
2.4 KiB
Go
106 lines
2.4 KiB
Go
//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")
|
|
}
|