blob: 3e7a9ad696570e5e2467814676814497d135fcd5 [file] [log] [blame]
Serge Bazanskide0330a2020-07-30 20:48:48 +02001package main
2
3import (
4 "context"
5 "flag"
6 "fmt"
7 "html/template"
8 "net"
9 "net/http"
10 "math/rand"
11 "strings"
12 "time"
13
14 "github.com/golang/glog"
15
16 mirko "code.hackerspace.pl/hscloud/go/mirko"
17 "code.hackerspace.pl/hscloud/hswaw/laserproxy/tpl"
18)
19
20var (
21 flagLaserAddress = "10.11.0.10"
22 flagLaserNetworkAddress = "10.11.0.1"
23 flagClientNetworkAddress = "10.8.1.2"
24 flagWebAddress = "127.0.0.1:8080"
radex81da4e52020-08-27 23:56:29 +020025 flagOwnershipDuration = "1h"
Serge Bazanskide0330a2020-07-30 20:48:48 +020026
27 tplIndex = template.Must(template.New("index.html").Parse(tpl.MustAssetString("index.html")))
28)
29
30type service struct {
31 lockCtrl chan *lockCtrl
32}
33
34func main() {
35 flag.StringVar(&flagLaserAddress, "laser_address", flagLaserAddress, "Address of Ruida controller on laser network")
36 flag.StringVar(&flagLaserNetworkAddress, "laser_network", flagLaserNetworkAddress, "Address of proxy on laser network")
37 flag.StringVar(&flagClientNetworkAddress, "client_network", flagClientNetworkAddress, "Address of proxy on client network")
38 flag.StringVar(&flagWebAddress, "web_address", flagWebAddress, "Address and port to listen on for public web connections")
radex81da4e52020-08-27 23:56:29 +020039 flag.StringVar(&flagOwnershipDuration, "timeout_duration", flagOwnershipDuration, "Time after which lasercutter lock will expire when not in use")
Serge Bazanskide0330a2020-07-30 20:48:48 +020040
41 flag.Parse()
42 m := mirko.New()
43 if err := m.Listen(); err != nil {
44 glog.Exitf("Could not listen: %v", err)
45 }
46
47 lisLaser, err := net.ListenPacket("udp", fmt.Sprintf("%s:40200", flagLaserNetworkAddress))
48 if err != nil {
49 glog.Fatalf("could not listen on laser network: %v", err)
50 }
51 defer lisLaser.Close()
52
53 lisClient, err := net.ListenPacket("udp", fmt.Sprintf("%s:50200", flagClientNetworkAddress))
54 if err != nil {
55 glog.Fatalf("could not listen on client network: %v", err)
56 }
57 defer lisClient.Close()
58
59 laserIP := net.ParseIP(flagLaserAddress)
60 if laserIP == nil {
61 glog.Fatalf("could not parse laser IP address %q", flagLaserAddress)
62 }
63 laserAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:50200", laserIP.String()))
64 if err != nil {
65 glog.Fatalf("could not make laser UDP address: %v", err)
66 }
67
radex81da4e52020-08-27 23:56:29 +020068 ownershipDuration, err := time.ParseDuration(flagOwnershipDuration)
69 if err != nil {
70 glog.Fatalf("could not parse timeout duration: %v", err)
71 }
72
Serge Bazanskide0330a2020-07-30 20:48:48 +020073 ctx := m.Context()
74 s := &service{
75 lockCtrl: make(chan *lockCtrl),
76 }
77 updates := make(chan *lockUpdate)
78 go s.runProxy(ctx, updates, laserAddr, lisLaser, lisClient)
radex81da4e52020-08-27 23:56:29 +020079 go s.runLocker(ctx, s.lockCtrl, ownershipDuration)
Serge Bazanskide0330a2020-07-30 20:48:48 +020080 s.lockCtrl <- &lockCtrl{
81 subscribe: &lockCtrlSubscribe{
82 subscriber: updates,
83 },
84 }
85
86 mux := http.NewServeMux()
87 mux.HandleFunc("/", s.handlerIndex)
88 mux.HandleFunc("/take", s.handlerTake)
89 mux.HandleFunc("/release", s.handlerRelease)
90 mux.HandleFunc("/force", s.handlerForce)
91 httpSrv := &http.Server{Addr: flagWebAddress, Handler: mux}
92
93 glog.Infof("Listening for web connections on %q...", flagWebAddress)
94 go func() {
95 if err := httpSrv.ListenAndServe(); err != nil {
96 glog.Error(err)
97 }
98 }()
99
100 if err := m.Serve(); err != nil {
101 glog.Exitf("Could not run: %v", err)
102 }
103
104 glog.Info("Running.")
105 <-m.Done()
106 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
107 defer cancel()
108 httpSrv.Shutdown(ctx)
109}
110
111var (
112 sampleNames = []string{
vuko8fcffb22023-01-05 19:55:15 +0100113 "elia", "radex", "qdot", "hans acker", "lars aser", "makłowicz",
Serge Bazanskide0330a2020-07-30 20:48:48 +0200114 }
115)
116
117func remoteAddr(r *http.Request) string {
118 if fwd := r.Header.Get("X-Forwarded-For"); fwd != "" {
119 return strings.Split(fwd, ":")[0]
120 }
121 return strings.Split(r.RemoteAddr, ":")[0]
122}
123
124func (s *service) handlerIndex(w http.ResponseWriter, r *http.Request) {
125 res := make(chan *lockResCurrent)
126 s.lockCtrl <- &lockCtrl{
127 getCurrent: &lockCtrlGetCurrent{
128 res: res,
129 },
130 }
131 cur := <-res
132
133 err := tplIndex.Execute(w, struct {
134 You bool
135 CurrentAddress string
radex81da4e52020-08-27 23:56:29 +0200136 CurrentName string
Serge Bazanskide0330a2020-07-30 20:48:48 +0200137 CurrentDeadline string
138 SampleName string
139 }{
140 You: cur.addr == remoteAddr(r),
141 CurrentAddress: cur.addr,
radex81da4e52020-08-27 23:56:29 +0200142 CurrentName: cur.note,
Serge Bazanskide0330a2020-07-30 20:48:48 +0200143 CurrentDeadline: fmt.Sprintf("%d minute(s)", int(cur.deadline.Sub(time.Now()).Minutes())),
144 SampleName: sampleNames[rand.Intn(len(sampleNames))],
145 })
146 if err != nil {
147 glog.Errorf("rendering template: %v", err)
148 }
149}
150
151func (s *service) handlerTake(w http.ResponseWriter, r *http.Request) {
152 if r.Method != "POST" {
153 return
154 }
155 r.ParseForm()
156 who := r.Form.Get("who")
157 prev := r.Form.Get("prev")
158 if who == "" {
radex81da4e52020-08-27 23:56:29 +0200159 fmt.Fprintf(w, "excuse me, who are you? please specify a name")
Serge Bazanskide0330a2020-07-30 20:48:48 +0200160 return
161 }
162
163 res := make(chan bool)
164 take := &lockCtrlTake{
165 note: who,
166 addr: remoteAddr(r),
167 prev: prev,
168 res: res,
169 }
170 s.lockCtrl <- &lockCtrl{
171 take: take,
172 }
173 won := <-res
174
175 if won {
176 http.Redirect(w, r, "/", 302)
177 } else {
178 fmt.Fprintf(w, "lock not taken")
179 }
180}
181
182func (s *service) handlerRelease(w http.ResponseWriter, r *http.Request) {
183 if r.Method != "POST" {
184 return
185 }
186 res := make(chan struct{})
187 s.lockCtrl <- &lockCtrl{
188 release: &lockCtrlRelease{
189 addr: remoteAddr(r),
190 res: res,
191 },
192 }
193 <-res
194
195 http.Redirect(w, r, "/", 302)
196}
197
198func (s *service) handlerForce(w http.ResponseWriter, r *http.Request) {
199 if r.Method != "POST" {
200 return
201 }
202 res := make(chan struct{})
203 s.lockCtrl <- &lockCtrl{
204 release: &lockCtrlRelease{
205 force: true,
206 res: res,
207 },
208 }
209 <-res
210
211 http.Redirect(w, r, "/", 302)
212}