<commit_before>/*
 * $Id$
 *
 * Copyright (c) 2009, <NAME>, <NAME>'. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Windows platform-specific module methods for _psutil_mswindows
 */

// Fixes clash between winsock2.h and windows.h
#define WIN32_LEAN_AND_MEAN

#include <Python.h>
#include <windows.h>
#include <Psapi.h>
#include <time.h>
#include <lm.h>
#include <WinIoCtl.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <wtsapi32.h>

// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")

#include "_psutil_mswindows.h"
#include "_psutil_common.h"
#include "arch/mswindows/security.h"
#include "arch/mswindows/process_info.h"
#include "arch/mswindows/process_handles.h"
#include "arch/mswindows/ntextapi.h"


/*
 * Return a Python float representing the system uptime expressed in seconds
 * since the epoch.
 */
static PyObject*
get_system_uptime(PyObject* self, PyObject* args)
{
    double uptime;
    time_t pt;
    FILETIME fileTime;
    long long ll;

    GetSystemTimeAsFileTime(&fileTime);

    /*
    HUGE thanks to:
    http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry

    This function converts the FILETIME structure to the 32 bit
    Unix time structure.
    The time_t is a 32-bit value for the number of seconds since
    January 1, 1970. A FILETIME is a 64-bit for the number of
    100-nanosecond periods since January 1, 1601. Convert by
    subtracting the number of 100-nanosecond period betwee 01-01-1970
    and 01-01-1601, from time_t the divide by 1e+7 to get to the same
    base granularity.
    */
    ll = (((LONGLONG)(fileTime.dwHighDateTime)) << 32) + fileTime.dwLowDateTime;
    pt = (time_t)((ll - 116444736000000000ull) / 10000000ull);

    // XXX - By using GetTickCount() time will wrap around to zero if the
    // system is run continuously for 49.7 days.
    uptime = GetTickCount() / 1000.00f;
    return Py_BuildValue("d", (double)pt - uptime);
}


/*
 * Return 1 if PID exists in the current process list, else 0.
 */
static PyObject*
pid_exists(PyObject* self, PyObject* args)
{
    long pid;
    int status;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    status = pid_is_running(pid);
    if (-1 == status) {
        return NULL; // exception raised in pid_is_running()
    }
    return PyBool_FromLong(status);
}


/*
 * Return a Python list of all the PIDs running on the system.
 */
static PyObject*
get_pid_list(PyObject* self, PyObject* args)
{
    DWORD *proclist = NULL;
    DWORD numberOfReturnedPIDs;
    DWORD i;
    PyObject* pid = NULL;
    PyObject* retlist = PyList_New(0);

    proclist = get_pids(&numberOfReturnedPIDs);
    if (NULL == proclist) {
        Py_DECREF(retlist);
        return NULL;
    }

    for (i = 0; i < numberOfReturnedPIDs; i++) {
        pid = Py_BuildValue("I", proclist[i]);
        PyList_Append(retlist, pid);
        Py_XDECREF(pid);
    }

    // free C array allocated for PIDs
    free(proclist);

    return retlist;
}


/*
 * Kill a process given its PID.
 */
static PyObject*
kill_process(PyObject* self, PyObject* args)
{
    HANDLE hProcess;
    long pid;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }
    if (pid == 0) {
        return AccessDenied();
    }

    hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
    if (hProcess == NULL) {
        if (GetLastError() == ERROR_INVALID_PARAMETER) {
            // see http://code.google.com/p/psutil/issues/detail?id=24
            NoSuchProcess();
        }
        else {
            PyErr_SetFromWindowsErr(0);
        }
        return NULL;
    }

    // kill the process
    if (! TerminateProcess(hProcess, 0)) {
        PyErr_SetFromWindowsErr(0);
        CloseHandle(hProcess);
        return NULL;
    }

    CloseHandle(hProcess);
    Py_INCREF(Py_None);
    return Py_None;
}


/*
 * Wait for process to terminate and return its exit code.
 */
