kvmswitcher/server.go

189 lines
3.9 KiB
Go

package main
import (
"flag"
"fmt"
"log"
"net/http"
"os"
"regexp"
"gitea.elkins.co/Networking/kvmswitcher/internal/config"
)
const (
hostTgt = "host"
dirTo = true
dirFrom = false
)
var (
confFile = flag.String("config", "/etc/kvmswitcher/config.toml", "Path to the configuration file")
listen = flag.String("listen", ":5001", "Address and port to listen on")
curTargetId = flag.String("initial", "host", "Intitially active target")
targets config.Targets
)
func targetIds(t config.Targets) []string {
x := make([]string, 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 len(targets) == 0 {
log.Fatal("No targets configured")
}
if c.Globals.Listen != nil {
listen = c.Globals.Listen
}
log.Printf("configured targets: %v\n", targetIds(targets))
log.Printf("listening on %s\n", *listen)
http.HandleFunc("/to/", doSwitchTo)
http.HandleFunc("/from/", doSwitchFrom)
http.HandleFunc("/fromcurto/", doSwitchFromCurTo)
http.HandleFunc("/cur", doShowCur)
http.HandleFunc("/fromcur", doSwitchFromCur)
http.HandleFunc("/setcur/", doSetCur)
http.ListenAndServe(*listen, nil)
}
func getTarget(r *http.Request) (string, error) {
ct, _ := regexp.Compile("^/(from|to|fromcurto|setcur)/")
u := ct.ReplaceAllString(r.URL.String(), "")
if u == r.URL.String() {
log.Printf("Malformed request: %s\n", u)
return "", fmt.Errorf("Malformed request: %s", u)
}
return u, nil
}
func doSetCur(w http.ResponseWriter, r *http.Request) {
tgt, err := getTarget(r)
if err != nil {
w.WriteHeader(404)
fmt.Fprintf(w, "%s", err)
return
}
found := false
for i := range targets {
if targets[i].Id == tgt {
curTargetId = &targets[i].Id
found = true
}
}
if !found {
w.WriteHeader(404)
fmt.Fprintf(w, "Target %s not found", tgt)
log.Printf("setcur: target %s not configured", tgt)
}
}
func doShowCur(w http.ResponseWriter, _ *http.Request) {
fmt.Fprint(w, *curTargetId)
}
func doSwitchFromCur(w http.ResponseWriter, _ *http.Request) {
doSwitch(dirFrom, *curTargetId, w)
}
func doSwitchFrom(w http.ResponseWriter, r *http.Request) {
t, err := getTarget(r)
if err != nil {
w.WriteHeader(404)
fmt.Fprintf(w, "%s", err)
return
}
doSwitch(dirFrom, t, w)
}
func doSwitchTo(w http.ResponseWriter, r *http.Request) {
t, err := getTarget(r)
if err != nil {
w.WriteHeader(404)
fmt.Fprintf(w, "%s", err)
return
}
doSwitch(dirTo, t, w)
}
func doSwitchFromCurTo(w http.ResponseWriter, r *http.Request) {
t, err := getTarget(r)
if err != nil {
w.WriteHeader(404)
fmt.Fprintf(w, "%s", err)
return
}
doSwitch(dirFrom, *curTargetId, w)
doSwitch(dirTo, t, w)
}
func doSwitch(dir bool, tgt string, w http.ResponseWriter) {
var t *config.Target
for i := range targets {
if targets[i].Id == tgt {
t = &targets[i]
}
}
if t == nil {
fmt.Fprintf(w, "Invalid Target: %s\n", tgt)
return
}
cmds := t.CommandsFrom
toFrom := "from"
if dir == dirTo {
cmds = t.CommandsTo
toFrom = "to"
}
log.Printf("Switching %s %s\n", toFrom, tgt)
res, errs := runCommands(cmds)
// if at least one cmd worked, consider the operation successful
if len(errs) < len(cmds) {
if dir == dirTo {
curTargetId = &t.Id
} else {
h := hostTgt
curTargetId = &h
}
}
if len(errs) > 0 {
w.WriteHeader(503)
log.Printf("Switch resulted in %d errors:", len(errs))
for i, e := range errs {
log.Printf("[%d/%d]: %s", i, len(errs), e)
}
}
for _, o := range res {
fmt.Fprint(w, o)
}
}
func runCommands(cmds []config.Command) (res []string, errs []error) {
for _, c := range cmds {
o, err := c.Exec()
if err != nil {
errs = append(errs, err)
log.Printf("Error on command execution (cmd = %s): %s\n", c, err)
}
res = append(res, string(o))
}
return
}