mirror of
https://github.com/checkpoint-restore/criu
synced 2025-08-22 09:58:09 +00:00
With this the code looks clearer and finally a ground for "check" code is prepared. Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
786 lines
15 KiB
C
786 lines
15 KiB
C
#include <stdio.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/types.h>
|
|
#include <dirent.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <string.h>
|
|
#include <linux/fs.h>
|
|
|
|
#include "types.h"
|
|
#include "list.h"
|
|
#include "util.h"
|
|
#include "crtools.h"
|
|
|
|
#include "proc_parse.h"
|
|
|
|
#include <stdlib.h>
|
|
|
|
struct buffer {
|
|
char buf[PAGE_SIZE];
|
|
char end; /* '\0' */
|
|
};
|
|
|
|
static struct buffer __buf;
|
|
static char *buf = __buf.buf;
|
|
|
|
#define BUF_SIZE sizeof(__buf.buf)
|
|
|
|
/* check the @line starts with "%lx-%lx" format */
|
|
static bool is_vma_range_fmt(char *line)
|
|
{
|
|
while (*line && is_hex_digit(*line))
|
|
line++;
|
|
|
|
if (*line++ != '-')
|
|
return false;
|
|
|
|
while (*line && is_hex_digit(*line))
|
|
line++;
|
|
|
|
if (*line++ != ' ')
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int parse_smaps(pid_t pid, struct list_head *vma_area_list, bool use_map_files)
|
|
{
|
|
struct vma_area *vma_area = NULL;
|
|
u64 start, end, pgoff;
|
|
unsigned long ino;
|
|
char r,w,x,s;
|
|
int dev_maj, dev_min;
|
|
int ret = -1, nr = 0;
|
|
|
|
DIR *map_files_dir = NULL;
|
|
FILE *smaps = NULL;
|
|
|
|
smaps = fopen_proc(pid, "smaps");
|
|
if (!smaps)
|
|
goto err;
|
|
|
|
if (use_map_files) {
|
|
map_files_dir = opendir_proc(pid, "map_files");
|
|
if (!map_files_dir) /* old kernel? */
|
|
goto err;
|
|
}
|
|
|
|
while (fgets(buf, BUF_SIZE, smaps)) {
|
|
int num;
|
|
char file_path[6];
|
|
|
|
if (!is_vma_range_fmt(buf)) {
|
|
if (!strncmp(buf, "Nonlinear", 9)) {
|
|
BUG_ON(!vma_area);
|
|
pr_err("Nonlinear mapping found %016lx-%016lx\n",
|
|
vma_area->vma.start, vma_area->vma.end);
|
|
/*
|
|
* VMA is already on list and will be
|
|
* freed later as list get destroyed.
|
|
*/
|
|
vma_area = NULL;
|
|
goto err;
|
|
} else
|
|
continue;
|
|
}
|
|
|
|
vma_area = alloc_vma_area();
|
|
if (!vma_area)
|
|
goto err;
|
|
|
|
memset(file_path, 0, 6);
|
|
num = sscanf(buf, "%lx-%lx %c%c%c%c %lx %02x:%02x %lu %5s",
|
|
&start, &end, &r, &w, &x, &s, &pgoff, &dev_maj,
|
|
&dev_min, &ino, file_path);
|
|
if (num < 10) {
|
|
pr_err("Can't parse: %s", buf);
|
|
goto err;
|
|
}
|
|
|
|
if (map_files_dir) {
|
|
char path[32];
|
|
|
|
/* Figure out if it's file mapping */
|
|
snprintf(path, sizeof(path), "%lx-%lx", start, end);
|
|
|
|
/*
|
|
* Note that we "open" it in dumper process space
|
|
* so later we might refer to it via /proc/self/fd/vm_file_fd
|
|
* if needed.
|
|
*/
|
|
vma_area->vm_file_fd = openat(dirfd(map_files_dir), path, O_RDONLY);
|
|
if (vma_area->vm_file_fd < 0) {
|
|
if (errno != ENOENT) {
|
|
pr_perror("Can't open %d's map %lu", pid, start);
|
|
goto err;
|
|
}
|
|
}
|
|
}
|
|
|
|
vma_area->vma.start = start;
|
|
vma_area->vma.end = end;
|
|
vma_area->vma.pgoff = pgoff;
|
|
vma_area->vma.prot = PROT_NONE;
|
|
|
|
if (r == 'r')
|
|
vma_area->vma.prot |= PROT_READ;
|
|
if (w == 'w')
|
|
vma_area->vma.prot |= PROT_WRITE;
|
|
if (x == 'x')
|
|
vma_area->vma.prot |= PROT_EXEC;
|
|
|
|
if (s == 's')
|
|
vma_area->vma.flags = MAP_SHARED;
|
|
else if (s == 'p')
|
|
vma_area->vma.flags = MAP_PRIVATE;
|
|
else {
|
|
pr_err("Unexpected VMA met (%c)\n", s);
|
|
goto err;
|
|
}
|
|
|
|
if (strstr(buf, "[stack")) {
|
|
vma_area->vma.status |= VMA_AREA_REGULAR | VMA_AREA_STACK;
|
|
vma_area->vma.flags |= MAP_GROWSDOWN;
|
|
} else if (strstr(buf, "[vsyscall]")) {
|
|
vma_area->vma.status |= VMA_AREA_VSYSCALL;
|
|
} else if (strstr(buf, "[vdso]")) {
|
|
vma_area->vma.status |= VMA_AREA_REGULAR | VMA_AREA_VDSO;
|
|
} else if (strstr(buf, "[heap]")) {
|
|
vma_area->vma.status |= VMA_AREA_REGULAR | VMA_AREA_HEAP;
|
|
} else {
|
|
vma_area->vma.status = VMA_AREA_REGULAR;
|
|
}
|
|
|
|
/*
|
|
* Some mapping hints for restore, we save this on
|
|
* disk and restore might need to analyze it.
|
|
*/
|
|
if (vma_area->vm_file_fd >= 0) {
|
|
struct stat st_buf;
|
|
|
|
if (fstat(vma_area->vm_file_fd, &st_buf) < 0) {
|
|
pr_perror("Failed fstat on %d's map %lu", pid, start);
|
|
goto err;
|
|
}
|
|
if (!S_ISREG(st_buf.st_mode)) {
|
|
pr_err("Can't handle non-regular mapping on %d's map %lu\n", pid, start);
|
|
goto err;
|
|
}
|
|
|
|
/*
|
|
* /dev/zero stands for anon-shared mapping
|
|
* otherwise it's some file mapping.
|
|
*/
|
|
if (MAJOR(st_buf.st_dev) == 0) {
|
|
if (!(vma_area->vma.flags & MAP_SHARED))
|
|
goto err_bogus_mapping;
|
|
vma_area->vma.flags |= MAP_ANONYMOUS;
|
|
vma_area->vma.status |= VMA_ANON_SHARED;
|
|
vma_area->vma.shmid = st_buf.st_ino;
|
|
|
|
if (!strcmp(file_path, "/SYSV")) {
|
|
pr_info("path: %s\n", file_path);
|
|
vma_area->vma.status |= VMA_AREA_SYSVIPC;
|
|
}
|
|
} else {
|
|
if (vma_area->vma.flags & MAP_PRIVATE)
|
|
vma_area->vma.status |= VMA_FILE_PRIVATE;
|
|
else
|
|
vma_area->vma.status |= VMA_FILE_SHARED;
|
|
}
|
|
} else {
|
|
/*
|
|
* No file but mapping -- anonymous one.
|
|
*/
|
|
if (vma_area->vma.flags & MAP_SHARED) {
|
|
vma_area->vma.status |= VMA_ANON_SHARED;
|
|
vma_area->vma.shmid = ino;
|
|
} else {
|
|
vma_area->vma.status |= VMA_ANON_PRIVATE;
|
|
}
|
|
vma_area->vma.flags |= MAP_ANONYMOUS;
|
|
}
|
|
|
|
list_add_tail(&vma_area->list, vma_area_list);
|
|
nr++;
|
|
}
|
|
|
|
vma_area = NULL;
|
|
ret = nr;
|
|
|
|
err:
|
|
if (smaps)
|
|
fclose(smaps);
|
|
|
|
if (map_files_dir)
|
|
closedir(map_files_dir);
|
|
|
|
xfree(vma_area);
|
|
return ret;
|
|
|
|
err_bogus_mapping:
|
|
pr_err("Bogus mapping 0x%lx-0x%lx (flags: %#x vm_file_fd: %d)\n",
|
|
vma_area->vma.start, vma_area->vma.end,
|
|
vma_area->vma.flags, vma_area->vm_file_fd);
|
|
goto err;
|
|
}
|
|
|
|
int parse_pid_stat_small(pid_t pid, struct proc_pid_stat_small *s)
|
|
{
|
|
char *tok, *p;
|
|
int fd;
|
|
int n;
|
|
|
|
fd = open_proc(pid, "stat");
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
n = read(fd, buf, BUF_SIZE);
|
|
if (n < 1) {
|
|
pr_err("stat for %d is corrupted\n", pid);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
|
|
memset(s, 0, sizeof(*s));
|
|
|
|
tok = strchr(buf, ' ');
|
|
if (!tok)
|
|
goto err;
|
|
*tok++ = '\0';
|
|
if (*tok != '(')
|
|
goto err;
|
|
|
|
s->pid = atoi(buf);
|
|
|
|
p = strrchr(tok + 1, ')');
|
|
if (!p)
|
|
goto err;
|
|
*tok = '\0';
|
|
*p = '\0';
|
|
|
|
strncpy(s->comm, tok + 1, sizeof(s->comm));
|
|
|
|
n = sscanf(p + 1, " %c %d %d %d", &s->state, &s->ppid, &s->pgid, &s->sid);
|
|
if (n < 4)
|
|
goto err;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
pr_err("Parsing %d's stat failed (#fields do not match)\n", pid);
|
|
return -1;
|
|
}
|
|
|
|
int parse_pid_stat(pid_t pid, struct proc_pid_stat *s)
|
|
{
|
|
char *tok, *p;
|
|
int fd;
|
|
int n;
|
|
|
|
fd = open_proc(pid, "stat");
|
|
if (fd < 0)
|
|
return -1;
|
|
|
|
n = read(fd, buf, BUF_SIZE);
|
|
if (n < 1) {
|
|
pr_err("stat for %d is corrupted\n", pid);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
close(fd);
|
|
|
|
memset(s, 0, sizeof(*s));
|
|
|
|
tok = strchr(buf, ' ');
|
|
if (!tok)
|
|
goto err;
|
|
*tok++ = '\0';
|
|
if (*tok != '(')
|
|
goto err;
|
|
|
|
s->pid = atoi(buf);
|
|
|
|
p = strrchr(tok + 1, ')');
|
|
if (!p)
|
|
goto err;
|
|
*tok = '\0';
|
|
*p = '\0';
|
|
|
|
strncpy(s->comm, tok + 1, sizeof(s->comm));
|
|
|
|
n = sscanf(p + 1,
|
|
" %c %d %d %d %d %d %u %lu %lu %lu %lu "
|
|
"%lu %lu %ld %ld %ld %ld %d %d %llu %lu %ld %lu %lu %lu %lu "
|
|
"%lu %lu %lu %lu %lu %lu %lu %lu %lu %d %d %u %u %llu %lu %ld "
|
|
"%lu %lu %lu %lu %lu %lu %lu %d",
|
|
&s->state,
|
|
&s->ppid,
|
|
&s->pgid,
|
|
&s->sid,
|
|
&s->tty_nr,
|
|
&s->tty_pgrp,
|
|
&s->flags,
|
|
&s->min_flt,
|
|
&s->cmin_flt,
|
|
&s->maj_flt,
|
|
&s->cmaj_flt,
|
|
&s->utime,
|
|
&s->stime,
|
|
&s->cutime,
|
|
&s->cstime,
|
|
&s->priority,
|
|
&s->nice,
|
|
&s->num_threads,
|
|
&s->zero0,
|
|
&s->start_time,
|
|
&s->vsize,
|
|
&s->mm_rss,
|
|
&s->rsslim,
|
|
&s->start_code,
|
|
&s->end_code,
|
|
&s->start_stack,
|
|
&s->esp,
|
|
&s->eip,
|
|
&s->sig_pending,
|
|
&s->sig_blocked,
|
|
&s->sig_ignored,
|
|
&s->sig_handled,
|
|
&s->wchan,
|
|
&s->zero1,
|
|
&s->zero2,
|
|
&s->exit_signal,
|
|
&s->task_cpu,
|
|
&s->rt_priority,
|
|
&s->policy,
|
|
&s->delayacct_blkio_ticks,
|
|
&s->gtime,
|
|
&s->cgtime,
|
|
&s->start_data,
|
|
&s->end_data,
|
|
&s->start_brk,
|
|
&s->arg_start,
|
|
&s->arg_end,
|
|
&s->env_start,
|
|
&s->env_end,
|
|
&s->exit_code);
|
|
if (n < 50)
|
|
goto err;
|
|
|
|
return 0;
|
|
|
|
err:
|
|
pr_err("Parsing %d's stat failed (#fields do not match)\n", pid);
|
|
return -1;
|
|
}
|
|
|
|
static int ids_parse(char *str, unsigned int *arr)
|
|
{
|
|
char *end;
|
|
|
|
arr[0] = strtol(str, &end, 10);
|
|
arr[1] = strtol(end + 1, &end, 10);
|
|
arr[2] = strtol(end + 1, &end, 10);
|
|
arr[3] = strtol(end + 1, &end, 10);
|
|
if (*end != '\n')
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static int cap_parse(char *str, unsigned int *res)
|
|
{
|
|
int i, ret;
|
|
|
|
for (i = 0; i < PROC_CAP_SIZE; i++) {
|
|
ret = sscanf(str, "%08x", &res[PROC_CAP_SIZE - 1 - i]);
|
|
if (ret != 1)
|
|
return -1;
|
|
str += 8;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int parse_pid_status(pid_t pid, struct proc_status_creds *cr)
|
|
{
|
|
int done = 0;
|
|
FILE *f;
|
|
char str[64];
|
|
|
|
f = fopen_proc(pid, "status");
|
|
if (f == NULL) {
|
|
pr_perror("Can't open proc status");
|
|
return -1;
|
|
}
|
|
|
|
while (done < 6 && fgets(str, sizeof(str), f)) {
|
|
if (!strncmp(str, "Uid:", 4)) {
|
|
if (ids_parse(str + 5, cr->uids))
|
|
goto err_parse;
|
|
|
|
done++;
|
|
}
|
|
|
|
if (!strncmp(str, "Gid:", 4)) {
|
|
if (ids_parse(str + 5, cr->gids))
|
|
goto err_parse;
|
|
|
|
done++;
|
|
}
|
|
|
|
if (!strncmp(str, "CapInh:", 7)) {
|
|
if (cap_parse(str + 8, cr->cap_inh))
|
|
goto err_parse;
|
|
|
|
done++;
|
|
}
|
|
|
|
if (!strncmp(str, "CapEff:", 7)) {
|
|
if (cap_parse(str + 8, cr->cap_eff))
|
|
goto err_parse;
|
|
|
|
done++;
|
|
}
|
|
|
|
if (!strncmp(str, "CapPrm:", 7)) {
|
|
if (cap_parse(str + 8, cr->cap_prm))
|
|
goto err_parse;
|
|
|
|
done++;
|
|
}
|
|
|
|
if (!strncmp(str, "CapBnd:", 7)) {
|
|
if (cap_parse(str + 8, cr->cap_bnd))
|
|
goto err_parse;
|
|
|
|
done++;
|
|
}
|
|
}
|
|
|
|
if (done != 6) {
|
|
err_parse:
|
|
pr_err("Error parsing proc status file\n");
|
|
fclose(f);
|
|
return -1;
|
|
}
|
|
|
|
fclose(f);
|
|
return 0;
|
|
}
|
|
|
|
struct opt2flag {
|
|
char *opt;
|
|
unsigned flag;
|
|
};
|
|
|
|
static int do_opt2flag(char *opt, unsigned *flags,
|
|
const struct opt2flag *opts, char *unknown)
|
|
{
|
|
int i;
|
|
char *end;
|
|
|
|
while (1) {
|
|
end = strchr(opt, ',');
|
|
if (end)
|
|
*end = '\0';
|
|
|
|
for (i = 0; opts[i].opt != NULL; i++)
|
|
if (!strcmp(opts[i].opt, opt)) {
|
|
(*flags) |= opts[i].flag;
|
|
break;
|
|
}
|
|
|
|
if (opts[i].opt == NULL) {
|
|
if (!unknown) {
|
|
pr_err("Unknown option [%s]\n", opt);
|
|
return -1;
|
|
}
|
|
|
|
strcpy(unknown, opt);
|
|
unknown += strlen(opt);
|
|
*unknown = ',';
|
|
unknown++;
|
|
}
|
|
|
|
if (!end) {
|
|
if (unknown)
|
|
*unknown = '\0';
|
|
break;
|
|
} else
|
|
opt = end + 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int parse_mnt_flags(char *opt, unsigned *flags)
|
|
{
|
|
const struct opt2flag mnt_opt2flag[] = {
|
|
{ "rw", 0, },
|
|
{ "ro", MS_RDONLY, },
|
|
{ "nosuid", MS_NOSUID, },
|
|
{ "nodev", MS_NODEV, } ,
|
|
{ "noexec", MS_NOEXEC, },
|
|
{ "noatime", MS_NOATIME, },
|
|
{ "nodiratime", MS_NODIRATIME, },
|
|
{ "relatime", MS_RELATIME, },
|
|
{ },
|
|
};
|
|
|
|
return do_opt2flag(opt, flags, mnt_opt2flag, NULL);
|
|
}
|
|
|
|
static int parse_sb_opt(char *opt, unsigned *flags, char *uopt)
|
|
{
|
|
const struct opt2flag sb_opt2flag[] = {
|
|
{ "rw", 0, },
|
|
{ "ro", MS_RDONLY, },
|
|
{ "sync", MS_SYNC, },
|
|
{ "dirsync", MS_DIRSYNC, },
|
|
{ "mad", MS_MANDLOCK, },
|
|
{ },
|
|
};
|
|
|
|
return do_opt2flag(opt, flags, sb_opt2flag, uopt);
|
|
}
|
|
|
|
static int parse_mnt_opt(char *str, struct mount_info *mi, int *off)
|
|
{
|
|
char *istr = str, *end;
|
|
|
|
while (1) {
|
|
end = strchr(str, ' ');
|
|
if (!end) {
|
|
pr_err("Error parsing mount options");
|
|
return -1;
|
|
}
|
|
|
|
*end = '\0';
|
|
if (!strncmp(str, "-", 1))
|
|
break;
|
|
else if (!strncmp(str, "shared:", 7)) {
|
|
mi->flags |= MS_SHARED;
|
|
mi->shared_id = atoi(str + 7);
|
|
} else if (!strncmp(str, "master:", 7)) {
|
|
mi->flags |= MS_SLAVE;
|
|
mi->master_id = atoi(str + 7);
|
|
} else if (!strncmp(str, "propagate_from:", 15)) {
|
|
/* skip */;
|
|
} else if (!strncmp(str, "unbindable", 11))
|
|
mi->flags |= MS_UNBINDABLE;
|
|
else {
|
|
pr_err("Unknown option [%s]\n", str);
|
|
return -1;
|
|
}
|
|
|
|
str = end + 1;
|
|
}
|
|
|
|
*off = end - istr + 1;
|
|
return 0;
|
|
}
|
|
|
|
static int parse_mountinfo_ent(char *str, struct mount_info *new)
|
|
{
|
|
unsigned int kmaj, kmin;
|
|
int ret, n;
|
|
char *opt;
|
|
|
|
ret = sscanf(str, "%i %i %u:%u %ms %ms %ms %n",
|
|
&new->mnt_id, &new->parent_mnt_id,
|
|
&kmaj, &kmin, &new->root, &new->mountpoint,
|
|
&opt, &n);
|
|
if (ret != 7)
|
|
return -1;
|
|
|
|
new->s_dev = MKKDEV(kmaj, kmin);
|
|
new->flags = 0;
|
|
if (parse_mnt_flags(opt, &new->flags))
|
|
return -1;
|
|
|
|
free(opt); /* after %ms scanf */
|
|
|
|
str += n;
|
|
if (parse_mnt_opt(str, new, &n))
|
|
return -1;
|
|
|
|
str += n;
|
|
ret = sscanf(str, "%ms %ms %ms", &new->fstype, &new->source, &opt);
|
|
if (ret != 3)
|
|
return -1;
|
|
|
|
new->options = xmalloc(strlen(opt));
|
|
if (!new->options)
|
|
return -1;
|
|
|
|
if (parse_sb_opt(opt, &new->flags, new->options))
|
|
return -1;
|
|
|
|
free(opt);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct mount_info *parse_mountinfo(pid_t pid)
|
|
{
|
|
struct mount_info *list = NULL;
|
|
FILE *f;
|
|
char str[256];
|
|
|
|
snprintf(str, sizeof(str), "/proc/%d/mountinfo", pid);
|
|
f = fopen(str, "r");
|
|
if (!f) {
|
|
pr_perror("Can't open %d mountinfo", pid);
|
|
return NULL;
|
|
}
|
|
|
|
while (fgets(str, sizeof(str), f)) {
|
|
struct mount_info *new;
|
|
int ret;
|
|
|
|
new = xmalloc(sizeof(*new));
|
|
if (!new)
|
|
goto err;
|
|
|
|
mnt_entry_init(new);
|
|
|
|
ret = parse_mountinfo_ent(str, new);
|
|
if (ret < 0) {
|
|
pr_err("Bad format in %d mountinfo\n", pid);
|
|
goto err;
|
|
}
|
|
|
|
pr_info("\ttype %s source %s %x %s @ %s flags %x options %s\n",
|
|
new->fstype, new->source,
|
|
new->s_dev, new->root, new->mountpoint,
|
|
new->flags, new->options);
|
|
|
|
new->next = list;
|
|
list = new;
|
|
}
|
|
out:
|
|
fclose(f);
|
|
return list;
|
|
|
|
err:
|
|
while (list) {
|
|
struct mount_info *next = list->next;
|
|
xfree(list);
|
|
list = next;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
static char nybble(const char n)
|
|
{
|
|
if (n >= '0' && n <= '9') return n - '0';
|
|
else if (n >= 'A' && n <= 'F') return n - ('A' - 10);
|
|
else if (n >= 'a' && n <= 'f') return n - ('a' - 10);
|
|
return 0;
|
|
}
|
|
|
|
static void parse_fhandle_encoded(char *tok, fh_t *f)
|
|
{
|
|
char *d = (char *)f->__handle;
|
|
int i = 0;
|
|
|
|
memzero(d, sizeof(f->__handle));
|
|
|
|
while (*tok == ' ')
|
|
tok++;
|
|
|
|
while (*tok) {
|
|
if (i >= sizeof(f->__handle))
|
|
break;
|
|
d[i++] = (nybble(tok[0]) << 4) | nybble(tok[1]);
|
|
if (tok[1])
|
|
tok += 2;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define fdinfo_field(str, field) !strncmp(str, field":", sizeof(field))
|
|
|
|
int parse_fdinfo(int fd, int type,
|
|
int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
|
|
{
|
|
FILE *f;
|
|
char str[256];
|
|
|
|
sprintf(str, "/proc/self/fdinfo/%d", fd);
|
|
f = fopen(str, "r");
|
|
if (!f) {
|
|
pr_perror("Can't open fdinfo to parse");
|
|
return -1;
|
|
}
|
|
|
|
while (fgets(str, sizeof(str), f)) {
|
|
int ret;
|
|
union fdinfo_entries entry;
|
|
|
|
if (fdinfo_field(str, "pos") || fdinfo_field(str, "counter"))
|
|
continue;
|
|
|
|
if (fdinfo_field(str, "eventfd-count")) {
|
|
if (type != FDINFO_EVENTFD)
|
|
goto parse_err;
|
|
ret = sscanf(str, "eventfd-count: %lx",
|
|
&entry.efd.counter);
|
|
if (ret != 1)
|
|
goto parse_err;
|
|
ret = cb(&entry, arg);
|
|
if (ret)
|
|
return ret;
|
|
continue;
|
|
}
|
|
if (fdinfo_field(str, "tfd")) {
|
|
if (type != FDINFO_EVENTPOLL)
|
|
goto parse_err;
|
|
ret = sscanf(str, "tfd: %d events: %x data: %lx",
|
|
&entry.epl.tfd, &entry.epl.events, &entry.epl.data);
|
|
if (ret != 3)
|
|
goto parse_err;
|
|
ret = cb(&entry, arg);
|
|
if (ret)
|
|
return ret;
|
|
continue;
|
|
}
|
|
if (fdinfo_field(str, "inotify wd")) {
|
|
int hoff;
|
|
|
|
if (type != FDINFO_INOTIFY)
|
|
goto parse_err;
|
|
ret = sscanf(str,
|
|
"inotify wd: %8d ino: %16lx sdev: %8x "
|
|
"mask: %8x ignored_mask: %8x "
|
|
"fhandle-bytes: %8x fhandle-type: %8x "
|
|
"f_handle: %n",
|
|
&entry.ify.wd, &entry.ify.i_ino, &entry.ify.s_dev,
|
|
&entry.ify.mask, &entry.ify.ignored_mask,
|
|
&entry.ify.f_handle.bytes, &entry.ify.f_handle.type,
|
|
&hoff);
|
|
if (ret != 7)
|
|
goto parse_err;
|
|
parse_fhandle_encoded(str + hoff, &entry.ify.f_handle);
|
|
|
|
ret = cb(&entry, arg);
|
|
if (ret)
|
|
return ret;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
fclose(f);
|
|
return 0;
|
|
|
|
parse_err:
|
|
pr_perror("%s: error parsing [%s] for %d\n", __func__, str, type);
|
|
return -1;
|
|
}
|