blob: fe9663b6eb40019fb55e6b51975ee7da2182b304 [file] [log] [blame]
Andrew Geisslerfc113ea2023-03-31 09:59:46 -05001From 524e58f3b2f9e4702293af66f6768755b300e8d3 Mon Sep 17 00:00:00 2001
2From: Alexander Kanavin <alex@linutronix.de>
3Date: Thu, 2 Mar 2023 13:59:13 +0100
4Subject: [PATCH] Support procps 4.x
5
6Upstream-Status: Submitted [https://gitlab.freedesktop.org/drm/igt-gpu-tools/-/issues/116#note_1785522]
7Signed-off-by: Alexander Kanavin <alex@linutronix.de>
8---
9 lib/igt_aux.c | 238 ++++++++++++++++++++++++++++++++++++++++--------
10 lib/meson.build | 7 +-
11 meson.build | 10 +-
12 3 files changed, 215 insertions(+), 40 deletions(-)
13
14diff --git a/lib/igt_aux.c b/lib/igt_aux.c
15index 15e30440..d23c9a40 100644
16--- a/lib/igt_aux.c
17+++ b/lib/igt_aux.c
18@@ -52,8 +52,16 @@
19 #include <assert.h>
20 #include <grp.h>
21
22+#ifdef HAVE_LIBPROCPS
23 #include <proc/readproc.h>
24+#endif
25+#ifdef HAVE_LIBPROC2
26+#include <libproc2/pids.h>
27+#endif
28+
29 #include <libudev.h>
30+#include <linux/limits.h>
31+#include <dirent.h>
32
33 #include "drmtest.h"
34 #include "i915_drm.h"
35@@ -1217,6 +1225,7 @@ void igt_unlock_mem(void)
36 */
37 int igt_is_process_running(const char *comm)
38 {
39+#if HAVE_LIBPROCPS
40 PROCTAB *proc;
41 proc_t *proc_info;
42 bool found = false;
43@@ -1235,6 +1244,26 @@ int igt_is_process_running(const char *comm)
44
45 closeproc(proc);
46 return found;
47+#endif
48+#ifdef HAVE_LIBPROC2
49+ enum pids_item Item[] = { PIDS_CMD };
50+ struct pids_info *info = NULL;
51+ struct pids_stack *stack;
52+ char *pid_comm;
53+ bool found = false;
54+
55+ if (procps_pids_new(&info, Item, 1) < 0)
56+ return false;
57+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
58+ pid_comm = PIDS_VAL(0, str, stack, info);
59+ if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
60+ found = true;
61+ break;
62+ }
63+ }
64+ procps_pids_unref(&info);
65+ return found;
66+#endif
67 }
68
69 /**
70@@ -1251,6 +1280,7 @@ int igt_is_process_running(const char *comm)
71 */
72 int igt_terminate_process(int sig, const char *comm)
73 {
74+#ifdef HAVE_LIBPROCPS
75 PROCTAB *proc;
76 proc_t *proc_info;
77 int err = 0;
78@@ -1272,6 +1302,29 @@ int igt_terminate_process(int sig, const char *comm)
79
80 closeproc(proc);
81 return err;
82+#endif
83+#ifdef HAVE_LIBPROC2
84+ enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
85+ struct pids_info *info = NULL;
86+ struct pids_stack *stack;
87+ char *pid_comm;
88+ int pid;
89+ int err = 0;
90+
91+ if (procps_pids_new(&info, Items, 2) < 0)
92+ return -errno;
93+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
94+ pid = PIDS_VAL(0, s_int, stack, info);
95+ pid_comm = PIDS_VAL(1, str, stack, info);
96+ if (!strncasecmp(pid_comm, comm, strlen(pid_comm))) {
97+ if (kill(pid, sig) < 0)
98+ err = -errno;
99+ break;
100+ }
101+ }
102+ procps_pids_unref(&info);
103+ return err;
104+#endif
105 }
106
107 struct pinfo {
108@@ -1341,9 +1394,9 @@ igt_show_stat_header(void)
109 }
110
111 static void
112-igt_show_stat(proc_t *info, int *state, const char *fn)
113+igt_show_stat(const pid_t tid, const char *cmd, int *state, const char *fn)
114 {
115- struct pinfo p = { .pid = info->tid, .comm = info->cmd, .fn = fn };
116+ struct pinfo p = { .pid = tid, .comm = cmd, .fn = fn };
117
118 if (!*state)
119 igt_show_stat_header();
120@@ -1353,7 +1406,7 @@ igt_show_stat(proc_t *info, int *state, const char *fn)
121 }
122
123 static void
124-__igt_lsof_fds(proc_t *proc_info, int *state, char *proc_path, const char *dir)
125+__igt_lsof_fds(const pid_t tid, const char *cmd, int *state, char *proc_path, const char *dir)
126 {
127 struct dirent *d;
128 struct stat st;
129@@ -1400,7 +1453,7 @@ again:
130 dirn = dirname(copy_fd_lnk);
131
132 if (!strncmp(dir, dirn, strlen(dir)))
133- igt_show_stat(proc_info, state, fd_lnk);
134+ igt_show_stat(tid, cmd, state, fd_lnk);
135
136 free(copy_fd_lnk);
137 free(fd_lnk);
138@@ -1416,13 +1469,14 @@ again:
139 static void
140 __igt_lsof(const char *dir)
141 {
142- PROCTAB *proc;
143- proc_t *proc_info;
144-
145 char path[30];
146 char *name_lnk;
147 struct stat st;
148 int state = 0;
149+#ifdef HAVE_LIBPROCPS
150+ PROCTAB *proc;
151+ proc_t *proc_info;
152+
153
154 proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
155 igt_assert(proc != NULL);
156@@ -1456,6 +1510,44 @@ __igt_lsof(const char *dir)
157 }
158
159 closeproc(proc);
160+#endif
161+#ifdef HAVE_LIBPROC2
162+ enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
163+ struct pids_info *info = NULL;
164+ struct pids_stack *stack;
165+
166+ if (procps_pids_new(&info, Items, 2) < 0)
167+ return;
168+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
169+ ssize_t read;
170+ int tid = PIDS_VAL(0, s_int, stack, info);
171+ char *pid_comm = PIDS_VAL(1, str, stack, info);
172+
173+ /* check current working directory */
174+ memset(path, 0, sizeof(path));
175+ snprintf(path, sizeof(path), "/proc/%d/cwd", tid);
176+
177+ if (stat(path, &st) == -1)
178+ continue;
179+
180+ name_lnk = malloc(st.st_size + 1);
181+
182+ igt_assert((read = readlink(path, name_lnk, st.st_size + 1)));
183+ name_lnk[read] = '\0';
184+
185+ if (!strncmp(dir, name_lnk, strlen(dir)))
186+ igt_show_stat(tid, pid_comm, &state, name_lnk);
187+
188+ /* check also fd, seems that lsof(8) doesn't look here */
189+ memset(path, 0, sizeof(path));
190+ snprintf(path, sizeof(path), "/proc/%d/fd", tid);
191+
192+ __igt_lsof_fds(tid, pid_comm, &state, path, dir);
193+
194+ free(name_lnk);
195+ }
196+ procps_pids_unref(&info);
197+#endif
198 }
199
200 /**
201@@ -1490,7 +1582,7 @@ igt_lsof(const char *dpath)
202 free(sanitized);
203 }
204
205-static void pulseaudio_unload_module(proc_t *proc_info)
206+static void pulseaudio_unload_module(const uid_t euid, const gid_t egid)
207 {
208 struct igt_helper_process pa_proc = {};
209 char xdg_dir[PATH_MAX];
210@@ -1498,14 +1590,14 @@ static void pulseaudio_unload_module(proc_t *proc_info)
211 struct passwd *pw;
212
213 igt_fork_helper(&pa_proc) {
214- pw = getpwuid(proc_info->euid);
215+ pw = getpwuid(euid);
216 homedir = pw->pw_dir;
217- snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
218+ snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
219
220 igt_info("Request pulseaudio to stop using audio device\n");
221
222- setgid(proc_info->egid);
223- setuid(proc_info->euid);
224+ setgid(egid);
225+ setuid(euid);
226 clearenv();
227 setenv("HOME", homedir, 1);
228 setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
229@@ -1524,10 +1616,12 @@ static void pipewire_reserve_wait(void)
230 char xdg_dir[PATH_MAX];
231 const char *homedir;
232 struct passwd *pw;
233- proc_t *proc_info;
234- PROCTAB *proc;
235+ int tid=0, euid, egid;
236
237+#ifdef HAVE_LIBPROCPS
238 igt_fork_helper(&pw_reserve_proc) {
239+ proc_t *proc_info;
240+ PROCTAB *proc;
241 igt_info("Preventing pipewire-pulse to use the audio drivers\n");
242
243 proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
244@@ -1539,21 +1633,44 @@ static void pipewire_reserve_wait(void)
245 freeproc(proc_info);
246 }
247 closeproc(proc);
248+ tid = proc_info->tid;
249+ euid = proc_info->euid;
250+ egid = proc_info->egid;
251+ freeproc(proc_info);
252+#endif
253+#ifdef HAVE_LIBPROC2
254+ igt_fork(child, 1) {
255+ enum pids_item Items[] = { PIDS_ID_PID, PIDS_ID_EUID, PIDS_ID_EGID };
256+ enum rel_items { EU_PID, EU_EUID, EU_EGID };
257+ struct pids_info *info = NULL;
258+ struct pids_stack *stack;
259+
260+ igt_info("Preventing pipewire-pulse to use the audio drivers\n");
261+
262+ if (procps_pids_new(&info, Items, 3) < 0)
263+ return;
264+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
265+ tid = PIDS_VAL(EU_PID, s_int, stack, info);
266+ if (pipewire_pulse_pid == tid)
267+ break;
268+ }
269+ euid = PIDS_VAL(EU_EUID, s_int, stack, info);
270+ egid = PIDS_VAL(EU_EGID, s_int, stack, info);
271+ procps_pids_unref(&info);
272+#endif
273
274 /* Sanity check: if it can't find the process, it means it has gone */
275- if (pipewire_pulse_pid != proc_info->tid)
276+ if (pipewire_pulse_pid != tid)
277 exit(0);
278
279- pw = getpwuid(proc_info->euid);
280+ pw = getpwuid(euid);
281 homedir = pw->pw_dir;
282- snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", proc_info->euid);
283- setgid(proc_info->egid);
284- setuid(proc_info->euid);
285+ snprintf(xdg_dir, sizeof(xdg_dir), "/run/user/%d", euid);
286+ setgid(egid);
287+ setuid(euid);
288 clearenv();
289 setenv("HOME", homedir, 1);
290 setenv("XDG_RUNTIME_DIR",xdg_dir, 1);
291- freeproc(proc_info);
292-
293 /*
294 * pw-reserve will run in background. It will only exit when
295 * igt_kill_children() is called later on. So, it shouldn't
296@@ -1570,9 +1687,7 @@ static void pipewire_reserve_wait(void)
297 int pipewire_pulse_start_reserve(void)
298 {
299 bool is_pw_reserve_running = false;
300- proc_t *proc_info;
301 int attempts = 0;
302- PROCTAB *proc;
303
304 if (!pipewire_pulse_pid)
305 return 0;
306@@ -1584,6 +1699,10 @@ int pipewire_pulse_start_reserve(void)
307 * pipewire version 0.3.50 or upper.
308 */
309 for (attempts = 0; attempts < PIPEWIRE_RESERVE_MAX_TIME; attempts++) {
310+#ifdef HAVE_LIBPROCPS
311+ proc_t *proc_info;
312+ PROCTAB *proc;
313+
314 usleep(1000);
315 proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
316 igt_assert(proc != NULL);
317@@ -1598,6 +1717,25 @@ int pipewire_pulse_start_reserve(void)
318 freeproc(proc_info);
319 }
320 closeproc(proc);
321+#endif
322+#ifdef HAVE_LIBPROC2
323+ enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD };
324+ struct pids_info *info = NULL;
325+ struct pids_stack *stack;
326+
327+ usleep(1000);
328+
329+ if (procps_pids_new(&info, Items, 2) < 0)
330+ return 1;
331+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
332+ if (!strcmp(PIDS_VAL(1, str, stack, info), "pw-reserve")) {
333+ is_pw_reserve_running = true;
334+ pipewire_pw_reserve_pid = PIDS_VAL(0, s_int, stack, info);
335+ break;
336+ }
337+ }
338+ procps_pids_unref(&info);
339+#endif
340 if (is_pw_reserve_running)
341 break;
342 }
343@@ -1645,7 +1783,7 @@ void pipewire_pulse_stop_reserve(void)
344 * If the check fails, it means that the process can simply be killed.
345 */
346 static int
347-__igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
348+__igt_lsof_audio_and_kill_proc(const pid_t tid, const char *cmd, const uid_t euid, const gid_t egid, char *proc_path)
349 {
350 const char *audio_dev = "/dev/snd/";
351 char path[PATH_MAX * 2];
352@@ -1670,10 +1808,10 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
353 * 2) unload/unbind the the audio driver(s);
354 * 3) stop the pw-reserve thread.
355 */
356- if (!strcmp(proc_info->cmd, "pipewire-pulse")) {
357+ if (!strcmp(cmd, "pipewire-pulse")) {
358 igt_info("process %d (%s) is using audio device. Should be requested to stop using them.\n",
359- proc_info->tid, proc_info->cmd);
360- pipewire_pulse_pid = proc_info->tid;
361+ tid, cmd);
362+ pipewire_pulse_pid = tid;
363 return 0;
364 }
365 /*
366@@ -1685,9 +1823,9 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
367 * will respawn them. So, just ignore here, they'll honor pw-reserve,
368 * when the time comes.
369 */
370- if (!strcmp(proc_info->cmd, "pipewire-media-session"))
371+ if (!strcmp(cmd, "pipewire-media-session"))
372 return 0;
373- if (!strcmp(proc_info->cmd, "wireplumber"))
374+ if (!strcmp(cmd, "wireplumber"))
375 return 0;
376
377 dp = opendir(proc_path);
378@@ -1723,22 +1861,22 @@ __igt_lsof_audio_and_kill_proc(proc_t *proc_info, char *proc_path)
379 * enough to unbind audio modules and won't cause race issues
380 * with systemd trying to reload it.
381 */
382- if (!strcmp(proc_info->cmd, "pulseaudio")) {
383- pulseaudio_unload_module(proc_info);
384+ if (!strcmp(cmd, "pulseaudio")) {
385+ pulseaudio_unload_module(euid, egid);
386 break;
387 }
388
389 /* For all other processes, just kill them */
390 igt_info("process %d (%s) is using audio device. Should be terminated.\n",
391- proc_info->tid, proc_info->cmd);
392+ tid, cmd);
393
394- if (kill(proc_info->tid, SIGTERM) < 0) {
395+ if (kill(tid, SIGTERM) < 0) {
396 igt_info("Fail to terminate %s (pid: %d) with SIGTERM\n",
397- proc_info->cmd, proc_info->tid);
398- if (kill(proc_info->tid, SIGABRT) < 0) {
399+ cmd, tid);
400+ if (kill(tid, SIGABRT) < 0) {
401 fail++;
402 igt_info("Fail to terminate %s (pid: %d) with SIGABRT\n",
403- proc_info->cmd, proc_info->tid);
404+ cmd, tid);
405 }
406 }
407
408@@ -1760,9 +1898,10 @@ int
409 igt_lsof_kill_audio_processes(void)
410 {
411 char path[PATH_MAX];
412+ int fail = 0;
413+#ifdef HAVE_LIBPROCPS
414 proc_t *proc_info;
415 PROCTAB *proc;
416- int fail = 0;
417
418 proc = openproc(PROC_FILLCOM | PROC_FILLSTAT | PROC_FILLARG);
419 igt_assert(proc != NULL);
420@@ -1772,12 +1911,35 @@ igt_lsof_kill_audio_processes(void)
421 if (snprintf(path, sizeof(path), "/proc/%d/fd", proc_info->tid) < 1)
422 fail++;
423 else
424- fail += __igt_lsof_audio_and_kill_proc(proc_info, path);
425+ fail += __igt_lsof_audio_and_kill_proc(proc_info->pid, proc_info->cmd, proc_info->euid, proc_info->egid, path);
426
427 freeproc(proc_info);
428 }
429 closeproc(proc);
430+#endif
431+#ifdef HAVE_LIBPROC2
432+ enum pids_item Items[] = { PIDS_ID_PID, PIDS_CMD, PIDS_ID_EUID, PIDS_ID_EGID };
433+ enum rel_items { EU_PID, EU_CMD, EU_EUID, EU_EGID };
434+ struct pids_info *info = NULL;
435+ struct pids_stack *stack;
436+ pid_t tid;
437+
438+ if (procps_pids_new(&info, Items, 4) < 0)
439+ return 1;
440+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
441+ tid = PIDS_VAL(EU_PID, s_int, stack, info);
442
443+ if (snprintf(path, sizeof(path), "/proc/%d/fd", tid) < 1)
444+ fail++;
445+ else
446+ fail += __igt_lsof_audio_and_kill_proc(tid,
447+ PIDS_VAL(EU_CMD, str, stack, info),
448+ PIDS_VAL(EU_EUID, s_int, stack, info),
449+ PIDS_VAL(EU_EGID, s_int, stack, info),
450+ path);
451+ }
452+ procps_pids_unref(&info);
453+#endif
454 return fail;
455 }
456
457diff --git a/lib/meson.build b/lib/meson.build
458index cc784686..90591e0e 100644
459--- a/lib/meson.build
460+++ b/lib/meson.build
461@@ -105,7 +105,6 @@ lib_deps = [
462 libdrm,
463 libdw,
464 libkmod,
465- libprocps,
466 libudev,
467 math,
468 pciaccess,
469@@ -169,6 +168,12 @@ if chamelium.found()
470 lib_sources += 'monitor_edids/monitor_edids_helper.c'
471 endif
472
473+if libprocps.found()
474+ lib_deps += libprocps
475+else
476+ lib_deps += libproc2
477+endif
478+
479 if get_option('srcdir') != ''
480 srcdir = join_paths(get_option('srcdir'), 'tests')
481 else
482diff --git a/meson.build b/meson.build
483index e7a68503..309b0af3 100644
484--- a/meson.build
485+++ b/meson.build
486@@ -120,7 +120,15 @@ build_info += 'With libdrm: ' + ','.join(libdrm_info)
487
488 pciaccess = dependency('pciaccess', version : '>=0.10')
489 libkmod = dependency('libkmod')
490-libprocps = dependency('libprocps', required : true)
491+libprocps = dependency('libprocps', required : false)
492+libproc2 = dependency('libproc2', required : false)
493+if libprocps.found()
494+ config.set('HAVE_LIBPROCPS', 1)
495+elif libproc2.found()
496+ config.set('HAVE_LIBPROC2', 1)
497+else
498+ error('Either libprocps or libproc2 is required')
499+endif
500
501 libunwind = dependency('libunwind', required : get_option('libunwind'))
502 build_info += 'With libunwind: @0@'.format(libunwind.found())