func Serve(ctx scope.Context, addr string) { http.Handle("/metrics", prometheus.Handler()) listener, err := net.Listen("tcp", addr) if err != nil { ctx.Terminate(err) } closed := false m := sync.Mutex{} closeListener := func() { m.Lock() if !closed { listener.Close() closed = true } m.Unlock() } // Spin off goroutine to watch ctx and close listener if shutdown requested. go func() { <-ctx.Done() closeListener() }() if err := http.Serve(listener, nil); err != nil { fmt.Printf("http[%s]: %s\n", addr, err) ctx.Terminate(err) } closeListener() ctx.WaitGroup().Done() }
func (cmd *serveEmbedCmd) run(ctx scope.Context, args []string) error { listener, err := net.Listen("tcp", cmd.addr) if err != nil { return err } closed := false m := sync.Mutex{} closeListener := func() { m.Lock() if !closed { listener.Close() closed = true } m.Unlock() } // Spin off goroutine to watch ctx and close listener if shutdown requested. go func() { <-ctx.Done() closeListener() }() if err := http.Serve(listener, cmd); err != nil { fmt.Printf("http[%s]: %s\n", cmd.addr, err) return err } closeListener() ctx.WaitGroup().Done() return ctx.Err() }
func (ctrl *Controller) terminal(ctx scope.Context, ch ssh.Channel) { defer ch.Close() lines := make(chan string) term := terminal.NewTerminal(ch, "> ") go func() { for ctx.Err() == nil { line, err := term.ReadLine() if err != nil { ctx.Terminate(err) return } lines <- line } }() for { var line string select { case <-ctx.Done(): return case line = <-lines: } cmd := parse(line) fmt.Printf("[control] > %v\n", cmd) switch cmd[0] { case "": continue case "quit": return case "shutdown": ctrl.ctx.Terminate(fmt.Errorf("shutdown initiated from console")) default: runCommand(ctx.Fork(), ctrl, cmd[0], term, cmd[1:]) } } }
func (jq *JobQueueBinding) WaitForJob(ctx scope.Context) error { ch := make(chan error) // background goroutine to wait on condition go func() { // synchronize with caller <-ch jq.m.Unlock() jq.Backend.jobQueueListener().wait(jq.Name()) ch <- nil }() // synchronize with background goroutine jq.m.Lock() ch <- nil jq.m.Lock() jq.m.Unlock() select { case <-ctx.Done(): jq.Backend.jobQueueListener().wakeAll(jq.Name()) <-ch return ctx.Err() case err := <-ch: return err } }
func (cmd *serveCmd) run(ctx scope.Context, args []string) error { listener, err := net.Listen("tcp", cmd.addr) if err != nil { return err } m := sync.Mutex{} closed := false closeListener := func() { m.Lock() if !closed { closed = true listener.Close() } m.Unlock() } defer closeListener() heim, err := getHeim(ctx) if err != nil { return fmt.Errorf("configuration error: %s", err) } defer heim.Backend.Close() if cmd.static != "" { heim.StaticPath = cmd.static } if err := controller(heim, cmd.consoleAddr); err != nil { return fmt.Errorf("controller error: %s", err) } serverDesc := backend.Config.Cluster.DescribeSelf() server, err := backend.NewServer(heim, serverDesc.ID, serverDesc.Era) if err != nil { return fmt.Errorf("server error: %s", err) } server.SetInsecureCookies(backend.Config.SetInsecureCookies) server.AllowRoomCreation(backend.Config.AllowRoomCreation) server.NewAccountMinAgentAge(backend.Config.NewAccountMinAgentAge) // Spin off goroutine to watch ctx and close listener if shutdown requested. go func() { <-ctx.Done() closeListener() }() fmt.Printf("serving era %s on %s\n", serverDesc.Era, cmd.addr) if err := http.Serve(listener, newVersioningHandler(server)); err != nil { if strings.HasSuffix(err.Error(), "use of closed network connection") { return nil } return err } return nil }
func (s *session) Send(ctx scope.Context, cmdType proto.PacketType, payload interface{}) error { // Special case: certain events have privileged info that may need to be stripped from them switch event := payload.(type) { case *proto.PresenceEvent: switch s.privilegeLevel() { case proto.Staff: case proto.Host: event.RealClientAddress = "" default: event.RealClientAddress = "" event.ClientAddress = "" } case *proto.Message: if s.privilegeLevel() == proto.General { event.Sender.ClientAddress = "" } case *proto.EditMessageEvent: if s.privilegeLevel() == proto.General { event.Sender.ClientAddress = "" } } var err error payload, err = proto.DecryptPayload(payload, &s.client.Authorization, s.privilegeLevel()) if err != nil { return err } encoded, err := json.Marshal(payload) if err != nil { return err } cmd := &proto.Packet{ Type: cmdType, Data: encoded, } // Add to outgoing channel. If channel is full, defer to goroutine so as not to block // the caller (this may result in deliveries coming out of order). select { case <-ctx.Done(): // Session is closed, return error. return ctx.Err() case s.outgoing <- cmd: // Packet delivered to queue. default: // Queue is full. logging.Logger(s.ctx).Printf("outgoing channel full, ordering cannot be guaranteed") go func() { s.outgoing <- cmd }() } return nil }
func (jq *JobQueue) WaitForJob(ctx scope.Context) error { ch := make(chan error) go func() { jq.m.Lock() jq.c.Wait() jq.m.Unlock() ch <- nil }() select { case <-ctx.Done(): jq.m.Lock() jq.c.Broadcast() jq.m.Unlock() <-ch return ctx.Err() case err := <-ch: return err } }
func ScanLoop(ctx scope.Context, c cluster.Cluster, pb *psql.Backend, interval time.Duration) { defer ctx.WaitGroup().Done() errCount := 0 for { t := time.After(interval) select { case <-ctx.Done(): return case <-t: if err := scan(ctx.Fork(), c, pb); err != nil { errCount++ fmt.Printf("scan error [%d/%d]: %s", errCount, maxErrors, err) if errCount > maxErrors { fmt.Printf("maximum scan errors exceeded, terminating\n") ctx.Terminate(fmt.Errorf("maximum scan errors exceeded")) return } continue } errCount = 0 } } }
func ScanLoop(ctx scope.Context, listener *pq.Listener) { defer ctx.WaitGroup().Done() logger := logging.Logger(ctx) for { select { case <-ctx.Done(): logger.Printf("received cancellation signal, shutting down") return case notice := <-listener.Notify: if notice == nil { logger.Printf("received nil from listener") continue } var msg psql.BroadcastMessage if err := json.Unmarshal([]byte(notice.Extra), &msg); err != nil { logger.Printf("error: pq listen: invalid broadcast: %s", err) logger.Printf(" payload: %#v", notice.Extra) continue } switch msg.Event.Type { case proto.BounceEventType: bounceActivity.WithLabelValues(msg.Room).Inc() case proto.JoinEventType: joinActivity.WithLabelValues(msg.Room).Inc() case proto.PartEventType: partActivity.WithLabelValues(msg.Room).Inc() case proto.SendEventType: messageActivity.WithLabelValues(msg.Room).Inc() } } } }