static PyObject*
process_wait(PyObject* self, PyObject* args)
{
    HANDLE hProcess;
    DWORD ExitCode;
    DWORD retVal;
    long pid;
    long timeout;

    if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) {
        return NULL;
    }
    if (pid == 0) {
        return AccessDenied();
    }

    hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (hProcess == NULL) {
        if (GetLastError() == ERROR_INVALID_PARAMETER) {
            // no such process; we do not want to raise NSP but
            // return None instead.
            Py_INCREF(Py_None);
            return Py_None;
        }
        else {
            PyErr_SetFromWindowsErr(0);
            return NULL;
        }
    }

    // wait until the process has terminated
    Py_BEGIN_ALLOW_THREADS
    retVal = WaitForSingleObject(hProcess, timeout);
    Py_END_ALLOW_THREADS

    if (retVal == WAIT_FAILED) {
        CloseHandle(hProcess);
        return PyErr_SetFromWindowsErr(GetLastError());
    }
    if (retVal == WAIT_TIMEOUT) {
        CloseHandle(hProcess);
        return Py_BuildValue("l", WAIT_TIMEOUT);
    }

    // get the exit code; note: subprocess module (erroneously?) uses
    // what returned by WaitForSingleObject
    if (GetExitCodeProcess(hProcess, &ExitCode) == 0) {
        CloseHandle(hProcess);
        return PyErr_SetFromWindowsErr(GetLastError());
    }
    CloseHandle(hProcess);
#if PY_MAJOR_VERSION >= 3
    return PyLong_FromLong((long) ExitCode);
#else
    return PyInt_FromLong((long) ExitCode);
#endif
}


/*
 * Return a Python tuple (user_time, kernel_time)
 */
static PyObject*
get_process_cpu_times(PyObject* self, PyObject* args)
{
    long        pid;
    HANDLE      hProcess;
    FILETIME    ftCreate, ftExit, ftKernel, ftUser;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    // special case for PID 0
    if (0 == pid){
       return Py_BuildValue("(dd)", 0.0, 0.0);
    }

    hProcess = handle_from_pid(pid);
    if (hProcess == NULL) {
        return NULL;
    }

    if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
        CloseHandle(hProcess);
        if (GetLastError() == ERROR_ACCESS_DENIED) {
            // usually means the process has died so we throw a NoSuchProcess
            // here
            return NoSuchProcess();
        }
        else {
            PyErr_SetFromWindowsErr(0);
            return NULL;
        }
    }

    CloseHandle(hProcess);

    /*
    user and kernel times are represented as a FILETIME structure wich contains
    a 64-bit value representing the number of 100-nanosecond intervals since
    January 1, 1601 (UTC).
    http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx

    To convert it into a float representing the seconds that the process has
    executed in user/kernel mode I borrowed the code below from Python's
    Modules/posixmodule.c
    */

    return Py_BuildValue(
        "(dd)",
        (double)(ftUser.dwHighDateTime*429.4967296 + \
                 ftUser.dwLowDateTime*1e-7),
        (double)(ftKernel.dwHighDateTime*429.4967296 + \
                 ftKernel.dwLowDateTime*1e-7)
        );
}


/*
 * Return a Python float indicating the process create time expressed in
 * seconds since the epoch.
 */
static PyObject*
get_process_create_time(PyObject* self, PyObject* args)
{
    long        pid;
    long long   unix_time;
    DWORD       exitCode;
    HANDLE      hProcess;
    BOOL WINAPI ret;
    FILETIME    ftCreate, ftExit, ftKernel, ftUser;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    // special case for PIDs 0 and 4
    if ( (0 == pid) || (4 == pid) ){
       return Py_BuildValue("d", 0.0);
    }

    hProcess = handle_from_pid(pid);
    if (hProcess == NULL) {
        return NULL;
    }

    if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
        CloseHandle(hProcess);
        if (GetLastError() == ERROR_ACCESS_DENIED) {
            // usually means the process has died so we throw a NoSuchProcess here
            return NoSuchProcess();
        }
        else {
            PyErr_SetFromWindowsErr(0);
            return NULL;
        }
    }

    // Make sure the process is not gone as OpenProcess alone seems to be
    // unreliable in doing so (it seems a previous call to p.wait() makes
    // it unreliable).
    // This check is important as creation time is used to make sure the
    // process is still running.
    ret = GetExitCodeProcess(hProcess, &exitCode);
    CloseHandle(hProcess);
    if (ret != 0) {
        if (exitCode != STILL_ACTIVE) {
            return NoSuchProcess();
        }
    }
    else {
        // Ignore access denied as it means the process is still alive.
        // For all other errors, we want an exception.
        if (GetLastError() != ERROR_ACCESS_DENIED) {
            PyErr_SetFromWindowsErr(0);
            return NULL;
        }
    }

    /*
    Convert the FILETIME structure to a Unix time.
    It's the best I could find by googling and borrowing code here and there.
    The time returned has a precision of 1 second.
    */
    unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32;
    unix_time += ftCreate.dwLowDateTime - 116444736000000000LL;
    unix_time /= 10000000;
    return Py_BuildValue("d", (double)unix_time);
}


