]> git.ipfire.org Git - thirdparty/util-linux.git/blob - lib/procutils.c
libsmartcols: cleanup and extend padding functionality
[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/stat.h>
22 #include <sys/types.h>
23 #include <dirent.h>
24 #include <ctype.h>
25
26 #include "procutils.h"
27 #include "fileutils.h"
28 #include "all-io.h"
29 #include "c.h"
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
78 if (!tasks || !tid)
79 return -EINVAL;
80
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;
91 errno = 0;
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
101 /* returns process command path, use free() for result */
102 static char *proc_file_strdup(pid_t pid, const char *name)
103 {
104 char buf[BUFSIZ], *res = NULL;
105 ssize_t sz = 0;
106 size_t i;
107 int fd;
108
109 snprintf(buf, sizeof(buf), "/proc/%d/%s", (int) pid, name);
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
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
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
190 errno = 0;
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
199 /* filter out by UID */
200 if (ps->has_fltr_uid) {
201 struct stat st;
202
203 if (fstatat(dirfd(ps->dir), d->d_name, &st, 0))
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];
212 FILE *f;
213
214 snprintf(buf, sizeof(buf), "%s/stat", d->d_name);
215 f = fopen_at(dirfd(ps->dir), buf, O_CLOEXEC|O_RDONLY, "r");
216 if (!f)
217 continue;
218
219 p = fgets(buf, sizeof(buf), f);
220 fclose(f);
221 if (!p)
222 continue;
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;
233 errno = 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;
237
238 return 0;
239 } while (1);
240
241 return 0;
242 }
243
244 #ifdef TEST_PROGRAM_PROCUTILS
245
246 static int test_tasks(int argc, char *argv[])
247 {
248 pid_t tid, pid;
249 struct proc_tasks *ts;
250
251 if (argc != 2)
252 return EXIT_FAILURE;
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 }
268
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
292 int main(int argc, char *argv[])
293 {
294 if (argc < 2) {
295 fprintf(stderr, "usage: %1$s --tasks <pid>\n"
296 " %1$s --processes [---name <name>] [--uid <uid>]\n",
297 program_invocation_short_name);
298 return EXIT_FAILURE;
299 }
300
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);
305
306 return EXIT_FAILURE;
307 }
308 #endif /* TEST_PROGRAM_PROCUTILS */