Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use logind instead of utmp because of Y2038 #2300

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
4 changes: 3 additions & 1 deletion psutil/_pslinux.py
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,9 @@ def multi_bcat(*paths):
def users():
"""Return currently connected users as a list of namedtuples."""
retlist = []
rawlist = cext.users()
rawlist = cext.users_systemd()
if rawlist is None:
rawlist = cext.users_utmp()
for item in rawlist:
user, tty, hostname, tstamp, pid = item
nt = _common.suser(user, tty or None, hostname, tstamp, pid)
Expand Down
3 changes: 2 additions & 1 deletion psutil/_psutil_linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ static PyMethodDef mod_methods[] = {
#endif
// --- system related functions
{"disk_partitions", psutil_disk_partitions, METH_VARARGS},
{"users", psutil_users, METH_VARARGS},
{"users_systemd", psutil_users_systemd, METH_VARARGS},
{"users_utmp", psutil_users_utmp, METH_VARARGS},
{"net_if_duplex_speed", psutil_net_if_duplex_speed, METH_VARARGS},
// --- linux specific
{"linux_sysinfo", psutil_linux_sysinfo, METH_VARARGS},
Expand Down
180 changes: 179 additions & 1 deletion psutil/arch/linux/users.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,192 @@
*/

#include <Python.h>
#include <dlfcn.h>
#include <utmp.h>
#include <stdlib.h>
#include <string.h>

#include "../../_psutil_common.h"


// Systemd function signatures that will be loaded dynamically.
int (*sd_booted)(void);
int (*sd_get_sessions)(char ***);
int (*sd_session_get_leader)(const char *, pid_t *);
int (*sd_session_get_remote_host)(const char *,char **);
int (*sd_session_get_start_time)(const char *, uint64_t *);
int (*sd_session_get_tty)(const char *, char **);
int (*sd_session_get_username)(const char *, char **);

// Handle for the libsystemd library
void *HANDLE = NULL;


#define dlsym_check(__h, __fn, __name) do { \
__fn = dlsym(__h, #__fn); \
if (dlerror() != NULL || __fn == NULL) { \
psutil_debug("missing '%s' fun", __name); \
dlclose(__h); \
return NULL; \
} \
} while (0)


static void *
load_systemd() {
void *handle = NULL;

if (HANDLE != NULL)
return HANDLE;

handle = dlopen("libsystemd.so.0", RTLD_LAZY);
if (dlerror() != NULL || handle == NULL) {
psutil_debug("can't open libsystemd.so.0");
return NULL;
aplanas marked this conversation as resolved.
Show resolved Hide resolved
}

dlsym_check(handle, sd_booted, "sd_booted");
dlsym_check(handle, sd_get_sessions, "sd_get_sessions");
dlsym_check(handle, sd_session_get_leader, "sd_session_get_leader");
dlsym_check(handle, sd_session_get_remote_host, "sd_session_get_remote_host");
dlsym_check(handle, sd_session_get_start_time, "sd_session_get_start_time");
dlsym_check(handle, sd_session_get_tty, "sd_session_get_tty");
dlsym_check(handle, sd_session_get_username, "sd_session_get_username");

if (! sd_booted()) {
psutil_debug("systemd not booted");
dlclose(handle);
aplanas marked this conversation as resolved.
Show resolved Hide resolved
return NULL;
}

HANDLE = handle;
return HANDLE;
}

static void
set_systemd_errno(const char *syscall, int neg_errno) {
PyObject *exc;
int pos_errno;
char fullmsg[1024];

pos_errno = abs(neg_errno);
snprintf(fullmsg, 1024, "%s (originated from %s)", strerror(pos_errno), syscall);
exc = PyObject_CallFunction(PyExc_OSError, "(is)", pos_errno, fullmsg);
PyErr_SetObject(PyExc_OSError, exc);
Py_XDECREF(exc);
}


PyObject *
psutil_users_systemd(PyObject *self, PyObject *args) {
int ret;
PyObject *py_retlist = NULL;
PyObject *py_tuple = NULL;
PyObject *py_username = NULL;
PyObject *py_tty = NULL;
PyObject *py_hostname = NULL;
double tstamp = 0.0;
pid_t pid = 0;
int sessions;
char **sessions_list = NULL;
const char *session_id = NULL;
char *username = NULL;
char *tty = NULL;
char *hostname = NULL;
uint64_t usec = 0;
void *handle = load_systemd();

if (! handle)
Py_RETURN_NONE;

py_retlist = PyList_New(0);
if (py_retlist == NULL)
goto error;

sessions = sd_get_sessions(&sessions_list);
for (int i = 0; i < sessions; i++) {
session_id = sessions_list[i];
py_tuple = NULL;

username = NULL;
if ((ret = sd_session_get_username(session_id, &username)) < 0) {
set_systemd_errno("sd_session_get_username", ret);
goto error;
}
py_username = PyUnicode_DecodeFSDefault(username);
free(username);
if (! py_username)
goto error;

tty = NULL;
if (sd_session_get_tty(session_id, &tty) < 0) {
py_tty = PyUnicode_DecodeFSDefault("");
}
else {
py_tty = PyUnicode_DecodeFSDefault(tty);
free(tty);
}
if (! py_tty)
goto error;

hostname = NULL;
if (sd_session_get_remote_host(session_id, &hostname) < 0) {
py_hostname = PyUnicode_DecodeFSDefault("");
}
else {
py_hostname = PyUnicode_DecodeFSDefault(hostname);
free(hostname);
}
if (! py_hostname)
goto error;

usec = 0;
if ((ret = sd_session_get_start_time(session_id, &usec)) < 0) {
set_systemd_errno("sd_session_get_start_time", ret);
goto error;
}
tstamp = (double)usec / 1000000.0;

if ((ret = sd_session_get_leader(session_id, &pid)) < 0) {
set_systemd_errno("sd_session_get_leader", ret);
goto error;
}

py_tuple = Py_BuildValue(
"OOOd" _Py_PARSE_PID,
py_username, // username
py_tty, // tty
py_hostname, // hostname
tstamp, // tstamp
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);
free(sessions_list[i]);
}
free(sessions_list);
return py_retlist;

error:
Py_XDECREF(py_username);
Py_XDECREF(py_tty);
Py_XDECREF(py_hostname);
Py_XDECREF(py_tuple);
Py_DECREF(py_retlist);
if (sessions_list)
free(sessions_list);
return NULL;
}


PyObject *
psutil_users(PyObject *self, PyObject *args) {
psutil_users_utmp(PyObject *self, PyObject *args) {
struct utmp *ut;
PyObject *py_retlist = PyList_New(0);
PyObject *py_tuple = NULL;
Expand Down
3 changes: 2 additions & 1 deletion psutil/arch/linux/users.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@

#include <Python.h>

PyObject *psutil_users(PyObject* self, PyObject* args);
PyObject *psutil_users_systemd(PyObject* self, PyObject* args);
PyObject *psutil_users_utmp(PyObject* self, PyObject* args);
8 changes: 8 additions & 0 deletions psutil/tests/test_memleaks.py
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,14 @@ def test_win_service_get_description(self):
name = next(psutil.win_service_iter()).name()
self.execute(lambda: cext.winservice_query_descr(name))

if LINUX:

def test_users_systemd(self):
self.execute(cext.users_systemd)

def test_users_utmp(self):
self.execute(cext.users_utmp)


if __name__ == '__main__':
from psutil.tests.runner import run_from_name
Expand Down