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 "types.h"
13 : : #include "list.h"
14 : : #include "util.h"
15 : : #include "crtools.h"
16 : : #include "mount.h"
17 : : #include "cpu.h"
18 : :
19 : : #include "proc_parse.h"
20 : : #include "protobuf.h"
21 : : #include "protobuf/fdinfo.pb-c.h"
22 : :
23 : : #include <stdlib.h>
24 : :
25 : : struct buffer {
26 : : char buf[PAGE_SIZE];
27 : : char end; /* '\0' */
28 : : };
29 : :
30 : : static struct buffer __buf;
31 : : static char *buf = __buf.buf;
32 : :
33 : : #define BUF_SIZE sizeof(__buf.buf)
34 : :
35 : 546 : int parse_cpuinfo_features(void)
36 : : {
37 : : FILE *cpuinfo;
38 : :
39 : 546 : cpuinfo = fopen("/proc/cpuinfo", "r");
40 [ + - ]: 546 : if (!cpuinfo) {
41 : 0 : pr_perror("Can't open cpuinfo file");
42 : 0 : return -1;
43 : : }
44 : :
45 [ + + ]: 22386 : while (fgets(buf, BUF_SIZE, cpuinfo)) {
46 : : char *tok;
47 : :
48 [ + + ]: 21840 : if (strncmp(buf, "flags\t\t:", 8))
49 : 20748 : continue;
50 : :
51 [ + + ]: 70980 : for (tok = strtok(buf, " \t\n"); tok;
52 : 49140 : tok = strtok(NULL, " \t\n")) {
53 [ + + ]: 49140 : if (!strcmp(tok, x86_cap_flags[X86_FEATURE_FXSR]))
54 : 1092 : cpu_set_feature(X86_FEATURE_FXSR);
55 [ - + ]: 48048 : else if (!strcmp(tok, x86_cap_flags[X86_FEATURE_XSAVE]))
56 : 0 : cpu_set_feature(X86_FEATURE_XSAVE);
57 [ + + ]: 48048 : else if (!strcmp(tok, x86_cap_flags[X86_FEATURE_FPU]))
58 : 1092 : cpu_set_feature(X86_FEATURE_FPU);
59 : : }
60 : : }
61 : :
62 : 546 : fclose(cpuinfo);
63 : 546 : return 0;
64 : : }
65 : :
66 : : /* check the @line starts with "%lx-%lx" format */
67 : 416720 : static bool is_vma_range_fmt(char *line)
68 : : {
69 [ + - ][ + + ]: 774634 : while (*line && is_hex_digit(*line))
[ + + ]
70 : 357914 : line++;
71 : :
72 [ + + ]: 416720 : if (*line++ != '-')
73 : : return false;
74 : :
75 [ + - ][ + + ]: 331869 : while (*line && is_hex_digit(*line))
[ - + ]
76 : 305824 : line++;
77 : :
78 [ + - ]: 26045 : if (*line++ != ' ')
79 : : return false;
80 : :
81 : 416720 : return true;
82 : : }
83 : :
84 : 26045 : static int parse_vmflags(char *buf, struct vma_area *vma_area)
85 : : {
86 : : char *tok;
87 : :
88 [ + - ]: 26045 : if (!buf[0])
89 : : return 0;
90 : :
91 : 26045 : tok = strtok(buf, " \n");
92 [ + - ]: 26045 : if (!tok)
93 : : return 0;
94 : :
95 : : #define _vmflag_match(_t, _s) (_t[0] == _s[0] && _t[1] == _s[1])
96 : :
97 : : do {
98 : : /* mmap() block */
99 [ + + ][ + - ]: 149840 : if (_vmflag_match(tok, "gd"))
100 : 1245 : vma_area->vma.flags |= MAP_GROWSDOWN;
101 [ + + ][ + - ]: 148595 : else if (_vmflag_match(tok, "lo"))
102 : 4 : vma_area->vma.flags |= MAP_LOCKED;
103 [ + + ][ + - ]: 148591 : else if (_vmflag_match(tok, "nr"))
104 : 60 : vma_area->vma.flags |= MAP_NORESERVE;
105 [ + + ][ - + ]: 148531 : else if (_vmflag_match(tok, "ht"))
106 : 0 : vma_area->vma.flags |= MAP_HUGETLB;
107 : :
108 : : /* madvise() block */
109 [ + + ][ + + ]: 149840 : if (_vmflag_match(tok, "sr"))
110 : 2 : vma_area->vma.madv |= (1ul << MADV_SEQUENTIAL);
111 [ + + ][ - + ]: 149838 : else if (_vmflag_match(tok, "rr"))
112 : 0 : vma_area->vma.madv |= (1ul << MADV_RANDOM);
113 [ + + ][ + + ]: 149838 : else if (_vmflag_match(tok, "dc"))
114 : 2 : vma_area->vma.madv |= (1ul << MADV_DONTFORK);
115 [ + + ][ + + ]: 149836 : else if (_vmflag_match(tok, "dd"))
116 : 2 : vma_area->vma.madv |= (1ul << MADV_DONTDUMP);
117 [ + + ][ + + ]: 149834 : else if (_vmflag_match(tok, "mg"))
118 : 2 : vma_area->vma.madv |= (1ul << MADV_MERGEABLE);
119 [ + + ][ + - ]: 149832 : else if (_vmflag_match(tok, "hg"))
120 : 2 : vma_area->vma.madv |= (1ul << MADV_HUGEPAGE);
121 [ + + ][ - + ]: 149830 : else if (_vmflag_match(tok, "nh"))
122 : 0 : vma_area->vma.madv |= (1ul << MADV_NOHUGEPAGE);
123 : :
124 : : /*
125 : : * Anything else is just ignored.
126 : : */
127 [ + + ]: 149840 : } while ((tok = strtok(NULL, " \n")));
128 : :
129 : : #undef _vmflag_match
130 : :
131 [ + + ]: 26045 : if (vma_area->vma.madv)
132 : 26045 : vma_area->vma.has_madv = true;
133 : :
134 : : return 0;
135 : : }
136 : :
137 : 3326 : static int is_anon_shmem_map(dev_t dev)
138 : : {
139 : : static dev_t shmem_dev = 0;
140 : :
141 [ + + ]: 3326 : if (!shmem_dev) {
142 : : void *map;
143 : : char maps[128];
144 : : struct stat buf;
145 : :
146 : 166 : map = mmap(NULL, PAGE_SIZE, PROT_READ | PROT_WRITE,
147 : : MAP_SHARED | MAP_ANONYMOUS, 0, 0);
148 [ - + ]: 166 : if (map == MAP_FAILED) {
149 : 0 : pr_perror("Can't mmap piggie");
150 : : return -1;
151 : : }
152 : :
153 : 166 : sprintf(maps, "/proc/%d/map_files/%lx-%lx",
154 : : getpid(), (unsigned long)map,
155 : 166 : (unsigned long)map + PAGE_SIZE);
156 [ - + ]: 166 : if (stat(maps, &buf) < 0) {
157 : 0 : pr_perror("Can't stat piggie");
158 : : return -1;
159 : : }
160 : :
161 : 166 : munmap(map, PAGE_SIZE);
162 : :
163 : 166 : shmem_dev = buf.st_dev;
164 : 166 : pr_info("Found anon-shmem piggie at %lx\n", shmem_dev);
165 : : }
166 : :
167 : 3326 : return shmem_dev == dev;
168 : : }
169 : :
170 : 698 : int parse_smaps(pid_t pid, struct list_head *vma_area_list, bool use_map_files)
171 : : {
172 : 698 : struct vma_area *vma_area = NULL;
173 : : u64 start, end, pgoff;
174 : : unsigned long ino;
175 : : char r, w, x, s;
176 : : int dev_maj, dev_min;
177 : 698 : int ret = -1, nr = 0;
178 : :
179 : 698 : DIR *map_files_dir = NULL;
180 : 698 : FILE *smaps = NULL;
181 : :
182 [ - + ][ + - ]: 698 : smaps = fopen_proc(pid, "smaps");
[ - + ]
183 [ + - ]: 698 : if (!smaps)
184 : : goto err;
185 : :
186 [ + + ]: 1047 : if (use_map_files) {
187 [ - + ][ + - ]: 349 : map_files_dir = opendir_proc(pid, "map_files");
[ - + ]
188 [ + - ]: 349 : if (!map_files_dir) /* old kernel? */
189 : : goto err;
190 : : }
191 : :
192 [ + + ]: 417418 : while (fgets(buf, BUF_SIZE, smaps)) {
193 : : int num;
194 : : char file_path[6];
195 : :
196 [ + + ]: 416720 : if (!is_vma_range_fmt(buf)) {
197 [ - + ]: 390675 : if (!strncmp(buf, "Nonlinear", 9)) {
198 [ # # ]: 0 : BUG_ON(!vma_area);
199 : 0 : pr_err("Nonlinear mapping found %016lx-%016lx\n",
200 : : vma_area->vma.start, vma_area->vma.end);
201 : : /*
202 : : * VMA is already on list and will be
203 : : * freed later as list get destroyed.
204 : : */
205 : 0 : vma_area = NULL;
206 : : goto err;
207 [ + + ]: 390675 : } else if (!strncmp(buf, "VmFlags: ", 9)) {
208 [ - + ]: 26045 : BUG_ON(!vma_area);
209 [ + - ]: 26045 : if (parse_vmflags(&buf[9], vma_area))
210 : : goto err;
211 : 26045 : continue;
212 : : } else
213 : 364630 : continue;
214 : : }
215 : :
216 [ - + ][ + - ]: 26045 : vma_area = alloc_vma_area();
217 [ + - ]: 26045 : if (!vma_area)
218 : : goto err;
219 : :
220 : 26045 : memset(file_path, 0, 6);
221 : 26045 : num = sscanf(buf, "%lx-%lx %c%c%c%c %lx %02x:%02x %lu %5s",
222 : : &start, &end, &r, &w, &x, &s, &pgoff, &dev_maj,
223 : : &dev_min, &ino, file_path);
224 [ - + ]: 26045 : if (num < 10) {
225 : 0 : pr_err("Can't parse: %s\n", buf);
226 : : goto err;
227 : : }
228 : :
229 [ + + ]: 26045 : if (map_files_dir) {
230 : : char path[32];
231 : :
232 : : /* Figure out if it's file mapping */
233 : 6055 : snprintf(path, sizeof(path), "%lx-%lx", start, end);
234 : :
235 : : /*
236 : : * Note that we "open" it in dumper process space
237 : : * so later we might refer to it via /proc/self/fd/vm_file_fd
238 : : * if needed.
239 : : */
240 : 6055 : vma_area->vm_file_fd = openat(dirfd(map_files_dir), path, O_RDONLY);
241 [ + + ]: 6055 : if (vma_area->vm_file_fd < 0) {
242 [ - + ]: 2729 : if (errno == ENXIO) {
243 : : struct stat buf;
244 : :
245 [ # # ]: 0 : if (fstatat(dirfd(map_files_dir), path, &buf, 0))
246 : : goto err_bogus_mapfile;
247 : :
248 [ # # ]: 0 : if (!S_ISSOCK(buf.st_mode))
249 : : goto err_bogus_mapfile;
250 : :
251 : 0 : pr_info("Found socket %lu mapping @%lx\n", buf.st_ino, start);
252 : 0 : vma_area->vma.status |= VMA_AREA_SOCKET | VMA_AREA_REGULAR;
253 : 0 : vma_area->vm_socket_id = buf.st_ino;
254 [ + - ]: 6055 : } else if (errno != ENOENT)
255 : : goto err_bogus_mapfile;
256 : : }
257 : : }
258 : :
259 : 26045 : vma_area->vma.start = start;
260 : 26045 : vma_area->vma.end = end;
261 : 26045 : vma_area->vma.pgoff = pgoff;
262 : 26045 : vma_area->vma.prot = PROT_NONE;
263 : :
264 [ + + ]: 26045 : if (r == 'r')
265 : 23584 : vma_area->vma.prot |= PROT_READ;
266 [ + + ]: 26045 : if (w == 'w')
267 : 17142 : vma_area->vma.prot |= PROT_WRITE;
268 [ + + ]: 26045 : if (x == 'x')
269 : 6821 : vma_area->vma.prot |= PROT_EXEC;
270 : :
271 [ + + ]: 26045 : if (s == 's')
272 : 1116 : vma_area->vma.flags = MAP_SHARED;
273 [ + - ]: 24929 : else if (s == 'p')
274 : 24929 : vma_area->vma.flags = MAP_PRIVATE;
275 : : else {
276 : 0 : pr_err("Unexpected VMA met (%c)\n", s);
277 : : goto err;
278 : : }
279 : :
280 [ + - ]: 26045 : if (vma_area->vma.status != 0) {
281 : : goto done;
282 [ + + ]: 26045 : } else if (strstr(buf, "[vsyscall]")) {
283 : 698 : vma_area->vma.status |= VMA_AREA_VSYSCALL;
284 [ + + ]: 25347 : } else if (strstr(buf, "[vdso]")) {
285 : 698 : vma_area->vma.status |= VMA_AREA_REGULAR | VMA_AREA_VDSO;
286 [ + + ]: 24649 : } else if (strstr(buf, "[heap]")) {
287 : 632 : vma_area->vma.status |= VMA_AREA_REGULAR | VMA_AREA_HEAP;
288 : : } else {
289 : 24017 : vma_area->vma.status = VMA_AREA_REGULAR;
290 : : }
291 : :
292 : : /*
293 : : * Some mapping hints for restore, we save this on
294 : : * disk and restore might need to analyze it.
295 : : */
296 [ + + ]: 26045 : if (vma_area->vm_file_fd >= 0) {
297 : : struct stat st_buf;
298 : :
299 [ - + ]: 3326 : if (fstat(vma_area->vm_file_fd, &st_buf) < 0) {
300 : 0 : pr_perror("Failed fstat on %d's map %lu", pid, start);
301 : : goto err;
302 : : }
303 [ - + ]: 3326 : if (!S_ISREG(st_buf.st_mode)) {
304 : 0 : pr_err("Can't handle non-regular mapping on %d's map %lu\n", pid, start);
305 : : goto err;
306 : : }
307 : :
308 : : /*
309 : : * /dev/zero stands for anon-shared mapping
310 : : * otherwise it's some file mapping.
311 : : */
312 [ + + ]: 3326 : if (is_anon_shmem_map(st_buf.st_dev)) {
313 [ + - ]: 49 : if (!(vma_area->vma.flags & MAP_SHARED))
314 : : goto err_bogus_mapping;
315 : 49 : vma_area->vma.flags |= MAP_ANONYMOUS;
316 : 49 : vma_area->vma.status |= VMA_ANON_SHARED;
317 : 49 : vma_area->vma.shmid = st_buf.st_ino;
318 : :
319 [ + + ]: 49 : if (!strcmp(file_path, "/SYSV")) {
320 : 5 : pr_info("path: %s\n", file_path);
321 : 5 : vma_area->vma.status |= VMA_AREA_SYSVIPC;
322 : : }
323 : : } else {
324 [ + + ]: 3277 : if (vma_area->vma.flags & MAP_PRIVATE)
325 : 3269 : vma_area->vma.status |= VMA_FILE_PRIVATE;
326 : : else
327 : 3326 : vma_area->vma.status |= VMA_FILE_SHARED;
328 : : }
329 : : } else {
330 : : /*
331 : : * No file but mapping -- anonymous one.
332 : : */
333 [ + + ]: 22719 : if (vma_area->vma.flags & MAP_SHARED) {
334 : 1059 : vma_area->vma.status |= VMA_ANON_SHARED;
335 : 1059 : vma_area->vma.shmid = ino;
336 : : } else {
337 : 21660 : vma_area->vma.status |= VMA_ANON_PRIVATE;
338 : : }
339 : 22719 : vma_area->vma.flags |= MAP_ANONYMOUS;
340 : : }
341 : : done:
342 : 26045 : list_add_tail(&vma_area->list, vma_area_list);
343 : 416720 : nr++;
344 : : }
345 : :
346 : : vma_area = NULL;
347 : : ret = nr;
348 : :
349 : : err:
350 [ + - ]: 698 : if (smaps)
351 : 698 : fclose(smaps);
352 : :
353 [ + + ]: 698 : if (map_files_dir)
354 : 349 : closedir(map_files_dir);
355 : :
356 [ - + ]: 698 : xfree(vma_area);
357 : 698 : return ret;
358 : :
359 : : err_bogus_mapping:
360 : 0 : pr_err("Bogus mapping 0x%lx-0x%lx (flags: %#x vm_file_fd: %d)\n",
361 : : vma_area->vma.start, vma_area->vma.end,
362 : : vma_area->vma.flags, vma_area->vm_file_fd);
363 : 0 : goto err;
364 : :
365 : : err_bogus_mapfile:
366 : 0 : pr_perror("Can't open %d's mapfile link %lx", pid, start);
367 : 0 : goto err;
368 : : }
369 : :
370 : 391 : int parse_pid_stat_small(pid_t pid, struct proc_pid_stat_small *s)
371 : : {
372 : : char *tok, *p;
373 : : int fd;
374 : : int n;
375 : :
376 [ - + ]: 391 : fd = open_proc(pid, "stat");
377 [ + - ]: 391 : if (fd < 0)
378 : : return -1;
379 : :
380 : 391 : n = read(fd, buf, BUF_SIZE);
381 [ - + ]: 391 : if (n < 1) {
382 : 0 : pr_err("stat for %d is corrupted\n", pid);
383 : 0 : close(fd);
384 : 0 : return -1;
385 : : }
386 : 391 : close(fd);
387 : :
388 : 391 : memset(s, 0, sizeof(*s));
389 : :
390 : 391 : tok = strchr(buf, ' ');
391 [ + - ]: 391 : if (!tok)
392 : : goto err;
393 : 391 : *tok++ = '\0';
394 [ + - ]: 391 : if (*tok != '(')
395 : : goto err;
396 : :
397 : 782 : s->pid = atoi(buf);
398 : :
399 : 391 : p = strrchr(tok + 1, ')');
400 [ + - ]: 391 : if (!p)
401 : : goto err;
402 : 391 : *tok = '\0';
403 : 391 : *p = '\0';
404 : :
405 : 391 : strncpy(s->comm, tok + 1, sizeof(s->comm));
406 : :
407 : 391 : n = sscanf(p + 1, " %c %d %d %d", &s->state, &s->ppid, &s->pgid, &s->sid);
408 [ - + ]: 391 : if (n < 4)
409 : : goto err;
410 : :
411 : : return 0;
412 : :
413 : : err:
414 : 0 : pr_err("Parsing %d's stat failed (#fields do not match)\n", pid);
415 : 391 : return -1;
416 : : }
417 : :
418 : 360 : int parse_pid_stat(pid_t pid, struct proc_pid_stat *s)
419 : : {
420 : : char *tok, *p;
421 : : int fd;
422 : : int n;
423 : :
424 [ - + ]: 360 : fd = open_proc(pid, "stat");
425 [ + - ]: 360 : if (fd < 0)
426 : : return -1;
427 : :
428 : 360 : n = read(fd, buf, BUF_SIZE);
429 [ - + ]: 360 : if (n < 1) {
430 : 0 : pr_err("stat for %d is corrupted\n", pid);
431 : 0 : close(fd);
432 : 0 : return -1;
433 : : }
434 : 360 : close(fd);
435 : :
436 : 360 : memset(s, 0, sizeof(*s));
437 : :
438 : 360 : tok = strchr(buf, ' ');
439 [ + - ]: 360 : if (!tok)
440 : : goto err;
441 : 360 : *tok++ = '\0';
442 [ + - ]: 360 : if (*tok != '(')
443 : : goto err;
444 : :
445 : 720 : s->pid = atoi(buf);
446 : :
447 : 360 : p = strrchr(tok + 1, ')');
448 [ + - ]: 360 : if (!p)
449 : : goto err;
450 : 360 : *tok = '\0';
451 : 360 : *p = '\0';
452 : :
453 : 360 : strncpy(s->comm, tok + 1, sizeof(s->comm));
454 : :
455 : 360 : n = sscanf(p + 1,
456 : : " %c %d %d %d %d %d %u %lu %lu %lu %lu "
457 : : "%lu %lu %ld %ld %ld %ld %d %d %llu %lu %ld %lu %lu %lu %lu "
458 : : "%lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld "
459 : : "%lu %lu %lu %lu %lu %lu %lu %d",
460 : : &s->state,
461 : : &s->ppid,
462 : : &s->pgid,
463 : : &s->sid,
464 : : &s->tty_nr,
465 : : &s->tty_pgrp,
466 : : &s->flags,
467 : : &s->min_flt,
468 : : &s->cmin_flt,
469 : : &s->maj_flt,
470 : : &s->cmaj_flt,
471 : : &s->utime,
472 : : &s->stime,
473 : : &s->cutime,
474 : : &s->cstime,
475 : : &s->priority,
476 : : &s->nice,
477 : : &s->num_threads,
478 : : &s->zero0,
479 : : &s->start_time,
480 : : &s->vsize,
481 : : &s->mm_rss,
482 : : &s->rsslim,
483 : : &s->start_code,
484 : : &s->end_code,
485 : : &s->start_stack,
486 : : &s->esp,
487 : : &s->eip,
488 : : &s->sig_pending,
489 : : &s->sig_blocked,
490 : : &s->sig_ignored,
491 : : &s->sig_handled,
492 : : &s->wchan,
493 : : &s->zero1,
494 : : &s->zero2,
495 : : &s->exit_signal,
496 : : &s->task_cpu,
497 : : &s->rt_priority,
498 : : &s->policy,
499 : : &s->delayacct_blkio_ticks,
500 : : &s->gtime,
501 : : &s->cgtime,
502 : : &s->start_data,
503 : : &s->end_data,
504 : : &s->start_brk,
505 : : &s->arg_start,
506 : : &s->arg_end,
507 : : &s->env_start,
508 : : &s->env_end,
509 : : &s->exit_code);
510 [ - + ]: 360 : if (n < 50)
511 : : goto err;
512 : :
513 : : return 0;
514 : :
515 : : err:
516 : 0 : pr_err("Parsing %d's stat failed (#fields do not match)\n", pid);
517 : 360 : return -1;
518 : : }
519 : :
520 : 698 : static int ids_parse(char *str, unsigned int *arr)
521 : : {
522 : : char *end;
523 : :
524 : 698 : arr[0] = strtol(str, &end, 10);
525 : 698 : arr[1] = strtol(end + 1, &end, 10);
526 : 698 : arr[2] = strtol(end + 1, &end, 10);
527 : 698 : arr[3] = strtol(end + 1, &end, 10);
528 [ + - ]: 698 : if (*end != '\n')
529 : : return -1;
530 : : else
531 : : return 0;
532 : : }
533 : :
534 : 1396 : static int cap_parse(char *str, unsigned int *res)
535 : : {
536 : : int i, ret;
537 : :
538 [ + + ]: 4188 : for (i = 0; i < PROC_CAP_SIZE; i++) {
539 : 2792 : ret = sscanf(str, "%08x", &res[PROC_CAP_SIZE - 1 - i]);
540 [ + - ]: 2792 : if (ret != 1)
541 : : return -1;
542 : 2792 : str += 8;
543 : : }
544 : :
545 : : return 0;
546 : : }
547 : :
548 : 349 : int parse_pid_status(pid_t pid, struct proc_status_creds *cr)
549 : : {
550 : 349 : int done = 0;
551 : : FILE *f;
552 : : char str[64];
553 : :
554 [ - + ][ + - ]: 349 : f = fopen_proc(pid, "status");
[ - + ]
555 [ + - ]: 349 : if (f == NULL) {
556 : 0 : pr_perror("Can't open proc status");
557 : : return -1;
558 : : }
559 : :
560 [ + + ][ + - ]: 11866 : while (done < 6 && fgets(str, sizeof(str), f)) {
561 [ + + ]: 11517 : if (!strncmp(str, "Uid:", 4)) {
562 [ + - ]: 349 : if (ids_parse(str + 5, cr->uids))
563 : : goto err_parse;
564 : :
565 : 349 : done++;
566 : : }
567 : :
568 [ + + ]: 11517 : if (!strncmp(str, "Gid:", 4)) {
569 [ + - ]: 349 : if (ids_parse(str + 5, cr->gids))
570 : : goto err_parse;
571 : :
572 : 349 : done++;
573 : : }
574 : :
575 [ + + ]: 11517 : if (!strncmp(str, "CapInh:", 7)) {
576 [ + - ]: 349 : if (cap_parse(str + 8, cr->cap_inh))
577 : : goto err_parse;
578 : :
579 : 349 : done++;
580 : : }
581 : :
582 [ + + ]: 11517 : if (!strncmp(str, "CapEff:", 7)) {
583 [ + - ]: 349 : if (cap_parse(str + 8, cr->cap_eff))
584 : : goto err_parse;
585 : :
586 : 349 : done++;
587 : : }
588 : :
589 [ + + ]: 11517 : if (!strncmp(str, "CapPrm:", 7)) {
590 [ + - ]: 349 : if (cap_parse(str + 8, cr->cap_prm))
591 : : goto err_parse;
592 : :
593 : 349 : done++;
594 : : }
595 : :
596 [ + + ]: 11517 : if (!strncmp(str, "CapBnd:", 7)) {
597 [ + - ]: 349 : if (cap_parse(str + 8, cr->cap_bnd))
598 : : goto err_parse;
599 : :
600 : 698 : done++;
601 : : }
602 : : }
603 : :
604 [ - + ]: 349 : if (done != 6) {
605 : : err_parse:
606 : 0 : pr_err("Error parsing proc status file\n");
607 : 0 : fclose(f);
608 : : return -1;
609 : : }
610 : :
611 : 349 : fclose(f);
612 : : return 0;
613 : : }
614 : :
615 : : struct opt2flag {
616 : : char *opt;
617 : : unsigned flag;
618 : : };
619 : :
620 : 10932 : static int do_opt2flag(char *opt, unsigned *flags,
621 : : const struct opt2flag *opts, char *unknown)
622 : : {
623 : : int i;
624 : : char *end;
625 : :
626 : : while (1) {
627 : 32252 : end = strchr(opt, ',');
628 [ + + ]: 32252 : if (end)
629 : 21320 : *end = '\0';
630 : :
631 [ + + ]: 126452 : for (i = 0; opts[i].opt != NULL; i++)
632 [ + + ]: 120231 : if (!strcmp(opts[i].opt, opt)) {
633 : 26031 : (*flags) |= opts[i].flag;
634 : 26031 : break;
635 : : }
636 : :
637 [ + + ]: 32252 : if (opts[i].opt == NULL) {
638 [ - + ]: 6221 : if (!unknown) {
639 : 0 : pr_err("Unknown option [%s]\n", opt);
640 : 10932 : return -1;
641 : : }
642 : :
643 : 6221 : strcpy(unknown, opt);
644 : 6221 : unknown += strlen(opt);
645 : 6221 : *unknown = ',';
646 : 6221 : unknown++;
647 : : }
648 : :
649 [ + + ]: 32252 : if (!end) {
650 [ + + ]: 10932 : if (unknown)
651 : 5466 : *unknown = '\0';
652 : : break;
653 : : } else
654 : 21320 : opt = end + 1;
655 : 21320 : }
656 : :
657 : : return 0;
658 : : }
659 : :
660 : 5466 : static int parse_mnt_flags(char *opt, unsigned *flags)
661 : : {
662 : 5466 : const struct opt2flag mnt_opt2flag[] = {
663 : : { "rw", 0, },
664 : : { "ro", MS_RDONLY, },
665 : : { "nosuid", MS_NOSUID, },
666 : : { "nodev", MS_NODEV, } ,
667 : : { "noexec", MS_NOEXEC, },
668 : : { "noatime", MS_NOATIME, },
669 : : { "nodiratime", MS_NODIRATIME, },
670 : : { "relatime", MS_RELATIME, },
671 : : { },
672 : : };
673 : :
674 : 5466 : return do_opt2flag(opt, flags, mnt_opt2flag, NULL);
675 : : }
676 : :
677 : 5466 : static int parse_sb_opt(char *opt, unsigned *flags, char *uopt)
678 : : {
679 : 5466 : const struct opt2flag sb_opt2flag[] = {
680 : : { "rw", 0, },
681 : : { "ro", MS_RDONLY, },
682 : : { "sync", MS_SYNC, },
683 : : { "dirsync", MS_DIRSYNC, },
684 : : { "mad", MS_MANDLOCK, },
685 : : { },
686 : : };
687 : :
688 : 5466 : return do_opt2flag(opt, flags, sb_opt2flag, uopt);
689 : : }
690 : :
691 : 5466 : static int parse_mnt_opt(char *str, struct mount_info *mi, int *off)
692 : : {
693 : 5466 : char *istr = str, *end;
694 : :
695 : : while (1) {
696 : 5466 : end = strchr(str, ' ');
697 [ - + ]: 5466 : if (!end) {
698 : 0 : pr_err("Error parsing mount options\n");
699 : 0 : return -1;
700 : : }
701 : :
702 : 5466 : *end = '\0';
703 [ - + ]: 5466 : if (!strncmp(str, "-", 1))
704 : : break;
705 [ # # ]: 0 : else if (!strncmp(str, "shared:", 7)) {
706 : 0 : mi->flags |= MS_SHARED;
707 : 0 : mi->shared_id = atoi(str + 7);
708 [ # # ]: 0 : } else if (!strncmp(str, "master:", 7)) {
709 : 0 : mi->flags |= MS_SLAVE;
710 : 0 : mi->master_id = atoi(str + 7);
711 [ # # ]: 0 : } else if (!strncmp(str, "propagate_from:", 15)) {
712 : : /* skip */;
713 [ # # ]: 0 : } else if (!strncmp(str, "unbindable", 11))
714 : 0 : mi->flags |= MS_UNBINDABLE;
715 : : else {
716 : 0 : pr_err("Unknown option [%s]\n", str);
717 : 0 : return -1;
718 : : }
719 : :
720 : 0 : str = end + 1;
721 : 0 : }
722 : :
723 : 5466 : *off = end - istr + 1;
724 : 5466 : return 0;
725 : : }
726 : :
727 : 5466 : static int parse_mountinfo_ent(char *str, struct mount_info *new)
728 : : {
729 : : unsigned int kmaj, kmin;
730 : : int ret, n;
731 : : char *opt;
732 : : char *fstype;
733 : :
734 : 5466 : ret = sscanf(str, "%i %i %u:%u %ms %ms %ms %n",
735 : : &new->mnt_id, &new->parent_mnt_id,
736 : : &kmaj, &kmin, &new->root, &new->mountpoint,
737 : : &opt, &n);
738 [ + - ]: 5466 : if (ret != 7)
739 : : return -1;
740 : :
741 : 5466 : new->s_dev = MKKDEV(kmaj, kmin);
742 : 5466 : new->flags = 0;
743 [ + - ]: 5466 : if (parse_mnt_flags(opt, &new->flags))
744 : : return -1;
745 : :
746 : 5466 : free(opt); /* after %ms scanf */
747 : :
748 : 5466 : str += n;
749 [ + - ]: 5466 : if (parse_mnt_opt(str, new, &n))
750 : : return -1;
751 : :
752 : 5466 : str += n;
753 : 5466 : ret = sscanf(str, "%ms %ms %ms", &fstype, &new->source, &opt);
754 [ + - ]: 5466 : if (ret != 3)
755 : : return -1;
756 : :
757 : 5466 : new->fstype = find_fstype_by_name(fstype);
758 : 5466 : free(fstype);
759 : :
760 [ - + ]: 5466 : new->options = xmalloc(strlen(opt));
761 [ + - ]: 5466 : if (!new->options)
762 : : return -1;
763 : :
764 [ + - ]: 5466 : if (parse_sb_opt(opt, &new->flags, new->options))
765 : : return -1;
766 : :
767 : 5466 : free(opt);
768 : :
769 : : return 0;
770 : : }
771 : :
772 : 291 : struct mount_info *parse_mountinfo(pid_t pid)
773 : : {
774 : 291 : struct mount_info *list = NULL;
775 : : FILE *f;
776 : : char str[1024];
777 : :
778 : 291 : snprintf(str, sizeof(str), "/proc/%d/mountinfo", pid);
779 : 291 : f = fopen(str, "r");
780 [ + - ]: 291 : if (!f) {
781 : 0 : pr_perror("Can't open %d mountinfo", pid);
782 : : return NULL;
783 : : }
784 : :
785 [ + + ]: 5757 : while (fgets(str, sizeof(str), f)) {
786 : : struct mount_info *new;
787 : : int ret;
788 : :
789 [ - + ]: 5466 : new = xmalloc(sizeof(*new));
790 [ + - ]: 5466 : if (!new)
791 : : goto err;
792 : :
793 : : mnt_entry_init(new);
794 : :
795 : 5466 : ret = parse_mountinfo_ent(str, new);
796 [ - + ]: 5466 : if (ret < 0) {
797 : 0 : pr_err("Bad format in %d mountinfo\n", pid);
798 : 0 : goto err;
799 : : }
800 : :
801 : 5466 : pr_info("\ttype %s source %s %x %s @ %s flags %x options %s\n",
802 : : new->fstype->name, new->source,
803 : : new->s_dev, new->root, new->mountpoint,
804 : : new->flags, new->options);
805 : :
806 : 5466 : new->next = list;
807 : 5466 : list = new;
808 : : }
809 : : out:
810 : 291 : fclose(f);
811 : : return list;
812 : :
813 : : err:
814 [ # # ]: 291 : while (list) {
815 : 0 : struct mount_info *next = list->next;
816 [ # # ]: 0 : xfree(list);
817 : 0 : list = next;
818 : : }
819 : : goto out;
820 : : }
821 : :
822 : : static char nybble(const char n)
823 : : {
824 [ + + ][ + + ]: 90 : if (n >= '0' && n <= '9')
825 : 61 : return n - '0';
826 [ - + ][ - + ]: 29 : else if (n >= 'A' && n <= 'F')
827 : 0 : return n - ('A' - 10);
828 [ + + ][ + + ]: 29 : else if (n >= 'a' && n <= 'f')
829 : 19 : return n - ('a' - 10);
830 : : return 0;
831 : : }
832 : :
833 : 5 : static void parse_fhandle_encoded(char *tok, FhEntry *fh)
834 : : {
835 : 5 : char *d = (char *)fh->handle;
836 : 5 : int i = 0;
837 : :
838 : 5 : memzero(d, pb_repeated_size(fh, handle));
839 : :
840 [ - + ]: 5 : while (*tok == ' ')
841 : 0 : tok++;
842 : :
843 [ + - ]: 45 : while (*tok) {
844 [ + - ]: 45 : if (i >= pb_repeated_size(fh, handle))
845 : : break;
846 : 135 : d[i++] = (nybble(tok[0]) << 4) | nybble(tok[1]);
847 [ + + ]: 45 : if (tok[1])
848 : 40 : tok += 2;
849 : : else
850 : : break;
851 : : }
852 : 5 : }
853 : :
854 : : #define fdinfo_field(str, field) !strncmp(str, field":", sizeof(field))
855 : :
856 : 12 : int parse_fdinfo(int fd, int type,
857 : : int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
858 : : {
859 : : FILE *f;
860 : : char str[256];
861 : 12 : bool entry_met = false;
862 : :
863 : 12 : sprintf(str, "/proc/self/fdinfo/%d", fd);
864 : 12 : f = fopen(str, "r");
865 [ + - ]: 12 : if (!f) {
866 : 0 : pr_perror("Can't open fdinfo to parse");
867 : : return -1;
868 : : }
869 : :
870 [ + + ]: 50 : while (fgets(str, sizeof(str), f)) {
871 : : int ret;
872 : : union fdinfo_entries entry;
873 : :
874 [ + + ][ - + ]: 38 : if (fdinfo_field(str, "pos") || fdinfo_field(str, "counter"))
875 : 12 : continue;
876 : :
877 [ + + ]: 26 : if (fdinfo_field(str, "eventfd-count")) {
878 : 3 : eventfd_file_entry__init(&entry.efd);
879 : :
880 [ + - ]: 3 : if (type != FD_TYPES__EVENTFD)
881 : : goto parse_err;
882 : 3 : ret = sscanf(str, "eventfd-count: %lx",
883 : : &entry.efd.counter);
884 [ + - ]: 3 : if (ret != 1)
885 : : goto parse_err;
886 : 3 : ret = cb(&entry, arg);
887 [ + - ]: 3 : if (ret)
888 : : return ret;
889 : :
890 : 3 : entry_met = true;
891 : 3 : continue;
892 : : }
893 [ + + ]: 23 : if (fdinfo_field(str, "tfd")) {
894 : 3 : eventpoll_tfd_entry__init(&entry.epl);
895 : :
896 [ + - ]: 3 : if (type != FD_TYPES__EVENTPOLL)
897 : : goto parse_err;
898 : 3 : ret = sscanf(str, "tfd: %d events: %x data: %lx",
899 : : &entry.epl.tfd, &entry.epl.events, &entry.epl.data);
900 [ + - ]: 3 : if (ret != 3)
901 : : goto parse_err;
902 : 3 : ret = cb(&entry, arg);
903 [ + - ]: 3 : if (ret)
904 : : return ret;
905 : :
906 : 3 : entry_met = true;
907 : 3 : continue;
908 : : }
909 [ + + ]: 20 : if (fdinfo_field(str, "sigmask")) {
910 : 3 : signalfd_entry__init(&entry.sfd);
911 : :
912 [ + - ]: 3 : if (type != FD_TYPES__SIGNALFD)
913 : : goto parse_err;
914 : 3 : ret = sscanf(str, "sigmask: %Lx",
915 : : (unsigned long long *)&entry.sfd.sigmask);
916 [ + - ]: 3 : if (ret != 1)
917 : : goto parse_err;
918 : 3 : ret = cb(&entry, arg);
919 [ + - ]: 3 : if (ret)
920 : : return ret;
921 : :
922 : 3 : entry_met = true;
923 : 3 : continue;
924 : : }
925 [ + + ]: 17 : if (fdinfo_field(str, "inotify wd")) {
926 : 5 : FhEntry f_handle = FH_ENTRY__INIT;
927 : : int hoff;
928 : :
929 : 5 : inotify_wd_entry__init(&entry.ify);
930 : 5 : entry.ify.f_handle = &f_handle;
931 : :
932 [ + - ]: 5 : if (type != FD_TYPES__INOTIFY)
933 : : goto parse_err;
934 : 5 : ret = sscanf(str,
935 : : "inotify wd:%x ino:%lx sdev:%x "
936 : : "mask:%x ignored_mask:%x "
937 : : "fhandle-bytes:%x fhandle-type:%x "
938 : : "f_handle: %n",
939 : : &entry.ify.wd, &entry.ify.i_ino, &entry.ify.s_dev,
940 : : &entry.ify.mask, &entry.ify.ignored_mask,
941 : : &entry.ify.f_handle->bytes, &entry.ify.f_handle->type,
942 : : &hoff);
943 [ + - ]: 5 : if (ret != 7)
944 : : goto parse_err;
945 : :
946 : 5 : f_handle.n_handle = FH_ENTRY_SIZES__min_entries;
947 [ - + ]: 5 : f_handle.handle = xmalloc(pb_repeated_size(&f_handle, handle));
948 [ + - ]: 5 : if (!f_handle.handle)
949 : : return -1;
950 : :
951 : 5 : parse_fhandle_encoded(str + hoff, entry.ify.f_handle);
952 : :
953 : 5 : ret = cb(&entry, arg);
954 : :
955 [ + - ]: 5 : xfree(f_handle.handle);
956 : :
957 [ + - ]: 5 : if (ret)
958 : : return ret;
959 : :
960 : 5 : entry_met = true;
961 : 38 : continue;
962 : : }
963 : : }
964 : :
965 : 12 : fclose(f);
966 : :
967 [ - + ]: 12 : if (entry_met)
968 : : return 0;
969 : : /*
970 : : * An eventpoll/inotify file may have no target fds set thus
971 : : * resulting in no tfd: lines in proc. This is normal.
972 : : */
973 [ # # ]: 0 : if (type == FD_TYPES__EVENTPOLL || type == FD_TYPES__INOTIFY)
974 : : return 0;
975 : :
976 : 0 : pr_err("No records of type %d found in fdinfo file\n", type);
977 : : parse_err:
978 : 12 : pr_perror("%s: error parsing [%s] for %d\n", __func__, str, type);
979 : : return -1;
980 : 10 : }
|