Skip to content

Commit

Permalink
#795 / win servers: implement enumerate function
Browse files Browse the repository at this point in the history
  • Loading branch information
giampaolo committed Mar 6, 2016
1 parent 8563d43 commit beca72d
Show file tree
Hide file tree
Showing 7 changed files with 223 additions and 7 deletions.
19 changes: 19 additions & 0 deletions psutil/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,13 @@
from ._psutil_windows import NORMAL_PRIORITY_CLASS # NOQA
from ._psutil_windows import REALTIME_PRIORITY_CLASS # NOQA
from ._pswindows import CONN_DELETE_TCB # NOQA
from ._pswindows import WINSERVICE_STATUS_CONTINUE_PENDING # NOQA
from ._pswindows import WINSERVICE_STATUS_PAUSE_PENDING # NOQA
from ._pswindows import WINSERVICE_STATUS_PAUSED # NOQA
from ._pswindows import WINSERVICE_STATUS_RUNNING # NOQA
from ._pswindows import WINSERVICE_STATUS_START_PENDING # NOQA
from ._pswindows import WINSERVICE_STATUS_STOP_PENDING # NOQA
from ._pswindows import WINSERVICE_STATUS_STOPPED # NOQA

elif OSX:
from . import _psosx as _psplatform
Expand Down Expand Up @@ -2040,6 +2047,18 @@ def test(): # pragma: no cover
pinfo['name'].strip() or '?'))


# =====================================================================
# --- Windows services
# =====================================================================

if WINDOWS:
def win_service_iter():
"""Return a generator yielding a WindowsService instance for all
installed Windows services.
"""
return _psplatform.win_service_iter()


del memoize, division, deprecated_method
if sys.version_info < (3, 0):
del num
Expand Down
25 changes: 24 additions & 1 deletion psutil/_psutil_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <iphlpapi.h>
#include <wtsapi32.h>
#include <ws2tcpip.h>
#include <Winsvc.h>

// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")
Expand All @@ -32,6 +33,7 @@
#include "arch/windows/process_handles.h"
#include "arch/windows/ntextapi.h"
#include "arch/windows/inet_ntop.h"
#include "arch/windows/services.h"

#ifdef __MINGW32__
#include "arch/windows/glpi.h"
Expand Down Expand Up @@ -3241,7 +3243,6 @@ psutil_cpu_stats(PyObject *self, PyObject *args) {
}



// ------------------------ Python init ---------------------------

static PyMethodDef
Expand Down Expand Up @@ -3349,6 +3350,10 @@ PsutilMethods[] = {
{"cpu_stats", psutil_cpu_stats, METH_VARARGS,
"Return NICs stats."},

// --- windows services
{"winservice_enumerate", psutil_winservice_enumerate, METH_VARARGS,
"List all services"},

// --- windows API bindings
{"win32_QueryDosDevice", psutil_win32_QueryDosDevice, METH_VARARGS,
"QueryDosDevice binding"},
Expand Down Expand Up @@ -3435,6 +3440,7 @@ void init_psutil_windows(void)
module, "NORMAL_PRIORITY_CLASS", NORMAL_PRIORITY_CLASS);
PyModule_AddIntConstant(
module, "REALTIME_PRIORITY_CLASS", REALTIME_PRIORITY_CLASS);

// connection status constants
// http://msdn.microsoft.com/en-us/library/cc669305.aspx
PyModule_AddIntConstant(
Expand Down Expand Up @@ -3465,6 +3471,23 @@ void init_psutil_windows(void)
module, "MIB_TCP_STATE_DELETE_TCB", MIB_TCP_STATE_DELETE_TCB);
PyModule_AddIntConstant(
module, "PSUTIL_CONN_NONE", PSUTIL_CONN_NONE);

