|
|
|
@ -218,52 +218,76 @@ func (b *LocalBackend) Status() *ipnstate.Status {
|
|
|
|
|
return sb.Status()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// StatusWithoutPeers is like Status but omits any details
|
|
|
|
|
// of peers.
|
|
|
|
|
func (b *LocalBackend) StatusWithoutPeers() *ipnstate.Status {
|
|
|
|
|
sb := new(ipnstate.StatusBuilder)
|
|
|
|
|
b.updateStatus(sb, nil)
|
|
|
|
|
return sb.Status()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// UpdateStatus implements ipnstate.StatusUpdater.
|
|
|
|
|
func (b *LocalBackend) UpdateStatus(sb *ipnstate.StatusBuilder) {
|
|
|
|
|
b.e.UpdateStatus(sb)
|
|
|
|
|
b.updateStatus(sb, b.populatePeerStatusLocked)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// updateStatus populates sb with status.
|
|
|
|
|
//
|
|
|
|
|
// extraLocked, if non-nil, is called while b.mu is still held.
|
|
|
|
|
func (b *LocalBackend) updateStatus(sb *ipnstate.StatusBuilder, extraLocked func(*ipnstate.StatusBuilder)) {
|
|
|
|
|
b.mu.Lock()
|
|
|
|
|
defer b.mu.Unlock()
|
|
|
|
|
|
|
|
|
|
sb.SetVersion(version.Long)
|
|
|
|
|
sb.SetBackendState(b.state.String())
|
|
|
|
|
sb.SetAuthURL(b.authURL)
|
|
|
|
|
|
|
|
|
|
// TODO: hostinfo, and its networkinfo
|
|
|
|
|
// TODO: EngineStatus copy (and deprecate it?)
|
|
|
|
|
|
|
|
|
|
if b.netMap != nil {
|
|
|
|
|
sb.SetMagicDNSSuffix(b.netMap.MagicDNSSuffix())
|
|
|
|
|
for id, up := range b.netMap.UserProfiles {
|
|
|
|
|
sb.AddUser(id, up)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if extraLocked != nil {
|
|
|
|
|
extraLocked(sb)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (b *LocalBackend) populatePeerStatusLocked(sb *ipnstate.StatusBuilder) {
|
|
|
|
|
if b.netMap == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for id, up := range b.netMap.UserProfiles {
|
|
|
|
|
sb.AddUser(id, up)
|
|
|
|
|
}
|
|
|
|
|
for _, p := range b.netMap.Peers {
|
|
|
|
|
var lastSeen time.Time
|
|
|
|
|
if p.LastSeen != nil {
|
|
|
|
|
lastSeen = *p.LastSeen
|
|
|
|
|
}
|
|
|
|
|
for _, p := range b.netMap.Peers {
|
|
|
|
|
var lastSeen time.Time
|
|
|
|
|
if p.LastSeen != nil {
|
|
|
|
|
lastSeen = *p.LastSeen
|
|
|
|
|
}
|
|
|
|
|
var tailAddr string
|
|
|
|
|
for _, addr := range p.Addresses {
|
|
|
|
|
// The peer struct currently only allows a single
|
|
|
|
|
// Tailscale IP address. For compatibility with the
|
|
|
|
|
// old display, make sure it's the IPv4 address.
|
|
|
|
|
if addr.IP.Is4() && addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.IP) {
|
|
|
|
|
tailAddr = addr.IP.String()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
var tailAddr string
|
|
|
|
|
for _, addr := range p.Addresses {
|
|
|
|
|
// The peer struct currently only allows a single
|
|
|
|
|
// Tailscale IP address. For compatibility with the
|
|
|
|
|
// old display, make sure it's the IPv4 address.
|
|
|
|
|
if addr.IP.Is4() && addr.IsSingleIP() && tsaddr.IsTailscaleIP(addr.IP) {
|
|
|
|
|
tailAddr = addr.IP.String()
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
sb.AddPeer(key.Public(p.Key), &ipnstate.PeerStatus{
|
|
|
|
|
InNetworkMap: true,
|
|
|
|
|
UserID: p.User,
|
|
|
|
|
TailAddr: tailAddr,
|
|
|
|
|
HostName: p.Hostinfo.Hostname,
|
|
|
|
|
DNSName: p.Name,
|
|
|
|
|
OS: p.Hostinfo.OS,
|
|
|
|
|
KeepAlive: p.KeepAlive,
|
|
|
|
|
Created: p.Created,
|
|
|
|
|
LastSeen: lastSeen,
|
|
|
|
|
ShareeNode: p.Hostinfo.ShareeNode,
|
|
|
|
|
ExitNode: p.StableID != "" && p.StableID == b.prefs.ExitNodeID,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
sb.AddPeer(key.Public(p.Key), &ipnstate.PeerStatus{
|
|
|
|
|
InNetworkMap: true,
|
|
|
|
|
UserID: p.User,
|
|
|
|
|
TailAddr: tailAddr,
|
|
|
|
|
HostName: p.Hostinfo.Hostname,
|
|
|
|
|
DNSName: p.Name,
|
|
|
|
|
OS: p.Hostinfo.OS,
|
|
|
|
|
KeepAlive: p.KeepAlive,
|
|
|
|
|
Created: p.Created,
|
|
|
|
|
LastSeen: lastSeen,
|
|
|
|
|
ShareeNode: p.Hostinfo.ShareeNode,
|
|
|
|
|
ExitNode: p.StableID != "" && p.StableID == b.prefs.ExitNodeID,
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|