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) }