]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/procutils.c
lib/procutils: add simple API to scan /proc/PID/*
[thirdparty/util-linux.git] / lib / procutils.c
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>
21 #include <sys/types.h>
22 #include <dirent.h>
23 #include <ctype.h>
24
25 #include "procutils.h"
26 #include "at.h"
27 #include "c.h"
28
29 /*
30 * @pid: process ID for which we want to obtain the threads group
31 *
32 * Returns: newly allocated tasks structure
33 */
34 struct proc_tasks *proc_open_tasks(pid_t pid)
35 {
36 struct proc_tasks *tasks;
37 char path[PATH_MAX];
38
39 sprintf(path, "/proc/%d/task/", pid);
40
41 tasks = malloc(sizeof(struct proc_tasks));
42 if (tasks) {
43 tasks->dir = opendir(path);
44 if (tasks->dir)
45 return tasks;
46 }
47
48 free(tasks);
49 return NULL;
50 }
51
52 /*
53 * @tasks: allocated tasks structure
54 *
55 * Returns: nothing
56 */
57 void proc_close_tasks(struct proc_tasks *tasks)
58 {
59 if (tasks && tasks->dir)
60 closedir(tasks->dir);
61 free(tasks);
62 }
63
64 /*
65 * @tasks: allocated task structure
66 * @tid: [output] one of the thread IDs belonging to the thread group
67 * If when an error occurs, it is set to 0.
68 *
69 * Returns: 0 on success, 1 on end, -1 on failure or no more threads
70 */
71 int proc_next_tid(struct proc_tasks *tasks, pid_t *tid)
72 {
73 struct dirent *d;
74 char *end;
75
76 if (!tasks || !tid)
77 return -EINVAL;
78
79 *tid = 0;
80 errno = 0;
81
82 do {
83 d = readdir(tasks->dir);
84 if (!d)
85 return errno ? -1 : 1; /* error or end-of-dir */
86
87 if (!isdigit((unsigned char) *d->d_name))
88 continue;
89
90 *tid = (pid_t) strtol(d->d_name, &end, 10);
91 if (errno || d->d_name == end || (end && *end))
92 return -1;
93
94 } while (!*tid);
95
96 return 0;
97 }
98
99 struct proc_processes *proc_open_processes(void)
100 {
101 struct proc_processes *ps;
102
103 ps = calloc(1, sizeof(struct proc_processes));
104 if (ps) {
105 ps->dir = opendir("/proc");
106 if (ps->dir)
107 return ps;
108 }
109
110 free(ps);
111 return NULL;
112 }
113
114 void proc_close_processes(struct proc_processes *ps)
115 {
116 if (ps && ps->dir)
117 closedir(ps->dir);
118 free(ps);
119 }
120
121 void proc_processes_filter_by_name(struct proc_processes *ps, const char *name)
122 {
123 ps->fltr_name = name;
124 ps->has_fltr_name = name ? 1 : 0;
125 }
126
127 void proc_processes_filter_by_uid(struct proc_processes *ps, uid_t uid)
128 {
129 ps->fltr_uid = uid;
130 ps->has_fltr_uid = 1;
131 }
132
133 int proc_next_pid(struct proc_processes *ps, pid_t *pid)
134 {
135 struct dirent *d;
136
137 if (!ps || !pid)
138 return -EINVAL;
139
140 *pid = 0;
141 errno = 0;
142
143 do {
144 char buf[BUFSIZ], *p;
145
146 d = readdir(ps->dir);
147 if (!d)
148 return errno ? -1 : 1; /* error or end-of-dir */
149
150
151 if (!isdigit((unsigned char) *d->d_name))
152 continue;
153
154 snprintf(buf, sizeof(buf), "%s/stat", d->d_name);
155
156 /* filter out by UID */
157 if (ps->has_fltr_uid) {
158 struct stat st;
159
160 if (fstat_at(dirfd(ps->dir), "/proc", buf, &st, 0))
161 continue;
162 if (ps->fltr_uid != st.st_uid)
163 continue;
164 }
165
166 /* filter out by NAME */
167 if (ps->has_fltr_name) {
168 char procname[256];
169 FILE *f = fopen_at(dirfd(ps->dir), "/proc", buf,
170 O_CLOEXEC|O_RDONLY, "r");
171 if (!f)
172 continue;
173
174 p = fgets(buf, sizeof(buf), f);
175 fclose(f);
176
177 if (sscanf(buf, "%*d (%255[^)])", procname) != 1)
178 continue;
179
180 /* ok, we got the process name. */
181 if (strcmp(procname, ps->fltr_name) != 0)
182 continue;
183 }
184
185 p = NULL;
186 *pid = (pid_t) strtol(d->d_name, &p, 10);
187 if (errno || d->d_name == p || (p && *p))
188 return errno ? -errno : -1;
189
190 return 0;
191 } while (1);
192
193 return 0;
194 }
195
196 #ifdef TEST_PROGRAM
197
198 static int test_tasks(int argc, char *argv[])
199 {
200 pid_t tid, pid;
201 struct proc_tasks *ts;
202
203 if (argc != 2)
204 return EXIT_FAILURE;
205
206 pid = strtol(argv[1], (char **) NULL, 10);
207 printf("PID=%d, TIDs:", pid);
208
209 ts = proc_open_tasks(pid);
210 if (!ts)
211 err(EXIT_FAILURE, "open list of tasks failed");
212
213 while (proc_next_tid(ts, &tid) == 0)
214 printf(" %d", tid);
215
216 printf("\n");
217 proc_close_tasks(ts);
218 return EXIT_SUCCESS;
219 }
220
221 static int test_processes(int argc, char *argv[])
222 {
223 pid_t pid;
224 struct proc_processes *ps;
225
226 ps = proc_open_processes();
227 if (!ps)
228 err(EXIT_FAILURE, "open list of processes failed");
229
230 if (argc >= 3 && strcmp(argv[1], "--name") == 0)
231 proc_processes_filter_by_name(ps, argv[2]);
232
233 if (argc >= 3 && strcmp(argv[1], "--uid") == 0)
234 proc_processes_filter_by_uid(ps, (uid_t) atol(argv[2]));
235
236 while (proc_next_pid(ps, &pid) == 0)
237 printf(" %d", pid);
238
239 printf("\n");
240 proc_close_processes(ps);
241 return EXIT_SUCCESS;
242 }
243
244 int main(int argc, char *argv[])
245 {
246 if (argc < 2) {
247 fprintf(stderr, "usage: %1$s --tasks <pid>\n"
248 " %1$s --processes [---name <name>] [--uid <uid>]\n",
249 program_invocation_short_name);
250 return EXIT_FAILURE;
251 }
252
253 if (strcmp(argv[1], "--tasks") == 0)
254 return test_tasks(argc - 1, argv + 1);
255 if (strcmp(argv[1], "--processes") == 0)
256 return test_processes(argc - 1, argv + 1);
257
258 return EXIT_FAILURE;
259 }
260 #endif /* TEST_PROGRAM */