This change implements Windows version of install-system-daemon and uninstall-system-daemon subcommands. When running the commands the user will install or remove Tailscale Windows service. Updates #1232 Signed-off-by: Alex Brainman <alex.brainman@gmail.com>main
parent
6fd9e28bd0
commit
7689213aaa
@ -0,0 +1,119 @@ |
||||
// Copyright (c) 2021 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 main |
||||
|
||||
import ( |
||||
"context" |
||||
"errors" |
||||
"fmt" |
||||
"os" |
||||
"time" |
||||
|
||||
"golang.org/x/sys/windows" |
||||
"golang.org/x/sys/windows/svc" |
||||
"golang.org/x/sys/windows/svc/mgr" |
||||
"tailscale.com/logtail/backoff" |
||||
"tailscale.com/types/logger" |
||||
) |
||||
|
||||
func init() { |
||||
installSystemDaemon = installSystemDaemonWindows |
||||
uninstallSystemDaemon = uninstallSystemDaemonWindows |
||||
} |
||||
|
||||
func installSystemDaemonWindows(args []string) (err error) { |
||||
m, err := mgr.Connect() |
||||
if err != nil { |
||||
return fmt.Errorf("failed to connect to Windows service manager: %v", err) |
||||
} |
||||
|
||||
service, err := m.OpenService(serviceName) |
||||
if err == nil { |
||||
service.Close() |
||||
return fmt.Errorf("service %q is already installed", serviceName) |
||||
} |
||||
|
||||
// no such service; proceed to install the service.
|
||||
|
||||
exe, err := os.Executable() |
||||
if err != nil { |
||||
return err |
||||
} |
||||
|
||||
c := mgr.Config{ |
||||
ServiceType: windows.SERVICE_WIN32_OWN_PROCESS, |
||||
StartType: mgr.StartAutomatic, |
||||
ErrorControl: mgr.ErrorNormal, |
||||
DisplayName: serviceName, |
||||
Description: "Connects this computer to others on the Tailscale network.", |
||||
} |
||||
|
||||
service, err = m.CreateService(serviceName, exe, c) |
||||
if err != nil { |
||||
return fmt.Errorf("failed to create %q service: %v", serviceName, err) |
||||
} |
||||
defer service.Close() |
||||
|
||||
// Exponential backoff is often too aggressive, so use (mostly)
|
||||
// squares instead.
|
||||
ra := []mgr.RecoveryAction{ |
||||
{mgr.ServiceRestart, 1 * time.Second}, |
||||
{mgr.ServiceRestart, 2 * time.Second}, |
||||
{mgr.ServiceRestart, 4 * time.Second}, |
||||
{mgr.ServiceRestart, 9 * time.Second}, |
||||
{mgr.ServiceRestart, 16 * time.Second}, |
||||
{mgr.ServiceRestart, 25 * time.Second}, |
||||
{mgr.ServiceRestart, 36 * time.Second}, |
||||
{mgr.ServiceRestart, 49 * time.Second}, |
||||
{mgr.ServiceRestart, 64 * time.Second}, |
||||
} |
||||
const resetPeriodSecs = 60 |
||||
err = service.SetRecoveryActions(ra, resetPeriodSecs) |
||||
if err != nil { |
||||
return fmt.Errorf("failed to set service recovery actions: %v", err) |
||||
} |
||||
|
||||
return nil |
||||
} |
||||
|
||||
func uninstallSystemDaemonWindows(args []string) (ret error) { |
||||
m, err := mgr.Connect() |
||||
if err != nil { |
||||
return fmt.Errorf("failed to connect to Windows service manager: %v", err) |
||||
} |
||||
defer m.Disconnect() |
||||
|
||||
service, err := m.OpenService(serviceName) |
||||
if err != nil { |
||||
return fmt.Errorf("failed to open %q service: %v", serviceName, err) |
||||
} |
||||
|
||||
st, err := service.Query() |
||||
if err != nil { |
||||
service.Close() |
||||
return fmt.Errorf("failed to query service state: %v", err) |
||||
} |
||||
if st.State != svc.Stopped { |
||||
service.Control(svc.Stop) |
||||
} |
||||
err = service.Delete() |
||||
service.Close() |
||||
if err != nil { |
||||
return fmt.Errorf("failed to delete service: %v", err) |
||||
} |
||||
|
||||
bo := backoff.NewBackoff("uninstall", logger.Discard, 30*time.Second) |
||||
end := time.Now().Add(15 * time.Second) |
||||
for time.Until(end) > 0 { |
||||
service, err = m.OpenService(serviceName) |
||||
if err != nil { |
||||
// service is no longer openable; success!
|
||||
break |
||||
} |
||||
service.Close() |
||||
bo.BackOff(context.Background(), errors.New("service not deleted")) |
||||
} |
||||
return nil |
||||
} |
||||
Loading…
Reference in new issue