2 * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org>
4 * procutils.c: General purpose procfs parsing utilities
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Library Public License for more details.
22 #include <sys/types.h>
26 #include "procutils.h"
27 #include "fileutils.h"
32 * @pid: process ID for which we want to obtain the threads group
34 * Returns: newly allocated tasks structure
36 struct proc_tasks
*proc_open_tasks(pid_t pid
)
38 struct proc_tasks
*tasks
;
41 sprintf(path
, "/proc/%d/task/", pid
);
43 tasks
= malloc(sizeof(struct proc_tasks
));
45 tasks
->dir
= opendir(path
);
55 * @tasks: allocated tasks structure
59 void proc_close_tasks(struct proc_tasks
*tasks
)
61 if (tasks
&& tasks
->dir
)
67 * @tasks: allocated task structure
68 * @tid: [output] one of the thread IDs belonging to the thread group
69 * If when an error occurs, it is set to 0.
71 * Returns: 0 on success, 1 on end, -1 on failure or no more threads
73 int proc_next_tid(struct proc_tasks
*tasks
, pid_t
*tid
)
85 d
= readdir(tasks
->dir
);
87 return errno
? -1 : 1; /* error or end-of-dir */
89 if (!isdigit((unsigned char) *d
->d_name
))
92 *tid
= (pid_t
) strtol(d
->d_name
, &end
, 10);
93 if (errno
|| d
->d_name
== end
|| (end
&& *end
))
101 /* returns process command path, use free() for result */
102 static char *proc_file_strdup(pid_t pid
, const char *name
)
104 char buf
[BUFSIZ
], *res
= NULL
;
109 snprintf(buf
, sizeof(buf
), "/proc/%d/%s", (int) pid
, name
);
110 fd
= open(buf
, O_RDONLY
);
114 sz
= read_all(fd
, buf
, sizeof(buf
));
118 for (i
= 0; i
< (size_t) sz
; i
++) {
131 /* returns process command path, use free() for result */
132 char *proc_get_command(pid_t pid
)
134 return proc_file_strdup(pid
, "cmdline");
137 /* returns process command name, use free() for result */
138 char *proc_get_command_name(pid_t pid
)
140 return proc_file_strdup(pid
, "comm");
143 struct proc_processes
*proc_open_processes(void)
145 struct proc_processes
*ps
;
147 ps
= calloc(1, sizeof(struct proc_processes
));
149 ps
->dir
= opendir("/proc");
158 void proc_close_processes(struct proc_processes
*ps
)
165 void proc_processes_filter_by_name(struct proc_processes
*ps
, const char *name
)
167 ps
->fltr_name
= name
;
168 ps
->has_fltr_name
= name
? 1 : 0;
171 void proc_processes_filter_by_uid(struct proc_processes
*ps
, uid_t uid
)
174 ps
->has_fltr_uid
= 1;
177 int proc_next_pid(struct proc_processes
*ps
, pid_t
*pid
)
188 char buf
[BUFSIZ
], *p
;
191 d
= readdir(ps
->dir
);
193 return errno
? -1 : 1; /* error or end-of-dir */
196 if (!isdigit((unsigned char) *d
->d_name
))
199 /* filter out by UID */
200 if (ps
->has_fltr_uid
) {
203 if (fstatat(dirfd(ps
->dir
), d
->d_name
, &st
, 0))
205 if (ps
->fltr_uid
!= st
.st_uid
)
209 /* filter out by NAME */
210 if (ps
->has_fltr_name
) {
214 snprintf(buf
, sizeof(buf
), "%s/stat", d
->d_name
);
215 f
= fopen_at(dirfd(ps
->dir
), buf
, O_CLOEXEC
|O_RDONLY
, "r");
219 p
= fgets(buf
, sizeof(buf
), f
);
224 if (sscanf(buf
, "%*d (%255[^)])", procname
) != 1)
227 /* ok, we got the process name. */
228 if (strcmp(procname
, ps
->fltr_name
) != 0)
234 *pid
= (pid_t
) strtol(d
->d_name
, &p
, 10);
235 if (errno
|| d
->d_name
== p
|| (p
&& *p
))
236 return errno
? -errno
: -1;
244 #ifdef TEST_PROGRAM_PROCUTILS
246 static int test_tasks(int argc
, char *argv
[])
249 struct proc_tasks
*ts
;
254 pid
= strtol(argv
[1], (char **) NULL
, 10);
255 printf("PID=%d, TIDs:", pid
);
257 ts
= proc_open_tasks(pid
);
259 err(EXIT_FAILURE
, "open list of tasks failed");
261 while (proc_next_tid(ts
, &tid
) == 0)
265 proc_close_tasks(ts
);
269 static int test_processes(int argc
, char *argv
[])
272 struct proc_processes
*ps
;
274 ps
= proc_open_processes();
276 err(EXIT_FAILURE
, "open list of processes failed");
278 if (argc
>= 3 && strcmp(argv
[1], "--name") == 0)
279 proc_processes_filter_by_name(ps
, argv
[2]);
281 if (argc
>= 3 && strcmp(argv
[1], "--uid") == 0)
282 proc_processes_filter_by_uid(ps
, (uid_t
) atol(argv
[2]));
284 while (proc_next_pid(ps
, &pid
) == 0)
288 proc_close_processes(ps
);
292 int main(int argc
, char *argv
[])
295 fprintf(stderr
, "usage: %1$s --tasks <pid>\n"
296 " %1$s --processes [---name <name>] [--uid <uid>]\n",
297 program_invocation_short_name
);
301 if (strcmp(argv
[1], "--tasks") == 0)
302 return test_tasks(argc
- 1, argv
+ 1);
303 if (strcmp(argv
[1], "--processes") == 0)
304 return test_processes(argc
- 1, argv
+ 1);
308 #endif /* TEST_PROGRAM_PROCUTILS */