/*
 * Return a Python integer indicating the number of CPUs on the system.
 */
static PyObject*
get_num_cpus(PyObject* self, PyObject* args)
{
    SYSTEM_INFO system_info;
    system_info.dwNumberOfProcessors = 0;

    GetSystemInfo(&system_info);
    if (system_info.dwNumberOfProcessors == 0){
        // GetSystemInfo failed for some reason; return 1 as default
        return Py_BuildValue("I", 1);
    }
    return Py_BuildValue("I", system_info.dwNumberOfProcessors);
}

/*
 * Return process name as a Python string.
 */
static PyObject*
get_process_name(PyObject* self, PyObject* args) {
    long pid;
    int pid_return;
    PyObject* name;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    if (pid == 0) {
        return Py_BuildValue("s", "System Idle Process");
    }
    else if (pid == 4) {
        return Py_BuildValue("s", "System");
    }

    pid_return = pid_is_running(pid);
    if (pid_return == 0) {
        return NoSuchProcess();
    }
    if (pid_return == -1) {
        return NULL;
    }

    name = get_name(pid);
    if (name == NULL) {
        return NULL;  // exception set in get_name()
    }
    return name;
}


/*
 * Return process parent pid as a Python integer.
 */
static PyObject*
get_process_ppid(PyObject* self, PyObject* args) {
    long pid;
    int pid_return;
    PyObject* ppid;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }
    if ((pid == 0) || (pid == 4)) {
        return Py_BuildValue("l", 0);
    }

    pid_return = pid_is_running(pid);
    if (pid_return == 0) {
        return NoSuchProcess();
    }
    if (pid_return == -1) {
        return NULL;
    }

    ppid = get_ppid(pid);
    if (ppid == NULL) {
        return NULL;  // exception set in get_ppid()
    }
    return ppid;
}

/*
 * Return process cmdline as a Python list of cmdline arguments.
 */
static PyObject*
get_process_cmdline(PyObject* self, PyObject* args) {
    long pid;
    int pid_return;
    PyObject* arglist;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }
    if ((pid == 0) || (pid == 4)) {
        return Py_BuildValue("[]");
    }

    pid_return = pid_is_running(pid);
    if (pid_return == 0) {
        return NoSuchProcess();
    }
    if (pid_return == -1) {
        return NULL;
    }

    // May fail any of several ReadProcessMemory calls etc. and not indicate
    // a real problem so we ignore any errors and just live without commandline
    arglist = get_arg_list(pid);
    if ( NULL == arglist ) {
        // carry on anyway, clear any exceptions too
        PyErr_Clear();
        return Py_BuildValue("[]");
    }

    return arglist;
}


/*
 * Return process executable path.
 */
static PyObject*
get_process_exe(PyObject* self, PyObject* args) {
    long pid;
    HANDLE hProcess;
    wchar_t exe[MAX_PATH];
    DWORD nSize = MAX_PATH;
    DWORD WINAPI ret;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }<commit_msg>Windows: fix some compiler warnings<commit_after>/*
 * $Id$
 *
 * Copyright (c) 2009, <NAME>, <NAME>'. All rights reserved.
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 *
 * Windows platform-specific module methods for _psutil_mswindows
 */

// Fixes clash between winsock2.h and windows.h
#define WIN32_LEAN_AND_MEAN

