]> git.ipfire.org Git - thirdparty/util-linux.git/blame - lib/procutils.c
bash-completion/umount: shell charaters escape
[thirdparty/util-linux.git] / lib / procutils.c
CommitLineData
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 */
36struct 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 */
59void 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 */
73int 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 */
102static 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);
125done:
126 if (fd >= 0)
127 close(fd);
128 return res;
129}
130
0e756dae
KZ
131/* returns process command path, use free() for result */
132char *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 */
138char *proc_get_command_name(pid_t pid)
139{
140 return proc_file_strdup(pid, "comm");
141}
142
13377dd6
KZ
143struct 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
158void proc_close_processes(struct proc_processes *ps)
159{
160 if (ps && ps->dir)
161 closedir(ps->dir);
162 free(ps);
163}
164
165void 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
171void 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
177int 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 246static 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
269static 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
292int 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 */