// service status constants
PyModule_AddIntConstant(
module, "SERVICE_CONTINUE_PENDING", SERVICE_CONTINUE_PENDING);
PyModule_AddIntConstant(
module, "SERVICE_PAUSE_PENDING", SERVICE_PAUSE_PENDING);
PyModule_AddIntConstant(
module, "SERVICE_PAUSED", SERVICE_PAUSED);
PyModule_AddIntConstant(
module, "SERVICE_RUNNING", SERVICE_RUNNING);
PyModule_AddIntConstant(
module, "SERVICE_START_PENDING", SERVICE_START_PENDING);
PyModule_AddIntConstant(
module, "SERVICE_STOP_PENDING", SERVICE_STOP_PENDING);
PyModule_AddIntConstant(
module, "SERVICE_STOPPED", SERVICE_STOPPED);

// ...for internal use in _psutil_windows.py
PyModule_AddIntConstant(
module, "INFINITE", INFINITE);
Expand Down
80 changes: 74 additions & 6 deletions psutil/_pswindows.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,38 @@

# process priority constants, import from __init__.py:
# http://msdn.microsoft.com/en-us/library/ms686219(v=vs.85).aspx
__extra__all__ = ["ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
"HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS",
"NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS",
"CONN_DELETE_TCB",
"AF_LINK",
]
__extra__all__ = [
"win_service_iter",
"ABOVE_NORMAL_PRIORITY_CLASS", "BELOW_NORMAL_PRIORITY_CLASS",
"HIGH_PRIORITY_CLASS", "IDLE_PRIORITY_CLASS",
"NORMAL_PRIORITY_CLASS", "REALTIME_PRIORITY_CLASS",
"CONN_DELETE_TCB",
"AF_LINK",
# service statuses
"WINSERVICE_STATUS_CONTINUE_PENDING",
"WINSERVICE_STATUS_PAUSE_PENDING",
"WINSERVICE_STATUS_PAUSED",
"WINSERVICE_STATUS_RUNNING",
"WINSERVICE_STATUS_START_PENDING",
"WINSERVICE_STATUS_STOP_PENDING",
"WINSERVICE_STATUS_STOPPED",
]

# --- module level constants (gets pushed up to psutil module)

CONN_DELETE_TCB = "DELETE_TCB"
WAIT_TIMEOUT = 0x00000102 # 258 in decimal
ACCESS_DENIED_SET = frozenset([errno.EPERM, errno.EACCES,
cext.ERROR_ACCESS_DENIED])

WINSERVICE_STATUS_CONTINUE_PENDING = "continue-pending"
WINSERVICE_STATUS_PAUSE_PENDING = "pause-pending"
WINSERVICE_STATUS_PAUSED = "paused"
WINSERVICE_STATUS_RUNNING = "running"
WINSERVICE_STATUS_START_PENDING = "start-pending"
WINSERVICE_STATUS_STOP_PENDING = "stop-pending"
WINSERVICE_STATUS_STOPPED = "stopped"

if enum is None:
AF_LINK = -1
else:
Expand All @@ -71,6 +90,16 @@
cext.PSUTIL_CONN_NONE: _common.CONN_NONE,
}

SERVICE_STATUSES = {
cext.SERVICE_CONTINUE_PENDING: WINSERVICE_STATUS_CONTINUE_PENDING,
cext.SERVICE_PAUSE_PENDING: WINSERVICE_STATUS_PAUSE_PENDING,
cext.SERVICE_PAUSED: WINSERVICE_STATUS_PAUSED,
cext.SERVICE_RUNNING: WINSERVICE_STATUS_RUNNING,
cext.SERVICE_START_PENDING: WINSERVICE_STATUS_START_PENDING,
cext.SERVICE_STOP_PENDING: WINSERVICE_STATUS_STOP_PENDING,
cext.SERVICE_STOPPED: WINSERVICE_STATUS_STOPPED,
}

if enum is not None:
class Priority(enum.IntEnum):
ABOVE_NORMAL_PRIORITY_CLASS = ABOVE_NORMAL_PRIORITY_CLASS
Expand Down Expand Up @@ -289,6 +318,45 @@ def users():
ppid_map = cext.ppid_map # not meant to be public


# --- Windows services


class WindowsService(object):
"""Represents an installed Windows service."""

def __init__(self, name, display_name, status):
self._name = name
self._display_name = display_name
self._status = status

def __str__(self):
details = "(name=%s, status=%s)" % (self.name, self.status)
return "%s%s" % (self.__class__.__name__, details)

