diff options
author | Julien Lebot <julien.ar.lebot@gmail.com> | 2020-07-05 04:02:03 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-07-05 04:02:03 +0200 |
commit | 54de8823918fc0484223a077eafc917755334b5b (patch) | |
tree | 662af625b081fdb6d6b3f8fe2cb271faf9974a59 | |
parent | 04b40609276c15b59df065659a0a8ce41b316cef (diff) | |
download | psutil-54de8823918fc0484223a077eafc917755334b5b.tar.gz |
Add support for Windows Nano Server (#1768)
-rw-r--r-- | psutil/_psutil_common.c | 11 | ||||
-rw-r--r-- | psutil/_psutil_windows.c | 66 | ||||
-rw-r--r-- | psutil/arch/windows/ntextapi.h | 126 | ||||
-rwxr-xr-x | setup.py | 2 |
4 files changed, 164 insertions, 41 deletions
diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c index d63b4d9c..7a87c3c1 100644 --- a/psutil/_psutil_common.c +++ b/psutil/_psutil_common.c @@ -264,10 +264,6 @@ psutil_loadlibs() { "ntdll.dll", "NtSetInformationProcess"); if (! NtSetInformationProcess) return 1; - WinStationQueryInformationW = psutil_GetProcAddressFromLib( - "winsta.dll", "WinStationQueryInformationW"); - if (! WinStationQueryInformationW) - return 1; NtQueryObject = psutil_GetProcAddressFromLib( "ntdll.dll", "NtQueryObject"); if (! NtQueryObject) @@ -320,6 +316,13 @@ psutil_loadlibs() { // minumum requirement: Win 7 GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib( "kernel32", "GetLogicalProcessorInformationEx"); + // minimum requirements: Windows Server Core + WTSEnumerateSessionsW = psutil_GetProcAddressFromLib( + "wtsapi32.dll", "WTSEnumerateSessionsW"); + WTSQuerySessionInformationW = psutil_GetProcAddressFromLib( + "wtsapi32.dll", "WTSQuerySessionInformationW"); + WTSFreeMemory = psutil_GetProcAddressFromLib( + "wtsapi32.dll", "WTSFreeMemory"); PyErr_Clear(); return 0; diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c index be4759ac..c8b2f383 100644 --- a/psutil/_psutil_windows.c +++ b/psutil/_psutil_windows.c @@ -22,7 +22,6 @@ #include <Psapi.h> // memory_info(), memory_maps() #include <signal.h> #include <tlhelp32.h> // threads(), PROCESSENTRY32 -#include <wtsapi32.h> // users() // Link with Iphlpapi.lib #pragma comment(lib, "IPHLPAPI.lib") @@ -1191,17 +1190,17 @@ psutil_proc_is_suspended(PyObject *self, PyObject *args) { static PyObject * psutil_users(PyObject *self, PyObject *args) { HANDLE hServer = WTS_CURRENT_SERVER_HANDLE; - WCHAR *buffer_user = NULL; - LPTSTR buffer_addr = NULL; - PWTS_SESSION_INFO sessions = NULL; + LPWSTR buffer_user = NULL; + LPWSTR buffer_addr = NULL; + LPWSTR buffer_info = NULL; + PWTS_SESSION_INFOW sessions = NULL; DWORD count; DWORD i; DWORD sessionId; DWORD bytes; PWTS_CLIENT_ADDRESS address; char address_str[50]; - WINSTATION_INFO station_info; - ULONG returnLen; + PWTSINFOW wts_info; PyObject *py_tuple = NULL; PyObject *py_address = NULL; PyObject *py_username = NULL; @@ -1210,8 +1209,21 @@ psutil_users(PyObject *self, PyObject *args) { if (py_retlist == NULL) return NULL; - if (WTSEnumerateSessions(hServer, 0, 1, &sessions, &count) == 0) { - PyErr_SetFromOSErrnoWithSyscall("WTSEnumerateSessions"); + if (WTSEnumerateSessionsW == NULL || + WTSQuerySessionInformationW == NULL || + WTSFreeMemory == NULL) { + // If we don't run in an environment that is a Remote Desktop Services environment + // the Wtsapi32 proc might not be present. + // https://docs.microsoft.com/en-us/windows/win32/termserv/run-time-linking-to-wtsapi32-dll + return py_retlist; + } + + if (WTSEnumerateSessionsW(hServer, 0, 1, &sessions, &count) == 0) { + if (ERROR_CALL_NOT_IMPLEMENTED == GetLastError()) { + // On Windows Nano server, the Wtsapi32 API can be present, but return WinError 120. + return py_retlist; + } + PyErr_SetFromOSErrnoWithSyscall("WTSEnumerateSessionsW"); goto error; } @@ -1223,9 +1235,12 @@ psutil_users(PyObject *self, PyObject *args) { WTSFreeMemory(buffer_user); if (buffer_addr != NULL) WTSFreeMemory(buffer_addr); + if (buffer_info != NULL) + WTSFreeMemory(buffer_info); buffer_user = NULL; buffer_addr = NULL; + buffer_info = NULL; // username bytes = 0; @@ -1239,21 +1254,22 @@ psutil_users(PyObject *self, PyObject *args) { // address bytes = 0; - if (WTSQuerySessionInformation(hServer, sessionId, WTSClientAddress, - &buffer_addr, &bytes) == 0) { - PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformation"); + if (WTSQuerySessionInformationW(hServer, sessionId, WTSClientAddress, + &buffer_addr, &bytes) == 0) { + PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW"); goto error; } address = (PWTS_CLIENT_ADDRESS)buffer_addr; - if (address->AddressFamily == 0) { // AF_INET + if (address->AddressFamily == 2) { // AF_INET == 2 sprintf_s(address_str, _countof(address_str), "%u.%u.%u.%u", - address->Address[0], - address->Address[1], + // The IP address is offset by two bytes from the start of the Address member of the WTS_CLIENT_ADDRESS structure. address->Address[2], - address->Address[3]); + address->Address[3], + address->Address[4], + address->Address[5]); py_address = Py_BuildValue("s", address_str); if (!py_address) goto error; @@ -1263,26 +1279,23 @@ psutil_users(PyObject *self, PyObject *args) { } // login time - if (! WinStationQueryInformationW( - hServer, - sessionId, - WinStationInformation, - &station_info, - sizeof(station_info), - &returnLen)) - { - PyErr_SetFromOSErrnoWithSyscall("WinStationQueryInformationW"); + bytes = 0; + if (WTSQuerySessionInformationW(hServer, sessionId, WTSSessionInfo, + &buffer_info, &bytes) == 0) { + PyErr_SetFromOSErrnoWithSyscall("WTSQuerySessionInformationW"); goto error; } + wts_info = (PWTSINFOW)buffer_info; py_username = PyUnicode_FromWideChar(buffer_user, wcslen(buffer_user)); if (py_username == NULL) goto error; + py_tuple = Py_BuildValue( "OOd", py_username, py_address, - psutil_FiletimeToUnixTime(station_info.ConnectTime) + psutil_LargeIntegerToUnixTime(wts_info->ConnectTime) ); if (!py_tuple) goto error; @@ -1296,6 +1309,7 @@ psutil_users(PyObject *self, PyObject *args) { WTSFreeMemory(sessions); WTSFreeMemory(buffer_user); WTSFreeMemory(buffer_addr); + WTSFreeMemory(buffer_info); return py_retlist; error: @@ -1310,6 +1324,8 @@ error: WTSFreeMemory(buffer_user); if (buffer_addr != NULL) WTSFreeMemory(buffer_addr); + if (buffer_info != NULL) + WTSFreeMemory(buffer_info); return NULL; } diff --git a/psutil/arch/windows/ntextapi.h b/psutil/arch/windows/ntextapi.h index 8cb00430..ea1f4281 100644 --- a/psutil/arch/windows/ntextapi.h +++ b/psutil/arch/windows/ntextapi.h @@ -19,6 +19,12 @@ typedef LONG NTSTATUS; #define STATUS_NOT_FOUND ((NTSTATUS)0xC0000225L) #define STATUS_BUFFER_OVERFLOW ((NTSTATUS)0x80000005L) +// WtsApi32.h +#define WTS_CURRENT_SERVER_HANDLE ((HANDLE)NULL) +#define WINSTATIONNAME_LENGTH 32 +#define DOMAIN_LENGTH 17 +#define USERNAME_LENGTH 20 + // ================================================================ // Enums // ================================================================ @@ -93,6 +99,53 @@ typedef enum _KWAIT_REASON { MaximumWaitReason } KWAIT_REASON, *PKWAIT_REASON; +// users() +typedef enum _WTS_INFO_CLASS { + WTSInitialProgram, + WTSApplicationName, + WTSWorkingDirectory, + WTSOEMId, + WTSSessionId, + WTSUserName, + WTSWinStationName, + WTSDomainName, + WTSConnectState, + WTSClientBuildNumber, + WTSClientName, + WTSClientDirectory, + WTSClientProductId, + WTSClientHardwareId, + WTSClientAddress, + WTSClientDisplay, + WTSClientProtocolType, + WTSIdleTime, + WTSLogonTime, + WTSIncomingBytes, + WTSOutgoingBytes, + WTSIncomingFrames, + WTSOutgoingFrames, + WTSClientInfo, + WTSSessionInfo, + WTSSessionInfoEx, + WTSConfigInfo, + WTSValidationInfo, // Info Class value used to fetch Validation Information through the WTSQuerySessionInformation + WTSSessionAddressV4, + WTSIsRemoteSession +} WTS_INFO_CLASS; + +typedef enum _WTS_CONNECTSTATE_CLASS { + WTSActive, // User logged on to WinStation + WTSConnected, // WinStation connected to client + WTSConnectQuery, // In the process of connecting to client + WTSShadow, // Shadowing another WinStation + WTSDisconnected, // WinStation logged on without client + WTSIdle, // Waiting for client to connect + WTSListen, // WinStation is listening for connection + WTSReset, // WinStation is being reset + WTSDown, // WinStation is down due to error + WTSInit, // WinStation in initialization +} WTS_CONNECTSTATE_CLASS; + // ================================================================ // Structs. // ================================================================ @@ -309,17 +362,42 @@ typedef struct { } RTL_USER_PROCESS_PARAMETERS_, *PRTL_USER_PROCESS_PARAMETERS_; // users() -typedef struct _WINSTATION_INFO { - BYTE Reserved1[72]; - ULONG SessionId; - BYTE Reserved2[4]; - FILETIME ConnectTime; - FILETIME DisconnectTime; - FILETIME LastInputTime; - FILETIME LoginTime; - BYTE Reserved3[1096]; - FILETIME CurrentTime; -} WINSTATION_INFO, *PWINSTATION_INFO; +typedef struct _WTS_SESSION_INFOW { + DWORD SessionId; // session id + LPWSTR pWinStationName; // name of WinStation this session is + // connected to + WTS_CONNECTSTATE_CLASS State; // connection state (see enum) +} WTS_SESSION_INFOW, * PWTS_SESSION_INFOW; + +#define PWTS_SESSION_INFO PWTS_SESSION_INFOW + +typedef struct _WTS_CLIENT_ADDRESS { + DWORD AddressFamily; // AF_INET, AF_INET6, AF_IPX, AF_NETBIOS, AF_UNSPEC + BYTE Address[20]; // client network address +} WTS_CLIENT_ADDRESS, * PWTS_CLIENT_ADDRESS; + +typedef struct _WTSINFOW { + WTS_CONNECTSTATE_CLASS State; // connection state (see enum) + DWORD SessionId; // session id + DWORD IncomingBytes; + DWORD OutgoingBytes; + DWORD IncomingFrames; + DWORD OutgoingFrames; + DWORD IncomingCompressedBytes; + DWORD OutgoingCompressedBytes; + WCHAR WinStationName[WINSTATIONNAME_LENGTH]; + WCHAR Domain[DOMAIN_LENGTH]; + WCHAR UserName[USERNAME_LENGTH + 1];// name of WinStation this session is + // connected to + LARGE_INTEGER ConnectTime; + LARGE_INTEGER DisconnectTime; + LARGE_INTEGER LastInputTime; + LARGE_INTEGER LogonTime; + LARGE_INTEGER CurrentTime; + +} WTSINFOW, * PWTSINFOW; + +#define PWTSINFO PWTSINFOW // cpu_count_phys() #if (_WIN32_WINNT < 0x0601) // Windows < 7 (Vista and XP) @@ -551,6 +629,32 @@ DWORD (CALLBACK *_GetActiveProcessorCount) ( #define GetActiveProcessorCount _GetActiveProcessorCount +BOOL(CALLBACK *_WTSQuerySessionInformationW) ( + HANDLE hServer, + DWORD SessionId, + WTS_INFO_CLASS WTSInfoClass, + LPWSTR* ppBuffer, + DWORD* pBytesReturned + ); + +#define WTSQuerySessionInformationW _WTSQuerySessionInformationW + +BOOL(CALLBACK *_WTSEnumerateSessionsW)( + HANDLE hServer, + DWORD Reserved, + DWORD Version, + PWTS_SESSION_INFO* ppSessionInfo, + DWORD* pCount + ); + +#define WTSEnumerateSessionsW _WTSEnumerateSessionsW + +VOID(CALLBACK *_WTSFreeMemory)( + IN PVOID pMemory + ); + +#define WTSFreeMemory _WTSFreeMemory + ULONGLONG (CALLBACK *_GetTickCount64) ( void); @@ -167,7 +167,7 @@ if WINDOWS: define_macros=macros, libraries=[ "psapi", "kernel32", "advapi32", "shell32", "netapi32", - "wtsapi32", "ws2_32", "PowrProf", "pdh", + "ws2_32", "PowrProf", "pdh", ], # extra_compile_args=["/W 4"], # extra_link_args=["/DEBUG"] |