#include <Python.h>
#include <windows.h>
#include <Psapi.h>
#include <time.h>
#include <lm.h>
#include <WinIoCtl.h>
#include <tchar.h>
#include <tlhelp32.h>
#include <winsock2.h>
#include <iphlpapi.h>
#include <wtsapi32.h>

// Link with Iphlpapi.lib
#pragma comment(lib, "IPHLPAPI.lib")

#include "_psutil_mswindows.h"
#include "_psutil_common.h"
#include "arch/mswindows/security.h"
#include "arch/mswindows/process_info.h"
#include "arch/mswindows/process_handles.h"
#include "arch/mswindows/ntextapi.h"


/*
 * Return a Python float representing the system uptime expressed in seconds
 * since the epoch.
 */
static PyObject*
get_system_uptime(PyObject* self, PyObject* args)
{
    double uptime;
    time_t pt;
    FILETIME fileTime;
    long long ll;

    GetSystemTimeAsFileTime(&fileTime);

    /*
    HUGE thanks to:
    http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry

    This function converts the FILETIME structure to the 32 bit
    Unix time structure.
    The time_t is a 32-bit value for the number of seconds since
    January 1, 1970. A FILETIME is a 64-bit for the number of
    100-nanosecond periods since January 1, 1601. Convert by
    subtracting the number of 100-nanosecond period betwee 01-01-1970
    and 01-01-1601, from time_t the divide by 1e+7 to get to the same
    base granularity.
    */
    ll = (((LONGLONG)(fileTime.dwHighDateTime)) << 32) + fileTime.dwLowDateTime;
    pt = (time_t)((ll - 116444736000000000ull) / 10000000ull);

    // XXX - By using GetTickCount() time will wrap around to zero if the
    // system is run continuously for 49.7 days.
    uptime = GetTickCount() / 1000.00f;
    return Py_BuildValue("d", (double)pt - uptime);
}


/*
 * Return 1 if PID exists in the current process list, else 0.
 */
static PyObject*
pid_exists(PyObject* self, PyObject* args)
{
    long pid;
    int status;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    status = pid_is_running(pid);
    if (-1 == status) {
        return NULL; // exception raised in pid_is_running()
    }
    return PyBool_FromLong(status);
}


/*
 * Return a Python list of all the PIDs running on the system.
 */
static PyObject*
get_pid_list(PyObject* self, PyObject* args)
{
    DWORD *proclist = NULL;
    DWORD numberOfReturnedPIDs;
    DWORD i;
    PyObject* pid = NULL;
    PyObject* retlist = PyList_New(0);

    proclist = get_pids(&numberOfReturnedPIDs);
    if (NULL == proclist) {
        Py_DECREF(retlist);
        return NULL;
    }

    for (i = 0; i < numberOfReturnedPIDs; i++) {
        pid = Py_BuildValue("I", proclist[i]);
        PyList_Append(retlist, pid);
        Py_XDECREF(pid);
    }

    // free C array allocated for PIDs
    free(proclist);

    return retlist;
}


/*
 * Kill a process given its PID.
 */
static PyObject*
kill_process(PyObject* self, PyObject* args)
{
    HANDLE hProcess;
    long pid;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }
    if (pid == 0) {
        return AccessDenied();
    }

    hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
    if (hProcess == NULL) {
        if (GetLastError() == ERROR_INVALID_PARAMETER) {
            // see http://code.google.com/p/psutil/issues/detail?id=24
            NoSuchProcess();
        }
        else {
            PyErr_SetFromWindowsErr(0);
        }
        return NULL;
    }

    // kill the process
    if (! TerminateProcess(hProcess, 0)) {
        PyErr_SetFromWindowsErr(0);
        CloseHandle(hProcess);
        return NULL;
    }

    CloseHandle(hProcess);
    Py_INCREF(Py_None);
    return Py_None;
}


/*
 * Wait for process to terminate and return its exit code.
 */
