Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: x/sys/windows/svc/mgr: New function to list the service statuses as well as names #62500

Open
ryharri opened this issue Sep 7, 2023 · 2 comments

Comments

@ryharri
Copy link

ryharri commented Sep 7, 2023

We have a use case where we want to connect to a remote windows server and list all of the services on it, along with their running status.
In x/sys/windows/svc/mgr, ListServices() returns all of the services that exist, and then we have to loop through them calling Query() to get the state. This doesn't perform well, so instead, I've got my own custom function in my code which returns the status along with the service name. It looks like this:

type nameAndState struct {
	name  string
	state int
}

func getServices(m *mgr.Mgr) ([]nameAndState, error) {
	var err error
	var bytesNeeded, servicesReturned uint32
	var buf []byte
	for {
		var p *byte
		if len(buf) > 0 {
			p = &buf[0]
		}
		err = windows.EnumServicesStatusEx(m.Handle, windows.SC_ENUM_PROCESS_INFO,
			windows.SERVICE_WIN32, windows.SERVICE_STATE_ALL,
			p, uint32(len(buf)), &bytesNeeded, &servicesReturned, nil, nil)
		if err == nil {
			break
		}
		if err != syscall.ERROR_MORE_DATA {
			return nil, err
		}
		if bytesNeeded <= uint32(len(buf)) {
			return nil, err
		}
		buf = make([]byte, bytesNeeded)
	}
	if servicesReturned == 0 {
		return nil, nil
	}
	services := (*[1 << 20]windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0]))[:servicesReturned]

	var n []nameAndState
	for _, s := range services {
		name := syscall.UTF16ToString((*[1 << 20]uint16)(unsafe.Pointer(s.ServiceName))[:])
		n = append(n, nameAndState{name: name, state: int(s.ServiceStatusProcess.CurrentState)})
	}

	return n, nil
}

It would be good if this functionality was included in the standard mgr package though. It would have to be a new function for backwards compatibility but it could potentially even return the whole ENUM_SERVICE_STATUS_PROCESS struct as an array to expose everything.

@gopherbot gopherbot added this to the Proposal milestone Sep 7, 2023
@qmuntal
Copy link
Contributor

qmuntal commented Sep 7, 2023

Considering the amount of inputs to EnumServicesStatusEx and the number of properties that it returns (see SERVICE_STATUS_PROCESS), I'm afraid that it makes no sense for windows/svc/mgr to provide specific helpers for each combination of inputs and outputs. For example, why returning the status but not the service type?

You can already get what you want with a few lines of code, and it could be customized further if your requirements change, e.g. you want to filter out some service types.

nit: you could simplify a bit the last part of your code sample by changing it to:

services := unsafe.Slice((*windows.ENUM_SERVICE_STATUS_PROCESS)(unsafe.Pointer(&buf[0])), int(servicesReturned))
var n []nameAndState
for _, s := range services {
	name := windows.UTF16PtrToString(s.ServiceName)
	n = append(n, nameAndState{name: name, state: int(s.ServiceStatusProcess.CurrentState)})
}

@golang/windows

@ryharri
Copy link
Author

ryharri commented Sep 7, 2023

Thanks for the response @qmuntal and for the more readable code.
I agree that it makes no sense to provide helpers for each combination of fields - would you view the proposal more favourably if the suggestion was to keep the existing ListServices function as it is, and just have one more function that returns everything from SERVICE_STATUS_PROCESS? It would be up to the end user which fields they want to use then. The range on "services" should already have access to this data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Incoming
Development

No branches or pull requests

3 participants