Initial commit.

This commit is contained in:
Joel Elkins 2022-10-04 22:12:09 -05:00
commit 9d440e71c1
Signed by: joel
GPG Key ID: 133589DC38921AE2
7 changed files with 152 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/kvmswitcher

22
config.sample.toml Normal file
View File

@ -0,0 +1,22 @@
[globals]
listen = ":5051"
[[targets]]
id = "host"
commands = [
"sudo virsh detach-device win11 --file ~/.config/kvmswitcher/keyboard.xml --current",
"sudo virsh detach-device win11 --file ~/.config/kvmswitcher/mouse.xml --current",
"sudo ddcutil setvcp 0x60 --display 1 0x0f",
"sleep 0.5",
"sudo chvt 1",
"sleep 0.5",
"sudo chvt 5",
]
[[targets]]
id = "guest"
commands = [
"sudo virsh attach-device win11 --file ~/.config/kvmswitcher/keyboard.xml --current",
"sudo virsh attach-device win11 --file ~/.config/kvmswitcher/mouse.xml --current",
"sudo ddcutil setvcp 0x60 --display 1 0x12",
]

5
go.mod Normal file
View File

@ -0,0 +1,5 @@
module gitea.elkins.co/Networking/kvmswitcher
go 1.19
require github.com/BurntSushi/toml v1.2.0

2
go.sum Normal file
View File

@ -0,0 +1,2 @@
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=

25
internal/config/config.go Normal file
View File

@ -0,0 +1,25 @@
package config
import (
"github.com/BurntSushi/toml"
)
type TargetId string
type Command string
type Target struct {
Id TargetId
Commands []Command
}
type Targets []Target
type T struct {
Globals struct {
Listen *string
}
Targets
}
func Read(f string) (t T, err error) {
_, err = toml.DecodeFile(f, &t)
return
}

9
kvmswitcher.service Normal file
View File

@ -0,0 +1,9 @@
[Unit]
Description=Switch kvm devices based on configured REST endpoint
[Service]
Type=simple
ExecStart=kvmswitcher
[Install]
WantedBy=default.target

88
server.go Normal file
View File

@ -0,0 +1,88 @@
package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"os/exec"
"path"
"regexp"
"gitea.elkins.co/Networking/kvmswitcher/internal/config"
)
var (
confFile = flag.String("config",
path.Join(os.Getenv("HOME"), ".config", "kvmswitcher", "config.toml"),
"Path to the configuration file",
)
listen = flag.String("listen", ":5001", "Address and port to listen on")
targets config.Targets
)
func targetIds(t config.Targets) []config.TargetId {
x := make([]config.TargetId, len(t))
for i := range t {
x[i] = t[i].Id
}
return x
}
func main() {
log.SetOutput(os.Stderr)
flag.Parse()
c, err := config.Read(*confFile)
if err != nil {
log.Println("Could not read config file: ", err)
}
targets = c.Targets
if c.Globals.Listen != nil {
listen = c.Globals.Listen
}
log.Printf("configured hosts: %v\n", targetIds(targets))
log.Printf("listening on %s\n", *listen)
http.HandleFunc("/to/", doSwitch)
http.ListenAndServe(*listen, nil)
}
func doSwitch(w http.ResponseWriter, r *http.Request) {
ct, _ := regexp.Compile("^/to/")
u := config.TargetId(ct.ReplaceAllString(r.URL.String(), ""))
if u == config.TargetId(r.URL.String()) {
fmt.Fprintf(w, "Malformed request: %s\n", u)
return
}
var t *config.Target
for i := range targets {
if targets[i].Id == config.TargetId(u) {
t = &targets[i]
}
}
if t == nil {
fmt.Fprintf(w, "Invalid Target: %s\n", u)
return
}
fmt.Fprintf(w, "Switching to %s\n", u)
out, err := runCommands(t.Commands)
if err != nil {
fmt.Fprintf(w, "ERROR:\n%s", err)
}
fmt.Fprintf(w, "OUTPUT:\n%s", out)
}
func runCommands(cmds []config.Command) (out string, err error) {
for _, c := range cmds {
o, err := exec.Command("/bin/sh", "-c", string(c)).Output()
if err != nil {
return out, fmt.Errorf("Error executing %s: %s\n", c, err)
}
out = out + string(o)
}
return
}