ipn{,/ipnlocal}: set new Notify.FilesWaiting when server has file(s)

Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
pull/1621/head
Brad Fitzpatrick 3 years ago committed by Brad Fitzpatrick
parent 0301ccd275
commit 672b9fd4bd

@ -68,6 +68,8 @@ type Notify struct {
BackendLogID *string // public logtail id used by backend
PingResult *ipnstate.PingResult
FilesWaiting *empty.Message `json:",omitempty"`
// LocalTCPPort, if non-nil, informs the UI frontend which
// (non-zero) localhost TCP port it's listening on.
// This is currently only used by Tailscale when run in the

@ -107,6 +107,7 @@ type LocalBackend struct {
authURL string
interact bool
prevIfState *interfaces.State
peerAPIServer *peerAPIServer // or nil
peerAPIListeners []*peerAPIListener
// statusLock must be held before calling statusChanged.Wait() or
@ -909,15 +910,20 @@ func (b *LocalBackend) readPoller() {
// connected, the notification is dropped without being delivered.
func (b *LocalBackend) send(n ipn.Notify) {
b.mu.Lock()
notify := b.notify
notifyFunc := b.notify
apiSrv := b.peerAPIServer
b.mu.Unlock()
if notify != nil {
n.Version = version.Long
notify(n)
} else {
if notifyFunc == nil {
b.logf("nil notify callback; dropping %+v", n)
return
}
n.Version = version.Long
if apiSrv != nil && apiSrv.hasFilesWaiting() {
n.FilesWaiting = &empty.Message{}
}
notifyFunc(n)
}
// popBrowserAuthNow shuts down the data plane and sends an auth URL
@ -1489,6 +1495,7 @@ func (b *LocalBackend) initPeerAPIListener() {
b.mu.Lock()
defer b.mu.Unlock()
b.peerAPIServer = nil
for _, pln := range b.peerAPIListeners {
pln.Close()
}
@ -1526,6 +1533,7 @@ func (b *LocalBackend) initPeerAPIListener() {
tunName: tunName,
selfNode: selfNode,
}
b.peerAPIServer = ps
isNetstack := wgengine.IsNetstack(b.e)
for i, a := range b.netMap.Addresses {

@ -22,7 +22,9 @@ import (
"strings"
"inet.af/netaddr"
"tailscale.com/ipn"
"tailscale.com/net/interfaces"
"tailscale.com/syncs"
"tailscale.com/tailcfg"
"tailscale.com/wgengine"
)
@ -30,10 +32,52 @@ import (
var initListenConfig func(*net.ListenConfig, netaddr.IP, *interfaces.State, string) error
type peerAPIServer struct {
b *LocalBackend
rootDir string
tunName string
selfNode *tailcfg.Node
b *LocalBackend
rootDir string
tunName string
selfNode *tailcfg.Node
knownEmpty syncs.AtomicBool
}
const partialSuffix = ".tspartial"
// hasFilesWaiting reports whether any files are buffered in the
// tailscaled daemon storage.
func (s *peerAPIServer) hasFilesWaiting() bool {
if s.rootDir == "" {
return false
}
if s.knownEmpty.Get() {
// Optimization: this is usually empty, so avoid opening
// the directory and checking. We can't cache the actual
// has-files-or-not values as the macOS/iOS client might
// in the future use+delete the files directly. So only
// keep this negative cache.
return false
}
f, err := os.Open(s.rootDir)
if err != nil {
return false
}
defer f.Close()
for {
des, err := f.ReadDir(10)
for _, de := range des {
if strings.HasSuffix(de.Name(), partialSuffix) {
continue
}
if de.Type().IsRegular() {
return true
}
}
if err == io.EOF {
s.knownEmpty.Set(true)
}
if err != nil {
break
}
}
return false
}
func (s *peerAPIServer) listen(ip netaddr.IP, ifState *interfaces.State) (ln net.Listener, err error) {
@ -221,7 +265,7 @@ func (h *peerAPIHandler) put(w http.ResponseWriter, r *http.Request) {
return
}
name := path.Base(r.URL.Path)
if name == "." || name == "/" {
if name == "." || name == "/" || strings.HasSuffix(name, partialSuffix) {
http.Error(w, "bad filename", http.StatusForbidden)
return
}
@ -258,4 +302,6 @@ func (h *peerAPIHandler) put(w http.ResponseWriter, r *http.Request) {
// TODO: some real response
success = true
io.WriteString(w, "{}\n")
h.ps.knownEmpty.Set(false)
h.ps.b.send(ipn.Notify{}) // it will set FilesWaiting
}

Loading…
Cancel
Save