github.com/mysteriumnetwork/node@v0.0.0-20240516044423-365054f76801/supervisor/install/install_windows.go (about)

     1  /*
     2   * Copyright (C) 2020 The "MysteriumNetwork/node" Authors.
     3   *
     4   * This program is free software: you can redistribute it and/or modify
     5   * it under the terms of the GNU General Public License as published by
     6   * the Free Software Foundation, either version 3 of the License, or
     7   * (at your option) any later version.
     8   *
     9   * This program is distributed in the hope that it will be useful,
    10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    12   * GNU General Public License for more details.
    13   *
    14   * You should have received a copy of the GNU General Public License
    15   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16   */
    17  
    18  package install
    19  
    20  import (
    21  	"errors"
    22  	"fmt"
    23  	"time"
    24  
    25  	"github.com/rs/zerolog/log"
    26  
    27  	"golang.org/x/sys/windows"
    28  	"golang.org/x/sys/windows/svc"
    29  	"golang.org/x/sys/windows/svc/eventlog"
    30  	"golang.org/x/sys/windows/svc/mgr"
    31  )
    32  
    33  const serviceName = "MysteriumVPNSupervisor"
    34  
    35  // Install installs service for Windows. If there is previous service instance
    36  // running it will be first uninstalled before installing new version.
    37  func Install(options Options) error {
    38  	if !options.valid() {
    39  		return errInvalid
    40  	}
    41  	m, err := mgr.Connect()
    42  	if err != nil {
    43  		return fmt.Errorf("could not connect to service manager: %w", err)
    44  	}
    45  	defer m.Disconnect()
    46  
    47  	log.Info().Msg("Checking previous installation")
    48  	if err := uninstallService(m, serviceName); err != nil {
    49  		log.Info().Err(err).Msg("Previous service was not uninstalled")
    50  	} else {
    51  		if err := waitServiceDeleted(m, serviceName); err != nil {
    52  			return fmt.Errorf("could not wait for service to deletion: %w", err)
    53  		}
    54  		log.Info().Msg("Uninstalled previous service")
    55  	}
    56  
    57  	config := mgr.Config{
    58  		ServiceType:  windows.SERVICE_WIN32_OWN_PROCESS,
    59  		StartType:    mgr.StartAutomatic,
    60  		ErrorControl: mgr.ErrorNormal,
    61  		DisplayName:  "MysteriumVPN Supervisor",
    62  		Description:  "Handles network configuration for MysteriumVPN application.",
    63  		Dependencies: []string{"Nsi"},
    64  	}
    65  	if err := installAndStartService(m, serviceName, options, config); err != nil {
    66  		return fmt.Errorf("could not install and run service: %w", err)
    67  	}
    68  	return nil
    69  }
    70  
    71  // Uninstall uninstalls service for Windows.
    72  func Uninstall() error {
    73  	m, err := mgr.Connect()
    74  	if err != nil {
    75  		return fmt.Errorf("could not connect to service manager: %w", err)
    76  	}
    77  	defer m.Disconnect()
    78  
    79  	return uninstallService(m, serviceName)
    80  }
    81  
    82  func installAndStartService(m *mgr.Mgr, name string, options Options, config mgr.Config) error {
    83  	s, err := m.CreateService(name, options.SupervisorPath, config, "-winservice")
    84  	if err != nil {
    85  		return fmt.Errorf("could not create service: %w", err)
    86  	}
    87  	defer s.Close()
    88  
    89  	err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info)
    90  	if err != nil {
    91  		s.Delete()
    92  		return fmt.Errorf("could not configure event logging: %s", err)
    93  	}
    94  
    95  	if err := s.Start(); err != nil {
    96  		return fmt.Errorf("could not start service: %w", err)
    97  	}
    98  	return nil
    99  }
   100  
   101  func uninstallService(m *mgr.Mgr, name string) error {
   102  	s, err := m.OpenService(name)
   103  	if err != nil {
   104  		return fmt.Errorf("skipping uninstall, service %s is not installed", name)
   105  	}
   106  	defer s.Close()
   107  
   108  	// Send stop signal and ignore errors as if service is already stopped it will
   109  	// return error which we don't care about as we just want to delete service anyway.
   110  	s.Control(svc.Stop)
   111  
   112  	err = s.Delete()
   113  	if err != nil {
   114  		return fmt.Errorf("could not mark service for deletion: %w", err)
   115  	}
   116  
   117  	err = eventlog.Remove(name)
   118  	if err != nil {
   119  		return fmt.Errorf("cound not remove event logging: %s", err)
   120  	}
   121  
   122  	return nil
   123  }
   124  
   125  // waitServiceDeleted checks if service is deleted.
   126  // It is considered as deleted if OpenService fails.
   127  func waitServiceDeleted(m *mgr.Mgr, name string) error {
   128  	timeout := time.After(10 * time.Second)
   129  	for {
   130  		select {
   131  		case <-timeout:
   132  			return errors.New("timeout waiting for service deletion")
   133  		case <-time.After(100 * time.Millisecond):
   134  			s, err := m.OpenService(name)
   135  			if err != nil {
   136  				return nil
   137  			}
   138  			s.Close()
   139  		}
   140  	}
   141  }