]>
Commit | Line | Data |
---|---|---|
5b1f0311 KZ |
1 | /* |
2 | * Copyright (C) 2011 Davidlohr Bueso <dave@gnu.org> | |
3 | * | |
4 | * procutils.c: General purpose procfs parsing utilities | |
5 | * | |
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) | |
9 | * any later version. | |
10 | * | |
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. | |
15 | */ | |
16 | ||
17 | #include <stdio.h> | |
18 | #include <stdlib.h> | |
19 | #include <string.h> | |
20 | #include <errno.h> | |
2208b3cc | 21 | #include <sys/stat.h> |
5b1f0311 KZ |
22 | #include <sys/types.h> |
23 | #include <dirent.h> | |
24 | #include <ctype.h> | |
25 | ||
26 | #include "procutils.h" | |
1a048dc5 | 27 | #include "fileutils.h" |
f6c01f4e | 28 | #include "all-io.h" |
4585ec0b | 29 | #include "c.h" |
5b1f0311 KZ |
30 | |
31 | /* | |
32 | * @pid: process ID for which we want to obtain the threads group | |
33 | * | |
34 | * Returns: newly allocated tasks structure | |
35 | */ | |
36 | struct proc_tasks *proc_open_tasks(pid_t pid) | |
37 | { | |
38 | struct proc_tasks *tasks; | |
39 | char path[PATH_MAX]; | |
40 | ||
41 | sprintf(path, "/proc/%d/task/", pid); | |
42 | ||
43 | tasks = malloc(sizeof(struct proc_tasks)); | |
44 | if (tasks) { | |
45 | tasks->dir = opendir(path); | |
46 | if (tasks->dir) | |
47 | return tasks; | |
48 | } | |
49 | ||
50 | free(tasks); | |
51 | return NULL; | |
52 | } | |
53 | ||
54 | /* | |
55 | * @tasks: allocated tasks structure | |
56 | * | |
57 | * Returns: nothing | |
58 | */ | |
59 | void proc_close_tasks(struct proc_tasks *tasks) | |
60 | { | |
61 | if (tasks && tasks->dir) | |
62 | closedir(tasks->dir); | |
63 | free(tasks); | |
64 | } | |
65 | ||
66 | /* | |
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. | |
70 | * | |
71 | * Returns: 0 on success, 1 on end, -1 on failure or no more threads | |
72 | */ | |
73 | int proc_next_tid(struct proc_tasks *tasks, pid_t *tid) | |
74 | { | |
75 | struct dirent *d; | |
76 | char *end; | |
77 | ||
4ef26746 | 78 | if (!tasks || !tid) |
13377dd6 | 79 | return -EINVAL; |
4ef26746 | 80 | |
5b1f0311 KZ |
81 | *tid = 0; |
82 | errno = 0; | |
83 | ||
84 | do { | |
85 | d = readdir(tasks->dir); | |
86 | if (!d) | |
87 | return errno ? -1 : 1; /* error or end-of-dir */ | |
88 | ||
89 | if (!isdigit((unsigned char) *d->d_name)) | |
90 | continue; | |
5889fbe5 | 91 | errno = 0; |
5b1f0311 KZ |
92 | *tid = (pid_t) strtol(d->d_name, &end, 10); |
93 | if (errno || d->d_name == end || (end && *end)) | |
94 | return -1; | |
95 | ||
96 | } while (!*tid); | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
0e756dae KZ |
101 | /* returns process command path, use free() for result */ |
102 | static char *proc_file_strdup(pid_t pid, const char *name) | |
f6c01f4e KZ |
103 | { |
104 | char buf[BUFSIZ], *res = NULL; | |
105 | ssize_t sz = 0; | |
106 | size_t i; | |
ee24ab6f | 107 | int fd; |
f6c01f4e | 108 | |
0e756dae | 109 | snprintf(buf, sizeof(buf), "/proc/%d/%s", (int) pid, name); |
f6c01f4e KZ |
110 | fd = open(buf, O_RDONLY); |
111 | if (fd < 0) | |
112 | goto done; | |
113 | ||
114 | sz = read_all(fd, buf, sizeof(buf)); | |
115 | if (sz <= 0) | |
116 | goto done; | |
117 | ||
118 | for (i = 0; i < (size_t) sz; i++) { | |
119 | ||
120 | if (buf[i] == '\0') | |
121 | buf[i] = ' '; | |
122 | } | |
123 | buf[sz - 1] = '\0'; | |
124 | res = strdup(buf); | |
125 | done: | |
126 | if (fd >= 0) | |
127 | close(fd); | |
128 | return res; | |
129 | } | |
130 | ||
0e756dae KZ |
131 | /* returns process command path, use free() for result */ |
132 | char *proc_get_command(pid_t pid) | |
133 | { | |
134 | return proc_file_strdup(pid, "cmdline"); | |
135 | } | |
136 | ||
137 | /* returns process command name, use free() for result */ | |
138 | char *proc_get_command_name(pid_t pid) | |
139 | { | |
140 | return proc_file_strdup(pid, "comm"); | |
141 | } | |
142 | ||
13377dd6 KZ |
143 | struct proc_processes *proc_open_processes(void) |
144 | { | |
145 | struct proc_processes *ps; | |
146 | ||
147 | ps = calloc(1, sizeof(struct proc_processes)); | |
148 | if (ps) { | |
149 | ps->dir = opendir("/proc"); | |
150 | if (ps->dir) | |
151 | return ps; | |
152 | } | |
153 | ||
154 | free(ps); | |
155 | return NULL; | |
156 | } | |
157 | ||
158 | void proc_close_processes(struct proc_processes *ps) | |
159 | { | |
160 | if (ps && ps->dir) | |
161 | closedir(ps->dir); | |
162 | free(ps); | |
163 | } | |
164 | ||
165 | void proc_processes_filter_by_name(struct proc_processes *ps, const char *name) | |
166 | { | |
167 | ps->fltr_name = name; | |
168 | ps->has_fltr_name = name ? 1 : 0; | |
169 | } | |
170 | ||
171 | void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid) | |
172 | { | |
173 | ps->fltr_uid = uid; | |
174 | ps->has_fltr_uid = 1; | |
175 | } | |
176 | ||
177 | int proc_next_pid(struct proc_processes *ps, pid_t *pid) | |
178 | { | |
179 | struct dirent *d; | |
180 | ||
181 | if (!ps || !pid) | |
182 | return -EINVAL; | |
183 | ||
184 | *pid = 0; | |
185 | errno = 0; | |
186 | ||
187 | do { | |
188 | char buf[BUFSIZ], *p; | |
189 | ||
4f4fc8c1 | 190 | errno = 0; |
13377dd6 KZ |
191 | d = readdir(ps->dir); |
192 | if (!d) | |
193 | return errno ? -1 : 1; /* error or end-of-dir */ | |
194 | ||
195 | ||
196 | if (!isdigit((unsigned char) *d->d_name)) | |
197 | continue; | |
198 | ||
13377dd6 KZ |
199 | /* filter out by UID */ |
200 | if (ps->has_fltr_uid) { | |
201 | struct stat st; | |
202 | ||
2208b3cc | 203 | if (fstatat(dirfd(ps->dir), d->d_name, &st, 0)) |
13377dd6 KZ |
204 | continue; |
205 | if (ps->fltr_uid != st.st_uid) | |
206 | continue; | |
207 | } | |
208 | ||
209 | /* filter out by NAME */ | |
210 | if (ps->has_fltr_name) { | |
211 | char procname[256]; | |
1786a9ac SK |
212 | FILE *f; |
213 | ||
214 | snprintf(buf, sizeof(buf), "%s/stat", d->d_name); | |
2208b3cc | 215 | f = fopen_at(dirfd(ps->dir), buf, O_CLOEXEC|O_RDONLY, "r"); |
13377dd6 KZ |
216 | if (!f) |
217 | continue; | |
218 | ||
219 | p = fgets(buf, sizeof(buf), f); | |
220 | fclose(f); | |
8471bc61 KZ |
221 | if (!p) |
222 | continue; | |
13377dd6 KZ |
223 | |
224 | if (sscanf(buf, "%*d (%255[^)])", procname) != 1) | |
225 | continue; | |
226 | ||
227 | /* ok, we got the process name. */ | |
228 | if (strcmp(procname, ps->fltr_name) != 0) | |
229 | continue; | |
230 | } | |
231 | ||
232 | p = NULL; | |
5889fbe5 | 233 | errno = 0; |
13377dd6 KZ |
234 | *pid = (pid_t) strtol(d->d_name, &p, 10); |
235 | if (errno || d->d_name == p || (p && *p)) | |
236 | return errno ? -errno : -1; | |
237 | ||
238 | return 0; | |
239 | } while (1); | |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
e8f7acb0 | 244 | #ifdef TEST_PROGRAM_PROCUTILS |
5b1f0311 | 245 | |
1bb53a54 | 246 | static int test_tasks(int argc, char *argv[]) |
5b1f0311 KZ |
247 | { |
248 | pid_t tid, pid; | |
249 | struct proc_tasks *ts; | |
250 | ||
1bb53a54 | 251 | if (argc != 2) |
5b1f0311 | 252 | return EXIT_FAILURE; |
5b1f0311 KZ |
253 | |
254 | pid = strtol(argv[1], (char **) NULL, 10); | |
255 | printf("PID=%d, TIDs:", pid); | |
256 | ||
257 | ts = proc_open_tasks(pid); | |
258 | if (!ts) | |
259 | err(EXIT_FAILURE, "open list of tasks failed"); | |
260 | ||
261 | while (proc_next_tid(ts, &tid) == 0) | |
262 | printf(" %d", tid); | |
263 | ||
264 | printf("\n"); | |
265 | proc_close_tasks(ts); | |
266 | return EXIT_SUCCESS; | |
267 | } | |
1bb53a54 | 268 | |
13377dd6 KZ |
269 | static int test_processes(int argc, char *argv[]) |
270 | { | |
271 | pid_t pid; | |
272 | struct proc_processes *ps; | |
273 | ||
274 | ps = proc_open_processes(); | |
275 | if (!ps) | |
276 | err(EXIT_FAILURE, "open list of processes failed"); | |
277 | ||
278 | if (argc >= 3 && strcmp(argv[1], "--name") == 0) | |
279 | proc_processes_filter_by_name(ps, argv[2]); | |
280 | ||
281 | if (argc >= 3 && strcmp(argv[1], "--uid") == 0) | |
282 | proc_processes_filter_by_uid(ps, (uid_t) atol(argv[2])); | |
283 | ||
284 | while (proc_next_pid(ps, &pid) == 0) | |
285 | printf(" %d", pid); | |
286 | ||
287 | printf("\n"); | |
288 | proc_close_processes(ps); | |
289 | return EXIT_SUCCESS; | |
290 | } | |
291 | ||
1bb53a54 KZ |
292 | int main(int argc, char *argv[]) |
293 | { | |
294 | if (argc < 2) { | |
13377dd6 KZ |
295 | fprintf(stderr, "usage: %1$s --tasks <pid>\n" |
296 | " %1$s --processes [---name <name>] [--uid <uid>]\n", | |
297 | program_invocation_short_name); | |
1bb53a54 KZ |
298 | return EXIT_FAILURE; |
299 | } | |
300 | ||
301 | if (strcmp(argv[1], "--tasks") == 0) | |
302 | return test_tasks(argc - 1, argv + 1); | |
13377dd6 KZ |
303 | if (strcmp(argv[1], "--processes") == 0) |
304 | return test_processes(argc - 1, argv + 1); | |
1bb53a54 KZ |
305 | |
306 | return EXIT_FAILURE; | |
307 | } | |
e8f7acb0 | 308 | #endif /* TEST_PROGRAM_PROCUTILS */ |