Branch data Line data Source code
1 : : #include <stdio.h>
2 : : #include <unistd.h>
3 : : #include <fcntl.h>
4 : : #include <sys/mman.h>
5 : : #include <sys/types.h>
6 : : #include <dirent.h>
7 : : #include <errno.h>
8 : : #include <sys/stat.h>
9 : : #include <string.h>
10 : : #include <linux/fs.h>
11 : :
12 : : #include "asm/types.h"
13 : : #include "list.h"
14 : : #include "util.h"
15 : : #include "mount.h"
16 : : #include "mman.h"
17 : : #include "cpu.h"
18 : : #include "file-lock.h"
19 : : #include "pstree.h"
20 : : #include "fsnotify.h"
21 : : #include "posix-timer.h"
22 : : #include "kerndat.h"
23 : : #include "vdso.h"
24 : : #include "vma.h"
25 : :
26 : : #include "proc_parse.h"
27 : : #include "protobuf.h"
28 : : #include "protobuf/fdinfo.pb-c.h"
29 : :
30 : : #include <stdlib.h>
31 : :
32 : : struct buffer {
33 : : char buf[PAGE_SIZE];
34 : : char end; /* '\0' */
35 : : };
36 : :
37 : : static struct buffer __buf;
38 : : static char *buf = __buf.buf;
39 : :
40 : : #define BUF_SIZE sizeof(__buf.buf)
41 : :
42 : 2804 : int parse_cpuinfo_features(int (*handler)(char *tok))
43 : : {
44 : : FILE *cpuinfo;
45 : :
46 : 2804 : cpuinfo = fopen("/proc/cpuinfo", "r");
47 [ + - ]: 2804 : if (!cpuinfo) {
48 : 0 : pr_perror("Can't open cpuinfo file");
49 : 0 : return -1;
50 : : }
51 : :
52 [ + + ]: 294420 : while (fgets(buf, BUF_SIZE, cpuinfo)) {
53 : : char *tok;
54 : :
55 [ + + ]: 291616 : if (strncmp(buf, "flags\t\t:", 8))
56 : 280400 : continue;
57 : :
58 [ + + ]: 1233760 : for (tok = strtok(buf, " \t\n"); tok;
59 : 942144 : tok = strtok(NULL, " \t\n")) {
60 [ + - ]: 942144 : if (handler(tok) < 0)
61 : : break;
62 : : }
63 : : }
64 : :
65 : 2804 : fclose(cpuinfo);
66 : 2804 : return 0;
67 : : }
68 : :
69 : : /* check the @line starts with "%lx-%lx" format */
70 : 2489344 : static bool is_vma_range_fmt(char *line)
71 : : {
72 : : #define ____is_vma_addr_char(__c) \
73 : : (((__c) <= '9' && (__c) >= '0') || \
74 : : ((__c) <= 'f' && (__c) >= 'a'))
75 : :
76 [ + - ][ + + ]: 4315200 : while (*line && ____is_vma_addr_char(*line))
77 : 1825856 : line++;
78 : :
79 [ + + ]: 2489344 : if (*line++ != '-')
80 : : return false;
81 : :
82 [ + - ][ + + ]: 1981440 : while (*line && ____is_vma_addr_char(*line))
83 : 1825856 : line++;
84 : :
85 [ + - ]: 155584 : if (*line++ != ' ')
86 : : return false;
87 : :
88 : 155584 : return true;
89 : : #undef ____is_vma_addr_char
90 : : }
91 : :
92 : 311168 : static int parse_vmflags(char *buf, struct vma_area *vma_area)
93 : : {
94 : : char *tok;
95 : : bool shared = false;
96 : : bool maywrite = false;
97 : :
98 [ + - ]: 155584 : if (!buf[0])
99 : : return 0;
100 : :
101 : 155584 : tok = strtok(buf, " \n");
102 [ + - ]: 155584 : if (!tok)
103 : : return 0;
104 : :
105 : : #define _vmflag_match(_t, _s) (_t[0] == _s[0] && _t[1] == _s[1])
106 : :
107 : : do {
108 : : /* open() block */
109 [ + + ][ + + ]: 905726 : if (_vmflag_match(tok, "sh"))
110 : : shared = true;
111 [ + + ][ + + ]: 905206 : else if (_vmflag_match(tok, "mw"))
112 : : maywrite = true;
113 : :
114 : : /* mmap() block */
115 [ + + ][ + - ]: 905726 : if (_vmflag_match(tok, "gd"))
116 : 3776 : vma_area->e->flags |= MAP_GROWSDOWN;
117 [ + + ][ + - ]: 901950 : else if (_vmflag_match(tok, "lo"))
118 : 16 : vma_area->e->flags |= MAP_LOCKED;
119 [ + + ][ + - ]: 901934 : else if (_vmflag_match(tok, "nr"))
120 : 1264 : vma_area->e->flags |= MAP_NORESERVE;
121 [ + + ][ - + ]: 900670 : else if (_vmflag_match(tok, "ht"))
122 : 0 : vma_area->e->flags |= MAP_HUGETLB;
123 : :
124 : : /* madvise() block */
125 [ + + ][ + + ]: 905726 : if (_vmflag_match(tok, "sr"))
126 : 16 : vma_area->e->madv |= (1ul << MADV_SEQUENTIAL);
127 [ + + ][ - + ]: 905710 : else if (_vmflag_match(tok, "rr"))
128 : 0 : vma_area->e->madv |= (1ul << MADV_RANDOM);
129 [ + + ][ + + ]: 905710 : else if (_vmflag_match(tok, "dc"))
130 : 16 : vma_area->e->madv |= (1ul << MADV_DONTFORK);
131 [ + + ][ + + ]: 905694 : else if (_vmflag_match(tok, "dd"))
132 : 16 : vma_area->e->madv |= (1ul << MADV_DONTDUMP);
133 [ + + ][ + + ]: 905678 : else if (_vmflag_match(tok, "mg"))
134 : 16 : vma_area->e->madv |= (1ul << MADV_MERGEABLE);
135 [ + + ][ + - ]: 905662 : else if (_vmflag_match(tok, "hg"))
136 : 16 : vma_area->e->madv |= (1ul << MADV_HUGEPAGE);
137 [ + + ][ - + ]: 905646 : else if (_vmflag_match(tok, "nh"))
138 : 0 : vma_area->e->madv |= (1ul << MADV_NOHUGEPAGE);
139 : :
140 : : /*
141 : : * Anything else is just ignored.
142 : : */
143 [ + + ]: 905726 : } while ((tok = strtok(NULL, " \n")));
144 : :
145 : : #undef _vmflag_match
146 : :
147 [ + + ]: 155584 : if (shared && maywrite)
148 : 520 : vma_area->e->fdflags = O_RDWR;
149 : : else
150 : 155064 : vma_area->e->fdflags = O_RDONLY;
151 : 155584 : vma_area->e->has_fdflags = true;
152 : :
153 [ + + ]: 155584 : if (vma_area->e->madv)
154 : 80 : vma_area->e->has_madv = true;
155 : :
156 : : return 0;
157 : : }
158 : :
159 : : static inline int is_anon_shmem_map(dev_t dev)
160 : : {
161 : 15816 : return kerndat_shmem_dev == dev;
162 : : }
163 : :
164 : : struct vma_file_info {
165 : : int dev_maj;
166 : : int dev_min;
167 : : unsigned long ino;
168 : : struct vma_area *vma;
169 : : };
170 : :
171 : : static inline int vfi_equal(struct vma_file_info *a, struct vma_file_info *b)
172 : : {
173 : 303840 : return ((a->ino ^ b->ino) |
174 : 303840 : (a->dev_maj ^ b->dev_maj) |
175 : 151920 : (a->dev_min ^ b->dev_min)) == 0;
176 : : }
177 : :
178 : 155584 : static int vma_get_mapfile(struct vma_area *vma, DIR *mfd,
179 : 151920 : struct vma_file_info *vfi, struct vma_file_info *prev_vfi)
180 : : {
181 : : char path[32];
182 : :
183 [ + - ]: 155584 : if (!mfd)
184 : : return 0;
185 : :
186 [ + + ][ + + ]: 155584 : if (prev_vfi->vma && vfi_equal(vfi, prev_vfi)) {
187 : : struct vma_area *prev = prev_vfi->vma;
188 : :
189 : : /*
190 : : * If vfi is equal (!) and negative @vm_file_fd --
191 : : * we have nothing to borrow for sure.
192 : : */
193 [ + + ]: 128912 : if (prev->vm_file_fd < 0)
194 : : return 0;
195 : :
196 : 23712 : pr_debug("vma %"PRIx64" borrows vfi from previous %"PRIx64"\n",
197 : : vma->e->start, prev->e->start);
198 : 23712 : vma->vm_file_fd = prev->vm_file_fd;
199 [ - + ]: 23712 : if (prev->e->status & VMA_AREA_SOCKET)
200 : 0 : vma->e->status |= VMA_AREA_SOCKET | VMA_AREA_REGULAR;
201 : 23712 : vma->file_borrowed = true;
202 : :
203 : 23712 : return 0;
204 : : }
205 : :
206 : : /* Figure out if it's file mapping */
207 : 26672 : snprintf(path, sizeof(path), "%"PRIx64"-%"PRIx64, vma->e->start, vma->e->end);
208 : :
209 : : /*
210 : : * Note that we "open" it in dumper process space
211 : : * so later we might refer to it via /proc/self/fd/vm_file_fd
212 : : * if needed.
213 : : */
214 : 26672 : vma->vm_file_fd = openat(dirfd(mfd), path, O_RDONLY);
215 [ + + ]: 26672 : if (vma->vm_file_fd < 0) {
216 [ - + ]: 10856 : if (errno == ENXIO) {
217 : : struct stat buf;
218 : :
219 [ # # ]: 0 : if (fstatat(dirfd(mfd), path, &buf, 0))
220 : 0 : return -1;
221 : :
222 [ # # ]: 0 : if (!S_ISSOCK(buf.st_mode))
223 : : return -1;
224 : :
225 : 0 : pr_info("Found socket %"PRIu64" mapping @%"PRIx64"\n",
226 : : buf.st_ino, vma->e->start);
227 : 0 : vma->e->status |= VMA_AREA_SOCKET | VMA_AREA_REGULAR;
228 : 0 : vma->vm_socket_id = buf.st_ino;
229 [ + - ]: 10856 : } else if (errno != ENOENT)
230 : : return -1;
231 : : }
232 : :
233 : : return 0;
234 : : }
235 : :
236 : 347 : int parse_self_maps_lite(struct vm_area_list *vms)
237 : : {
238 : : FILE *maps;
239 : :
240 : : vm_area_list_init(vms);
241 : :
242 : 347 : maps = fopen("/proc/self/maps", "r");
243 [ + - ]: 347 : if (maps == NULL) {
244 : 0 : pr_perror("Can't open self maps");
245 : 0 : return -1;
246 : : }
247 : :
248 [ + + ]: 37335 : while (fgets(buf, BUF_SIZE, maps) != NULL) {
249 : : struct vma_area *vma;
250 : : char *end;
251 : :
252 : 36988 : vma = alloc_vma_area();
253 [ - + ]: 36988 : if (!vma) {
254 : 0 : fclose(maps);
255 : 0 : return -1;
256 : : }
257 : :
258 : 36988 : vma->e->start = strtoul(buf, &end, 16);
259 : 36988 : vma->e->end = strtoul(end + 1, NULL, 16);
260 : 36988 : list_add_tail(&vma->list, &vms->h);
261 : 36988 : vms->nr++;
262 : :
263 : 36988 : pr_debug("Parsed %"PRIx64"-%"PRIx64" vma\n", vma->e->start, vma->e->end);
264 : : }
265 : :
266 : 347 : fclose(maps);
267 : 347 : return 0;
268 : : }
269 : :
270 : : static char smaps_buf[PAGE_SIZE];
271 : :
272 : 3664 : int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, bool use_map_files)
273 : : {
274 : : struct vma_area *vma_area = NULL;
275 : : unsigned long start, end, pgoff;
276 : : bool prev_growsdown = false;
277 : : char r, w, x, s;
278 : : int ret = -1;
279 : : struct vma_file_info vfi;
280 : 3664 : struct vma_file_info prev_vfi = {};
281 : :
282 : : DIR *map_files_dir = NULL;
283 : : FILE *smaps = NULL;
284 : :
285 : 3664 : vma_area_list->nr = 0;
286 : 3664 : vma_area_list->longest = 0;
287 : 3664 : vma_area_list->priv_size = 0;
288 : 3664 : INIT_LIST_HEAD(&vma_area_list->h);
289 : :
290 [ - + ][ + - ]: 3664 : smaps = fopen_proc(pid, "smaps");
[ - + ]
291 [ + - ]: 3664 : if (!smaps)
292 : : goto err;
293 : :
294 : 3664 : setvbuf(smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
295 : :
296 [ + - ]: 3664 : if (use_map_files) {
297 [ - + ][ + - ]: 3664 : map_files_dir = opendir_proc(pid, "map_files");
[ - + ]
298 [ + - ]: 2493008 : if (!map_files_dir) /* old kernel? */
299 : : goto err;
300 : : }
301 : :
302 : : while (1) {
303 : : int num;
304 : : char file_path[6];
305 : : bool eof;
306 : :
307 : 4986016 : eof = (fgets(buf, BUF_SIZE, smaps) == NULL);
308 : :
309 [ + + ][ + + ]: 2493008 : if (!eof && !is_vma_range_fmt(buf)) {
310 [ - + ]: 2333760 : if (!strncmp(buf, "Nonlinear", 9)) {
311 [ # # ]: 0 : BUG_ON(!vma_area);
312 : 0 : pr_err("Nonlinear mapping found %016"PRIx64"-%016"PRIx64"\n",
313 : : vma_area->e->start, vma_area->e->end);
314 : : /*
315 : : * VMA is already on list and will be
316 : : * freed later as list get destroyed.
317 : : */
318 : : vma_area = NULL;
319 : 0 : goto err;
320 [ + + ]: 2333760 : } else if (!strncmp(buf, "VmFlags: ", 9)) {
321 [ - + ]: 155584 : BUG_ON(!vma_area);
322 [ + - ]: 155584 : if (parse_vmflags(&buf[9], vma_area))
323 : : goto err;
324 : 2337424 : continue;
325 : : } else
326 : 2178176 : continue;
327 : : }
328 : :
329 [ + + ]: 159248 : if (vma_area) {
330 : : /* If we've split the stack vma, only the lowest one has the guard page. */
331 [ + + ][ + + ]: 155584 : if ((vma_area->e->flags & MAP_GROWSDOWN) && !prev_growsdown)
332 : 3744 : vma_area->e->start -= PAGE_SIZE; /* Guard page */
333 : 155584 : prev_growsdown = (bool)(vma_area->e->flags & MAP_GROWSDOWN);
334 : :
335 : 155584 : list_add_tail(&vma_area->list, &vma_area_list->h);
336 : 155584 : vma_area_list->nr++;
337 [ + + ]: 155584 : if (privately_dump_vma(vma_area)) {
338 : : unsigned long pages;
339 : :
340 : 151400 : pages = vma_area_len(vma_area) / PAGE_SIZE;
341 : 151400 : vma_area_list->priv_size += pages;
342 : 151400 : vma_area_list->longest = max(vma_area_list->longest, pages);
343 : : }
344 : :
345 : 155584 : prev_vfi = vfi;
346 : 155584 : prev_vfi.vma = vma_area;
347 : : }
348 : :
349 [ + + ]: 159248 : if (eof)
350 : : break;
351 : :
352 : 155584 : vma_area = alloc_vma_area();
353 [ + - ]: 155584 : if (!vma_area)
354 : : goto err;
355 : :
356 : : memset(file_path, 0, 6);
357 : 155584 : num = sscanf(buf, "%lx-%lx %c%c%c%c %lx %x:%x %lu %5s",
358 : : &start, &end, &r, &w, &x, &s, &pgoff,
359 : : &vfi.dev_maj, &vfi.dev_min, &vfi.ino, file_path);
360 [ - + ]: 155584 : if (num < 10) {
361 : 0 : pr_err("Can't parse: %s\n", buf);
362 : 0 : goto err;
363 : : }
364 : :
365 : 155584 : vma_area->e->start = start;
366 : 155584 : vma_area->e->end = end;
367 : 155584 : vma_area->e->pgoff = pgoff;
368 : 155584 : vma_area->e->prot = PROT_NONE;
369 : :
370 [ + - ]: 155584 : if (vma_get_mapfile(vma_area, map_files_dir, &vfi, &prev_vfi))
371 : : goto err_bogus_mapfile;
372 : :
373 [ + + ]: 155584 : if (r == 'r')
374 : 107290 : vma_area->e->prot |= PROT_READ;
375 [ + + ]: 155584 : if (w == 'w')
376 : 76907 : vma_area->e->prot |= PROT_WRITE;
377 [ + + ]: 155584 : if (x == 'x')
378 : 50777 : vma_area->e->prot |= PROT_EXEC;
379 : :
380 [ + + ]: 155584 : if (s == 's')
381 : 520 : vma_area->e->flags = MAP_SHARED;
382 [ + - ]: 155064 : else if (s == 'p')
383 : 155064 : vma_area->e->flags = MAP_PRIVATE;
384 : : else {
385 : 0 : pr_err("Unexpected VMA met (%c)\n", s);
386 : 0 : goto err;
387 : : }
388 : :
389 [ - + ]: 155584 : if (vma_area->e->status != 0) {
390 : 0 : continue;
391 [ + + ][ - + ]: 155584 : } else if (strstr(buf, "[vsyscall]") || strstr(buf, "[vectors]")) {
392 : 3664 : vma_area->e->status |= VMA_AREA_VSYSCALL;
393 [ + + ]: 151920 : } else if (strstr(buf, "[vdso]")) {
394 : 3664 : vma_area->e->status |= VMA_AREA_REGULAR;
395 [ + - ]: 3664 : if ((vma_area->e->prot & VDSO_PROT) == VDSO_PROT)
396 : 3664 : vma_area->e->status |= VMA_AREA_VDSO;
397 [ + + ]: 148256 : } else if (strstr(buf, "[heap]")) {
398 : 2824 : vma_area->e->status |= VMA_AREA_REGULAR | VMA_AREA_HEAP;
399 : : } else {
400 : 145432 : vma_area->e->status = VMA_AREA_REGULAR;
401 : : }
402 : :
403 : : /*
404 : : * Some mapping hints for restore, we save this on
405 : : * disk and restore might need to analyze it.
406 : : */
407 [ + + ]: 155584 : if (vma_area->file_borrowed) {
408 : 23712 : struct vma_area *prev = prev_vfi.vma;
409 : :
410 : : /*
411 : : * Pick-up flags that might be set in the branch below.
412 : : * Status is copied as-is as it should be zero here,
413 : : * and have full match with the previous.
414 : : */
415 : 23712 : vma_area->e->flags |= (prev->e->flags & MAP_ANONYMOUS);
416 : 23712 : vma_area->e->status = prev->e->status;
417 : 23712 : vma_area->e->shmid = prev->e->shmid;
418 : 23712 : vma_area->st = prev->st;
419 [ + + ]: 131872 : } else if (vma_area->vm_file_fd >= 0) {
420 : : struct stat *st_buf;
421 : :
422 [ - + ]: 15816 : st_buf = vma_area->st = xmalloc(sizeof(*st_buf));
423 [ + - ]: 15816 : if (!st_buf)
424 : : goto err;
425 : :
426 [ - + ]: 15816 : if (fstat(vma_area->vm_file_fd, st_buf) < 0) {
427 : 0 : pr_perror("Failed fstat on %d's map %lu", pid, start);
428 : 0 : goto err;
429 : : }
430 : :
431 [ - + ][ # # ]: 15816 : if (!S_ISREG(st_buf->st_mode) &&
432 [ # # ]: 0 : !(S_ISCHR(st_buf->st_mode) && st_buf->st_rdev == DEVZERO)) {
433 : 0 : pr_err("Can't handle non-regular mapping on %d's map %lu\n", pid, start);
434 : 0 : goto err;
435 : : }
436 : :
437 : : /*
438 : : * /dev/zero stands for anon-shared mapping
439 : : * otherwise it's some file mapping.
440 : : */
441 [ + + ]: 15816 : if (is_anon_shmem_map(st_buf->st_dev)) {
442 [ + - ]: 392 : if (!(vma_area->e->flags & MAP_SHARED))
443 : : goto err_bogus_mapping;
444 : 392 : vma_area->e->flags |= MAP_ANONYMOUS;
445 : 392 : vma_area->e->status |= VMA_ANON_SHARED;
446 : 392 : vma_area->e->shmid = st_buf->st_ino;
447 : :
448 [ + + ]: 392 : if (!strcmp(file_path, "/SYSV")) {
449 : 24 : pr_info("path: %s\n", file_path);
450 : 24 : vma_area->e->status |= VMA_AREA_SYSVIPC;
451 : : }
452 : : } else {
453 [ + + ]: 15424 : if (vma_area->e->flags & MAP_PRIVATE)
454 : 15344 : vma_area->e->status |= VMA_FILE_PRIVATE;
455 : : else
456 : 80 : vma_area->e->status |= VMA_FILE_SHARED;
457 : : }
458 : : } else {
459 : : /*
460 : : * No file but mapping -- anonymous one.
461 : : */
462 [ - + ]: 116056 : if (vma_area->e->flags & MAP_SHARED) {
463 : 0 : vma_area->e->status |= VMA_ANON_SHARED;
464 : 0 : vma_area->e->shmid = vfi.ino;
465 : : } else {
466 : 116056 : vma_area->e->status |= VMA_ANON_PRIVATE;
467 : : }
468 : 155584 : vma_area->e->flags |= MAP_ANONYMOUS;
469 : : }
470 : : }
471 : :
472 : : vma_area = NULL;
473 : : ret = 0;
474 : :
475 : : err:
476 [ + - ]: 3664 : if (smaps)
477 : 3664 : fclose(smaps);
478 : :
479 [ + - ]: 3664 : if (map_files_dir)
480 : 3664 : closedir(map_files_dir);
481 : :
482 [ - + ]: 3664 : xfree(vma_area);
483 : 3664 : return ret;
484 : :
485 : : err_bogus_mapping:
486 : 0 : pr_err("Bogus mapping 0x%"PRIx64"-0x%"PRIx64" (flags: %#x vm_file_fd: %d)\n",
487 : : vma_area->e->start, vma_area->e->end,
488 : : vma_area->e->flags, vma_area->vm_file_fd);
489 : 0 : goto err;
490 : :
491 : : err_bogus_mapfile:
492 : 0 : pr_perror("Can't open %d's mapfile link %lx", pid, start);
493 : 0 : goto err;
494 : : }
495 : :
496 : 5657 : int parse_pid_stat_small(pid_t pid, struct proc_pid_stat_small *s)
497 : : {
498 : : char *tok, *p;
499 : : int fd;
500 : : int n;
501 : :
502 [ + + ]: 5657 : fd = open_proc(pid, "stat");
503 [ + + ]: 5657 : if (fd < 0)
504 : : return -1;
505 : :
506 : 11292 : n = read(fd, buf, BUF_SIZE);
507 [ - + ]: 5646 : if (n < 1) {
508 : 0 : pr_err("stat for %d is corrupted\n", pid);
509 : 0 : close(fd);
510 : 0 : return -1;
511 : : }
512 : 5646 : close(fd);
513 : :
514 : : memset(s, 0, sizeof(*s));
515 : :
516 : 5646 : tok = strchr(buf, ' ');
517 [ + - ]: 5646 : if (!tok)
518 : : goto err;
519 : 5646 : *tok++ = '\0';
520 [ + - ]: 5646 : if (*tok != '(')
521 : : goto err;
522 : :
523 : 5646 : s->pid = atoi(buf);
524 : :
525 : 5646 : p = strrchr(tok + 1, ')');
526 [ + - ]: 5646 : if (!p)
527 : : goto err;
528 : 5646 : *tok = '\0';
529 : 5646 : *p = '\0';
530 : :
531 : 5646 : strncpy(s->comm, tok + 1, sizeof(s->comm));
532 : :
533 : 5646 : n = sscanf(p + 1, " %c %d %d %d", &s->state, &s->ppid, &s->pgid, &s->sid);
534 [ - + ]: 5646 : if (n < 4)
535 : : goto err;
536 : :
537 : : return 0;
538 : :
539 : : err:
540 : 0 : pr_err("Parsing %d's stat failed (#fields do not match)\n", pid);
541 : 0 : return -1;
542 : : }
543 : :
544 : 942 : int parse_pid_stat(pid_t pid, struct proc_pid_stat *s)
545 : : {
546 : : char *tok, *p;
547 : : int fd;
548 : : int n;
549 : :
550 [ - + ]: 942 : fd = open_proc(pid, "stat");
551 [ + - ]: 942 : if (fd < 0)
552 : : return -1;
553 : :
554 : 1884 : n = read(fd, buf, BUF_SIZE);
555 [ - + ]: 942 : if (n < 1) {
556 : 0 : pr_err("stat for %d is corrupted\n", pid);
557 : 0 : close(fd);
558 : 0 : return -1;
559 : : }
560 : 942 : close(fd);
561 : :
562 : : memset(s, 0, sizeof(*s));
563 : :
564 : 942 : tok = strchr(buf, ' ');
565 [ + - ]: 942 : if (!tok)
566 : : goto err;
567 : 942 : *tok++ = '\0';
568 [ + - ]: 942 : if (*tok != '(')
569 : : goto err;
570 : :
571 : 942 : s->pid = atoi(buf);
572 : :
573 : 942 : p = strrchr(tok + 1, ')');
574 [ + - ]: 942 : if (!p)
575 : : goto err;
576 : 942 : *tok = '\0';
577 : 942 : *p = '\0';
578 : :
579 : 942 : strncpy(s->comm, tok + 1, sizeof(s->comm));
580 : :
581 : 942 : n = sscanf(p + 1,
582 : : " %c %d %d %d %d %d %u %lu %lu %lu %lu "
583 : : "%lu %lu %ld %ld %ld %ld %d %d %llu %lu %ld %lu %lu %lu %lu "
584 : : "%lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld "
585 : : "%lu %lu %lu %lu %lu %lu %lu %d",
586 : : &s->state,
587 : : &s->ppid,
588 : : &s->pgid,
589 : : &s->sid,
590 : : &s->tty_nr,
591 : : &s->tty_pgrp,
592 : : &s->flags,
593 : : &s->min_flt,
594 : : &s->cmin_flt,
595 : : &s->maj_flt,
596 : : &s->cmaj_flt,
597 : : &s->utime,
598 : : &s->stime,
599 : : &s->cutime,
600 : : &s->cstime,
601 : : &s->priority,
602 : : &s->nice,
603 : : &s->num_threads,
604 : : &s->zero0,
605 : : &s->start_time,
606 : : &s->vsize,
607 : : &s->mm_rss,
608 : : &s->rsslim,
609 : : &s->start_code,
610 : : &s->end_code,
611 : : &s->start_stack,
612 : : &s->esp,
613 : : &s->eip,
614 : : &s->sig_pending,
615 : : &s->sig_blocked,
616 : : &s->sig_ignored,
617 : : &s->sig_handled,
618 : : &s->wchan,
619 : : &s->zero1,
620 : : &s->zero2,
621 : : &s->exit_signal,
622 : : &s->task_cpu,
623 : : &s->rt_priority,
624 : : &s->policy,
625 : : &s->delayacct_blkio_ticks,
626 : : &s->gtime,
627 : : &s->cgtime,
628 : : &s->start_data,
629 : : &s->end_data,
630 : : &s->start_brk,
631 : : &s->arg_start,
632 : : &s->arg_end,
633 : : &s->env_start,
634 : : &s->env_end,
635 : : &s->exit_code);
636 [ - + ]: 942 : if (n < 50)
637 : : goto err;
638 : :
639 : : return 0;
640 : :
641 : : err:
642 : 0 : pr_err("Parsing %d's stat failed (#fields do not match)\n", pid);
643 : 0 : return -1;
644 : : }
645 : :
646 : 1838 : static int ids_parse(char *str, unsigned int *arr)
647 : : {
648 : : char *end;
649 : :
650 : 1838 : arr[0] = strtol(str, &end, 10);
651 : 1838 : arr[1] = strtol(end + 1, &end, 10);
652 : 1838 : arr[2] = strtol(end + 1, &end, 10);
653 : 1838 : arr[3] = strtol(end + 1, &end, 10);
654 [ + - ]: 1838 : if (*end != '\n')
655 : : return -1;
656 : : else
657 : 1838 : return 0;
658 : : }
659 : :
660 : 3676 : static int cap_parse(char *str, unsigned int *res)
661 : : {
662 : : int i, ret;
663 : :
664 [ + + ]: 11028 : for (i = 0; i < PROC_CAP_SIZE; i++) {
665 : 7352 : ret = sscanf(str, "%08x", &res[PROC_CAP_SIZE - 1 - i]);
666 [ + - ]: 7352 : if (ret != 1)
667 : : return -1;
668 : 7352 : str += 8;
669 : : }
670 : :
671 : : return 0;
672 : : }
673 : :
674 : 919 : int parse_pid_status(pid_t pid, struct proc_status_creds *cr)
675 : : {
676 : : int done = 0;
677 : : FILE *f;
678 : : char str[64];
679 : :
680 [ - + ][ + - ]: 919 : f = fopen_proc(pid, "status");
[ - + ]
681 [ + - ]: 919 : if (f == NULL) {
682 : 0 : pr_perror("Can't open proc status");
683 : 0 : return -1;
684 : : }
685 : :
686 [ + + + - ]: 63411 : while (done < 6 && fgets(str, sizeof(str), f)) {
687 [ + + ]: 31246 : if (!strncmp(str, "Uid:", 4)) {
688 [ + - ]: 919 : if (ids_parse(str + 5, cr->uids))
689 : : goto err_parse;
690 : :
691 : 919 : done++;
692 : : }
693 : :
694 [ + + ]: 31246 : if (!strncmp(str, "Gid:", 4)) {
695 [ + - ]: 919 : if (ids_parse(str + 5, cr->gids))
696 : : goto err_parse;
697 : :
698 : 919 : done++;
699 : : }
700 : :
701 [ + + ]: 31246 : if (!strncmp(str, "CapInh:", 7)) {
702 [ + - ]: 919 : if (cap_parse(str + 8, cr->cap_inh))
703 : : goto err_parse;
704 : :
705 : 919 : done++;
706 : : }
707 : :
708 [ + + ]: 31246 : if (!strncmp(str, "CapEff:", 7)) {
709 [ + - ]: 919 : if (cap_parse(str + 8, cr->cap_eff))
710 : : goto err_parse;
711 : :
712 : 919 : done++;
713 : : }
714 : :
715 [ + + ]: 31246 : if (!strncmp(str, "CapPrm:", 7)) {
716 [ + - ]: 919 : if (cap_parse(str + 8, cr->cap_prm))
717 : : goto err_parse;
718 : :
719 : 919 : done++;
720 : : }
721 : :
722 [ + + ]: 31246 : if (!strncmp(str, "CapBnd:", 7)) {
723 [ + - ]: 919 : if (cap_parse(str + 8, cr->cap_bnd))
724 : : goto err_parse;
725 : :
726 : 1838 : done++;
727 : : }
728 : : }
729 : :
730 [ - + ]: 919 : if (done != 6) {
731 : : err_parse:
732 : 0 : pr_err("Error parsing proc status file\n");
733 : 0 : fclose(f);
734 : 0 : return -1;
735 : : }
736 : :
737 : 919 : fclose(f);
738 : 919 : return 0;
739 : : }
740 : :
741 : : struct opt2flag {
742 : : char *opt;
743 : : unsigned flag;
744 : : };
745 : :
746 : 292008 : static int do_opt2flag(char *opt, unsigned *flags,
747 : : const struct opt2flag *opts, char *unknown)
748 : : {
749 : : int i;
750 : : char *end;
751 : :
752 : : while (1) {
753 : 351988 : end = strchr(opt, ',');
754 [ + + ]: 351988 : if (end)
755 : 205984 : *end = '\0';
756 : :
757 [ + + ]: 1419336 : for (i = 0; opts[i].opt != NULL; i++)
758 [ + + ]: 1338380 : if (!strcmp(opts[i].opt, opt)) {
759 : 271032 : (*flags) |= opts[i].flag;
760 : 271032 : break;
761 : : }
762 : :
763 [ + + ]: 351988 : if (opts[i].opt == NULL) {
764 [ - + ]: 80956 : if (!unknown) {
765 : 0 : pr_err("Unknown option [%s]\n", opt);
766 : 0 : return -1;
767 : : }
768 : :
769 : : strcpy(unknown, opt);
770 : 80956 : unknown += strlen(opt);
771 : 80956 : *unknown = ',';
772 : 80956 : unknown++;
773 : : }
774 : :
775 [ + + ]: 351988 : if (!end) {
776 [ + + ]: 146004 : if (unknown)
777 : 73002 : *unknown = '\0';
778 : : break;
779 : : } else
780 : 205984 : opt = end + 1;
781 : 205984 : }
782 : :
783 : : return 0;
784 : : }
785 : :
786 : 73002 : static int parse_mnt_flags(char *opt, unsigned *flags)
787 : : {
788 : 73002 : const struct opt2flag mnt_opt2flag[] = {
789 : : { "rw", 0, },
790 : : { "ro", MS_RDONLY, },
791 : : { "nosuid", MS_NOSUID, },
792 : : { "nodev", MS_NODEV, } ,
793 : : { "noexec", MS_NOEXEC, },
794 : : { "noatime", MS_NOATIME, },
795 : : { "nodiratime", MS_NODIRATIME, },
796 : : { "relatime", MS_RELATIME, },
797 : : { },
798 : : };
799 : :
800 : 73002 : return do_opt2flag(opt, flags, mnt_opt2flag, NULL);
801 : : }
802 : :
803 : 73002 : static int parse_sb_opt(char *opt, unsigned *flags, char *uopt)
804 : : {
805 : 73002 : const struct opt2flag sb_opt2flag[] = {
806 : : { "rw", 0, },
807 : : { "ro", MS_RDONLY, },
808 : : { "sync", MS_SYNC, },
809 : : { "dirsync", MS_DIRSYNC, },
810 : : { "mad", MS_MANDLOCK, },
811 : : { },
812 : : };
813 : :
814 : 73002 : return do_opt2flag(opt, flags, sb_opt2flag, uopt);
815 : : }
816 : :
817 : 146004 : static int parse_mnt_opt(char *str, struct mount_info *mi, int *off)
818 : : {
819 : : char *istr = str, *end;
820 : :
821 : : while (1) {
822 : 73002 : end = strchr(str, ' ');
823 [ - + ]: 73002 : if (!end) {
824 : 0 : pr_err("Error parsing mount options\n");
825 : 0 : return -1;
826 : : }
827 : :
828 : 73002 : *end = '\0';
829 [ - + ]: 73002 : if (!strncmp(str, "-", 1))
830 : : break;
831 [ # # ]: 0 : else if (!strncmp(str, "shared:", 7)) {
832 : 0 : mi->flags |= MS_SHARED;
833 : 0 : mi->shared_id = atoi(str + 7);
834 [ # # ]: 0 : } else if (!strncmp(str, "master:", 7)) {
835 : 0 : mi->flags |= MS_SLAVE;
836 : 0 : mi->master_id = atoi(str + 7);
837 [ # # ]: 0 : } else if (!strncmp(str, "propagate_from:", 15)) {
838 : : /* skip */;
839 [ # # ]: 0 : } else if (!strncmp(str, "unbindable", 11))
840 : 0 : mi->flags |= MS_UNBINDABLE;
841 : : else {
842 : 0 : pr_err("Unknown option [%s]\n", str);
843 : 0 : return -1;
844 : : }
845 : :
846 : 0 : str = end + 1;
847 : 0 : }
848 : :
849 : 73002 : *off = end - istr + 1;
850 : 73002 : return 0;
851 : : }
852 : :
853 : 73002 : static int parse_mountinfo_ent(char *str, struct mount_info *new)
854 : : {
855 : : unsigned int kmaj, kmin;
856 : : int ret, n;
857 : : char *opt;
858 : : char *fstype;
859 : :
860 [ - + ]: 73002 : new->mountpoint = xmalloc(PATH_MAX);
861 [ + - ]: 73002 : if (new->mountpoint == NULL)
862 : : return -1;
863 : :
864 : 73002 : new->mountpoint[0] = '.';
865 : 73002 : ret = sscanf(str, "%i %i %u:%u %ms %s %ms %n",
866 : : &new->mnt_id, &new->parent_mnt_id,
867 : 73002 : &kmaj, &kmin, &new->root, new->mountpoint + 1,
868 : : &opt, &n);
869 [ - + ]: 73002 : if (ret != 7) {
870 [ # # ]: 0 : xfree(new->mountpoint);
871 : : return -1;
872 : : }
873 : :
874 [ - + ]: 73002 : new->mountpoint = xrealloc(new->mountpoint, strlen(new->mountpoint) + 1);
875 : :
876 : 73002 : new->s_dev = MKKDEV(kmaj, kmin);
877 : 73002 : new->flags = 0;
878 [ + - ]: 73002 : if (parse_mnt_flags(opt, &new->flags))
879 : : return -1;
880 : :
881 : 73002 : free(opt); /* after %ms scanf */
882 : :
883 : 73002 : str += n;
884 [ + - ]: 73002 : if (parse_mnt_opt(str, new, &n))
885 : : return -1;
886 : :
887 : 73002 : str += n;
888 : 73002 : ret = sscanf(str, "%ms %ms %ms", &fstype, &new->source, &opt);
889 [ + - ]: 73002 : if (ret != 3)
890 : : return -1;
891 : :
892 : : ret = -1;
893 : 73002 : new->fstype = find_fstype_by_name(fstype);
894 : :
895 [ - + ]: 73002 : new->options = xmalloc(strlen(opt) + 1);
896 [ + - ]: 73002 : if (!new->options)
897 : : goto err;
898 : :
899 [ + - ]: 73002 : if (parse_sb_opt(opt, &new->flags, new->options))
900 : : goto err;
901 : :
902 : : ret = 0;
903 : : err:
904 : 73002 : free(opt);
905 : 73002 : free(fstype);
906 : 73002 : return ret;
907 : : }
908 : :
909 : 3102 : struct mount_info *parse_mountinfo(pid_t pid, struct ns_id *nsid)
910 : : {
911 : : struct mount_info *list = NULL;
912 : : FILE *f;
913 : : char str[1024];
914 : :
915 : : snprintf(str, sizeof(str), "/proc/%d/mountinfo", pid);
916 : 3102 : f = fopen(str, "r");
917 [ + - ]: 3102 : if (!f) {
918 : 0 : pr_perror("Can't open %d mountinfo", pid);
919 : 0 : return NULL;
920 : : }
921 : :
922 [ + + ]: 76104 : while (fgets(str, sizeof(str), f)) {
923 : : struct mount_info *new;
924 : : int ret;
925 : :
926 : 73002 : new = mnt_entry_alloc();
927 [ + - ]: 73002 : if (!new)
928 : : goto err;
929 : :
930 : 73002 : new->nsid = nsid;
931 : :
932 : 73002 : new->next = list;
933 : : list = new;
934 : :
935 : 73002 : ret = parse_mountinfo_ent(str, new);
936 [ - + ]: 73002 : if (ret < 0) {
937 : 0 : pr_err("Bad format in %d mountinfo\n", pid);
938 : 0 : goto err;
939 : : }
940 : :
941 : 73002 : pr_info("\ttype %s source %s %x %s @ %s flags %x options %s\n",
942 : : new->fstype->name, new->source,
943 : : new->s_dev, new->root, new->mountpoint,
944 : : new->flags, new->options);
945 : :
946 [ - + ]: 73002 : if (new->fstype->parse) {
947 : 0 : ret = new->fstype->parse(new);
948 [ # # ]: 0 : if (ret) {
949 : 0 : pr_err("Failed to parse FS specific data on %s\n",
950 : : new->mountpoint);
951 : 73002 : goto err;
952 : : }
953 : : }
954 : : }
955 : : out:
956 : 3102 : fclose(f);
957 : 3102 : return list;
958 : :
959 : : err:
960 [ # # ]: 0 : while (list) {
961 : 0 : struct mount_info *next = list->next;
962 : 0 : mnt_entry_free(list);
963 : : list = next;
964 : : }
965 : : goto out;
966 : : }
967 : :
968 : : static char nybble(const char n)
969 : : {
970 [ + + ][ + + ]: 900 : if (n >= '0' && n <= '9')
971 : 566 : return n - '0';
972 [ - + ][ - + ]: 334 : else if (n >= 'A' && n <= 'F')
973 : 0 : return n - ('A' - 10);
974 [ + + ][ + + ]: 334 : else if (n >= 'a' && n <= 'f')
975 : 234 : return n - ('a' - 10);
976 : : return 0;
977 : : }
978 : :
979 : 100 : static int alloc_fhandle(FhEntry *fh)
980 : : {
981 : 50 : fh->n_handle = FH_ENTRY_SIZES__min_entries;
982 [ - + ]: 50 : fh->handle = xmalloc(pb_repeated_size(fh, handle));
983 : :
984 [ + - ]: 50 : return fh->handle == NULL ? -1 : 0;
985 : : }
986 : :
987 : : static void free_fhandle(FhEntry *fh)
988 : : {
989 [ + + + + ]: 50 : if (fh->handle)
990 [ + - ][ + - ]: 14 : xfree(fh->handle);
991 : : }
992 : :
993 : 50 : static void parse_fhandle_encoded(char *tok, FhEntry *fh)
994 : : {
995 : 50 : char *d = (char *)fh->handle;
996 : : int i = 0;
997 : :
998 : 50 : memzero(d, pb_repeated_size(fh, handle));
999 : :
1000 [ - + ]: 50 : while (*tok == ' ')
1001 : 0 : tok++;
1002 : :
1003 [ + - ]: 450 : while (*tok) {
1004 [ + - ]: 450 : if (i >= pb_repeated_size(fh, handle))
1005 : : break;
1006 : 1350 : d[i++] = (nybble(tok[0]) << 4) | nybble(tok[1]);
1007 [ + + ]: 450 : if (tok[1])
1008 : 400 : tok += 2;
1009 : : else
1010 : : break;
1011 : : }
1012 : 50 : }
1013 : :
1014 : : #define fdinfo_field(str, field) !strncmp(str, field":", sizeof(field))
1015 : :
1016 : 16456 : static int parse_fdinfo_pid_s(char *pid, int fd, int type,
1017 : : int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
1018 : : {
1019 : : FILE *f;
1020 : : char str[256];
1021 : : bool entry_met = false;
1022 : : int ret = -1;
1023 : :
1024 : : sprintf(str, "/proc/%s/fdinfo/%d", pid, fd);
1025 : 16456 : f = fopen(str, "r");
1026 [ + - ]: 16456 : if (!f) {
1027 : 0 : pr_perror("Can't open %s to parse", str);
1028 : 0 : return -1;
1029 : : }
1030 : :
1031 [ + + ]: 49500 : while (fgets(str, sizeof(str), f)) {
1032 : : union fdinfo_entries entry;
1033 : :
1034 [ + + ][ + + ]: 33044 : if (fdinfo_field(str, "pos") ||
1035 [ - + ]: 132 : fdinfo_field(str, "flags") ||
1036 : 132 : fdinfo_field(str, "mnt_id")) {
1037 : : unsigned long long val;
1038 : : struct fdinfo_common *fdinfo = arg;
1039 : :
1040 [ + + ]: 32912 : if (type != FD_TYPES__UND)
1041 : 32912 : continue;
1042 : 32808 : ret = sscanf(str, "%*s %lli", &val);
1043 [ + - ]: 32808 : if (ret != 1)
1044 : : goto parse_err;
1045 : :
1046 [ + + ]: 32808 : if (fdinfo_field(str, "pos"))
1047 : 16404 : fdinfo->pos = val;
1048 [ + - ]: 16404 : else if (fdinfo_field(str, "flags"))
1049 : 16404 : fdinfo->flags = val;
1050 [ # # ]: 0 : else if (fdinfo_field(str, "mnt_id"))
1051 : 0 : fdinfo->mnt_id = val;
1052 : :
1053 : : entry_met = true;
1054 : 32808 : continue;
1055 : : }
1056 : :
1057 [ + + ]: 132 : if (type == FD_TYPES__UND)
1058 : 32 : continue;
1059 : :
1060 [ + + ]: 100 : if (fdinfo_field(str, "eventfd-count")) {
1061 : 6 : eventfd_file_entry__init(&entry.efd);
1062 : :
1063 [ + - ]: 6 : if (type != FD_TYPES__EVENTFD)
1064 : : goto parse_err;
1065 : 6 : ret = sscanf(str, "eventfd-count: %"PRIx64,
1066 : : &entry.efd.counter);
1067 [ + - ]: 6 : if (ret != 1)
1068 : : goto parse_err;
1069 : 6 : ret = cb(&entry, arg);
1070 [ + - ]: 6 : if (ret)
1071 : : goto out;
1072 : :
1073 : : entry_met = true;
1074 : 6 : continue;
1075 : : }
1076 [ + + ]: 94 : if (fdinfo_field(str, "tfd")) {
1077 : 6 : eventpoll_tfd_entry__init(&entry.epl);
1078 : :
1079 [ + - ]: 6 : if (type != FD_TYPES__EVENTPOLL)
1080 : : goto parse_err;
1081 : 6 : ret = sscanf(str, "tfd: %d events: %x data: %"PRIx64,
1082 : : &entry.epl.tfd, &entry.epl.events, &entry.epl.data);
1083 [ + - ]: 6 : if (ret != 3)
1084 : : goto parse_err;
1085 : 6 : ret = cb(&entry, arg);
1086 [ + - ]: 6 : if (ret)
1087 : : goto out;
1088 : :
1089 : : entry_met = true;
1090 : 6 : continue;
1091 : : }
1092 [ + + ]: 88 : if (fdinfo_field(str, "sigmask")) {
1093 : 6 : signalfd_entry__init(&entry.sfd);
1094 : :
1095 [ + - ]: 6 : if (type != FD_TYPES__SIGNALFD)
1096 : : goto parse_err;
1097 : 6 : ret = sscanf(str, "sigmask: %Lx",
1098 : : (unsigned long long *)&entry.sfd.sigmask);
1099 [ + - ]: 6 : if (ret != 1)
1100 : : goto parse_err;
1101 : 6 : ret = cb(&entry, arg);
1102 [ + - ]: 6 : if (ret)
1103 : : goto out;
1104 : :
1105 : : entry_met = true;
1106 : 6 : continue;
1107 : : }
1108 [ + + ]: 82 : if (fdinfo_field(str, "fanotify flags")) {
1109 : : struct fsnotify_params *p = arg;
1110 : :
1111 [ + - ]: 16 : if (type != FD_TYPES__FANOTIFY)
1112 : : goto parse_err;
1113 : :
1114 : 16 : ret = sscanf(str, "fanotify flags:%x event-flags:%x",
1115 : : &p->faflags, &p->evflags);
1116 [ + - ]: 16 : if (ret != 2)
1117 : : goto parse_err;
1118 : : entry_met = true;
1119 : 16 : continue;
1120 : : }
1121 [ + + ]: 66 : if (fdinfo_field(str, "fanotify ino")) {
1122 : 16 : FanotifyInodeMarkEntry ie = FANOTIFY_INODE_MARK_ENTRY__INIT;
1123 : 16 : FhEntry f_handle = FH_ENTRY__INIT;
1124 : : int hoff;
1125 : :
1126 [ + - ]: 16 : if (type != FD_TYPES__FANOTIFY)
1127 : : goto parse_err;
1128 : :
1129 : 16 : fanotify_mark_entry__init(&entry.ffy);
1130 : 16 : ie.f_handle = &f_handle;
1131 : 16 : entry.ffy.ie = &ie;
1132 : :
1133 : 16 : ret = sscanf(str,
1134 : : "fanotify ino:%"PRIx64" sdev:%x mflags:%x mask:%x ignored_mask:%x "
1135 : : "fhandle-bytes:%x fhandle-type:%x f_handle: %n",
1136 : : &ie.i_ino, &entry.ffy.s_dev,
1137 : : &entry.ffy.mflags, &entry.ffy.mask, &entry.ffy.ignored_mask,
1138 : : &f_handle.bytes, &f_handle.type,
1139 : : &hoff);
1140 [ + - ]: 16 : if (ret != 7)
1141 : : goto parse_err;
1142 : :
1143 [ + - ]: 16 : if (alloc_fhandle(&f_handle)) {
1144 : : ret = -1;
1145 : 0 : goto out;
1146 : : }
1147 : 16 : parse_fhandle_encoded(str + hoff, &f_handle);
1148 : :
1149 : 16 : entry.ffy.type = MARK_TYPE__INODE;
1150 : 16 : ret = cb(&entry, arg);
1151 : :
1152 : 16 : free_fhandle(&f_handle);
1153 : :
1154 [ + - ]: 16 : if (ret)
1155 : : goto out;
1156 : :
1157 : : entry_met = true;
1158 : 16 : continue;
1159 : : }
1160 [ + + ]: 50 : if (fdinfo_field(str, "fanotify mnt_id")) {
1161 : 16 : FanotifyMountMarkEntry me = FANOTIFY_MOUNT_MARK_ENTRY__INIT;
1162 : :
1163 [ + - ]: 16 : if (type != FD_TYPES__FANOTIFY)
1164 : : goto parse_err;
1165 : :
1166 : 16 : fanotify_mark_entry__init(&entry.ffy);
1167 : 16 : entry.ffy.me = &me;
1168 : :
1169 : 16 : ret = sscanf(str,
1170 : : "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x",
1171 : : &me.mnt_id, &entry.ffy.mflags,
1172 : : &entry.ffy.mask, &entry.ffy.ignored_mask);
1173 [ + - ]: 16 : if (ret != 4)
1174 : : goto parse_err;
1175 : :
1176 : 16 : entry.ffy.type = MARK_TYPE__MOUNT;
1177 : 16 : ret = cb(&entry, arg);
1178 [ + - ]: 16 : if (ret)
1179 : : goto out;
1180 : :
1181 : : entry_met = true;
1182 : 16 : continue;
1183 : : }
1184 [ + - ]: 34 : if (fdinfo_field(str, "inotify wd")) {
1185 : 34 : FhEntry f_handle = FH_ENTRY__INIT;
1186 : : int hoff;
1187 : :
1188 : 34 : inotify_wd_entry__init(&entry.ify);
1189 : 34 : entry.ify.f_handle = &f_handle;
1190 : :
1191 [ + - ]: 34 : if (type != FD_TYPES__INOTIFY)
1192 : : goto parse_err;
1193 : 34 : ret = sscanf(str,
1194 : : "inotify wd:%x ino:%"PRIx64" sdev:%x "
1195 : : "mask:%x ignored_mask:%x "
1196 : : "fhandle-bytes:%x fhandle-type:%x "
1197 : : "f_handle: %n",
1198 : : &entry.ify.wd, &entry.ify.i_ino, &entry.ify.s_dev,
1199 : : &entry.ify.mask, &entry.ify.ignored_mask,
1200 : 34 : &entry.ify.f_handle->bytes, &entry.ify.f_handle->type,
1201 : : &hoff);
1202 [ + - ]: 34 : if (ret != 7)
1203 : : goto parse_err;
1204 : :
1205 [ + - ]: 34 : if (alloc_fhandle(&f_handle)) {
1206 : : ret = -1;
1207 : 33044 : goto out;
1208 : : }
1209 : :
1210 : 34 : parse_fhandle_encoded(str + hoff, entry.ify.f_handle);
1211 : :
1212 : 34 : ret = cb(&entry, arg);
1213 : :
1214 : 34 : free_fhandle(&f_handle);
1215 : :
1216 [ + - ]: 34 : if (ret)
1217 : : goto out;
1218 : :
1219 : : entry_met = true;
1220 : 34 : continue;
1221 : : }
1222 : : }
1223 : :
1224 : : ret = 0;
1225 [ - + ]: 16456 : if (entry_met)
1226 : : goto out;
1227 : : /*
1228 : : * An eventpoll/inotify file may have no target fds set thus
1229 : : * resulting in no tfd: lines in proc. This is normal.
1230 : : */
1231 [ # # ]: 0 : if (type == FD_TYPES__EVENTPOLL || type == FD_TYPES__INOTIFY)
1232 : : goto out;
1233 : :
1234 : 0 : pr_err("No records of type %d found in fdinfo file\n", type);
1235 : : parse_err:
1236 : : ret = -1;
1237 : 0 : pr_perror("%s: error parsing [%s] for %d", __func__, str, type);
1238 : : out:
1239 : 16456 : fclose(f);
1240 : 16456 : return ret;
1241 : : }
1242 : :
1243 : 24 : int parse_fdinfo_pid(int pid, int fd, int type,
1244 : : int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
1245 : : {
1246 : : char pid_s[10];
1247 : :
1248 : : sprintf(pid_s, "%d", pid);
1249 : 24 : return parse_fdinfo_pid_s(pid_s, fd, type, cb, arg);
1250 : : }
1251 : :
1252 : 16432 : int parse_fdinfo(int fd, int type,
1253 : : int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
1254 : : {
1255 : 16432 : return parse_fdinfo_pid_s("self", fd, type, cb, arg);
1256 : : }
1257 : :
1258 : 17738 : static int parse_file_lock_buf(char *buf, struct file_lock *fl,
1259 : : bool is_blocked)
1260 : : {
1261 : : int num;
1262 : :
1263 [ - + ]: 17738 : if (is_blocked) {
1264 : 0 : num = sscanf(buf, "%lld: -> %s %s %s %d %x:%x:%ld %lld %s",
1265 : 0 : &fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
1266 : : &fl->fl_owner, &fl->maj, &fl->min, &fl->i_no,
1267 : 0 : &fl->start, fl->end);
1268 : : } else {
1269 : 17738 : num = sscanf(buf, "%lld:%s %s %s %d %x:%x:%ld %lld %s",
1270 : 17738 : &fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
1271 : : &fl->fl_owner, &fl->maj, &fl->min, &fl->i_no,
1272 : 17738 : &fl->start, fl->end);
1273 : : }
1274 : :
1275 [ - + ]: 17738 : if (num < 10) {
1276 : 0 : pr_err("Invalid file lock info (%d): %s", num, buf);
1277 : 0 : return -1;
1278 : : }
1279 : :
1280 : : return 0;
1281 : : }
1282 : :
1283 : 448 : int parse_file_locks(void)
1284 : : {
1285 : : struct file_lock *fl;
1286 : :
1287 : : FILE *fl_locks;
1288 : : int ret = 0;
1289 : : bool is_blocked;
1290 : :
1291 : 448 : fl_locks = fopen("/proc/locks", "r");
1292 [ + - ]: 448 : if (!fl_locks) {
1293 : 0 : pr_perror("Can't open file locks file!");
1294 : 0 : return -1;
1295 : : }
1296 : :
1297 [ + + ]: 18186 : while (fgets(buf, BUF_SIZE, fl_locks)) {
1298 : 17738 : is_blocked = strstr(buf, "->") != NULL;
1299 : :
1300 : 17738 : fl = alloc_file_lock();
1301 [ - + ]: 17738 : if (!fl) {
1302 : 0 : pr_perror("Alloc file lock failed!");
1303 : : ret = -1;
1304 : 0 : goto err;
1305 : : }
1306 : :
1307 [ - + ]: 17738 : if (parse_file_lock_buf(buf, fl, is_blocked)) {
1308 [ # # ]: 0 : xfree(fl);
1309 : : ret = -1;
1310 : : goto err;
1311 : : }
1312 : :
1313 [ + + ]: 17738 : if (!pid_in_pstree(fl->fl_owner)) {
1314 : : /*
1315 : : * We only care about tasks which are taken
1316 : : * into dump, so we only collect file locks
1317 : : * belong to these tasks.
1318 : : */
1319 [ + - ]: 17718 : xfree(fl);
1320 : 17718 : continue;
1321 : : }
1322 : :
1323 [ - + ]: 20 : if (is_blocked) {
1324 : : /*
1325 : : * Here the task is in the pstree.
1326 : : * If it is blocked on a flock, when we try to
1327 : : * ptrace-seize it, the kernel will unblock task
1328 : : * from flock and will stop it in another place.
1329 : : * So in dumping, a blocked file lock should never
1330 : : * be here.
1331 : : */
1332 : 0 : pr_perror("We have a blocked file lock!");
1333 : : ret = -1;
1334 [ # # ]: 0 : xfree(fl);
1335 : : goto err;
1336 : : }
1337 : :
1338 : 20 : pr_info("lockinfo: %lld:%s %s %s %d %02x:%02x:%ld %lld %s\n",
1339 : : fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
1340 : : fl->fl_owner, fl->maj, fl->min, fl->i_no,
1341 : : fl->start, fl->end);
1342 : :
1343 : 20 : list_add_tail(&fl->list, &file_lock_list);
1344 : : }
1345 : :
1346 : : err:
1347 : 448 : fclose(fl_locks);
1348 : 448 : return ret;
1349 : : }
1350 : :
1351 : 919 : void free_posix_timers(struct proc_posix_timers_stat *st)
1352 : : {
1353 [ + + ]: 1846 : while (!list_empty(&st->timers)) {
1354 : : struct proc_posix_timer *timer;
1355 : : timer = list_first_entry(&st->timers, struct proc_posix_timer, list);
1356 : : list_del(&timer->list);
1357 [ - + ]: 927 : xfree(timer);
1358 : : }
1359 : 919 : }
1360 : :
1361 : 919 : int parse_posix_timers(pid_t pid, struct proc_posix_timers_stat *args)
1362 : : {
1363 : : int ret = 0;
1364 : : int pid_t;
1365 : :
1366 : : FILE * file;
1367 : :
1368 : : char sigpid[7];
1369 : : char tidpid[4];
1370 : :
1371 : : struct proc_posix_timer *timer = NULL;
1372 : :
1373 : 919 : INIT_LIST_HEAD(&args->timers);
1374 : 919 : args->timer_n = 0;
1375 : :
1376 [ - + ][ + - ]: 919 : file = fopen_proc(pid, "timers");
[ - + ]
1377 [ + - ]: 919 : if (file == NULL) {
1378 : 0 : pr_perror("Can't open posix timers file!");
1379 : 0 : return -1;
1380 : : }
1381 : :
1382 : : while (1) {
1383 : : char pbuf[17]; /* 16 + eol */
1384 [ - + ]: 927 : timer = xzalloc(sizeof(struct proc_posix_timer));
1385 [ + - ]: 927 : if (timer == NULL)
1386 : : goto err;
1387 : :
1388 : 927 : ret = fscanf(file, "ID: %ld\n"
1389 : : "signal: %d/%16s\n"
1390 : : "notify: %6[a-z]/%3[a-z].%d\n"
1391 : : "ClockID: %d\n",
1392 : : &timer->spt.it_id,
1393 : : &timer->spt.si_signo, pbuf,
1394 : : sigpid, tidpid, &pid_t,
1395 : : &timer->spt.clock_id);
1396 [ + + ]: 927 : if (ret != 7) {
1397 : : ret = 0;
1398 [ + - ]: 919 : xfree(timer);
1399 [ - + ]: 919 : if (feof(file))
1400 : : goto out;
1401 : : goto err;
1402 : : }
1403 : :
1404 : 8 : timer->spt.sival_ptr = NULL;
1405 [ + + ][ - + ]: 8 : if (sscanf(pbuf, "%p", &timer->spt.sival_ptr) != 1 &&
1406 : 4 : strcmp(pbuf, "(null)")) {
1407 : 0 : pr_err("Unable to parse '%s'\n", pbuf);
1408 [ # # ]: 0 : xfree(timer);
1409 : : goto err;
1410 : : }
1411 : :
1412 [ - + ]: 8 : if ( tidpid[0] == 't') {
1413 : 0 : timer->spt.it_sigev_notify = SIGEV_THREAD_ID;
1414 : : } else {
1415 [ + - - ]: 8 : switch (sigpid[0]) {
1416 : : case 's' :
1417 : 8 : timer->spt.it_sigev_notify = SIGEV_SIGNAL;
1418 : 8 : break;
1419 : : case 't' :
1420 : 0 : timer->spt.it_sigev_notify = SIGEV_THREAD;
1421 : 0 : break;
1422 : : default :
1423 : 0 : timer->spt.it_sigev_notify = SIGEV_NONE;
1424 : 0 : break;
1425 : : }
1426 : : }
1427 : :
1428 : 8 : list_add(&timer->list, &args->timers);
1429 : : timer = NULL;
1430 : 8 : args->timer_n++;
1431 : 8 : }
1432 : : err:
1433 : 0 : free_posix_timers(args);
1434 : 919 : pr_perror("Parse error in posix timers proc file!");
1435 : : ret = -1;
1436 : : out:
1437 : 919 : fclose(file);
1438 : 919 : return ret;
1439 : : }
1440 : :
1441 : 4804 : int parse_threads(int pid, struct pid **_t, int *_n)
1442 : : {
1443 : : struct dirent *de;
1444 : : DIR *dir;
1445 : : struct pid *t = NULL;
1446 : : int nr = 1;
1447 : :
1448 [ + + ]: 4804 : if (*_t)
1449 : : t = *_t;
1450 : :
1451 [ - + ][ + - ]: 4804 : dir = opendir_proc(pid, "task");
[ - + ]
1452 [ + - ]: 4804 : if (!dir)
1453 : : return -1;
1454 : :
1455 [ + + ]: 24352 : while ((de = readdir(dir))) {
1456 : : struct pid *tmp;
1457 : :
1458 : : /* We expect numbers only here */
1459 [ + + ]: 19548 : if (de->d_name[0] == '.')
1460 : 9608 : continue;
1461 : :
1462 [ + + ]: 9940 : if (*_t == NULL) {
1463 [ - + ]: 8551 : tmp = xrealloc(t, nr * sizeof(struct pid));
1464 [ - + ]: 8551 : if (!tmp) {
1465 [ # # ]: 0 : xfree(t);
1466 : : return -1;
1467 : : }
1468 : : t = tmp;
1469 : 8551 : t[nr - 1].virt = -1;
1470 : : }
1471 : 19880 : t[nr - 1].real = atoi(de->d_name);
1472 : 19548 : nr++;
1473 : : }
1474 : :
1475 : 4804 : closedir(dir);
1476 : :
1477 [ + + ]: 4804 : if (*_t == NULL) {
1478 : 3885 : *_t = t;
1479 : 3885 : *_n = nr - 1;
1480 : : } else
1481 [ - + ]: 919 : BUG_ON(nr - 1 != *_n);
1482 : :
1483 : : return 0;
1484 : : }
|