1 /*
2  * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved.
3  * Use of this source code is governed by a BSD-style license that can be
4  * found in the LICENSE file.
5  *
6  * Routines common to all platforms.
7  */
8 
9 #include <Python.h>
10 #include "_psutil_common.h"
11 
12 // ====================================================================
13 // --- Global vars
14 // ====================================================================
15 
16 int PSUTIL_DEBUG = 0;
17 int PSUTIL_TESTING = 0;
18 // PSUTIL_CONN_NONE
19 
20 
21 // ====================================================================
22 // --- Backward compatibility with missing Python.h APIs
23 // ====================================================================
24 
25 // PyPy on Windows
26 #if defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION)
27 #if !defined(PyErr_SetFromWindowsErrWithFilename)
28 PyObject *
PyErr_SetFromWindowsErrWithFilename(int winerr,const char * filename)29 PyErr_SetFromWindowsErrWithFilename(int winerr, const char *filename) {
30     PyObject *py_exc = NULL;
31     PyObject *py_winerr = NULL;
32 
33     if (winerr == 0)
34         winerr = GetLastError();
35     if (filename == NULL) {
36         py_exc = PyObject_CallFunction(PyExc_OSError, "(is)", winerr,
37                                        strerror(winerr));
38     }
39     else {
40         py_exc = PyObject_CallFunction(PyExc_OSError, "(iss)", winerr,
41                                        strerror(winerr), filename);
42     }
43     if (py_exc == NULL)
44         return NULL;
45 
46     py_winerr = Py_BuildValue("i", winerr);
47     if (py_winerr == NULL)
48         goto error;
49     if (PyObject_SetAttrString(py_exc, "winerror", py_winerr) != 0)
50         goto error;
51     PyErr_SetObject(PyExc_OSError, py_exc);
52     Py_XDECREF(py_exc);
53     return NULL;
54 
55 error:
56     Py_XDECREF(py_exc);
57     Py_XDECREF(py_winerr);
58     return NULL;
59 }
60 #endif  // !defined(PyErr_SetFromWindowsErrWithFilename)
61 
62 
63 // PyPy 2.7
64 #if !defined(PyErr_SetFromWindowsErr)
65 PyObject *
PyErr_SetFromWindowsErr(int winerr)66 PyErr_SetFromWindowsErr(int winerr) {
67     return PyErr_SetFromWindowsErrWithFilename(winerr, "");
68 }
69 #endif  // !defined(PyErr_SetFromWindowsErr)
70 #endif  // defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION)
71 
72 
73 // ====================================================================
74 // --- Custom exceptions
75 // ====================================================================
76 
77 /*
78  * Same as PyErr_SetFromErrno(0) but adds the syscall to the exception
79  * message.
80  */
81 PyObject *
PyErr_SetFromOSErrnoWithSyscall(const char * syscall)82 PyErr_SetFromOSErrnoWithSyscall(const char *syscall) {
83     char fullmsg[1024];
84 
85 #ifdef PSUTIL_WINDOWS
86     sprintf(fullmsg, "(originated from %s)", syscall);
87     PyErr_SetFromWindowsErrWithFilename(GetLastError(), fullmsg);
88 #else
89     PyObject *exc;
90     sprintf(fullmsg, "%s (originated from %s)", strerror(errno), syscall);
91     exc = PyObject_CallFunction(PyExc_OSError, "(is)", errno, fullmsg);
92     PyErr_SetObject(PyExc_OSError, exc);
93     Py_XDECREF(exc);
94 #endif
95     return NULL;
96 }
97 
98 
99 /*
100  * Set OSError(errno=ESRCH, strerror="No such process (originated from")
101  * Python exception.
102  */
103 PyObject *
NoSuchProcess(const char * syscall)104 NoSuchProcess(const char *syscall) {
105     PyObject *exc;
106     char msg[1024];
107 
108     sprintf(msg, "assume no such process (originated from %s)", syscall);
109     exc = PyObject_CallFunction(PyExc_OSError, "(is)", ESRCH, msg);
110     PyErr_SetObject(PyExc_OSError, exc);
111     Py_XDECREF(exc);
112     return NULL;
113 }
114 
115 
116 /*
117  * Set OSError(errno=EACCES, strerror="Permission denied" (originated from ...)
118  * Python exception.
119  */
120 PyObject *
AccessDenied(const char * syscall)121 AccessDenied(const char *syscall) {
122     PyObject *exc;
123     char msg[1024];
124 
125     sprintf(msg, "assume access denied (originated from %s)", syscall);
126     exc = PyObject_CallFunction(PyExc_OSError, "(is)", EACCES, msg);
127     PyErr_SetObject(PyExc_OSError, exc);
128     Py_XDECREF(exc);
129     return NULL;
130 }
131 
132 
133 // ====================================================================
134 // --- Global utils
135 // ====================================================================
136 
137 /*
138  * Enable testing mode. This has the same effect as setting PSUTIL_TESTING
139  * env var. This dual method exists because updating os.environ on
140  * Windows has no effect. Called on unit tests setup.
141  */
142 PyObject *
psutil_set_testing(PyObject * self,PyObject * args)143 psutil_set_testing(PyObject *self, PyObject *args) {
144     PSUTIL_TESTING = 1;
145     PSUTIL_DEBUG = 1;
146     Py_INCREF(Py_None);
147     return Py_None;
148 }
149 
150 
151 /*
152  * Print a debug message on stderr. No-op if PSUTIL_DEBUG env var is not set.
153  */
154 void
psutil_debug(const char * format,...)155 psutil_debug(const char* format, ...) {
156     va_list argptr;
157     if (PSUTIL_DEBUG) {
158         va_start(argptr, format);
159         fprintf(stderr, "psutil-debug> ");
160         vfprintf(stderr, format, argptr);
161         fprintf(stderr, "\n");
162         va_end(argptr);
163     }
164 }
165 
166 
167 /*
168  * Called on module import on all platforms.
169  */
170 int
psutil_setup(void)171 psutil_setup(void) {
172     if (getenv("PSUTIL_DEBUG") != NULL)
173         PSUTIL_DEBUG = 1;
174     if (getenv("PSUTIL_TESTING") != NULL)
175         PSUTIL_TESTING = 1;
176     return 0;
177 }
178 
179 
180 // ============================================================================
181 // Utility functions (BSD)
182 // ============================================================================
183 
184 #if defined(PSUTIL_FREEBSD) || defined(PSUTIL_OPENBSD) || defined(PSUTIL_NETBSD) || defined(PSUTIL_DRAGONFLY)
185 void
convert_kvm_err(const char * syscall,char * errbuf)186 convert_kvm_err(const char *syscall, char *errbuf) {
187     char fullmsg[8192];
188 
189     sprintf(fullmsg, "(originated from %s: %s)", syscall, errbuf);
190     if (strstr(errbuf, "Permission denied") != NULL)
191         AccessDenied(fullmsg);
192     else if (strstr(errbuf, "Operation not permitted") != NULL)
193         AccessDenied(fullmsg);
194     else
195         PyErr_Format(PyExc_RuntimeError, fullmsg);
196 }
197 #endif
198 
199 
200 // ====================================================================
201 // --- Windows
202 // ====================================================================
203 
204 #ifdef PSUTIL_WINDOWS
205 #include <windows.h>
206 
207 // Needed to make these globally visible.
208 int PSUTIL_WINVER;
209 SYSTEM_INFO          PSUTIL_SYSTEM_INFO;
210 CRITICAL_SECTION     PSUTIL_CRITICAL_SECTION;
211 
212 
213 // A wrapper around GetModuleHandle and GetProcAddress.
214 PVOID
psutil_GetProcAddress(LPCSTR libname,LPCSTR procname)215 psutil_GetProcAddress(LPCSTR libname, LPCSTR procname) {
216     HMODULE mod;
217     FARPROC addr;
218 
219     if ((mod = GetModuleHandleA(libname)) == NULL) {
220         PyErr_SetFromWindowsErrWithFilename(0, libname);
221         return NULL;
222     }
223     if ((addr = GetProcAddress(mod, procname)) == NULL) {
224         PyErr_SetFromWindowsErrWithFilename(0, procname);
225         return NULL;
226     }
227     return addr;
228 }
229 
230 
231 // A wrapper around LoadLibrary and GetProcAddress.
232 PVOID
psutil_GetProcAddressFromLib(LPCSTR libname,LPCSTR procname)233 psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname) {
234     HMODULE mod;
235     FARPROC addr;
236 
237     Py_BEGIN_ALLOW_THREADS
238     mod = LoadLibraryA(libname);
239     Py_END_ALLOW_THREADS
240     if (mod  == NULL) {
241         PyErr_SetFromWindowsErrWithFilename(0, libname);
242         return NULL;
243     }
244     if ((addr = GetProcAddress(mod, procname)) == NULL) {
245         PyErr_SetFromWindowsErrWithFilename(0, procname);
246         FreeLibrary(mod);
247         return NULL;
248     }
249     // Causes crash.
250     // FreeLibrary(mod);
251     return addr;
252 }
253 
254 
255 /*
256  * Convert a NTSTATUS value to a Win32 error code and set the proper
257  * Python exception.
258  */
259 PVOID
psutil_SetFromNTStatusErr(NTSTATUS Status,const char * syscall)260 psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall) {
261     ULONG err;
262     char fullmsg[1024];
263 
264     if (NT_NTWIN32(Status))
265         err = WIN32_FROM_NTSTATUS(Status);
266     else
267         err = RtlNtStatusToDosErrorNoTeb(Status);
268     // if (GetLastError() != 0)
269     //     err = GetLastError();
270     sprintf(fullmsg, "(originated from %s)", syscall);
271     return PyErr_SetFromWindowsErrWithFilename(err, fullmsg);
272 }
273 
274 
275 static int
psutil_loadlibs()276 psutil_loadlibs() {
277     // --- Mandatory
278     NtQuerySystemInformation = psutil_GetProcAddressFromLib(
279         "ntdll.dll", "NtQuerySystemInformation");
280     if (! NtQuerySystemInformation)
281         return 1;
282     NtQueryInformationProcess = psutil_GetProcAddress(
283         "ntdll.dll", "NtQueryInformationProcess");
284     if (! NtQueryInformationProcess)
285         return 1;
286     NtSetInformationProcess = psutil_GetProcAddress(
287         "ntdll.dll", "NtSetInformationProcess");
288     if (! NtSetInformationProcess)
289         return 1;
290     NtQueryObject = psutil_GetProcAddressFromLib(
291         "ntdll.dll", "NtQueryObject");
292     if (! NtQueryObject)
293         return 1;
294     RtlIpv4AddressToStringA = psutil_GetProcAddressFromLib(
295         "ntdll.dll", "RtlIpv4AddressToStringA");
296     if (! RtlIpv4AddressToStringA)
297         return 1;
298     GetExtendedTcpTable = psutil_GetProcAddressFromLib(
299         "iphlpapi.dll", "GetExtendedTcpTable");
300     if (! GetExtendedTcpTable)
301         return 1;
302     GetExtendedUdpTable = psutil_GetProcAddressFromLib(
303         "iphlpapi.dll", "GetExtendedUdpTable");
304     if (! GetExtendedUdpTable)
305         return 1;
306     RtlGetVersion = psutil_GetProcAddressFromLib(
307         "ntdll.dll", "RtlGetVersion");
308     if (! RtlGetVersion)
309         return 1;
310     NtSuspendProcess = psutil_GetProcAddressFromLib(
311         "ntdll", "NtSuspendProcess");
312     if (! NtSuspendProcess)
313         return 1;
314     NtResumeProcess = psutil_GetProcAddressFromLib(
315         "ntdll", "NtResumeProcess");
316     if (! NtResumeProcess)
317         return 1;
318     NtQueryVirtualMemory = psutil_GetProcAddressFromLib(
319         "ntdll", "NtQueryVirtualMemory");
320     if (! NtQueryVirtualMemory)
321         return 1;
322     RtlNtStatusToDosErrorNoTeb = psutil_GetProcAddressFromLib(
323         "ntdll", "RtlNtStatusToDosErrorNoTeb");
324     if (! RtlNtStatusToDosErrorNoTeb)
325         return 1;
326     GetTickCount64 = psutil_GetProcAddress(
327         "kernel32", "GetTickCount64");
328     if (! GetTickCount64)
329         return 1;
330     RtlIpv6AddressToStringA = psutil_GetProcAddressFromLib(
331         "ntdll.dll", "RtlIpv6AddressToStringA");
332     if (! RtlIpv6AddressToStringA)
333         return 1;
334 
335     // --- Optional
336     // minimum requirement: Win 7
337     GetActiveProcessorCount = psutil_GetProcAddress(
338         "kernel32", "GetActiveProcessorCount");
339     // minumum requirement: Win 7
340     GetLogicalProcessorInformationEx = psutil_GetProcAddressFromLib(
341         "kernel32", "GetLogicalProcessorInformationEx");
342     // minimum requirements: Windows Server Core
343     WTSEnumerateSessionsW = psutil_GetProcAddressFromLib(
344         "wtsapi32.dll", "WTSEnumerateSessionsW");
345     WTSQuerySessionInformationW = psutil_GetProcAddressFromLib(
346         "wtsapi32.dll", "WTSQuerySessionInformationW");
347     WTSFreeMemory = psutil_GetProcAddressFromLib(
348         "wtsapi32.dll", "WTSFreeMemory");
349 
350     PyErr_Clear();
351     return 0;
352 }
353 
354 
355 static int
psutil_set_winver()356 psutil_set_winver() {
357     RTL_OSVERSIONINFOEXW versionInfo;
358     ULONG maj;
359     ULONG min;
360 
361     versionInfo.dwOSVersionInfoSize = sizeof(RTL_OSVERSIONINFOEXW);
362     memset(&versionInfo, 0, sizeof(RTL_OSVERSIONINFOEXW));
363     RtlGetVersion((PRTL_OSVERSIONINFOW)&versionInfo);
364     maj = versionInfo.dwMajorVersion;
365     min = versionInfo.dwMinorVersion;
366     if (maj == 6 && min == 0)
367         PSUTIL_WINVER = PSUTIL_WINDOWS_VISTA;  // or Server 2008
368     else if (maj == 6 && min == 1)
369         PSUTIL_WINVER = PSUTIL_WINDOWS_7;
370     else if (maj == 6 && min == 2)
371         PSUTIL_WINVER = PSUTIL_WINDOWS_8;
372     else if (maj == 6 && min == 3)
373         PSUTIL_WINVER = PSUTIL_WINDOWS_8_1;
374     else if (maj == 10 && min == 0)
375         PSUTIL_WINVER = PSUTIL_WINDOWS_10;
376     else
377         PSUTIL_WINVER = PSUTIL_WINDOWS_NEW;
378     return 0;
379 }
380 
381 
382 int
psutil_load_globals()383 psutil_load_globals() {
384     if (psutil_loadlibs() != 0)
385         return 1;
386     if (psutil_set_winver() != 0)
387         return 1;
388     GetSystemInfo(&PSUTIL_SYSTEM_INFO);
389     InitializeCriticalSection(&PSUTIL_CRITICAL_SECTION);
390     return 0;
391 }
392 
393 
394 /*
395  * Convert the hi and lo parts of a FILETIME structure or a LARGE_INTEGER
396  * to a UNIX time.
397  * A FILETIME contains a 64-bit value representing the number of
398  * 100-nanosecond intervals since January 1, 1601 (UTC).
399  * A UNIX time is the number of seconds that have elapsed since the
400  * UNIX epoch, that is the time 00:00:00 UTC on 1 January 1970.
401  */
402 static double
_to_unix_time(ULONGLONG hiPart,ULONGLONG loPart)403 _to_unix_time(ULONGLONG hiPart, ULONGLONG loPart) {
404     ULONGLONG ret;
405 
406     // 100 nanosecond intervals since January 1, 1601.
407     ret = hiPart << 32;
408     ret += loPart;
409     // Change starting time to the Epoch (00:00:00 UTC, January 1, 1970).
410     ret -= 116444736000000000ull;
411     // Convert nano secs to secs.
412     return (double) ret / 10000000ull;
413 }
414 
415 
416 double
psutil_FiletimeToUnixTime(FILETIME ft)417 psutil_FiletimeToUnixTime(FILETIME ft) {
418     return _to_unix_time((ULONGLONG)ft.dwHighDateTime,
419                          (ULONGLONG)ft.dwLowDateTime);
420 }
421 
422 
423 double
psutil_LargeIntegerToUnixTime(LARGE_INTEGER li)424 psutil_LargeIntegerToUnixTime(LARGE_INTEGER li) {
425     return _to_unix_time((ULONGLONG)li.HighPart,
426                          (ULONGLONG)li.LowPart);
427 }
428 #endif  // PSUTIL_WINDOWS
429