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

     1  package osdetail
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"errors"
     7  	"fmt"
     8  	"os/exec"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  )
    13  
    14  var (
    15  	serviceNames     map[int32][]string
    16  	serviceNamesLock sync.Mutex
    17  )
    18  
    19  // Errors
    20  var (
    21  	ErrServiceNotFound = errors.New("no service with the given PID was found")
    22  )
    23  
    24  // GetServiceNames returns all service names assosicated with a svchost.exe process on Windows.
    25  func GetServiceNames(pid int32) ([]string, error) {
    26  	serviceNamesLock.Lock()
    27  	defer serviceNamesLock.Unlock()
    28  
    29  	if serviceNames != nil {
    30  		names, ok := serviceNames[pid]
    31  		if ok {
    32  			return names, nil
    33  		}
    34  	}
    35  
    36  	serviceNames, err := GetAllServiceNames()
    37  	if err != nil {
    38  		return nil, err
    39  	}
    40  
    41  	names, ok := serviceNames[pid]
    42  	if ok {
    43  		return names, nil
    44  	}
    45  
    46  	return nil, ErrServiceNotFound
    47  }
    48  
    49  // GetAllServiceNames returns a list of service names assosicated with svchost.exe processes on Windows.
    50  func GetAllServiceNames() (map[int32][]string, error) {
    51  	output, err := exec.Command("tasklist", "/svc", "/fi", "imagename eq svchost.exe").Output()
    52  	if err != nil {
    53  		return nil, fmt.Errorf("failed to get svchost tasklist: %s", err)
    54  	}
    55  
    56  	// file scanner
    57  	scanner := bufio.NewScanner(bytes.NewReader(output))
    58  	scanner.Split(bufio.ScanLines)
    59  
    60  	// skip output header
    61  	for scanner.Scan() {
    62  		if strings.HasPrefix(scanner.Text(), "=") {
    63  			break
    64  		}
    65  	}
    66  
    67  	var (
    68  		pid        int32
    69  		services   []string
    70  		collection = make(map[int32][]string)
    71  	)
    72  
    73  	for scanner.Scan() {
    74  		// get fields of line
    75  		fields := strings.Fields(scanner.Text())
    76  
    77  		// check fields length
    78  		if len(fields) == 0 {
    79  			continue
    80  		}
    81  
    82  		// new entry
    83  		if fields[0] == "svchost.exe" {
    84  			// save old entry
    85  			if pid != 0 {
    86  				collection[pid] = services
    87  			}
    88  			// reset PID
    89  			pid = 0
    90  			services = make([]string, 0, len(fields))
    91  
    92  			// check fields length
    93  			if len(fields) < 3 {
    94  				continue
    95  			}
    96  
    97  			// get pid
    98  			i, err := strconv.ParseInt(fields[1], 10, 32)
    99  			if err != nil {
   100  				continue
   101  			}
   102  			pid = int32(i)
   103  
   104  			// skip used fields
   105  			fields = fields[2:]
   106  		}
   107  
   108  		// add service names
   109  		for _, field := range fields {
   110  			services = append(services, strings.Trim(strings.TrimSpace(field), ","))
   111  		}
   112  	}
   113  
   114  	if pid != 0 {
   115  		// save last entry
   116  		collection[pid] = services
   117  	}
   118  
   119  	return collection, nil
   120  }