static PyObject*
process_wait(PyObject* self, PyObject* args)
{
    HANDLE hProcess;
    DWORD ExitCode;
    DWORD retVal;
    long pid;
    long timeout;

    if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) {
        return NULL;
    }
    if (pid == 0) {
        return AccessDenied();
    }

    hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid);
    if (hProcess == NULL) {
        if (GetLastError() == ERROR_INVALID_PARAMETER) {
            // no such process; we do not want to raise NSP but
            // return None instead.
            Py_INCREF(Py_None);
            return Py_None;
        }
        else {
            PyErr_SetFromWindowsErr(0);
            return NULL;
        }
    }

    // wait until the process has terminated
    Py_BEGIN_ALLOW_THREADS
    retVal = WaitForSingleObject(hProcess, timeout);
    Py_END_ALLOW_THREADS

    if (retVal == WAIT_FAILED) {
        CloseHandle(hProcess);
        return PyErr_SetFromWindowsErr(GetLastError());
    }
    if (retVal == WAIT_TIMEOUT) {
        CloseHandle(hProcess);
        return Py_BuildValue("l", WAIT_TIMEOUT);
    }

    // get the exit code; note: subprocess module (erroneously?) uses
    // what returned by WaitForSingleObject
    if (GetExitCodeProcess(hProcess, &ExitCode) == 0) {
        CloseHandle(hProcess);
        return PyErr_SetFromWindowsErr(GetLastError());
    }
    CloseHandle(hProcess);
#if PY_MAJOR_VERSION >= 3
    return PyLong_FromLong((long) ExitCode);
#else
    return PyInt_FromLong((long) ExitCode);
#endif
}


/*
 * Return a Python tuple (user_time, kernel_time)
 */
static PyObject*
get_process_cpu_times(PyObject* self, PyObject* args)
{
    long        pid;
    HANDLE      hProcess;
    FILETIME    ftCreate, ftExit, ftKernel, ftUser;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    // special case for PID 0
    if (0 == pid){
       return Py_BuildValue("(dd)", 0.0, 0.0);
    }

    hProcess = handle_from_pid(pid);
    if (hProcess == NULL) {
        return NULL;
    }

    if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
        CloseHandle(hProcess);
        if (GetLastError() == ERROR_ACCESS_DENIED) {
            // usually means the process has died so we throw a NoSuchProcess
            // here
            return NoSuchProcess();
        }
        else {
            PyErr_SetFromWindowsErr(0);
            return NULL;
        }
    }

    CloseHandle(hProcess);

    /*
    user and kernel times are represented as a FILETIME structure wich contains
    a 64-bit value representing the number of 100-nanosecond intervals since
    January 1, 1601 (UTC).
    http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx

    To convert it into a float representing the seconds that the process has
    executed in user/kernel mode I borrowed the code below from Python's
    Modules/posixmodule.c
    */

    return Py_BuildValue(
        "(dd)",
        (double)(ftUser.dwHighDateTime*429.4967296 + \
                 ftUser.dwLowDateTime*1e-7),
        (double)(ftKernel.dwHighDateTime*429.4967296 + \
                 ftKernel.dwLowDateTime*1e-7)
        );
}


/*
 * Return a Python float indicating the process create time expressed in
 * seconds since the epoch.
 */
static PyObject*
get_process_create_time(PyObject* self, PyObject* args)
{
    long        pid;
    long long   unix_time;
    DWORD       exitCode;
    HANDLE      hProcess;
    BOOL        ret;
    FILETIME    ftCreate, ftExit, ftKernel, ftUser;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    // special case for PIDs 0 and 4
    if ( (0 == pid) || (4 == pid) ){
       return Py_BuildValue("d", 0.0);
    }

    hProcess = handle_from_pid(pid);
    if (hProcess == NULL) {
        return NULL;
    }

    if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) {
        CloseHandle(hProcess);
        if (GetLastError() == ERROR_ACCESS_DENIED) {
            // usually means the process has died so we throw a NoSuchProcess here
            return NoSuchProcess();
        }
        else {
            PyErr_SetFromWindowsErr(0);
            return NULL;
        }
    }

    // Make sure the process is not gone as OpenProcess alone seems to be
    // unreliable in doing so (it seems a previous call to p.wait() makes
    // it unreliable).
    // This check is important as creation time is used to make sure the
    // process is still running.
    ret = GetExitCodeProcess(hProcess, &exitCode);
    CloseHandle(hProcess);
    if (ret != 0) {
        if (exitCode != STILL_ACTIVE) {
            return NoSuchProcess();
        }
    }
    else {
        // Ignore access denied as it means the process is still alive.
        // For all other errors, we want an exception.
        if (GetLastError() != ERROR_ACCESS_DENIED) {
            PyErr_SetFromWindowsErr(0);
            return NULL;
        }
    }

    /*
    Convert the FILETIME structure to a Unix time.
    It's the best I could find by googling and borrowing code here and there.
    The time returned has a precision of 1 second.
    */
    unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32;
    unix_time += ftCreate.dwLowDateTime - 116444736000000000LL;
    unix_time /= 10000000;
    return Py_BuildValue("d", (double)unix_time);
}


