blob: 3e7a9ad696570e5e2467814676814497d135fcd5 [file] [log] [blame]
package main
import (
"context"
"flag"
"fmt"
"html/template"
"net"
"net/http"
"math/rand"
"strings"
"time"
"github.com/golang/glog"
mirko "code.hackerspace.pl/hscloud/go/mirko"
"code.hackerspace.pl/hscloud/hswaw/laserproxy/tpl"
)
var (
flagLaserAddress = "10.11.0.10"
flagLaserNetworkAddress = "10.11.0.1"
flagClientNetworkAddress = "10.8.1.2"
flagWebAddress = "127.0.0.1:8080"
flagOwnershipDuration = "1h"
tplIndex = template.Must(template.New("index.html").Parse(tpl.MustAssetString("index.html")))
)
type service struct {
lockCtrl chan *lockCtrl
}
func main() {
flag.StringVar(&flagLaserAddress, "laser_address", flagLaserAddress, "Address of Ruida controller on laser network")
flag.StringVar(&flagLaserNetworkAddress, "laser_network", flagLaserNetworkAddress, "Address of proxy on laser network")
flag.StringVar(&flagClientNetworkAddress, "client_network", flagClientNetworkAddress, "Address of proxy on client network")
flag.StringVar(&flagWebAddress, "web_address", flagWebAddress, "Address and port to listen on for public web connections")
flag.StringVar(&flagOwnershipDuration, "timeout_duration", flagOwnershipDuration, "Time after which lasercutter lock will expire when not in use")
flag.Parse()
m := mirko.New()
if err := m.Listen(); err != nil {
glog.Exitf("Could not listen: %v", err)
}
lisLaser, err := net.ListenPacket("udp", fmt.Sprintf("%s:40200", flagLaserNetworkAddress))
if err != nil {
glog.Fatalf("could not listen on laser network: %v", err)
}
defer lisLaser.Close()
lisClient, err := net.ListenPacket("udp", fmt.Sprintf("%s:50200", flagClientNetworkAddress))
if err != nil {
glog.Fatalf("could not listen on client network: %v", err)
}
defer lisClient.Close()
laserIP := net.ParseIP(flagLaserAddress)
if laserIP == nil {
glog.Fatalf("could not parse laser IP address %q", flagLaserAddress)
}
laserAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:50200", laserIP.String()))
if err != nil {
glog.Fatalf("could not make laser UDP address: %v", err)
}
ownershipDuration, err := time.ParseDuration(flagOwnershipDuration)
if err != nil {
glog.Fatalf("could not parse timeout duration: %v", err)
}
ctx := m.Context()
s := &service{
lockCtrl: make(chan *lockCtrl),
}
updates := make(chan *lockUpdate)
go s.runProxy(ctx, updates, laserAddr, lisLaser, lisClient)
go s.runLocker(ctx, s.lockCtrl, ownershipDuration)
s.lockCtrl <- &lockCtrl{
subscribe: &lockCtrlSubscribe{
subscriber: updates,
},
}
mux := http.NewServeMux()
mux.HandleFunc("/", s.handlerIndex)
mux.HandleFunc("/take", s.handlerTake)
mux.HandleFunc("/release", s.handlerRelease)
mux.HandleFunc("/force", s.handlerForce)
httpSrv := &http.Server{Addr: flagWebAddress, Handler: mux}
glog.Infof("Listening for web connections on %q...", flagWebAddress)
go func() {
if err := httpSrv.ListenAndServe(); err != nil {
glog.Error(err)
}
}()
if err := m.Serve(); err != nil {
glog.Exitf("Could not run: %v", err)
}
glog.Info("Running.")
<-m.Done()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
httpSrv.Shutdown(ctx)
}
var (
sampleNames = []string{
"elia", "radex", "qdot", "hans acker", "lars aser", "makłowicz",
}
)
func remoteAddr(r *http.Request) string {
if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
return strings.Split(fwd, ":")[0]
}
return strings.Split(r.RemoteAddr, ":")[0]
}
func (s *service) handlerIndex(w http.ResponseWriter, r *http.Request) {
res := make(chan *lockResCurrent)
s.lockCtrl <- &lockCtrl{
getCurrent: &lockCtrlGetCurrent{
res: res,
},
}
cur := <-res
err := tplIndex.Execute(w, struct {
You bool
CurrentAddress string
CurrentName string
CurrentDeadline string
SampleName string
}{
You: cur.addr == remoteAddr(r),
CurrentAddress: cur.addr,
CurrentName: cur.note,
CurrentDeadline: fmt.Sprintf("%d minute(s)", int(cur.deadline.Sub(time.Now()).Minutes())),
SampleName: sampleNames[rand.Intn(len(sampleNames))],
})
if err != nil {
glog.Errorf("rendering template: %v", err)
}
}
func (s *service) handlerTake(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
return
}
r.ParseForm()
who := r.Form.Get("who")
prev := r.Form.Get("prev")
if who == "" {
fmt.Fprintf(w, "excuse me, who are you? please specify a name")
return
}
res := make(chan bool)
take := &lockCtrlTake{
note: who,
addr: remoteAddr(r),
prev: prev,
res: res,
}
s.lockCtrl <- &lockCtrl{
take: take,
}
won := <-res
if won {
http.Redirect(w, r, "/", 302)
} else {
fmt.Fprintf(w, "lock not taken")
}
}
func (s *service) handlerRelease(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
return
}
res := make(chan struct{})
s.lockCtrl <- &lockCtrl{
release: &lockCtrlRelease{
addr: remoteAddr(r),
res: res,
},
}
<-res
http.Redirect(w, r, "/", 302)
}
func (s *service) handlerForce(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
return
}
res := make(chan struct{})
s.lockCtrl <- &lockCtrl{
release: &lockCtrlRelease{
force: true,
res: res,
},
}
<-res
http.Redirect(w, r, "/", 302)
}