def __repr__(self):
return "<%s at %s>" % (self.__str__(), id(self))

@property
def name(self):
return self._name

@property
def display_name(self):
return self._display_name

@property
def status(self):
return SERVICE_STATUSES.get(self._status, self._status)


def win_service_iter():
"""Return a list of WindowsService instances."""
for name, display_name, status in cext.winservice_enumerate():
yield WindowsService(name, display_name, status)


# --- decorators

def wrap_exceptions(fun):
"""Decorator which translates bare OSError and WindowsError
exceptions into NoSuchProcess and AccessDenied.
Expand Down
77 changes: 77 additions & 0 deletions psutil/arch/windows/services.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#include <Python.h>
#include <windows.h>
#include <Winsvc.h>

#include "services.h"


/*
* Enumerate all services.
*/
PyObject *
psutil_winservice_enumerate(PyObject *self, PyObject *args) {
ENUM_SERVICE_STATUS *lpService = NULL;
SC_HANDLE sc = NULL;
BOOL ok;
DWORD bytesNeeded = 0;
DWORD srvCount;
DWORD resumeHandle = 0;
DWORD dwBytes = 0;
DWORD i;
PyObject *py_retlist = PyList_New(0);
PyObject *py_tuple = NULL;

if (py_retlist == NULL)
return NULL;

sc = OpenSCManager(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
if (sc == NULL) {
PyErr_SetFromWindowsErr(0);
return NULL;
}

for (;;) {
ok = EnumServicesStatus(
sc, SERVICE_WIN32, SERVICE_STATE_ALL, lpService, dwBytes,
&bytesNeeded, &srvCount, &resumeHandle);
if (ok || (GetLastError() != ERROR_MORE_DATA))
break;
if (lpService)
free(lpService);
dwBytes = bytesNeeded;
lpService = (ENUM_SERVICE_STATUS*)malloc(dwBytes);
}

for (i = 0; i < srvCount; i++) {
py_tuple = Py_BuildValue(
"(ssi)",
lpService[i].lpServiceName, // name
lpService[i].lpDisplayName, // display name
lpService[i].ServiceStatus.dwCurrentState // status
);
if (py_tuple == NULL)
goto error;
if (PyList_Append(py_retlist, py_tuple))
goto error;
Py_DECREF(py_tuple);
}

CloseServiceHandle(sc);
free(lpService);
return py_retlist;

error:
Py_XDECREF(py_tuple);
Py_DECREF(py_retlist);
if (sc != NULL)
CloseServiceHandle(sc);
if (lpService != NULL)
free(lpService);
return NULL;
}
9 changes: 9 additions & 0 deletions psutil/arch/windows/services.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/

#include <Python.h>

PyObject *psutil_winservice_enumerate();
19 changes: 19 additions & 0 deletions psutil/tests/test_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -576,5 +576,24 @@ def test_environ_64(self):
self.assertEquals(e["THINK_OF_A_NUMBER"], str(os.getpid()))


@unittest.skipUnless(WINDOWS, "not a Windows system")
class TestServices(unittest.TestCase):

def test_win_service_iter(self):
statuses = [
psutil.WINSERVICE_STATUS_CONTINUE_PENDING,
psutil.WINSERVICE_STATUS_PAUSE_PENDING,
psutil.WINSERVICE_STATUS_PAUSED,
psutil.WINSERVICE_STATUS_RUNNING,
psutil.WINSERVICE_STATUS_START_PENDING,
psutil.WINSERVICE_STATUS_STOP_PENDING,
psutil.WINSERVICE_STATUS_STOPPED,
]
for serv in psutil.win_service_iter():
self.assertTrue(serv.name)
self.assertTrue(serv.display_name)
self.assertIn(serv.status, statuses)


if __name__ == '__main__':
run_test_module_by_name(__file__)
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ def get_winver():
'psutil/arch/windows/process_handles.c',
'psutil/arch/windows/security.c',
'psutil/arch/windows/inet_ntop.c',
'psutil/arch/windows/services.c',
],
define_macros=[
VERSION_MACRO,
Expand Down

0 comments on commit beca72d

Please sign in to comment.