github.com/safing/portbase@v0.19.5/utils/osdetail/service_windows.go (about)

     1  package osdetail
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os/exec"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  // Service Status
    12  const (
    13  	StatusUnknown uint8 = iota
    14  	StatusRunningStoppable
    15  	StatusRunningNotStoppable
    16  	StatusStartPending
    17  	StatusStopPending
    18  	StatusStopped
    19  )
    20  
    21  // Exported errors
    22  var (
    23  	ErrServiceNotStoppable = errors.New("the service is not stoppable")
    24  )
    25  
    26  // GetServiceStatus returns the current status of a Windows Service (limited implementation).
    27  func GetServiceStatus(name string) (status uint8, err error) {
    28  
    29  	output, err := exec.Command("sc", "query", name).Output()
    30  	if err != nil {
    31  		return StatusUnknown, fmt.Errorf("failed to query service: %s", err)
    32  	}
    33  	outputString := string(output)
    34  
    35  	switch {
    36  	case strings.Contains(outputString, "RUNNING"):
    37  		if strings.Contains(outputString, "NOT_STOPPABLE") {
    38  			return StatusRunningNotStoppable, nil
    39  		}
    40  		return StatusRunningStoppable, nil
    41  	case strings.Contains(outputString, "STOP_PENDING"):
    42  		return StatusStopPending, nil
    43  	case strings.Contains(outputString, "STOPPED"):
    44  		return StatusStopped, nil
    45  	case strings.Contains(outputString, "START_PENDING"):
    46  		return StatusStopPending, nil
    47  	}
    48  
    49  	return StatusUnknown, errors.New("unknown service status")
    50  }
    51  
    52  // StopService stops a Windows Service.
    53  func StopService(name string) (err error) {
    54  	pendingCnt := 0
    55  	for {
    56  
    57  		// get status
    58  		status, err := GetServiceStatus(name)
    59  		if err != nil {
    60  			return err
    61  		}
    62  
    63  		switch status {
    64  		case StatusRunningStoppable:
    65  			err := exec.Command("sc", "stop", name).Run()
    66  			if err != nil {
    67  				return fmt.Errorf("failed to stop service: %s", err)
    68  			}
    69  		case StatusRunningNotStoppable:
    70  			return ErrServiceNotStoppable
    71  		case StatusStartPending, StatusStopPending:
    72  			pendingCnt++
    73  			if pendingCnt > 50 {
    74  				return errors.New("service stuck in pending status (5s)")
    75  			}
    76  		case StatusStopped:
    77  			return nil
    78  		}
    79  
    80  		time.Sleep(100 * time.Millisecond)
    81  	}
    82  }
    83  
    84  // SartService starts a Windows Service.
    85  func SartService(name string) (err error) {
    86  	pendingCnt := 0
    87  	for {
    88  
    89  		// get status
    90  		status, err := GetServiceStatus(name)
    91  		if err != nil {
    92  			return err
    93  		}
    94  
    95  		switch status {
    96  		case StatusRunningStoppable, StatusRunningNotStoppable:
    97  			return nil
    98  		case StatusStartPending, StatusStopPending:
    99  			pendingCnt++
   100  			if pendingCnt > 50 {
   101  				return errors.New("service stuck in pending status (5s)")
   102  			}
   103  		case StatusStopped:
   104  			err := exec.Command("sc", "start", name).Run()
   105  			if err != nil {
   106  				return fmt.Errorf("failed to stop service: %s", err)
   107  			}
   108  		}
   109  
   110  		time.Sleep(100 * time.Millisecond)
   111  	}
   112  }