install_windows.go 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. // Copyright (c) Tailscale Inc & AUTHORS
  2. // SPDX-License-Identifier: BSD-3-Clause
  3. //go:build go1.19
  4. package main
  5. import (
  6. "context"
  7. "errors"
  8. "fmt"
  9. "os"
  10. "time"
  11. "golang.org/x/sys/windows"
  12. "golang.org/x/sys/windows/svc"
  13. "golang.org/x/sys/windows/svc/mgr"
  14. "tailscale.com/cmd/tailscaled/tailscaledhooks"
  15. "tailscale.com/types/logger"
  16. "tailscale.com/util/backoff"
  17. )
  18. func init() {
  19. installSystemDaemon = installSystemDaemonWindows
  20. uninstallSystemDaemon = uninstallSystemDaemonWindows
  21. }
  22. // serviceDependencies lists all system services that tailscaled depends on.
  23. // This list must be kept in sync with the TailscaledDependencies preprocessor
  24. // variable in the installer.
  25. var serviceDependencies = []string{
  26. "Dnscache",
  27. "iphlpsvc",
  28. "netprofm",
  29. "WinHttpAutoProxySvc",
  30. }
  31. func installSystemDaemonWindows(args []string) (err error) {
  32. m, err := mgr.Connect()
  33. if err != nil {
  34. return fmt.Errorf("failed to connect to Windows service manager: %v", err)
  35. }
  36. service, err := m.OpenService(serviceName)
  37. if err == nil {
  38. service.Close()
  39. return fmt.Errorf("service %q is already installed", serviceName)
  40. }
  41. // no such service; proceed to install the service.
  42. exe, err := os.Executable()
  43. if err != nil {
  44. return err
  45. }
  46. c := mgr.Config{
  47. ServiceType: windows.SERVICE_WIN32_OWN_PROCESS,
  48. StartType: mgr.StartAutomatic,
  49. ErrorControl: mgr.ErrorNormal,
  50. Dependencies: serviceDependencies,
  51. DisplayName: serviceName,
  52. Description: "Connects this computer to others on the Tailscale network.",
  53. }
  54. service, err = m.CreateService(serviceName, exe, c)
  55. if err != nil {
  56. return fmt.Errorf("failed to create %q service: %v", serviceName, err)
  57. }
  58. defer service.Close()
  59. // Exponential backoff is often too aggressive, so use (mostly)
  60. // squares instead.
  61. ra := []mgr.RecoveryAction{
  62. {mgr.ServiceRestart, 1 * time.Second},
  63. {mgr.ServiceRestart, 2 * time.Second},
  64. {mgr.ServiceRestart, 4 * time.Second},
  65. {mgr.ServiceRestart, 9 * time.Second},
  66. {mgr.ServiceRestart, 16 * time.Second},
  67. {mgr.ServiceRestart, 25 * time.Second},
  68. {mgr.ServiceRestart, 36 * time.Second},
  69. {mgr.ServiceRestart, 49 * time.Second},
  70. {mgr.ServiceRestart, 64 * time.Second},
  71. }
  72. const resetPeriodSecs = 60
  73. err = service.SetRecoveryActions(ra, resetPeriodSecs)
  74. if err != nil {
  75. return fmt.Errorf("failed to set service recovery actions: %v", err)
  76. }
  77. return nil
  78. }
  79. func uninstallSystemDaemonWindows(args []string) (ret error) {
  80. for _, f := range tailscaledhooks.UninstallSystemDaemonWindows {
  81. f()
  82. }
  83. m, err := mgr.Connect()
  84. if err != nil {
  85. return fmt.Errorf("failed to connect to Windows service manager: %v", err)
  86. }
  87. defer m.Disconnect()
  88. service, err := m.OpenService(serviceName)
  89. if err != nil {
  90. return fmt.Errorf("failed to open %q service: %v", serviceName, err)
  91. }
  92. st, err := service.Query()
  93. if err != nil {
  94. service.Close()
  95. return fmt.Errorf("failed to query service state: %v", err)
  96. }
  97. if st.State != svc.Stopped {
  98. service.Control(svc.Stop)
  99. }
  100. err = service.Delete()
  101. service.Close()
  102. if err != nil {
  103. return fmt.Errorf("failed to delete service: %v", err)
  104. }
  105. bo := backoff.NewBackoff("uninstall", logger.Discard, 30*time.Second)
  106. end := time.Now().Add(15 * time.Second)
  107. for time.Until(end) > 0 {
  108. service, err = m.OpenService(serviceName)
  109. if err != nil {
  110. // service is no longer openable; success!
  111. break
  112. }
  113. service.Close()
  114. bo.BackOff(context.Background(), errors.New("service not deleted"))
  115. }
  116. return nil
  117. }