blob: ef969417dd665e115896b5c9758b56ff625cf20f [file] [log] [blame]
bigbiff7b4c7a62015-01-01 19:44:14 -05001/*
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 */
34struct 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 */
57void 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 */
71int 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 errno = 0;
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
99struct 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
114void proc_close_processes(struct proc_processes *ps)
115{
116 if (ps && ps->dir)
117 closedir(ps->dir);
118 free(ps);
119}
120
121void 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
127void 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
133int 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 /* filter out by UID */
155 if (ps->has_fltr_uid) {
156 struct stat st;
157
158 if (fstat_at(dirfd(ps->dir), "/proc", d->d_name, &st, 0))
159 continue;
160 if (ps->fltr_uid != st.st_uid)
161 continue;
162 }
163
164 /* filter out by NAME */
165 if (ps->has_fltr_name) {
166 char procname[256];
167 FILE *f;
168
169 snprintf(buf, sizeof(buf), "%s/stat", d->d_name);
170 f = fopen_at(dirfd(ps->dir), "/proc", buf,
171 O_CLOEXEC|O_RDONLY, "r");
172 if (!f)
173 continue;
174
175 p = fgets(buf, sizeof(buf), f);
176 fclose(f);
177 if (!p)
178 continue;
179
180 if (sscanf(buf, "%*d (%255[^)])", procname) != 1)
181 continue;
182
183 /* ok, we got the process name. */
184 if (strcmp(procname, ps->fltr_name) != 0)
185 continue;
186 }
187
188 p = NULL;
189 errno = 0;
190 *pid = (pid_t) strtol(d->d_name, &p, 10);
191 if (errno || d->d_name == p || (p && *p))
192 return errno ? -errno : -1;
193
194 return 0;
195 } while (1);
196
197 return 0;
198}
199
200#ifdef TEST_PROGRAM
201
202static int test_tasks(int argc, char *argv[])
203{
204 pid_t tid, pid;
205 struct proc_tasks *ts;
206
207 if (argc != 2)
208 return EXIT_FAILURE;
209
210 pid = strtol(argv[1], (char **) NULL, 10);
211 printf("PID=%d, TIDs:", pid);
212
213 ts = proc_open_tasks(pid);
214 if (!ts)
215 err(EXIT_FAILURE, "open list of tasks failed");
216
217 while (proc_next_tid(ts, &tid) == 0)
218 printf(" %d", tid);
219
220 printf("\n");
221 proc_close_tasks(ts);
222 return EXIT_SUCCESS;
223}
224
225static int test_processes(int argc, char *argv[])
226{
227 pid_t pid;
228 struct proc_processes *ps;
229
230 ps = proc_open_processes();
231 if (!ps)
232 err(EXIT_FAILURE, "open list of processes failed");
233
234 if (argc >= 3 && strcmp(argv[1], "--name") == 0)
235 proc_processes_filter_by_name(ps, argv[2]);
236
237 if (argc >= 3 && strcmp(argv[1], "--uid") == 0)
238 proc_processes_filter_by_uid(ps, (uid_t) atol(argv[2]));
239
240 while (proc_next_pid(ps, &pid) == 0)
241 printf(" %d", pid);
242
243 printf("\n");
244 proc_close_processes(ps);
245 return EXIT_SUCCESS;
246}
247
248int main(int argc, char *argv[])
249{
250 if (argc < 2) {
251 fprintf(stderr, "usage: %1$s --tasks <pid>\n"
252 " %1$s --processes [---name <name>] [--uid <uid>]\n",
253 program_invocation_short_name);
254 return EXIT_FAILURE;
255 }
256
257 if (strcmp(argv[1], "--tasks") == 0)
258 return test_tasks(argc - 1, argv + 1);
259 if (strcmp(argv[1], "--processes") == 0)
260 return test_processes(argc - 1, argv + 1);
261
262 return EXIT_FAILURE;
263}
264#endif /* TEST_PROGRAM */