summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--psutil/_psutil_common.c11
-rw-r--r--psutil/_psutil_windows.c66
-rw-r--r--psutil/arch/windows/ntextapi.h126
-rwxr-xr-xsetup.py2
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);
diff --git a/setup.py b/setup.py
index 3c442302..893eb46b 100755
--- a/setup.py
+++ b/setup.py
@@ -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"]