From 89b2d1896ffa8e5c9a8d9b89c3af0bb9027ce23b Mon Sep 17 00:00:00 2001 From: Alberto Planas Date: Tue, 22 Aug 2023 15:54:09 +0200 Subject: [PATCH] Use logind instead of utmp because of Y2038 Bi-arch systems line x86-64 present the Y2038 problem, where an overflow can be produced because some glibc compatibility decissions (see https://github.com/thkukuk/utmpx/blob/main/Y2038.md for more information) This patch uses logind from systemd instead of utmp on Linux systems, if the systemd version is support the new API (>= 254). Signed-off-by: Alberto Planas --- INSTALL.rst | 4 +-- psutil/_psutil_linux.c | 81 ++++++++++++++++++++++++++++++++++++++++-- setup.py | 22 ++++++++++++ 3 files changed, 102 insertions(+), 5 deletions(-) Index: psutil-5.9.5/INSTALL.rst =================================================================== --- psutil-5.9.5.orig/INSTALL.rst +++ psutil-5.9.5/INSTALL.rst @@ -17,12 +17,12 @@ Linux (build) Ubuntu / Debian:: - sudo apt-get install gcc python3-dev + sudo apt-get install gcc python3-dev libsystemd-dev pip install --no-binary :all: psutil RedHat / CentOS:: - sudo yum install gcc python3-devel + sudo yum install gcc python3-devel systemd-devel pip install --no-binary :all: psutil Alpine:: Index: psutil-5.9.5/psutil/_psutil_linux.c =================================================================== --- psutil-5.9.5.orig/psutil/_psutil_linux.c +++ psutil-5.9.5/psutil/_psutil_linux.c @@ -14,7 +14,11 @@ #include #include #include -#include +#ifdef SYSTEMD_LINUX + #include +#else + #include +#endif #include #include #include @@ -363,42 +367,102 @@ psutil_proc_cpu_affinity_set(PyObject *s */ static PyObject * psutil_users(PyObject *self, PyObject *args) { +#ifdef SYSTEMD_LINUX + char **sessions_list = NULL; +#else struct utmp *ut; +#endif 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; + double tstamp = 0.0; + pid_t pid = 0; if (py_retlist == NULL) return NULL; +#ifdef SYSTEMD_LINUX + int sessions = sd_get_sessions(&sessions_list); + for (int i = 0; i < sessions; i++) { + const char *session_id = sessions_list[i]; +#else setutent(); while (NULL != (ut = getutent())) { +#endif py_tuple = NULL; py_user_proc = NULL; + #ifdef SYSTEMD_LINUX + py_user_proc = Py_True; + #else if (ut->ut_type == USER_PROCESS) py_user_proc = Py_True; else py_user_proc = Py_False; + #endif + + #ifdef SYSTEMD_LINUX + char *username = NULL; + if (sd_session_get_username(session_id, &username) < 0) + goto error; + py_username = PyUnicode_DecodeFSDefault(username); + free(username); + #else py_username = PyUnicode_DecodeFSDefault(ut->ut_user); + #endif if (! py_username) goto error; + + #ifdef SYSTEMD_LINUX + char *tty = NULL; + if (sd_session_get_tty(session_id, &tty) < 0) { + py_tty = PyUnicode_DecodeFSDefault("n/a"); + } else { + py_tty = PyUnicode_DecodeFSDefault(tty); + free(tty); + } + #else py_tty = PyUnicode_DecodeFSDefault(ut->ut_line); + #endif if (! py_tty) goto error; + #ifdef SYSTEMD_LINUX + char *hostname = NULL; + if (sd_session_get_remote_host(session_id, &hostname) < 0) + goto error; + py_hostname = PyUnicode_DecodeFSDefault(hostname); + free(hostname); + #else py_hostname = PyUnicode_DecodeFSDefault(ut->ut_host); + #endif if (! py_hostname) goto error; + #ifdef SYSTEMD_LINUX + uint64_t usec = 0; + if (sd_session_get_start_time(session_id, &usec) < 0) + goto error; + tstamp = (double)usec / 1000000.0; + #else + tstamp = (double)ut->ut_tv.tv_sec; + #endif + + #ifdef SYSTEMD_LINUX + if (sd_session_get_leader(session_id, &pid) < 0) + goto error; + #else + pid = ut->ut_pid; + #endif + py_tuple = Py_BuildValue( "OOOdO" _Py_PARSE_PID, py_username, // username py_tty, // tty py_hostname, // hostname - (double)ut->ut_tv.tv_sec, // tstamp + tstamp, // tstamp py_user_proc, // (bool) user process - ut->ut_pid // process id + pid // process id ); if (! py_tuple) goto error; @@ -408,8 +472,15 @@ psutil_users(PyObject *self, PyObject *a Py_CLEAR(py_tty); Py_CLEAR(py_hostname); Py_CLEAR(py_tuple); + #ifdef SYSTEMD_LINUX + free (sessions_list[i]); + #endif } +#ifdef SYSTEMD_LINUX + free(sessions_list); +#else endutent(); +#endif return py_retlist; error: @@ -418,7 +489,11 @@ error: Py_XDECREF(py_hostname); Py_XDECREF(py_tuple); Py_DECREF(py_retlist); +#ifdef SYSTEMD_LINUX + free(sessions_list); +#else endutent(); +#endif return NULL; } Index: psutil-5.9.5/setup.py =================================================================== --- psutil-5.9.5.orig/setup.py +++ psutil-5.9.5/setup.py @@ -184,6 +184,20 @@ def unix_can_compile(c_code): shutil.rmtree(tempdir) +def get_systemd_version(): + r = subprocess.run(["systemctl", "--version"], capture_output=True) + if r.returncode != 0: + return 0 + out = r.stdout.split() + if len(out) < 2: + return 0 + version = out[1] + try: + return int(version) + except ValueError: + return 0 + + if WINDOWS: def get_winver(): maj, min = sys.getwindowsversion()[0:2] @@ -302,10 +316,18 @@ elif LINUX: if not unix_can_compile("#include "): macros.append(("PSUTIL_ETHTOOL_MISSING_TYPES", 1)) + libraries = [] + # Systemd >= 254 can replace utmp. See: + # https://github.com/thkukuk/utmpx/blob/main/utmp-to-logind.md + if get_systemd_version() >= 254: + macros.append(("SYSTEMD_LINUX", 1)) + libraries.append("systemd") + macros.append(("PSUTIL_LINUX", 1)) ext = Extension( 'psutil._psutil_linux', sources=sources + ['psutil/_psutil_linux.c'], + libraries=libraries, define_macros=macros, **py_limited_api)