polling.go 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build !windows && !darwin
  4. package netmon
  5. import (
  6. "bytes"
  7. "errors"
  8. "os"
  9. "runtime"
  10. "sync"
  11. "time"
  12. "tailscale.com/types/logger"
  13. )
  14. func newPollingMon(logf logger.Logf, m *Monitor) (osMon, error) {
  15. return &pollingMon{
  16. logf: logf,
  17. m: m,
  18. stop: make(chan struct{}),
  19. }, nil
  20. }
  21. // pollingMon is a bad but portable implementation of the link monitor
  22. // that works by polling the interface state every 10 seconds, in lieu
  23. // of anything to subscribe to.
  24. type pollingMon struct {
  25. logf logger.Logf
  26. m *Monitor
  27. closeOnce sync.Once
  28. stop chan struct{}
  29. }
  30. func (pm *pollingMon) IsInterestingInterface(iface string) bool {
  31. return true
  32. }
  33. func (pm *pollingMon) Close() error {
  34. pm.closeOnce.Do(func() {
  35. close(pm.stop)
  36. })
  37. return nil
  38. }
  39. func (pm *pollingMon) isCloudRun() bool {
  40. // https: //cloud.google.com/run/docs/reference/container-contract#env-vars
  41. if os.Getenv("K_REVISION") == "" || os.Getenv("K_CONFIGURATION") == "" ||
  42. os.Getenv("K_SERVICE") == "" || os.Getenv("PORT") == "" {
  43. return false
  44. }
  45. vers, err := os.ReadFile("/proc/version")
  46. if err != nil {
  47. pm.logf("Failed to read /proc/version: %v", err)
  48. return false
  49. }
  50. return string(bytes.TrimSpace(vers)) == "Linux version 4.4.0 #1 SMP Sun Jan 10 15:06:54 PST 2016"
  51. }
  52. func (pm *pollingMon) Receive() (message, error) {
  53. d := 10 * time.Second
  54. if runtime.GOOS == "android" {
  55. // We'll have Android notify the link monitor to wake up earlier,
  56. // so this can go very slowly there, to save battery.
  57. // https://github.com/tailscale/tailscale/issues/1427
  58. d = 10 * time.Minute
  59. } else if pm.isCloudRun() {
  60. // Cloud Run routes never change at runtime. the containers are killed within
  61. // 15 minutes by default, set the interval long enough to be effectively infinite.
  62. pm.logf("monitor polling: Cloud Run detected, reduce polling interval to 24h")
  63. d = 24 * time.Hour
  64. }
  65. timer := time.NewTimer(d)
  66. defer timer.Stop()
  67. for {
  68. select {
  69. case <-timer.C:
  70. return unspecifiedMessage{}, nil
  71. case <-pm.stop:
  72. return nil, errors.New("stopped")
  73. }
  74. }
  75. }