-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathread_proc.h
500 lines (407 loc) · 8.59 KB
/
read_proc.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
//SPDX-License-Identifier: LGPL-2.1-or-later
/*
Copyright (C) 2024 Cyril Hrubis <[email protected]>
*/
/**
* @file read_proc.h
* @brief Optimized /proc/$PID/{stat,status} iterator and parser.
*/
#ifndef READ_PROC_H
#define READ_PROC_H
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <stdint.h>
/**
* @brief Read proc iterator state.
*/
struct read_proc {
/** @brief A proc directory. */
DIR *pdir;
/** @brief A current pid. */
pid_t pid;
};
static inline int read_proc_init(struct read_proc *p)
{
p->pdir = opendir("/proc/");
return p->pdir == NULL;
}
static inline int read_proc_exit(struct read_proc *p)
{
return closedir(p->pdir);
}
/**
* @brief Parses a proc directory name into a pid.
*
* @param name A proc directory name.
* @return A pid or 0 if the name is not numeric.
*/
static inline pid_t read_proc_get_pid(const char *name)
{
pid_t pid = 0;
for (;;) {
char c = *(name++);
switch (c) {
case '0' ... '9':
pid = 10 * pid + c - '0';
break;
default:
return 0;
}
if (!*name)
return pid;
}
}
/**
* @brief Moves to the next process.
*
* Fills in the p->pid field if there is a next process.
*
* @param p A read proc state.
* @return Non-zero if there is a process, 0 if there is none.
*/
static inline int read_proc_next(struct read_proc *p)
{
struct dirent *dir;
pid_t pid = 0;
for (;;) {
dir = readdir(p->pdir);
if (!dir)
return 0;
pid = read_proc_get_pid(dir->d_name);
if (pid) {
p->pid = pid;
return 1;
}
}
}
struct read_proc_buf {
ssize_t len;
int fail;
char *buf;
};
static inline uint64_t read_proc_buf_get_unum(struct read_proc_buf *buf)
{
uint64_t ret = 0;
int parsed_something = 0;
if (!buf->len) {
buf->fail = 1;
return ret;
}
for (;;) {
char c = *(buf->buf);
switch (c) {
case '0' ... '9':
ret = 10 * ret + c - '0';
parsed_something = 1;
break;
default:
if (!parsed_something)
buf->fail = 1;
return ret;
}
buf->len--;
buf->buf++;
if (!buf->len)
return ret;
}
}
static inline int64_t read_proc_buf_get_snum(struct read_proc_buf *buf)
{
int neg = 1;
if (!buf->len) {
buf->fail = 1;
return 0;
}
if (*(buf->buf) == '-') {
neg = -1;
buf->len--;
buf->buf++;
}
int64_t ret = read_proc_buf_get_unum(buf);
return neg * ret;
}
static inline char read_proc_buf_getc(struct read_proc_buf *buf)
{
if (!buf->len) {
buf->fail = 1;
return 0;
}
char ret = *(buf->buf);
buf->len -= 1;
buf->buf += 1;
return ret;
}
static inline void read_proc_buf_get_str(struct read_proc_buf *buf,
struct read_proc_buf *dst,
char delim)
{
for (;;) {
if (!buf->len) {
buf->fail = 1;
goto ret;
}
char c = *(buf->buf);
if (c == delim)
goto ret;
if (dst->len > 1) {
*(dst->buf) = c;
dst->buf++;
dst->len--;
}
buf->len--;
buf->buf++;
}
ret:
*(dst->buf) = 0;
return;
}
static inline void read_proc_buf_eat_ws(struct read_proc_buf *buf)
{
int parsed_something = 0;
for (;;) {
if (!buf->len) {
buf->fail = 1;
return;
}
char c = *(buf->buf);
switch (c) {
case ' ':
case '\t':
parsed_something = 1;
break;
default:
if (!parsed_something)
buf->fail = 1;
return;
}
buf->buf++;
buf->len--;
}
}
static inline void read_proc_buf_next_line(struct read_proc_buf *buf)
{
for (;;) {
if (!buf->len) {
buf->fail = 1;
return;
}
char c = *(buf->buf);
buf->buf++;
buf->len--;
if (c == '\n')
return;
}
}
/** @brief Kernel limit to comm size */
#define READ_PROC_COMM_SIZE 32
/**
* @brief A struct to read subset of /proc/$PID/stat fields into.
*/
struct read_proc_stat {
/**
* @brief Task pid.
*/
pid_t pid;
/**
* @brief Task parent pid.
*/
pid_t ppid;
/**
* @brief Task process group.
*/
pid_t pgrp;
/**
* @brief Real UID.
*/
uid_t uid;
/**
* @brief Effective UID.
*/
uid_t euid;
/**
* @brief Real GID.
*/
gid_t gid;
/**
* @brief Effective GID.
*/
gid_t egid;
/**
* @brief Task state.
*
* One of:
* R - process is running
* S - process is sleeping
* D - process sleeping uninterruptibly
* Z - zombie process
* T - process is traced
*/
char state;
/**
* @brief User mode time counter for the process.
*
* The value is in system ticks use sysconf(_SC_CLK_TCK) to get number
* of ticks per second.
*/
uint64_t utime;
/**
* @brief Kernel mode time counter for the process.
*
* The value is in system ticks use sysconf(_SC_CLK_TCK) to get number
* of ticks per second.
*/
uint64_t stime;
/**
* @brief Process schedulling priority.
*/
int nice;
/**
* @brief Process start time.
*
* The value is in system ticks since the machine boot use
* sysconf(_SC_CLK_TCK) to get number of ticks per second.
*/
uint64_t start_time;
/**
* @brief Program memory resident set in pages.
*/
uint32_t rss;
/**
* @brief The command name.
*
* The string from /proc/$pid/comm
*/
char comm[READ_PROC_COMM_SIZE];
};
/**
* @brief Parses subset of /proc/$PID/stat and /proc/$PID/status fields.
*
* @param p A read proc state.
* @param stat A struct to store the parsed stat data into.
*
* There is a race window between listing the /proc/ directory content and
* reading the stat file. This function may return non-zero if the process did
* exit() and was waited for between the readdir() and open() of /proc/$PID/foo
* files. The user of this function should skip to the next pid with
* read_proc_next() if this functions returns non-zero.
*
* @return Zero on success, non-zero otherwise.
*/
static inline int read_proc_stat(struct read_proc *p, struct read_proc_stat *stat)
{
char str[2048];
struct read_proc_buf buf = {}, comm = {};
int fd;
unsigned int i;
snprintf(str, sizeof(str), "/proc/%i/stat", p->pid);
fd = open(str, O_RDONLY);
if (!fd)
return 1;
buf.len = read(fd, str, sizeof(str));
buf.buf = str;
if (buf.len < 0) {
close(fd);
return 1;
}
/* PID */
stat->pid = read_proc_buf_get_snum(&buf);
/* Remove ' (' */
read_proc_buf_getc(&buf);
read_proc_buf_getc(&buf);
/* Read comm */
comm.buf = stat->comm;
comm.len = sizeof(stat->comm);
read_proc_buf_get_str(&buf, &comm, ')');
/* Remove ') ' */
read_proc_buf_getc(&buf);
read_proc_buf_getc(&buf);
/* Read state and remove ' ' */
stat->state = read_proc_buf_getc(&buf);
read_proc_buf_getc(&buf);
/* Read parent PID and remove ' ' */
stat->ppid = read_proc_buf_get_snum(&buf);
read_proc_buf_getc(&buf);
/* Read process group and remove ' ' */
stat->pgrp = read_proc_buf_get_snum(&buf);
read_proc_buf_getc(&buf);
/* Ignore 8 numeric fields */
for (i = 0; i < 8; i++) {
read_proc_buf_get_snum(&buf);
read_proc_buf_getc(&buf);
}
/* Read User and System time */
stat->utime = read_proc_buf_get_unum(&buf);
read_proc_buf_getc(&buf);
stat->stime = read_proc_buf_get_unum(&buf);
read_proc_buf_getc(&buf);
/* Ignore cstime, cutime and realtime prio */
for (i = 0; i < 3; i++) {
read_proc_buf_get_snum(&buf);
read_proc_buf_getc(&buf);
}
/* Read nice value. */
stat->nice = read_proc_buf_get_snum(&buf);
read_proc_buf_getc(&buf);
/* Ignore num threads and itrealvalue */
for (i = 0; i < 2; i++) {
read_proc_buf_get_snum(&buf);
read_proc_buf_getc(&buf);
}
/* Read process start time. */
stat->start_time = read_proc_buf_get_unum(&buf);
read_proc_buf_getc(&buf);
/* Ignore vmsize. */
read_proc_buf_get_unum(&buf);
read_proc_buf_getc(&buf);
/* Read program resident set. */
stat->rss = read_proc_buf_get_unum(&buf);
read_proc_buf_getc(&buf);
close(fd);
if (buf.fail)
return 1;
snprintf(str, sizeof(str), "/proc/%i/status", p->pid);
fd = open(str, O_RDONLY);
if (!fd)
return 1;
buf.len = read(fd, str, sizeof(str));
buf.buf = str;
if (buf.len < 0) {
close(fd);
return 1;
}
while (buf.len) {
char id_str[32];
struct read_proc_buf id = {
.buf = id_str,
.len = sizeof(id_str)
};
read_proc_buf_get_str(&buf, &id, ':');
if (id_str[0] == 'U' && id_str[1] == 'i' &&
id_str[2] == 'd' && id_str[3] == 0) {
/* Get rid of ':' */
read_proc_buf_getc(&buf);
read_proc_buf_eat_ws(&buf);
stat->uid = read_proc_buf_get_unum(&buf);
read_proc_buf_eat_ws(&buf);
stat->euid = read_proc_buf_get_unum(&buf);
}
if (id_str[0] == 'G' && id_str[1] == 'i' &&
id_str[2] == 'd' && id_str[3] == 0) {
/* Get rid of ':' */
read_proc_buf_getc(&buf);
read_proc_buf_eat_ws(&buf);
stat->gid = read_proc_buf_get_unum(&buf);
read_proc_buf_eat_ws(&buf);
stat->egid = read_proc_buf_get_unum(&buf);
}
read_proc_buf_next_line(&buf);
}
close(fd);
if (buf.fail)
return 1;
return 0;
}
#endif /* READ_PROC_H */