From 08cc3b6cd727157cb4f3729ff77812d18247e6d8 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 19 Oct 2023 23:30:07 +0200 Subject: [PATCH 1/7] doc wording Signed-off-by: Giampaolo Rodola --- docs/index.rst | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 0e782ebc7..00a6f6006 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2630,21 +2630,22 @@ If you want to develop psutil take a look at the `DEVGUIDE.rst`_. Platforms support history ========================= -* psutil 5.9.6 (XXXX-XX): drop Python 3.4 and 3.5 support -* psutil 5.9.1 (2022-05): drop Python 2.6 support -* psutil 5.9.0 (2021-12): **MidnightBSD** -* psutil 5.8.0 (2020-12): **PyPy 2** on Windows -* psutil 5.7.1 (2020-07): **Windows Nano** -* psutil 5.7.0 (2020-02): drop Windows XP & Server 2003 support -* psutil 5.7.0 (2020-02): **PyPy 3** on Windows -* psutil 5.4.0 (2017-11): **AIX** -* psutil 3.4.1 (2016-01): **NetBSD** -* psutil 3.3.0 (2015-11): **OpenBSD** -* psutil 1.0.0 (2013-07): **Solaris** -* psutil 0.1.1 (2009-03): **FreeBSD** -* psutil 0.1.0 (2009-01): **Linux, Windows, macOS** - -Supported Python versions are 2.7, 3.6+ and PyPy3. +* psutil 5.9.6 (2023-10): drop Python 3.4 and 3.5 +* psutil 5.9.1 (2022-05): drop Python 2.6 +* psutil 5.9.0 (2021-12): add **MidnightBSD** +* psutil 5.8.0 (2020-12): add **PyPy 2** on Windows +* psutil 5.7.1 (2020-07): add **Windows Nano** +* psutil 5.7.0 (2020-02): drop Windows XP & Windows Server 2003 +* psutil 5.7.0 (2020-02): add **PyPy 3** on Windows +* psutil 5.4.0 (2017-11): add **AIX** +* psutil 3.4.1 (2016-01): add **NetBSD** +* psutil 3.3.0 (2015-11): add **OpenBSD** +* psutil 1.0.0 (2013-07): add **Solaris** +* psutil 0.1.1 (2009-03): add **FreeBSD** +* psutil 0.1.0 (2009-01): add **Linux, Windows, macOS** + +Supported Python versions at the time of writing are cPython 2.7, 3.6+ and +PyPy3. Timeline ======== From a7205fc9d77b13a161da1502bd0988db8b9b5971 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 20 Oct 2023 18:05:16 +0200 Subject: [PATCH 2/7] Linux C files refactoring (#2320) --- MANIFEST.in | 10 + docs/index.rst | 31 +-- psutil/_psutil_linux.c | 496 +------------------------------------- psutil/arch/linux/disk.c | 75 ++++++ psutil/arch/linux/disk.h | 9 + psutil/arch/linux/mem.c | 30 +++ psutil/arch/linux/mem.h | 9 + psutil/arch/linux/net.c | 119 +++++++++ psutil/arch/linux/net.h | 9 + psutil/arch/linux/proc.c | 200 +++++++++++++++ psutil/arch/linux/proc.h | 25 ++ psutil/arch/linux/users.c | 72 ++++++ psutil/arch/linux/users.h | 9 + setup.py | 6 +- 14 files changed, 595 insertions(+), 505 deletions(-) create mode 100644 psutil/arch/linux/disk.c create mode 100644 psutil/arch/linux/disk.h create mode 100644 psutil/arch/linux/mem.c create mode 100644 psutil/arch/linux/mem.h create mode 100644 psutil/arch/linux/net.c create mode 100644 psutil/arch/linux/net.h create mode 100644 psutil/arch/linux/proc.c create mode 100644 psutil/arch/linux/proc.h create mode 100644 psutil/arch/linux/users.c create mode 100644 psutil/arch/linux/users.h diff --git a/MANIFEST.in b/MANIFEST.in index fb25643af..ae8056349 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -72,6 +72,16 @@ include psutil/arch/freebsd/sensors.c include psutil/arch/freebsd/sensors.h include psutil/arch/freebsd/sys_socks.c include psutil/arch/freebsd/sys_socks.h +include psutil/arch/linux/disk.c +include psutil/arch/linux/disk.h +include psutil/arch/linux/mem.c +include psutil/arch/linux/mem.h +include psutil/arch/linux/net.c +include psutil/arch/linux/net.h +include psutil/arch/linux/proc.c +include psutil/arch/linux/proc.h +include psutil/arch/linux/users.c +include psutil/arch/linux/users.h include psutil/arch/netbsd/cpu.c include psutil/arch/netbsd/cpu.h include psutil/arch/netbsd/disk.c diff --git a/docs/index.rst b/docs/index.rst index 0e782ebc7..00a6f6006 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -2630,21 +2630,22 @@ If you want to develop psutil take a look at the `DEVGUIDE.rst`_. Platforms support history ========================= -* psutil 5.9.6 (XXXX-XX): drop Python 3.4 and 3.5 support -* psutil 5.9.1 (2022-05): drop Python 2.6 support -* psutil 5.9.0 (2021-12): **MidnightBSD** -* psutil 5.8.0 (2020-12): **PyPy 2** on Windows -* psutil 5.7.1 (2020-07): **Windows Nano** -* psutil 5.7.0 (2020-02): drop Windows XP & Server 2003 support -* psutil 5.7.0 (2020-02): **PyPy 3** on Windows -* psutil 5.4.0 (2017-11): **AIX** -* psutil 3.4.1 (2016-01): **NetBSD** -* psutil 3.3.0 (2015-11): **OpenBSD** -* psutil 1.0.0 (2013-07): **Solaris** -* psutil 0.1.1 (2009-03): **FreeBSD** -* psutil 0.1.0 (2009-01): **Linux, Windows, macOS** - -Supported Python versions are 2.7, 3.6+ and PyPy3. +* psutil 5.9.6 (2023-10): drop Python 3.4 and 3.5 +* psutil 5.9.1 (2022-05): drop Python 2.6 +* psutil 5.9.0 (2021-12): add **MidnightBSD** +* psutil 5.8.0 (2020-12): add **PyPy 2** on Windows +* psutil 5.7.1 (2020-07): add **Windows Nano** +* psutil 5.7.0 (2020-02): drop Windows XP & Windows Server 2003 +* psutil 5.7.0 (2020-02): add **PyPy 3** on Windows +* psutil 5.4.0 (2017-11): add **AIX** +* psutil 3.4.1 (2016-01): add **NetBSD** +* psutil 3.3.0 (2015-11): add **OpenBSD** +* psutil 1.0.0 (2013-07): add **Solaris** +* psutil 0.1.1 (2009-03): add **FreeBSD** +* psutil 0.1.0 (2009-01): add **Linux, Windows, macOS** + +Supported Python versions at the time of writing are cPython 2.7, 3.6+ and +PyPy3. Timeline ======== diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c index 3e6b3b900..38fa2cbaf 100644 --- a/psutil/_psutil_linux.c +++ b/psutil/_psutil_linux.c @@ -10,499 +10,19 @@ #define _GNU_SOURCE 1 #endif #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -// see: https://github.com/giampaolo/psutil/issues/659 -#ifdef PSUTIL_ETHTOOL_MISSING_TYPES - #include - typedef __u64 u64; - typedef __u32 u32; - typedef __u16 u16; - typedef __u8 u8; -#endif -/* Avoid redefinition of struct sysinfo with musl libc */ -#define _LINUX_SYSINFO_H -#include - -/* The minimum number of CPUs allocated in a cpu_set_t */ -static const int NCPUS_START = sizeof(unsigned long) * CHAR_BIT; - -// Linux >= 2.6.13 -#define PSUTIL_HAVE_IOPRIO defined(__NR_ioprio_get) && defined(__NR_ioprio_set) - -// Should exist starting from CentOS 6 (year 2011). -#ifdef CPU_ALLOC - #define PSUTIL_HAVE_CPU_AFFINITY -#endif +#include // DUPLEX_* #include "_psutil_common.h" -#include "_psutil_posix.h" - -// May happen on old RedHat versions, see: -// https://github.com/giampaolo/psutil/issues/607 -#ifndef DUPLEX_UNKNOWN - #define DUPLEX_UNKNOWN 0xff -#endif - - -#ifndef SPEED_UNKNOWN - #define SPEED_UNKNOWN -1 -#endif - - -#if PSUTIL_HAVE_IOPRIO -enum { - IOPRIO_WHO_PROCESS = 1, -}; - -static inline int -ioprio_get(int which, int who) { - return syscall(__NR_ioprio_get, which, who); -} - -static inline int -ioprio_set(int which, int who, int ioprio) { - return syscall(__NR_ioprio_set, which, who, ioprio); -} - -// * defined in linux/ethtool.h but not always available (e.g. Android) -// * #ifdef check needed for old kernels, see: -// https://github.com/giampaolo/psutil/issues/2164 -static inline uint32_t -psutil_ethtool_cmd_speed(const struct ethtool_cmd *ecmd) { -#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) - return ecmd->speed; -#else - return (ecmd->speed_hi << 16) | ecmd->speed; -#endif -} - -#define IOPRIO_CLASS_SHIFT 13 -#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) - -#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) -#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) -#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) - - -/* - * Return a (ioclass, iodata) Python tuple representing process I/O priority. - */ -static PyObject * -psutil_proc_ioprio_get(PyObject *self, PyObject *args) { - pid_t pid; - int ioprio, ioclass, iodata; - if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) - return NULL; - ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); - if (ioprio == -1) - return PyErr_SetFromErrno(PyExc_OSError); - ioclass = IOPRIO_PRIO_CLASS(ioprio); - iodata = IOPRIO_PRIO_DATA(ioprio); - return Py_BuildValue("ii", ioclass, iodata); -} - - -/* - * A wrapper around ioprio_set(); sets process I/O priority. - * ioclass can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE - * or 0. iodata goes from 0 to 7 depending on ioclass specified. - */ -static PyObject * -psutil_proc_ioprio_set(PyObject *self, PyObject *args) { - pid_t pid; - int ioprio, ioclass, iodata; - int retval; - - if (! PyArg_ParseTuple( - args, _Py_PARSE_PID "ii", &pid, &ioclass, &iodata)) { - return NULL; - } - ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata); - retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio); - if (retval == -1) - return PyErr_SetFromErrno(PyExc_OSError); - Py_RETURN_NONE; -} -#endif - - -/* - * Return disk mounted partitions as a list of tuples including device, - * mount point and filesystem type - */ -static PyObject * -psutil_disk_partitions(PyObject *self, PyObject *args) { - FILE *file = NULL; - struct mntent *entry; - char *mtab_path; - PyObject *py_dev = NULL; - PyObject *py_mountp = NULL; - PyObject *py_tuple = NULL; - PyObject *py_retlist = PyList_New(0); - - if (py_retlist == NULL) - return NULL; +#include "arch/linux/disk.h" +#include "arch/linux/mem.h" +#include "arch/linux/net.h" +#include "arch/linux/proc.h" +#include "arch/linux/users.h" - if (!PyArg_ParseTuple(args, "s", &mtab_path)) - return NULL; - - Py_BEGIN_ALLOW_THREADS - file = setmntent(mtab_path, "r"); - Py_END_ALLOW_THREADS - if ((file == 0) || (file == NULL)) { - psutil_debug("setmntent() failed"); - PyErr_SetFromErrnoWithFilename(PyExc_OSError, mtab_path); - goto error; - } - - while ((entry = getmntent(file))) { - if (entry == NULL) { - PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed"); - goto error; - } - py_dev = PyUnicode_DecodeFSDefault(entry->mnt_fsname); - if (! py_dev) - goto error; - py_mountp = PyUnicode_DecodeFSDefault(entry->mnt_dir); - if (! py_mountp) - goto error; - py_tuple = Py_BuildValue("(OOss)", - py_dev, // device - py_mountp, // mount point - entry->mnt_type, // fs type - entry->mnt_opts); // options - if (! py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_CLEAR(py_dev); - Py_CLEAR(py_mountp); - Py_CLEAR(py_tuple); - } - endmntent(file); - return py_retlist; - -error: - if (file != NULL) - endmntent(file); - Py_XDECREF(py_dev); - Py_XDECREF(py_mountp); - Py_XDECREF(py_tuple); - Py_DECREF(py_retlist); - return NULL; -} - - -/* - * A wrapper around sysinfo(), return system memory usage statistics. - */ -static PyObject * -psutil_linux_sysinfo(PyObject *self, PyObject *args) { - struct sysinfo info; - - if (sysinfo(&info) != 0) - return PyErr_SetFromErrno(PyExc_OSError); - // note: boot time might also be determined from here - return Py_BuildValue( - "(kkkkkkI)", - info.totalram, // total - info.freeram, // free - info.bufferram, // buffer - info.sharedram, // shared - info.totalswap, // swap tot - info.freeswap, // swap free - info.mem_unit // multiplier - ); -} - - -/* - * Return process CPU affinity as a Python list - */ -#ifdef PSUTIL_HAVE_CPU_AFFINITY - -static PyObject * -psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { - int cpu, ncpus, count, cpucount_s; - pid_t pid; - size_t setsize; - cpu_set_t *mask = NULL; - PyObject *py_list = NULL; - - if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) - return NULL; - ncpus = NCPUS_START; - while (1) { - setsize = CPU_ALLOC_SIZE(ncpus); - mask = CPU_ALLOC(ncpus); - if (mask == NULL) { - psutil_debug("CPU_ALLOC() failed"); - return PyErr_NoMemory(); - } - if (sched_getaffinity(pid, setsize, mask) == 0) - break; - CPU_FREE(mask); - if (errno != EINVAL) - return PyErr_SetFromErrno(PyExc_OSError); - if (ncpus > INT_MAX / 2) { - PyErr_SetString(PyExc_OverflowError, "could not allocate " - "a large enough CPU set"); - return NULL; - } - ncpus = ncpus * 2; - } - - py_list = PyList_New(0); - if (py_list == NULL) - goto error; - - cpucount_s = CPU_COUNT_S(setsize, mask); - for (cpu = 0, count = cpucount_s; count; cpu++) { - if (CPU_ISSET_S(cpu, setsize, mask)) { -#if PY_MAJOR_VERSION >= 3 - PyObject *cpu_num = PyLong_FromLong(cpu); -#else - PyObject *cpu_num = PyInt_FromLong(cpu); -#endif - if (cpu_num == NULL) - goto error; - if (PyList_Append(py_list, cpu_num)) { - Py_DECREF(cpu_num); - goto error; - } - Py_DECREF(cpu_num); - --count; - } - } - CPU_FREE(mask); - return py_list; - -error: - if (mask) - CPU_FREE(mask); - Py_XDECREF(py_list); - return NULL; -} - - -/* - * Set process CPU affinity; expects a bitmask - */ -static PyObject * -psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { - cpu_set_t cpu_set; - size_t len; - pid_t pid; - Py_ssize_t i, seq_len; - PyObject *py_cpu_set; - - if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set)) - return NULL; - - if (!PySequence_Check(py_cpu_set)) { - return PyErr_Format( - PyExc_TypeError, -#if PY_MAJOR_VERSION >= 3 - "sequence argument expected, got %R", Py_TYPE(py_cpu_set) -#else - "sequence argument expected, got %s", Py_TYPE(py_cpu_set)->tp_name -#endif - ); - } - - seq_len = PySequence_Size(py_cpu_set); - if (seq_len < 0) { - return NULL; - } - CPU_ZERO(&cpu_set); - for (i = 0; i < seq_len; i++) { - PyObject *item = PySequence_GetItem(py_cpu_set, i); - if (!item) { - return NULL; - } -#if PY_MAJOR_VERSION >= 3 - long value = PyLong_AsLong(item); -#else - long value = PyInt_AsLong(item); -#endif - Py_XDECREF(item); - if ((value == -1) || PyErr_Occurred()) { - if (!PyErr_Occurred()) - PyErr_SetString(PyExc_ValueError, "invalid CPU value"); - return NULL; - } - CPU_SET(value, &cpu_set); - } - - len = sizeof(cpu_set); - if (sched_setaffinity(pid, len, &cpu_set)) { - return PyErr_SetFromErrno(PyExc_OSError); - } - - Py_RETURN_NONE; -} -#endif /* PSUTIL_HAVE_CPU_AFFINITY */ - - -/* - * Return currently connected users as a list of tuples. - */ -static PyObject * -psutil_users(PyObject *self, PyObject *args) { - struct utmp *ut; - PyObject *py_retlist = PyList_New(0); - PyObject *py_tuple = NULL; - PyObject *py_username = NULL; - PyObject *py_tty = NULL; - PyObject *py_hostname = NULL; - PyObject *py_user_proc = NULL; - - if (py_retlist == NULL) - return NULL; - setutent(); - while (NULL != (ut = getutent())) { - py_tuple = NULL; - py_user_proc = NULL; - if (ut->ut_type == USER_PROCESS) - py_user_proc = Py_True; - else - py_user_proc = Py_False; - py_username = PyUnicode_DecodeFSDefault(ut->ut_user); - if (! py_username) - goto error; - py_tty = PyUnicode_DecodeFSDefault(ut->ut_line); - if (! py_tty) - goto error; - py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host); - if (! py_hostname) - goto error; - - py_tuple = Py_BuildValue( - "OOOdO" _Py_PARSE_PID, - py_username, // username - py_tty, // tty - py_hostname, // hostname - (double)ut->ut_tv.tv_sec, // tstamp - py_user_proc, // (bool) user process - ut->ut_pid // process id - ); - if (! py_tuple) - goto error; - if (PyList_Append(py_retlist, py_tuple)) - goto error; - Py_CLEAR(py_username); - Py_CLEAR(py_tty); - Py_CLEAR(py_hostname); - Py_CLEAR(py_tuple); - } - endutent(); - return py_retlist; - -error: - Py_XDECREF(py_username); - Py_XDECREF(py_tty); - Py_XDECREF(py_hostname); - Py_XDECREF(py_tuple); - Py_DECREF(py_retlist); - endutent(); - return NULL; -} - - -/* - * Return stats about a particular network - * interface. References: - * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py - * http://www.i-scream.org/libstatgrab/ - */ -static PyObject* -psutil_net_if_duplex_speed(PyObject* self, PyObject* args) { - char *nic_name; - int sock = 0; - int ret; - int duplex; - __u32 uint_speed; - int speed; - struct ifreq ifr; - struct ethtool_cmd ethcmd; - PyObject *py_retlist = NULL; - - if (! PyArg_ParseTuple(args, "s", &nic_name)) - return NULL; - - sock = socket(AF_INET, SOCK_DGRAM, 0); - if (sock == -1) - return PyErr_SetFromOSErrnoWithSyscall("socket()"); - PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); - - // duplex and speed - memset(ðcmd, 0, sizeof ethcmd); - ethcmd.cmd = ETHTOOL_GSET; - ifr.ifr_data = (void *)ðcmd; - ret = ioctl(sock, SIOCETHTOOL, &ifr); - - if (ret != -1) { - duplex = ethcmd.duplex; - // speed is returned from ethtool as a __u32 ranging from 0 to INT_MAX - // or SPEED_UNKNOWN (-1) - uint_speed = psutil_ethtool_cmd_speed(ðcmd); - if (uint_speed == (__u32)SPEED_UNKNOWN || uint_speed > INT_MAX) { - speed = 0; - } - else { - speed = (int)uint_speed; - } - } - else { - if ((errno == EOPNOTSUPP) || (errno == EINVAL)) { - // EOPNOTSUPP may occur in case of wi-fi cards. - // For EINVAL see: - // https://github.com/giampaolo/psutil/issues/797 - // #issuecomment-202999532 - duplex = DUPLEX_UNKNOWN; - speed = 0; - } - else { - PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCETHTOOL)"); - goto error; - } - } - - py_retlist = Py_BuildValue("[ii]", duplex, speed); - if (!py_retlist) - goto error; - close(sock); - return py_retlist; - -error: - if (sock != -1) - close(sock); - return NULL; -} - - -/* - * Module init. - */ static PyMethodDef mod_methods[] = { // --- per-process functions - -#if PSUTIL_HAVE_IOPRIO +#ifdef PSUTIL_HAVE_IOPRIO {"proc_ioprio_get", psutil_proc_ioprio_get, METH_VARARGS}, {"proc_ioprio_set", psutil_proc_ioprio_set, METH_VARARGS}, #endif @@ -514,13 +34,11 @@ static PyMethodDef mod_methods[] = { {"disk_partitions", psutil_disk_partitions, METH_VARARGS}, {"users", psutil_users, METH_VARARGS}, {"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS}, - // --- linux specific {"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS}, // --- others {"check_pid_range", psutil_check_pid_range, METH_VARARGS}, {"set_debug", psutil_set_debug, METH_VARARGS}, - {NULL, NULL, 0, NULL} }; diff --git a/psutil/arch/linux/disk.c b/psutil/arch/linux/disk.c new file mode 100644 index 000000000..692a7d5d4 --- /dev/null +++ b/psutil/arch/linux/disk.c @@ -0,0 +1,75 @@ +/* + * 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 +#include + +#include "../../_psutil_common.h" + + +// Return disk mounted partitions as a list of tuples including device, +// mount point and filesystem type. +PyObject * +psutil_disk_partitions(PyObject *self, PyObject *args) { + FILE *file = NULL; + struct mntent *entry; + char *mtab_path; + PyObject *py_dev = NULL; + PyObject *py_mountp = NULL; + PyObject *py_tuple = NULL; + PyObject *py_retlist = PyList_New(0); + + if (py_retlist == NULL) + return NULL; + + if (!PyArg_ParseTuple(args, "s", &mtab_path)) + return NULL; + + Py_BEGIN_ALLOW_THREADS + file = setmntent(mtab_path, "r"); + Py_END_ALLOW_THREADS + if ((file == 0) || (file == NULL)) { + psutil_debug("setmntent() failed"); + PyErr_SetFromErrnoWithFilename(PyExc_OSError, mtab_path); + goto error; + } + + while ((entry = getmntent(file))) { + if (entry == NULL) { + PyErr_Format(PyExc_RuntimeError, "getmntent() syscall failed"); + goto error; + } + py_dev = PyUnicode_DecodeFSDefault(entry->mnt_fsname); + if (! py_dev) + goto error; + py_mountp = PyUnicode_DecodeFSDefault(entry->mnt_dir); + if (! py_mountp) + goto error; + py_tuple = Py_BuildValue("(OOss)", + py_dev, // device + py_mountp, // mount point + entry->mnt_type, // fs type + entry->mnt_opts); // options + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_dev); + Py_CLEAR(py_mountp); + Py_CLEAR(py_tuple); + } + endmntent(file); + return py_retlist; + +error: + if (file != NULL) + endmntent(file); + Py_XDECREF(py_dev); + Py_XDECREF(py_mountp); + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + return NULL; +} diff --git a/psutil/arch/linux/disk.h b/psutil/arch/linux/disk.h new file mode 100644 index 000000000..90a86d611 --- /dev/null +++ b/psutil/arch/linux/disk.h @@ -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 + +PyObject *psutil_disk_partitions(PyObject* self, PyObject* args); diff --git a/psutil/arch/linux/mem.c b/psutil/arch/linux/mem.c new file mode 100644 index 000000000..3b9b4fef3 --- /dev/null +++ b/psutil/arch/linux/mem.c @@ -0,0 +1,30 @@ +/* + * 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 +#include + +#include "../../_psutil_common.h" + + +PyObject * +psutil_linux_sysinfo(PyObject *self, PyObject *args) { + struct sysinfo info; + + if (sysinfo(&info) != 0) + return PyErr_SetFromErrno(PyExc_OSError); + // note: boot time might also be determined from here + return Py_BuildValue( + "(kkkkkkI)", + info.totalram, // total + info.freeram, // free + info.bufferram, // buffer + info.sharedram, // shared + info.totalswap, // swap tot + info.freeswap, // swap free + info.mem_unit // multiplier + ); +} diff --git a/psutil/arch/linux/mem.h b/psutil/arch/linux/mem.h new file mode 100644 index 000000000..582d3e031 --- /dev/null +++ b/psutil/arch/linux/mem.h @@ -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 + +PyObject *psutil_linux_sysinfo(PyObject* self, PyObject* args); diff --git a/psutil/arch/linux/net.c b/psutil/arch/linux/net.c new file mode 100644 index 000000000..522a55dfc --- /dev/null +++ b/psutil/arch/linux/net.c @@ -0,0 +1,119 @@ +/* + * 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 +#include +#include +#include +#include +#include + +// see: https://github.com/giampaolo/psutil/issues/659 +#ifdef PSUTIL_ETHTOOL_MISSING_TYPES + #include + typedef __u64 u64; + typedef __u32 u32; + typedef __u16 u16; + typedef __u8 u8; +#endif + +// Avoid redefinition of struct sysinfo with musl libc. +#define _LINUX_SYSINFO_H +#include + +#include "../../_psutil_common.h" + + +// * defined in linux/ethtool.h but not always available (e.g. Android) +// * #ifdef check needed for old kernels, see: +// https://github.com/giampaolo/psutil/issues/2164 +static inline uint32_t +psutil_ethtool_cmd_speed(const struct ethtool_cmd *ecmd) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 27) + return ecmd->speed; +#else + return (ecmd->speed_hi << 16) | ecmd->speed; +#endif +} + +// May happen on old RedHat versions, see: +// https://github.com/giampaolo/psutil/issues/607 +#ifndef DUPLEX_UNKNOWN + #define DUPLEX_UNKNOWN 0xff +#endif +// https://github.com/giampaolo/psutil/pull/2156 +#ifndef SPEED_UNKNOWN + #define SPEED_UNKNOWN -1 +#endif + + +// References: +// * https://github.com/dpaleino/wicd/blob/master/wicd/backends/be-ioctl.py +// * http://www.i-scream.org/libstatgrab/ +PyObject* +psutil_net_if_duplex_speed(PyObject* self, PyObject* args) { + char *nic_name; + int sock = 0; + int ret; + int duplex; + __u32 uint_speed; + int speed; + struct ifreq ifr; + struct ethtool_cmd ethcmd; + PyObject *py_retlist = NULL; + + if (! PyArg_ParseTuple(args, "s", &nic_name)) + return NULL; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) + return PyErr_SetFromOSErrnoWithSyscall("socket()"); + PSUTIL_STRNCPY(ifr.ifr_name, nic_name, sizeof(ifr.ifr_name)); + + // duplex and speed + memset(ðcmd, 0, sizeof ethcmd); + ethcmd.cmd = ETHTOOL_GSET; + ifr.ifr_data = (void *)ðcmd; + ret = ioctl(sock, SIOCETHTOOL, &ifr); + + if (ret != -1) { + duplex = ethcmd.duplex; + // speed is returned from ethtool as a __u32 ranging from 0 to INT_MAX + // or SPEED_UNKNOWN (-1) + uint_speed = psutil_ethtool_cmd_speed(ðcmd); + if (uint_speed == (__u32)SPEED_UNKNOWN || uint_speed > INT_MAX) { + speed = 0; + } + else { + speed = (int)uint_speed; + } + } + else { + if ((errno == EOPNOTSUPP) || (errno == EINVAL)) { + // EOPNOTSUPP may occur in case of wi-fi cards. + // For EINVAL see: + // https://github.com/giampaolo/psutil/issues/797 + // #issuecomment-202999532 + duplex = DUPLEX_UNKNOWN; + speed = 0; + } + else { + PyErr_SetFromOSErrnoWithSyscall("ioctl(SIOCETHTOOL)"); + goto error; + } + } + + py_retlist = Py_BuildValue("[ii]", duplex, speed); + if (!py_retlist) + goto error; + close(sock); + return py_retlist; + +error: + if (sock != -1) + close(sock); + return NULL; +} diff --git a/psutil/arch/linux/net.h b/psutil/arch/linux/net.h new file mode 100644 index 000000000..55095c06c --- /dev/null +++ b/psutil/arch/linux/net.h @@ -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 + +PyObject *psutil_net_if_duplex_speed(PyObject* self, PyObject* args); diff --git a/psutil/arch/linux/proc.c b/psutil/arch/linux/proc.c new file mode 100644 index 000000000..ac87af917 --- /dev/null +++ b/psutil/arch/linux/proc.c @@ -0,0 +1,200 @@ +/* + * 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 +#include +#include + +#include "proc.h" +#include "../../_psutil_common.h" + + +#ifdef PSUTIL_HAVE_IOPRIO +enum { + IOPRIO_WHO_PROCESS = 1, +}; + +static inline int +ioprio_get(int which, int who) { + return syscall(__NR_ioprio_get, which, who); +} + +static inline int +ioprio_set(int which, int who, int ioprio) { + return syscall(__NR_ioprio_set, which, who, ioprio); +} + +#define IOPRIO_CLASS_SHIFT 13 +#define IOPRIO_PRIO_MASK ((1UL << IOPRIO_CLASS_SHIFT) - 1) + +#define IOPRIO_PRIO_CLASS(mask) ((mask) >> IOPRIO_CLASS_SHIFT) +#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK) +#define IOPRIO_PRIO_VALUE(class, data) (((class) << IOPRIO_CLASS_SHIFT) | data) + + +// Return a (ioclass, iodata) Python tuple representing process I/O +// priority. +PyObject * +psutil_proc_ioprio_get(PyObject *self, PyObject *args) { + pid_t pid; + int ioprio, ioclass, iodata; + if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) + return NULL; + ioprio = ioprio_get(IOPRIO_WHO_PROCESS, pid); + if (ioprio == -1) + return PyErr_SetFromErrno(PyExc_OSError); + ioclass = IOPRIO_PRIO_CLASS(ioprio); + iodata = IOPRIO_PRIO_DATA(ioprio); + return Py_BuildValue("ii", ioclass, iodata); +} + + +// A wrapper around ioprio_set(); sets process I/O priority. ioclass +// can be either IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE or +// 0. iodata goes from 0 to 7 depending on ioclass specified. +PyObject * +psutil_proc_ioprio_set(PyObject *self, PyObject *args) { + pid_t pid; + int ioprio, ioclass, iodata; + int retval; + + if (! PyArg_ParseTuple( + args, _Py_PARSE_PID "ii", &pid, &ioclass, &iodata)) { + return NULL; + } + ioprio = IOPRIO_PRIO_VALUE(ioclass, iodata); + retval = ioprio_set(IOPRIO_WHO_PROCESS, pid, ioprio); + if (retval == -1) + return PyErr_SetFromErrno(PyExc_OSError); + Py_RETURN_NONE; +} +#endif // PSUTIL_HAVE_IOPRIO + + +#ifdef PSUTIL_HAVE_CPU_AFFINITY + +// Return process CPU affinity as a Python list. +PyObject * +psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args) { + int cpu, ncpus, count, cpucount_s; + pid_t pid; + size_t setsize; + cpu_set_t *mask = NULL; + PyObject *py_list = NULL; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID, &pid)) + return NULL; + + // The minimum number of CPUs allocated in a cpu_set_t. + ncpus = sizeof(unsigned long) * CHAR_BIT; + while (1) { + setsize = CPU_ALLOC_SIZE(ncpus); + mask = CPU_ALLOC(ncpus); + if (mask == NULL) { + psutil_debug("CPU_ALLOC() failed"); + return PyErr_NoMemory(); + } + if (sched_getaffinity(pid, setsize, mask) == 0) + break; + CPU_FREE(mask); + if (errno != EINVAL) + return PyErr_SetFromErrno(PyExc_OSError); + if (ncpus > INT_MAX / 2) { + PyErr_SetString(PyExc_OverflowError, "could not allocate " + "a large enough CPU set"); + return NULL; + } + ncpus = ncpus * 2; + } + + py_list = PyList_New(0); + if (py_list == NULL) + goto error; + + cpucount_s = CPU_COUNT_S(setsize, mask); + for (cpu = 0, count = cpucount_s; count; cpu++) { + if (CPU_ISSET_S(cpu, setsize, mask)) { +#if PY_MAJOR_VERSION >= 3 + PyObject *cpu_num = PyLong_FromLong(cpu); +#else + PyObject *cpu_num = PyInt_FromLong(cpu); +#endif + if (cpu_num == NULL) + goto error; + if (PyList_Append(py_list, cpu_num)) { + Py_DECREF(cpu_num); + goto error; + } + Py_DECREF(cpu_num); + --count; + } + } + CPU_FREE(mask); + return py_list; + +error: + if (mask) + CPU_FREE(mask); + Py_XDECREF(py_list); + return NULL; +} + + +// Set process CPU affinity; expects a bitmask. +PyObject * +psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { + cpu_set_t cpu_set; + size_t len; + pid_t pid; + Py_ssize_t i, seq_len; + PyObject *py_cpu_set; + + if (!PyArg_ParseTuple(args, _Py_PARSE_PID "O", &pid, &py_cpu_set)) + return NULL; + + if (!PySequence_Check(py_cpu_set)) { + return PyErr_Format( + PyExc_TypeError, +#if PY_MAJOR_VERSION >= 3 + "sequence argument expected, got %R", Py_TYPE(py_cpu_set) +#else + "sequence argument expected, got %s", Py_TYPE(py_cpu_set)->tp_name +#endif + ); + } + + seq_len = PySequence_Size(py_cpu_set); + if (seq_len < 0) { + return NULL; + } + CPU_ZERO(&cpu_set); + for (i = 0; i < seq_len; i++) { + PyObject *item = PySequence_GetItem(py_cpu_set, i); + if (!item) { + return NULL; + } +#if PY_MAJOR_VERSION >= 3 + long value = PyLong_AsLong(item); +#else + long value = PyInt_AsLong(item); +#endif + Py_XDECREF(item); + if ((value == -1) || PyErr_Occurred()) { + if (!PyErr_Occurred()) + PyErr_SetString(PyExc_ValueError, "invalid CPU value"); + return NULL; + } + CPU_SET(value, &cpu_set); + } + + len = sizeof(cpu_set); + if (sched_setaffinity(pid, len, &cpu_set)) { + return PyErr_SetFromErrno(PyExc_OSError); + } + + Py_RETURN_NONE; +} +#endif // PSUTIL_HAVE_CPU_AFFINITY diff --git a/psutil/arch/linux/proc.h b/psutil/arch/linux/proc.h new file mode 100644 index 000000000..94a84c62e --- /dev/null +++ b/psutil/arch/linux/proc.h @@ -0,0 +1,25 @@ +/* + * 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 +#include // __NR_* +#include // CPU_ALLOC + +// Linux >= 2.6.13 +#if defined(__NR_ioprio_get) && defined(__NR_ioprio_set) + #define PSUTIL_HAVE_IOPRIO + + PyObject *psutil_proc_ioprio_get(PyObject *self, PyObject *args); + PyObject *psutil_proc_ioprio_set(PyObject *self, PyObject *args); +#endif + +// Should exist starting from CentOS 6 (year 2011). +#ifdef CPU_ALLOC + #define PSUTIL_HAVE_CPU_AFFINITY + + PyObject *psutil_proc_cpu_affinity_get(PyObject *self, PyObject *args); + PyObject *psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args); +#endif diff --git a/psutil/arch/linux/users.c b/psutil/arch/linux/users.c new file mode 100644 index 000000000..9ca010a19 --- /dev/null +++ b/psutil/arch/linux/users.c @@ -0,0 +1,72 @@ +/* + * 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 +#include + +#include "../../_psutil_common.h" + + +PyObject * +psutil_users(PyObject *self, PyObject *args) { + struct utmp *ut; + PyObject *py_retlist = PyList_New(0); + PyObject *py_tuple = NULL; + PyObject *py_username = NULL; + PyObject *py_tty = NULL; + PyObject *py_hostname = NULL; + PyObject *py_user_proc = NULL; + + if (py_retlist == NULL) + return NULL; + setutent(); + while (NULL != (ut = getutent())) { + py_tuple = NULL; + py_user_proc = NULL; + if (ut->ut_type == USER_PROCESS) + py_user_proc = Py_True; + else + py_user_proc = Py_False; + py_username = PyUnicode_DecodeFSDefault(ut->ut_user); + if (! py_username) + goto error; + py_tty = PyUnicode_DecodeFSDefault(ut->ut_line); + if (! py_tty) + goto error; + py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host); + if (! py_hostname) + goto error; + + py_tuple = Py_BuildValue( + "OOOdO" _Py_PARSE_PID, + py_username, // username + py_tty, // tty + py_hostname, // hostname + (double)ut->ut_tv.tv_sec, // tstamp + py_user_proc, // (bool) user process + ut->ut_pid // process id + ); + if (! py_tuple) + goto error; + if (PyList_Append(py_retlist, py_tuple)) + goto error; + Py_CLEAR(py_username); + Py_CLEAR(py_tty); + Py_CLEAR(py_hostname); + Py_CLEAR(py_tuple); + } + endutent(); + return py_retlist; + +error: + Py_XDECREF(py_username); + Py_XDECREF(py_tty); + Py_XDECREF(py_hostname); + Py_XDECREF(py_tuple); + Py_DECREF(py_retlist); + endutent(); + return NULL; +} diff --git a/psutil/arch/linux/users.h b/psutil/arch/linux/users.h new file mode 100644 index 000000000..ba2735d1d --- /dev/null +++ b/psutil/arch/linux/users.h @@ -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 + +PyObject *psutil_users(PyObject* self, PyObject* args); diff --git a/setup.py b/setup.py index eef7bf455..6702268f8 100755 --- a/setup.py +++ b/setup.py @@ -297,7 +297,11 @@ def get_winver(): macros.append(("PSUTIL_LINUX", 1)) ext = Extension( 'psutil._psutil_linux', - sources=sources + ['psutil/_psutil_linux.c'], + sources=( + sources + + ["psutil/_psutil_linux.c"] + + glob.glob("psutil/arch/linux/*.c") + ), define_macros=macros, **py_limited_api) From 0c3a1c5a4e6a56ccf77aa15fc20d31334cd02f5e Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Tue, 24 Oct 2023 00:22:59 +0200 Subject: [PATCH 3/7] users() / Linux: make ":0" -> "localhost" host conversion in C instead of python --- psutil/_pslinux.py | 9 +-------- psutil/arch/linux/users.c | 17 ++++++++--------- psutil/tests/test_linux.py | 24 +++++------------------- 3 files changed, 14 insertions(+), 36 deletions(-) diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index 628cd4b35..d778075f2 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -1549,14 +1549,7 @@ def users(): retlist = [] rawlist = cext.users() for item in rawlist: - user, tty, hostname, tstamp, user_process, pid = item - # note: the underlying C function includes entries about - # system boot, run level and others. We might want - # to use them in the future. - if not user_process: - continue - if hostname in (':0.0', ':0'): - hostname = 'localhost' + user, tty, hostname, tstamp, pid = item nt = _common.suser(user, tty or None, hostname, tstamp, pid) retlist.append(nt) return retlist diff --git a/psutil/arch/linux/users.c b/psutil/arch/linux/users.c index 9ca010a19..546e29ee9 100644 --- a/psutil/arch/linux/users.c +++ b/psutil/arch/linux/users.c @@ -6,6 +6,7 @@ #include #include +#include #include "../../_psutil_common.h" @@ -18,35 +19,33 @@ psutil_users(PyObject *self, PyObject *args) { PyObject *py_username = NULL; PyObject *py_tty = NULL; PyObject *py_hostname = NULL; - PyObject *py_user_proc = NULL; if (py_retlist == NULL) return NULL; setutent(); while (NULL != (ut = getutent())) { + if (ut->ut_type != USER_PROCESS) + continue; py_tuple = NULL; - py_user_proc = NULL; - if (ut->ut_type == USER_PROCESS) - py_user_proc = Py_True; - else - py_user_proc = Py_False; py_username = PyUnicode_DecodeFSDefault(ut->ut_user); if (! py_username) goto error; py_tty = PyUnicode_DecodeFSDefault(ut->ut_line); if (! py_tty) goto error; - py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host); + if (strcmp(ut->ut_host, ":0") || strcmp(ut->ut_host, ":0.0")) + py_hostname = PyUnicode_DecodeFSDefault("localhost"); + else + py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host); if (! py_hostname) goto error; py_tuple = Py_BuildValue( - "OOOdO" _Py_PARSE_PID, + "OOOd" _Py_PARSE_PID, py_username, // username py_tty, // tty py_hostname, // hostname (double)ut->ut_tv.tv_sec, // tstamp - py_user_proc, // (bool) user process ut->ut_pid // process id ); if (! py_tuple) diff --git a/psutil/tests/test_linux.py b/psutil/tests/test_linux.py index 55839a1ba..12814ea87 100755 --- a/psutil/tests/test_linux.py +++ b/psutil/tests/test_linux.py @@ -1513,25 +1513,11 @@ def test_boot_time_mocked(self): psutil._pslinux.boot_time) assert m.called - def test_users_mocked(self): - # Make sure ':0' and ':0.0' (returned by C ext) are converted - # to 'localhost'. - with mock.patch('psutil._pslinux.cext.users', - return_value=[('giampaolo', 'pts/2', ':0', - 1436573184.0, True, 2)]) as m: - self.assertEqual(psutil.users()[0].host, 'localhost') - assert m.called - with mock.patch('psutil._pslinux.cext.users', - return_value=[('giampaolo', 'pts/2', ':0.0', - 1436573184.0, True, 2)]) as m: - self.assertEqual(psutil.users()[0].host, 'localhost') - assert m.called - # ...otherwise it should be returned as-is - with mock.patch('psutil._pslinux.cext.users', - return_value=[('giampaolo', 'pts/2', 'foo', - 1436573184.0, True, 2)]) as m: - self.assertEqual(psutil.users()[0].host, 'foo') - assert m.called + def test_users(self): + # Make sure the C extension converts ':0' and ':0.0' to + # 'localhost'. + for user in psutil.users(): + self.assertNotIn(user.host, (":0", ":0.0")) def test_procfs_path(self): tdir = self.get_testfn() From e9dabbbcae38a52a725722c6e3df0dc94c6051ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miro=20Hron=C4=8Dok?= Date: Tue, 24 Oct 2023 22:25:00 +0200 Subject: [PATCH 4/7] Include to avoid implicit-function-declaration for close and syscall (#2321) See https://docs.python.org/3.13/whatsnew/3.13.html """ Python.h no longer includes the standard header file. If needed, it should now be included explicitly. For example, it provides the functions: read(), write(), close(), isatty(), lseek(), getpid(), getcwd(), sysconf() and getpagesize(). """ --- psutil/_psutil_sunos.c | 1 + psutil/arch/linux/net.c | 1 + psutil/arch/linux/proc.c | 1 + psutil/arch/solaris/environ.c | 1 + 4 files changed, 4 insertions(+) diff --git a/psutil/_psutil_sunos.c b/psutil/_psutil_sunos.c index 11335993f..54f353c10 100644 --- a/psutil/_psutil_sunos.c +++ b/psutil/_psutil_sunos.c @@ -51,6 +51,7 @@ #include #include #include // fabs() +#include #include "_psutil_common.h" #include "_psutil_posix.h" diff --git a/psutil/arch/linux/net.c b/psutil/arch/linux/net.c index 522a55dfc..d193e9408 100644 --- a/psutil/arch/linux/net.c +++ b/psutil/arch/linux/net.c @@ -10,6 +10,7 @@ #include #include #include +#include // see: https://github.com/giampaolo/psutil/issues/659 #ifdef PSUTIL_ETHTOOL_MISSING_TYPES diff --git a/psutil/arch/linux/proc.c b/psutil/arch/linux/proc.c index ac87af917..b58a3ce2a 100644 --- a/psutil/arch/linux/proc.c +++ b/psutil/arch/linux/proc.c @@ -7,6 +7,7 @@ #include #include #include +#include #include "proc.h" #include "../../_psutil_common.h" diff --git a/psutil/arch/solaris/environ.c b/psutil/arch/solaris/environ.c index dd627eb00..4b4e041a4 100644 --- a/psutil/arch/solaris/environ.c +++ b/psutil/arch/solaris/environ.c @@ -19,6 +19,7 @@ #include #include #include +#include #include "environ.h" From 902fada98ef1899d86717e7b34be46485d55e016 Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Fri, 27 Oct 2023 00:00:32 +0200 Subject: [PATCH 5/7] Enforce ruff rule `raw-string-in-exception rule` (#2324) --- HISTORY.rst | 10 ++++++++++ docs/conf.py | 3 ++- psutil/__init__.py | 16 ++++++++++------ psutil/_compat.py | 15 +++++++++------ psutil/_psbsd.py | 3 ++- psutil/_pslinux.py | 10 ++++++---- psutil/_psposix.py | 4 +++- psutil/_pssunos.py | 3 ++- psutil/_pswindows.py | 9 ++++++--- psutil/tests/__init__.py | 2 +- pyproject.toml | 2 +- scripts/internal/winmake.py | 2 +- setup.py | 3 ++- 13 files changed, 55 insertions(+), 27 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index b9891788f..6044d8c50 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -1,5 +1,15 @@ *Bug tracker at https://github.com/giampaolo/psutil/issues* +5.9.7 (IN DEVELOPMENT) +====================== + +XXXX-XX-XX + +**Enhancements** + +- 2324_: enforce Ruff rule `raw-string-in-exception`, which helps providing + clearer tracebacks when exceptions are raised by psutil. + 5.9.6 ===== diff --git a/docs/conf.py b/docs/conf.py index 9e0434706..1079293de 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -44,7 +44,8 @@ def get_version(): assert num.isdigit(), ret return ret else: - raise ValueError("couldn't find version string") + msg = "couldn't find version string" + raise ValueError(msg) VERSION = get_version() diff --git a/psutil/__init__.py b/psutil/__init__.py index 5bf6501ab..6a5a5eedf 100644 --- a/psutil/__init__.py +++ b/psutil/__init__.py @@ -715,8 +715,8 @@ def username(self): if POSIX: if pwd is None: # might happen if python was installed from sources - raise ImportError( - "requires pwd module shipped with standard python") + msg = "requires pwd module shipped with standard python" + raise ImportError(msg) real_uid = self.uids().real try: return pwd.getpwuid(real_uid).pw_name @@ -803,7 +803,8 @@ def ionice(self, ioclass=None, value=None): """ if ioclass is None: if value is not None: - raise ValueError("'ioclass' argument must be specified") + msg = "'ioclass' argument must be specified" + raise ValueError(msg) return self._proc.ionice_get() else: self._raise_if_pid_reused() @@ -1198,10 +1199,12 @@ def _send_signal(self, sig): self._raise_if_pid_reused() if self.pid == 0: # see "man 2 kill" - raise ValueError( + msg = ( "preventing sending signal to process with PID 0 as it " "would affect every process in the process group of the " - "calling process (os.getpid()) instead of PID 0") + "calling process (os.getpid()) instead of PID 0" + ) + raise ValueError(msg) try: os.kill(self.pid, sig) except ProcessLookupError: @@ -1288,7 +1291,8 @@ def wait(self, timeout=None): To wait for multiple Process(es) use psutil.wait_procs(). """ if timeout is not None and not timeout >= 0: - raise ValueError("timeout must be a positive integer") + msg = "timeout must be a positive integer" + raise ValueError(msg) if self._exitcode is not _SENTINEL: return self._exitcode self._exitcode = self._proc.wait(timeout) diff --git a/psutil/_compat.py b/psutil/_compat.py index 95754113d..613bd8a2b 100644 --- a/psutil/_compat.py +++ b/psutil/_compat.py @@ -83,7 +83,8 @@ def super(type_=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1): # Get the function's first positional argument. type_or_obj = f.f_locals[f.f_code.co_varnames[0]] except (IndexError, KeyError): - raise RuntimeError('super() used in a function with no args') + msg = 'super() used in a function with no args' + raise RuntimeError(msg) try: # Get the MRO so we can crawl it. mro = type_or_obj.__mro__ @@ -91,7 +92,8 @@ def super(type_=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1): try: mro = type_or_obj.__class__.__mro__ except AttributeError: - raise RuntimeError('super() used in a non-newstyle class') + msg = 'super() used in a non-newstyle class' + raise RuntimeError(msg) for type_ in mro: # Find the class that owns the currently-executing method. for meth in type_.__dict__.values(): @@ -118,7 +120,8 @@ def super(type_=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1): continue break # found else: - raise RuntimeError('super() called outside a method') + msg = 'super() called outside a method' + raise RuntimeError(msg) # Dispatch to builtin super(). if type_or_obj is not _SENTINEL: @@ -199,9 +202,9 @@ def FileExistsError(inst): except FileExistsError: pass except OSError: - raise RuntimeError( - "broken or incompatible Python implementation, see: " - "https://github.com/giampaolo/psutil/issues/1659") + msg = ("broken or incompatible Python implementation, see: " + "https://github.com/giampaolo/psutil/issues/1659") + raise RuntimeError(msg) # --- stdlib additions diff --git a/psutil/_psbsd.py b/psutil/_psbsd.py index eeab18a9a..6653dc9e1 100644 --- a/psutil/_psbsd.py +++ b/psutil/_psbsd.py @@ -253,7 +253,8 @@ def per_cpu_times(): if cpu_count_logical() == 1: return [cpu_times()] if per_cpu_times.__called__: - raise NotImplementedError("supported only starting from FreeBSD 8") + msg = "supported only starting from FreeBSD 8" + raise NotImplementedError(msg) per_cpu_times.__called__ = True return [cpu_times()] diff --git a/psutil/_pslinux.py b/psutil/_pslinux.py index d778075f2..8c253d2f0 100644 --- a/psutil/_pslinux.py +++ b/psutil/_pslinux.py @@ -745,8 +745,8 @@ def cpu_freq(): # https://github.com/giampaolo/psutil/issues/1071 curr = bcat(pjoin(path, "cpuinfo_cur_freq"), fallback=None) if curr is None: - raise NotImplementedError( - "can't find current frequency file") + msg = "can't find current frequency file" + raise NotImplementedError(msg) curr = int(curr) / 1000 max_ = int(bcat(pjoin(path, "scaling_max_freq"))) / 1000 min_ = int(bcat(pjoin(path, "scaling_min_freq"))) / 1000 @@ -2157,7 +2157,8 @@ def ionice_set(self, ioclass, value): if value and ioclass in (IOPRIO_CLASS_IDLE, IOPRIO_CLASS_NONE): raise ValueError("%r ioclass accepts no value" % ioclass) if value < 0 or value > 7: - raise ValueError("value not in 0-7 range") + msg = "value not in 0-7 range" + raise ValueError(msg) return cext.proc_ioprio_set(self.pid, ioclass, value) if prlimit is not None: @@ -2168,7 +2169,8 @@ def rlimit(self, resource_, limits=None): # we don't want that. We should never get here though as # PID 0 is not supported on Linux. if self.pid == 0: - raise ValueError("can't use prlimit() against PID 0 process") + msg = "can't use prlimit() against PID 0 process" + raise ValueError(msg) try: if limits is None: # get diff --git a/psutil/_psposix.py b/psutil/_psposix.py index 1b26589db..408cd56c7 100644 --- a/psutil/_psposix.py +++ b/psutil/_psposix.py @@ -100,7 +100,9 @@ def wait_pid(pid, timeout=None, proc_name=None, timeout=0 is also possible (either return immediately or raise). """ if pid <= 0: - raise ValueError("can't wait for PID 0") # see "man waitpid" + # see "man waitpid" + msg = "can't wait for PID 0" + raise ValueError(msg) interval = 0.0001 flags = 0 if timeout is not None: diff --git a/psutil/_pssunos.py b/psutil/_pssunos.py index 291dc5a00..4061af014 100644 --- a/psutil/_pssunos.py +++ b/psutil/_pssunos.py @@ -151,7 +151,8 @@ def swap_memory(): lines = stdout.strip().split('\n')[1:] if not lines: - raise RuntimeError('no swap device(s) configured') + msg = 'no swap device(s) configured' + raise RuntimeError(msg) total = free = 0 for line in lines: line = line.split() diff --git a/psutil/_pswindows.py b/psutil/_pswindows.py index 6fd2b54bb..c536b24e3 100644 --- a/psutil/_pswindows.py +++ b/psutil/_pswindows.py @@ -891,9 +891,11 @@ def send_signal(self, sig): getattr(signal, "CTRL_BREAK_EVENT", object())): os.kill(self.pid, sig) else: - raise ValueError( + msg = ( "only SIGTERM, CTRL_C_EVENT and CTRL_BREAK_EVENT signals " - "are supported on Windows") + "are supported on Windows" + ) + raise ValueError(msg) @wrap_exceptions def wait(self, timeout=None): @@ -1045,7 +1047,8 @@ def ionice_get(self): @wrap_exceptions def ionice_set(self, ioclass, value): if value: - raise TypeError("value argument not accepted on Windows") + msg = "value argument not accepted on Windows" + raise TypeError(msg) if ioclass not in (IOPRIO_VERYLOW, IOPRIO_LOW, IOPRIO_NORMAL, IOPRIO_HIGH): raise ValueError("%s is not a valid priority" % ioclass) diff --git a/psutil/tests/__init__.py b/psutil/tests/__init__.py index d4d8faf6d..8e552e9ab 100644 --- a/psutil/tests/__init__.py +++ b/psutil/tests/__init__.py @@ -1806,7 +1806,7 @@ def reload_module(module): def import_module_by_path(path): name = os.path.splitext(os.path.basename(path))[0] - if sys.version_info[0] == 2: + if sys.version_info[0] < 3: import imp return imp.load_source(name, path) else: diff --git a/pyproject.toml b/pyproject.toml index d99de4271..6f2aeb032 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,7 +28,6 @@ ignore = [ "COM812", # Trailing comma missing "D", # pydocstyle "DTZ", # flake8-datetimez - "EM", # flake8-errmsg "ERA001", # Found commented-out code "FBT", # flake8-boolean-trap (makes zero sense) "FIX", # Line contains TODO / XXX / ..., consider resolving the issue @@ -71,6 +70,7 @@ ignore = [ [tool.ruff.per-file-ignores] # T201 == print(), T203 == pprint() ".github/workflows/*" = ["T201", "T203"] +"psutil/tests/*" = ["EM101"] # raw-string-in-exception "psutil/tests/runner.py" = ["T201", "T203"] "scripts/*" = ["T201", "T203"] "scripts/internal/*" = ["T201", "T203"] diff --git a/scripts/internal/winmake.py b/scripts/internal/winmake.py index 6978a7e61..931a2c001 100755 --- a/scripts/internal/winmake.py +++ b/scripts/internal/winmake.py @@ -46,7 +46,7 @@ "wheel", ] -if sys.version_info[0] == 2: +if sys.version_info[0] < 3: DEPS.append('mock') DEPS.append('ipaddress') DEPS.append('enum34') diff --git a/setup.py b/setup.py index 6702268f8..1995fa055 100755 --- a/setup.py +++ b/setup.py @@ -107,7 +107,8 @@ def get_version(): for num in ret.split('.'): assert num.isdigit(), ret return ret - raise ValueError("couldn't find version string") + msg = "couldn't find version string" + raise ValueError(msg) VERSION = get_version() From ccdb775abe50125a845362fd0b65f1189c02e78b Mon Sep 17 00:00:00 2001 From: Giampaolo Rodola Date: Thu, 9 Nov 2023 14:34:11 -0800 Subject: [PATCH 6/7] fix #2325: fix compilation on PyPy --- HISTORY.rst | 5 +++++ psutil/_psutil_common.c | 12 ++++++++++++ psutil/_psutil_common.h | 14 +++++++++----- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 6044d8c50..0c62d823c 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -10,6 +10,11 @@ XXXX-XX-XX - 2324_: enforce Ruff rule `raw-string-in-exception`, which helps providing clearer tracebacks when exceptions are raised by psutil. +**Bug fixes** + +- 2325_, [PyPy]: psutil did not compile on PyPy due to missing + `PyErr_SetExcFromWindowsErrWithFilenameObject` cPython API. + 5.9.6 ===== diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c index d8d78bf64..d78c140ec 100644 --- a/psutil/_psutil_common.c +++ b/psutil/_psutil_common.c @@ -58,6 +58,18 @@ PyErr_SetFromWindowsErrWithFilename(int winerr, const char *filename) { #endif // !defined(PyErr_SetFromWindowsErrWithFilename) +#if !defined(PyErr_SetExcFromWindowsErrWithFilenameObject) +PyObject * +PyErr_SetExcFromWindowsErrWithFilenameObject(PyObject *type, + int ierr, + PyObject *filename) { + // Original function is too complex. Just raise OSError without + // filename. + return PyErr_SetFromWindowsErrWithFilename(ierr, NULL); +} +#endif // !defined(PyErr_SetExcFromWindowsErrWithFilenameObject) + + // PyPy 2.7 #if !defined(PyErr_SetFromWindowsErr) PyObject * diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h index a425f8685..20c03ebd7 100644 --- a/psutil/_psutil_common.h +++ b/psutil/_psutil_common.h @@ -31,11 +31,15 @@ static const int PSUTIL_CONN_NONE = 128; #define PyUnicode_DecodeFSDefaultAndSize PyString_FromStringAndSize #endif -#if defined(PSUTIL_WINDOWS) && \ - defined(PYPY_VERSION) && \ - !defined(PyErr_SetFromWindowsErrWithFilename) - PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr, - const char *filename); +#if defined(PSUTIL_WINDOWS) && defined(PYPY_VERSION) + #if !defined(PyErr_SetFromWindowsErrWithFilename) + PyObject *PyErr_SetFromWindowsErrWithFilename(int ierr, + const char *filename); + #endif + #if !defined(PyErr_SetExcFromWindowsErrWithFilenameObject) + PyObject *PyErr_SetExcFromWindowsErrWithFilenameObject( + PyObject *type, int ierr, PyObject *filename); + #endif #endif // --- _Py_PARSE_PID From 4407540e720c46641622a33bb579430e047cd511 Mon Sep 17 00:00:00 2001 From: shawn Date: Wed, 29 Nov 2023 16:06:41 +0800 Subject: [PATCH 7/7] Update README.rst (#2330) fix psutil.cpu_times() output --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 98a911918..12bf64254 100644 --- a/README.rst +++ b/README.rst @@ -173,7 +173,7 @@ CPU >>> import psutil >>> >>> psutil.cpu_times() - scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, nice=0.0) + scputimes(user=3961.46, nice=169.729, system=2150.659, idle=16900.540, iowait=629.59, irq=0.0, softirq=19.42, steal=0.0, guest=0, guest_nice=0.0) >>> >>> for x in range(3): ... psutil.cpu_percent(interval=1)