tailscale/portlist/poller.go
Brad Fitzpatrick 19b0cfe89e all: prepare for GOOS=ios in Go 1.16
Work with either way for now on iOS (darwin/arm64 vs ios/arm64).

In February when Go 1.16 comes out we'll have a universal binary for
darwin/arm64 (macOS) and will drop support for Go 1.15 and its
darwin/amd64 meaning iOS. (it'll mean macOS).

Context:

* https://tip.golang.org/doc/go1.16#darwin
* https://github.com/golang/go/issues/38485
* https://github.com/golang/go/issues/42100
2020-11-11 09:17:04 -08:00

102 lines
2.0 KiB
Go

// Copyright (c) 2020 Tailscale Inc & AUTHORS All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package portlist
import (
"context"
"errors"
"time"
"tailscale.com/version"
)
// Poller scans the systems for listening ports periodically and sends
// the results to C.
type Poller struct {
// C received the list of ports periodically. It's closed when
// Run completes, after which Err can be checked.
C <-chan List
c chan List
// Err is the error from the final GetList call. It is only
// valid to read once C has been closed. Err is nil if Close
// is called or the context is canceled.
Err error
quitCh chan struct{} // close this to force exit
prev List // most recent data
}
// NewPoller returns a new portlist Poller. It returns an error
// if the portlist couldn't be obtained.
func NewPoller() (*Poller, error) {
if version.OS() == "iOS" {
return nil, errors.New("not available on iOS")
}
p := &Poller{
c: make(chan List),
quitCh: make(chan struct{}),
}
p.C = p.c
// Do one initial poll synchronously so we can return an error
// early.
var err error
p.prev, err = GetList(nil)
if err != nil {
return nil, err
}
return p, nil
}
func (p *Poller) Close() error {
select {
case <-p.quitCh:
return nil
default:
}
close(p.quitCh)
<-p.C
return nil
}
// Run runs the Poller periodically until either the context
// is done, or the Close is called.
func (p *Poller) Run(ctx context.Context) error {
defer close(p.c)
tick := time.NewTicker(pollInterval)
defer tick.Stop()
// Send out the pre-generated initial value
p.c <- p.prev
for {
select {
case <-tick.C:
pl, err := GetList(p.prev)
if err != nil {
p.Err = err
return err
}
if pl.SameInodes(p.prev) {
continue
}
p.prev = pl
select {
case p.c <- pl:
case <-ctx.Done():
return ctx.Err()
case <-p.quitCh:
return nil
}
case <-ctx.Done():
return ctx.Err()
case <-p.quitCh:
return nil
}
}
}