/*
 * Return a Python integer indicating the number of CPUs on the system.
 */
static PyObject*
get_num_cpus(PyObject* self, PyObject* args)
{
    SYSTEM_INFO system_info;
    system_info.dwNumberOfProcessors = 0;

    GetSystemInfo(&system_info);
    if (system_info.dwNumberOfProcessors == 0){
        // GetSystemInfo failed for some reason; return 1 as default
        return Py_BuildValue("I", 1);
    }
    return Py_BuildValue("I", system_info.dwNumberOfProcessors);
}

/*
 * Return process name as a Python string.
 */
static PyObject*
get_process_name(PyObject* self, PyObject* args) {
    long pid;
    int pid_return;
    PyObject* name;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }

    if (pid == 0) {
        return Py_BuildValue("s", "System Idle Process");
    }
    else if (pid == 4) {
        return Py_BuildValue("s", "System");
    }

    pid_return = pid_is_running(pid);
    if (pid_return == 0) {
        return NoSuchProcess();
    }
    if (pid_return == -1) {
        return NULL;
    }

    name = get_name(pid);
    if (name == NULL) {
        return NULL;  // exception set in get_name()
    }
    return name;
}


/*
 * Return process parent pid as a Python integer.
 */
static PyObject*
get_process_ppid(PyObject* self, PyObject* args) {
    long pid;
    int pid_return;
    PyObject* ppid;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }
    if ((pid == 0) || (pid == 4)) {
        return Py_BuildValue("l", 0);
    }

    pid_return = pid_is_running(pid);
    if (pid_return == 0) {
        return NoSuchProcess();
    }
    if (pid_return == -1) {
        return NULL;
    }

    ppid = get_ppid(pid);
    if (ppid == NULL) {
        return NULL;  // exception set in get_ppid()
    }
    return ppid;
}

/*
 * Return process cmdline as a Python list of cmdline arguments.
 */
static PyObject*
get_process_cmdline(PyObject* self, PyObject* args) {
    long pid;
    int pid_return;
    PyObject* arglist;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }
    if ((pid == 0) || (pid == 4)) {
        return Py_BuildValue("[]");
    }

    pid_return = pid_is_running(pid);
    if (pid_return == 0) {
        return NoSuchProcess();
    }
    if (pid_return == -1) {
        return NULL;
    }

    // May fail any of several ReadProcessMemory calls etc. and not indicate
    // a real problem so we ignore any errors and just live without commandline
    arglist = get_arg_list(pid);
    if ( NULL == arglist ) {
        // carry on anyway, clear any exceptions too
        PyErr_Clear();
        return Py_BuildValue("[]");
    }

    return arglist;
}


/*
 * Return process executable path.
 */
static PyObject*
get_process_exe(PyObject* self, PyObject* args) {
    long pid;
    HANDLE hProcess;
    wchar_t exe[MAX_PATH];
    DWORD nSize = MAX_PATH;

    if (! PyArg_ParseTuple(args, "l", &pid)) {
        return NULL;
    }
{'fraction_non_alphanumeric': 0.1089469005091732, 'fraction_numerical': 0.01753818798997818, 'mean_word_length': 2.481570061902082, 'pattern_counts': {'":': 0, '<': 39, '<?xml version=': 0, '>': 31, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 8, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6244024', 'n_tokens_mistral': 8333, 'n_tokens_neox': 7925, 'n_words': 2274}