From 50d51999dc577096ad6f87abb4d9724f6883dc9e 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 | 79 ++++++++++++++++++++++++++++++++++++++++-- setup.py | 22 ++++++++++++ 3 files changed, 100 insertions(+), 5 deletions(-) diff --git a/INSTALL.rst b/INSTALL.rst index 2e8a1cd512..959f1da0ef 100644 --- a/INSTALL.rst +++ b/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:: diff --git a/psutil/_psutil_linux.c b/psutil/_psutil_linux.c index 3e6b3b9004..1a2693a010 100644 --- a/psutil/_psutil_linux.c +++ b/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,100 @@ psutil_proc_cpu_affinity_set(PyObject *self, PyObject *args) { */ 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) + goto error; + 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 +470,15 @@ psutil_users(PyObject *self, PyObject *args) { 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 +487,11 @@ psutil_users(PyObject *self, PyObject *args) { Py_XDECREF(py_hostname); Py_XDECREF(py_tuple); Py_DECREF(py_retlist); +#ifdef SYSTEMD_LINUX + free(sessions_list); +#else endutent(); +#endif return NULL; } diff --git a/setup.py b/setup.py index 35467e131a..086a562db1 100755 --- a/setup.py +++ b/setup.py @@ -192,6 +192,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] @@ -293,10 +307,18 @@ def get_winver(): 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)