2012-01-13 20:52:35 +04:00
|
|
|
#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>
|
2012-01-27 21:37:13 +04:00
|
|
|
#include <string.h>
|
2014-08-19 22:31:07 -07:00
|
|
|
#include <ctype.h>
|
2012-05-13 08:30:06 +04:00
|
|
|
#include <linux/fs.h>
|
2012-01-14 01:48:29 +04:00
|
|
|
|
2013-01-09 17:02:47 +04:00
|
|
|
#include "asm/types.h"
|
2012-01-13 20:52:35 +04:00
|
|
|
#include "list.h"
|
|
|
|
#include "util.h"
|
2012-08-09 16:27:30 +04:00
|
|
|
#include "mount.h"
|
2013-11-14 22:48:30 +04:00
|
|
|
#include "mman.h"
|
2012-12-21 17:35:34 +04:00
|
|
|
#include "cpu.h"
|
2013-01-17 16:09:32 +08:00
|
|
|
#include "file-lock.h"
|
|
|
|
#include "pstree.h"
|
2013-01-14 20:48:10 +04:00
|
|
|
#include "fsnotify.h"
|
2013-11-05 12:32:56 +04:00
|
|
|
#include "posix-timer.h"
|
2013-04-15 13:02:09 +04:00
|
|
|
#include "kerndat.h"
|
2013-05-24 01:42:19 +04:00
|
|
|
#include "vdso.h"
|
2013-11-05 12:33:03 +04:00
|
|
|
#include "vma.h"
|
2014-09-19 17:31:11 +04:00
|
|
|
#include "bfd.h"
|
2012-01-13 20:52:35 +04:00
|
|
|
#include "proc_parse.h"
|
2014-08-19 22:31:07 -07:00
|
|
|
#include "cr_options.h"
|
|
|
|
#include "sysfs_parse.h"
|
2012-07-17 07:25:42 +04:00
|
|
|
#include "protobuf.h"
|
2012-07-19 10:18:37 +04:00
|
|
|
#include "protobuf/fdinfo.pb-c.h"
|
2014-08-19 22:31:07 -07:00
|
|
|
#include "protobuf/mnt.pb-c.h"
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2012-05-29 20:11:00 +04:00
|
|
|
#include <stdlib.h>
|
|
|
|
|
2012-05-02 15:22:00 +04:00
|
|
|
struct buffer {
|
|
|
|
char buf[PAGE_SIZE];
|
|
|
|
char end; /* '\0' */
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct buffer __buf;
|
|
|
|
static char *buf = __buf.buf;
|
|
|
|
|
|
|
|
#define BUF_SIZE sizeof(__buf.buf)
|
|
|
|
|
2013-02-18 17:54:49 +04:00
|
|
|
int parse_cpuinfo_features(int (*handler)(char *tok))
|
2012-12-21 17:35:34 +04:00
|
|
|
{
|
|
|
|
FILE *cpuinfo;
|
|
|
|
|
2014-09-17 16:41:00 +04:00
|
|
|
cpuinfo = fopen_proc(PROC_GEN, "cpuinfo");
|
2012-12-21 17:35:34 +04:00
|
|
|
if (!cpuinfo) {
|
|
|
|
pr_perror("Can't open cpuinfo file");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(buf, BUF_SIZE, cpuinfo)) {
|
|
|
|
char *tok;
|
|
|
|
|
|
|
|
if (strncmp(buf, "flags\t\t:", 8))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (tok = strtok(buf, " \t\n"); tok;
|
|
|
|
tok = strtok(NULL, " \t\n")) {
|
2013-02-18 17:54:49 +04:00
|
|
|
if (handler(tok) < 0)
|
|
|
|
break;
|
2012-12-21 17:35:34 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(cpuinfo);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-05-10 21:07:00 +04:00
|
|
|
/* check the @line starts with "%lx-%lx" format */
|
|
|
|
static bool is_vma_range_fmt(char *line)
|
|
|
|
{
|
2014-02-03 17:24:00 +04:00
|
|
|
#define ____is_vma_addr_char(__c) \
|
|
|
|
(((__c) <= '9' && (__c) >= '0') || \
|
|
|
|
((__c) <= 'f' && (__c) >= 'a'))
|
|
|
|
|
|
|
|
while (*line && ____is_vma_addr_char(*line))
|
2012-05-10 21:07:00 +04:00
|
|
|
line++;
|
|
|
|
|
|
|
|
if (*line++ != '-')
|
|
|
|
return false;
|
|
|
|
|
2014-02-03 17:24:00 +04:00
|
|
|
while (*line && ____is_vma_addr_char(*line))
|
2012-05-10 21:07:00 +04:00
|
|
|
line++;
|
|
|
|
|
|
|
|
if (*line++ != ' ')
|
|
|
|
return false;
|
|
|
|
|
|
|
|
return true;
|
2014-02-03 17:24:00 +04:00
|
|
|
#undef ____is_vma_addr_char
|
2012-05-10 21:07:00 +04:00
|
|
|
}
|
|
|
|
|
2012-10-26 00:16:05 +04:00
|
|
|
static int parse_vmflags(char *buf, struct vma_area *vma_area)
|
|
|
|
{
|
|
|
|
char *tok;
|
2014-04-04 12:04:00 +04:00
|
|
|
bool shared = false;
|
|
|
|
bool maywrite = false;
|
2012-10-26 00:16:05 +04:00
|
|
|
|
|
|
|
if (!buf[0])
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
tok = strtok(buf, " \n");
|
|
|
|
if (!tok)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#define _vmflag_match(_t, _s) (_t[0] == _s[0] && _t[1] == _s[1])
|
|
|
|
|
|
|
|
do {
|
2014-04-04 12:04:00 +04:00
|
|
|
/* open() block */
|
|
|
|
if (_vmflag_match(tok, "sh"))
|
|
|
|
shared = true;
|
|
|
|
else if (_vmflag_match(tok, "mw"))
|
|
|
|
maywrite = true;
|
|
|
|
|
2012-10-26 00:16:05 +04:00
|
|
|
/* mmap() block */
|
2013-08-26 17:14:59 +04:00
|
|
|
if (_vmflag_match(tok, "gd"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags |= MAP_GROWSDOWN;
|
2013-08-26 17:14:59 +04:00
|
|
|
else if (_vmflag_match(tok, "lo"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags |= MAP_LOCKED;
|
2012-10-26 00:16:05 +04:00
|
|
|
else if (_vmflag_match(tok, "nr"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags |= MAP_NORESERVE;
|
2012-10-26 00:16:05 +04:00
|
|
|
else if (_vmflag_match(tok, "ht"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags |= MAP_HUGETLB;
|
2012-10-26 00:16:05 +04:00
|
|
|
|
|
|
|
/* madvise() block */
|
|
|
|
if (_vmflag_match(tok, "sr"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->madv |= (1ul << MADV_SEQUENTIAL);
|
2012-10-26 00:16:05 +04:00
|
|
|
else if (_vmflag_match(tok, "rr"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->madv |= (1ul << MADV_RANDOM);
|
2012-10-26 00:16:05 +04:00
|
|
|
else if (_vmflag_match(tok, "dc"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->madv |= (1ul << MADV_DONTFORK);
|
2012-10-26 00:16:05 +04:00
|
|
|
else if (_vmflag_match(tok, "dd"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->madv |= (1ul << MADV_DONTDUMP);
|
2012-10-26 00:16:05 +04:00
|
|
|
else if (_vmflag_match(tok, "mg"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->madv |= (1ul << MADV_MERGEABLE);
|
2012-10-26 00:16:05 +04:00
|
|
|
else if (_vmflag_match(tok, "hg"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->madv |= (1ul << MADV_HUGEPAGE);
|
2012-10-26 00:16:05 +04:00
|
|
|
else if (_vmflag_match(tok, "nh"))
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->madv |= (1ul << MADV_NOHUGEPAGE);
|
2012-10-26 00:16:05 +04:00
|
|
|
|
2014-05-19 23:02:00 +04:00
|
|
|
/* vmsplice doesn't work for VM_IO and VM_PFNMAP mappings. */
|
2014-06-20 19:35:08 +04:00
|
|
|
if (_vmflag_match(tok, "io") || _vmflag_match(tok, "pf")) {
|
|
|
|
#ifdef CONFIG_VDSO
|
|
|
|
/*
|
|
|
|
* VVAR area mapped by the kernel as
|
|
|
|
* VM_IO | VM_PFNMAP| VM_DONTEXPAND | VM_DONTDUMP
|
|
|
|
*/
|
|
|
|
if (!vma_area_is(vma_area, VMA_AREA_VVAR))
|
|
|
|
#endif
|
|
|
|
vma_area->e->status |= VMA_UNSUPP;
|
|
|
|
}
|
2014-05-19 23:02:00 +04:00
|
|
|
|
2012-10-26 00:16:05 +04:00
|
|
|
/*
|
|
|
|
* Anything else is just ignored.
|
|
|
|
*/
|
|
|
|
} while ((tok = strtok(NULL, " \n")));
|
|
|
|
|
|
|
|
#undef _vmflag_match
|
|
|
|
|
2014-04-04 12:04:00 +04:00
|
|
|
if (shared && maywrite)
|
|
|
|
vma_area->e->fdflags = O_RDWR;
|
|
|
|
else
|
|
|
|
vma_area->e->fdflags = O_RDONLY;
|
|
|
|
vma_area->e->has_fdflags = true;
|
|
|
|
|
2014-02-04 00:08:16 +04:00
|
|
|
if (vma_area->e->madv)
|
|
|
|
vma_area->e->has_madv = true;
|
2012-10-26 00:16:05 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-04-15 13:02:09 +04:00
|
|
|
static inline int is_anon_shmem_map(dev_t dev)
|
2012-11-06 19:07:38 +03:00
|
|
|
{
|
2014-11-10 10:47:42 +04:00
|
|
|
return kdat.shmem_dev == dev;
|
2012-11-06 19:07:38 +03:00
|
|
|
}
|
|
|
|
|
2014-01-31 20:31:06 +04:00
|
|
|
struct vma_file_info {
|
|
|
|
int dev_maj;
|
|
|
|
int dev_min;
|
|
|
|
unsigned long ino;
|
|
|
|
struct vma_area *vma;
|
|
|
|
};
|
|
|
|
|
|
|
|
static inline int vfi_equal(struct vma_file_info *a, struct vma_file_info *b)
|
|
|
|
{
|
|
|
|
return ((a->ino ^ b->ino) |
|
|
|
|
(a->dev_maj ^ b->dev_maj) |
|
|
|
|
(a->dev_min ^ b->dev_min)) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int vma_get_mapfile(struct vma_area *vma, DIR *mfd,
|
|
|
|
struct vma_file_info *vfi, struct vma_file_info *prev_vfi)
|
2014-01-31 20:30:47 +04:00
|
|
|
{
|
|
|
|
char path[32];
|
|
|
|
|
2014-01-31 20:31:06 +04:00
|
|
|
if (prev_vfi->vma && vfi_equal(vfi, prev_vfi)) {
|
|
|
|
struct vma_area *prev = prev_vfi->vma;
|
|
|
|
|
2014-03-06 21:32:15 +04:00
|
|
|
/*
|
|
|
|
* If vfi is equal (!) and negative @vm_file_fd --
|
|
|
|
* we have nothing to borrow for sure.
|
|
|
|
*/
|
|
|
|
if (prev->vm_file_fd < 0)
|
|
|
|
return 0;
|
|
|
|
|
2014-02-04 20:40:02 +04:00
|
|
|
pr_debug("vma %"PRIx64" borrows vfi from previous %"PRIx64"\n",
|
2014-02-04 00:08:16 +04:00
|
|
|
vma->e->start, prev->e->start);
|
2014-01-31 20:31:06 +04:00
|
|
|
vma->vm_file_fd = prev->vm_file_fd;
|
2014-02-04 00:08:16 +04:00
|
|
|
if (prev->e->status & VMA_AREA_SOCKET)
|
|
|
|
vma->e->status |= VMA_AREA_SOCKET | VMA_AREA_REGULAR;
|
2014-09-23 20:32:00 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* FIXME -- in theory there can be vmas that have
|
|
|
|
* dev:ino match, but live in different mount
|
|
|
|
* namespaces. However, we only borrow files for
|
|
|
|
* subsequent vmas. These are _very_ likely to
|
|
|
|
* have files from the same namespaces.
|
|
|
|
*/
|
2014-01-31 20:31:06 +04:00
|
|
|
vma->file_borrowed = true;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-01-31 20:30:47 +04:00
|
|
|
/* Figure out if it's file mapping */
|
2014-02-04 20:40:02 +04:00
|
|
|
snprintf(path, sizeof(path), "%"PRIx64"-%"PRIx64, vma->e->start, vma->e->end);
|
2014-01-31 20:30:47 +04:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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->vm_file_fd = openat(dirfd(mfd), path, O_RDONLY);
|
|
|
|
if (vma->vm_file_fd < 0) {
|
|
|
|
if (errno == ENXIO) {
|
|
|
|
struct stat buf;
|
|
|
|
|
|
|
|
if (fstatat(dirfd(mfd), path, &buf, 0))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
if (!S_ISSOCK(buf.st_mode))
|
|
|
|
return -1;
|
|
|
|
|
2014-02-04 20:40:02 +04:00
|
|
|
pr_info("Found socket %"PRIu64" mapping @%"PRIx64"\n",
|
2014-02-04 00:08:16 +04:00
|
|
|
buf.st_ino, vma->e->start);
|
|
|
|
vma->e->status |= VMA_AREA_SOCKET | VMA_AREA_REGULAR;
|
2014-01-31 20:30:47 +04:00
|
|
|
vma->vm_socket_id = buf.st_ino;
|
|
|
|
} else if (errno != ENOENT)
|
|
|
|
return -1;
|
2014-08-19 22:31:07 -07:00
|
|
|
} else if (opts.aufs && fixup_aufs_vma_fd(vma) < 0)
|
|
|
|
return -1;
|
2014-01-31 20:30:47 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-02-07 13:32:21 +04:00
|
|
|
int parse_self_maps_lite(struct vm_area_list *vms)
|
|
|
|
{
|
|
|
|
FILE *maps;
|
|
|
|
|
|
|
|
vm_area_list_init(vms);
|
|
|
|
|
2014-06-05 20:15:59 +04:00
|
|
|
maps = fopen_proc(PROC_SELF, "maps");
|
2014-02-07 13:32:21 +04:00
|
|
|
if (maps == NULL) {
|
|
|
|
pr_perror("Can't open self maps");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(buf, BUF_SIZE, maps) != NULL) {
|
|
|
|
struct vma_area *vma;
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
vma = alloc_vma_area();
|
|
|
|
if (!vma) {
|
|
|
|
fclose(maps);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
vma->e->start = strtoul(buf, &end, 16);
|
|
|
|
vma->e->end = strtoul(end + 1, NULL, 16);
|
|
|
|
list_add_tail(&vma->list, &vms->h);
|
|
|
|
vms->nr++;
|
|
|
|
|
2014-02-07 20:51:21 +04:00
|
|
|
pr_debug("Parsed %"PRIx64"-%"PRIx64" vma\n", vma->e->start, vma->e->end);
|
2014-02-07 13:32:21 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
fclose(maps);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2014-12-19 15:57:00 +03:00
|
|
|
int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list)
|
2012-01-13 20:52:35 +04:00
|
|
|
{
|
|
|
|
struct vma_area *vma_area = NULL;
|
2014-05-05 14:50:00 +04:00
|
|
|
unsigned long start, end, pgoff, prev_end = 0;
|
2012-08-11 22:19:34 +04:00
|
|
|
char r, w, x, s;
|
2013-03-01 20:11:51 +04:00
|
|
|
int ret = -1;
|
2014-01-31 20:31:06 +04:00
|
|
|
struct vma_file_info vfi;
|
|
|
|
struct vma_file_info prev_vfi = {};
|
2012-01-13 20:52:35 +04:00
|
|
|
|
|
|
|
DIR *map_files_dir = NULL;
|
2014-09-19 17:31:25 +04:00
|
|
|
struct bfd f;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2013-03-01 20:11:51 +04:00
|
|
|
vma_area_list->nr = 0;
|
2013-03-01 20:12:33 +04:00
|
|
|
vma_area_list->longest = 0;
|
|
|
|
vma_area_list->priv_size = 0;
|
2013-03-01 20:11:51 +04:00
|
|
|
INIT_LIST_HEAD(&vma_area_list->h);
|
|
|
|
|
2014-09-19 17:31:25 +04:00
|
|
|
f.fd = open_proc(pid, "smaps");
|
|
|
|
if (f.fd < 0)
|
|
|
|
goto err_n;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2014-10-29 15:44:00 +04:00
|
|
|
if (bfdopen(&f, O_RDONLY))
|
2014-09-19 17:31:25 +04:00
|
|
|
goto err_n;
|
2014-02-14 16:46:15 +04:00
|
|
|
|
2014-12-19 15:57:00 +03:00
|
|
|
map_files_dir = opendir_proc(pid, "map_files");
|
|
|
|
if (!map_files_dir) /* old kernel? */
|
|
|
|
goto err;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2013-05-21 14:37:52 +04:00
|
|
|
while (1) {
|
2012-01-13 20:52:35 +04:00
|
|
|
int num;
|
2014-10-26 01:00:00 +04:00
|
|
|
char file_path[32];
|
2013-05-21 14:37:52 +04:00
|
|
|
bool eof;
|
2014-09-19 17:31:25 +04:00
|
|
|
char *str;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2014-09-19 17:31:25 +04:00
|
|
|
str = breadline(&f);
|
|
|
|
eof = (str == NULL);
|
2013-05-21 14:37:52 +04:00
|
|
|
|
2014-09-19 17:31:25 +04:00
|
|
|
if (!eof && !is_vma_range_fmt(str)) {
|
|
|
|
if (!strncmp(str, "Nonlinear", 9)) {
|
2012-05-10 21:07:00 +04:00
|
|
|
BUG_ON(!vma_area);
|
2013-01-16 19:20:08 +04:00
|
|
|
pr_err("Nonlinear mapping found %016"PRIx64"-%016"PRIx64"\n",
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->start, vma_area->e->end);
|
2012-05-10 21:07:00 +04:00
|
|
|
/*
|
|
|
|
* VMA is already on list and will be
|
|
|
|
* freed later as list get destroyed.
|
|
|
|
*/
|
|
|
|
vma_area = NULL;
|
|
|
|
goto err;
|
2014-09-19 17:31:25 +04:00
|
|
|
} else if (!strncmp(str, "VmFlags: ", 9)) {
|
2012-10-26 00:16:05 +04:00
|
|
|
BUG_ON(!vma_area);
|
2014-09-19 17:31:25 +04:00
|
|
|
if (parse_vmflags(&str[9], vma_area))
|
2012-10-26 00:16:05 +04:00
|
|
|
goto err;
|
|
|
|
continue;
|
2012-05-10 21:07:00 +04:00
|
|
|
} else
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-05-21 14:37:52 +04:00
|
|
|
if (vma_area) {
|
2014-05-19 23:02:00 +04:00
|
|
|
if (vma_area->e->status & VMA_UNSUPP) {
|
|
|
|
pr_err("Unsupported mapping found %016"PRIx64"-%016"PRIx64"\n",
|
|
|
|
vma_area->e->start, vma_area->e->end);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-05-05 14:50:00 +04:00
|
|
|
/* Add a guard page only if here is enough space for it */
|
|
|
|
if ((vma_area->e->flags & MAP_GROWSDOWN) &&
|
|
|
|
prev_end < vma_area->e->start)
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->start -= PAGE_SIZE; /* Guard page */
|
2014-05-05 14:50:00 +04:00
|
|
|
prev_end = vma_area->e->end;
|
2013-08-26 17:14:59 +04:00
|
|
|
|
2013-05-21 14:37:52 +04:00
|
|
|
list_add_tail(&vma_area->list, &vma_area_list->h);
|
|
|
|
vma_area_list->nr++;
|
|
|
|
if (privately_dump_vma(vma_area)) {
|
|
|
|
unsigned long pages;
|
|
|
|
|
|
|
|
pages = vma_area_len(vma_area) / PAGE_SIZE;
|
|
|
|
vma_area_list->priv_size += pages;
|
|
|
|
vma_area_list->longest = max(vma_area_list->longest, pages);
|
|
|
|
}
|
2014-01-31 20:31:06 +04:00
|
|
|
|
|
|
|
prev_vfi = vfi;
|
|
|
|
prev_vfi.vma = vma_area;
|
2013-05-21 14:37:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (eof)
|
|
|
|
break;
|
|
|
|
|
2012-05-10 21:07:00 +04:00
|
|
|
vma_area = alloc_vma_area();
|
|
|
|
if (!vma_area)
|
|
|
|
goto err;
|
2012-02-14 20:19:49 +03:00
|
|
|
|
2014-10-26 01:00:00 +04:00
|
|
|
memzero(file_path, sizeof(file_path));
|
|
|
|
num = sscanf(str, "%lx-%lx %c%c%c%c %lx %x:%x %lu %31s",
|
2014-01-31 20:31:06 +04:00
|
|
|
&start, &end, &r, &w, &x, &s, &pgoff,
|
|
|
|
&vfi.dev_maj, &vfi.dev_min, &vfi.ino, file_path);
|
2012-02-14 20:19:49 +03:00
|
|
|
if (num < 10) {
|
2014-09-19 17:31:25 +04:00
|
|
|
pr_err("Can't parse: %s\n", str);
|
2012-01-13 20:52:35 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->start = start;
|
|
|
|
vma_area->e->end = end;
|
|
|
|
vma_area->e->pgoff = pgoff;
|
|
|
|
vma_area->e->prot = PROT_NONE;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2014-01-31 20:31:06 +04:00
|
|
|
if (vma_get_mapfile(vma_area, map_files_dir, &vfi, &prev_vfi))
|
2014-01-31 20:30:47 +04:00
|
|
|
goto err_bogus_mapfile;
|
|
|
|
|
2012-01-13 20:52:35 +04:00
|
|
|
if (r == 'r')
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->prot |= PROT_READ;
|
2012-01-13 20:52:35 +04:00
|
|
|
if (w == 'w')
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->prot |= PROT_WRITE;
|
2012-01-13 20:52:35 +04:00
|
|
|
if (x == 'x')
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->prot |= PROT_EXEC;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
|
|
|
if (s == 's')
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags = MAP_SHARED;
|
2012-01-13 20:52:35 +04:00
|
|
|
else if (s == 'p')
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags = MAP_PRIVATE;
|
2012-03-03 19:35:10 +03:00
|
|
|
else {
|
|
|
|
pr_err("Unexpected VMA met (%c)\n", s);
|
|
|
|
goto err;
|
|
|
|
}
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2014-02-04 00:08:16 +04:00
|
|
|
if (vma_area->e->status != 0) {
|
2013-05-21 14:37:52 +04:00
|
|
|
continue;
|
2014-10-26 01:00:00 +04:00
|
|
|
} else if (!strcmp(file_path, "[vsyscall]") ||
|
|
|
|
!strcmp(file_path, "[vectors]")) {
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->status |= VMA_AREA_VSYSCALL;
|
2014-10-26 01:00:00 +04:00
|
|
|
} else if (!strcmp(file_path, "[vdso]")) {
|
2014-05-26 11:50:15 +04:00
|
|
|
#ifdef CONFIG_VDSO
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->status |= VMA_AREA_REGULAR;
|
|
|
|
if ((vma_area->e->prot & VDSO_PROT) == VDSO_PROT)
|
|
|
|
vma_area->e->status |= VMA_AREA_VDSO;
|
2014-05-26 11:50:15 +04:00
|
|
|
#else
|
|
|
|
pr_warn_once("Found vDSO area without support\n");
|
|
|
|
goto err;
|
2014-06-20 19:35:08 +04:00
|
|
|
#endif
|
2014-10-26 01:00:00 +04:00
|
|
|
} else if (!strcmp(file_path, "[vvar]")) {
|
2014-06-20 19:35:08 +04:00
|
|
|
#ifdef CONFIG_VDSO
|
|
|
|
vma_area->e->status |= VMA_AREA_REGULAR;
|
|
|
|
if ((vma_area->e->prot & VVAR_PROT) == VVAR_PROT)
|
|
|
|
vma_area->e->status |= VMA_AREA_VVAR;
|
|
|
|
#else
|
|
|
|
pr_warn_once("Found VVAR area without support\n");
|
|
|
|
goto err;
|
2014-05-26 11:50:15 +04:00
|
|
|
#endif
|
2014-10-26 01:00:00 +04:00
|
|
|
} else if (!strcmp(file_path, "[heap]")) {
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->status |= VMA_AREA_REGULAR | VMA_AREA_HEAP;
|
2012-01-13 20:52:35 +04:00
|
|
|
} else {
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->status = VMA_AREA_REGULAR;
|
2012-01-13 20:52:35 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some mapping hints for restore, we save this on
|
|
|
|
* disk and restore might need to analyze it.
|
|
|
|
*/
|
2014-01-31 20:31:06 +04:00
|
|
|
if (vma_area->file_borrowed) {
|
|
|
|
struct vma_area *prev = prev_vfi.vma;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pick-up flags that might be set in the branch below.
|
|
|
|
* Status is copied as-is as it should be zero here,
|
|
|
|
* and have full match with the previous.
|
|
|
|
*/
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags |= (prev->e->flags & MAP_ANONYMOUS);
|
|
|
|
vma_area->e->status = prev->e->status;
|
|
|
|
vma_area->e->shmid = prev->e->shmid;
|
2014-09-23 20:31:16 +04:00
|
|
|
vma_area->vmst = prev->vmst;
|
2014-09-23 20:32:00 +04:00
|
|
|
vma_area->mnt_id = prev->mnt_id;
|
2014-01-31 20:31:06 +04:00
|
|
|
} else if (vma_area->vm_file_fd >= 0) {
|
2014-02-03 00:18:32 +04:00
|
|
|
struct stat *st_buf;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2014-09-23 20:31:16 +04:00
|
|
|
st_buf = vma_area->vmst = xmalloc(sizeof(*st_buf));
|
2014-02-03 00:18:32 +04:00
|
|
|
if (!st_buf)
|
|
|
|
goto err;
|
|
|
|
|
2014-08-19 22:31:07 -07:00
|
|
|
/*
|
|
|
|
* For AUFS support, we cannot fstat() a file descriptor that
|
|
|
|
* is a symbolic link to a branch (it would return different
|
|
|
|
* dev/ino than the real file). Instead, we stat() using the
|
|
|
|
* full pathname that we saved before.
|
|
|
|
*/
|
|
|
|
if (vma_area->aufs_fpath) {
|
|
|
|
if (stat(vma_area->aufs_fpath, st_buf) < 0) {
|
|
|
|
pr_perror("Failed stat on %d's map %lu (%s)",
|
|
|
|
pid, start, vma_area->aufs_fpath);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
} else if (fstat(vma_area->vm_file_fd, st_buf) < 0) {
|
2012-01-31 15:31:23 +04:00
|
|
|
pr_perror("Failed fstat on %d's map %lu", pid, start);
|
2012-01-13 20:52:35 +04:00
|
|
|
goto err;
|
|
|
|
}
|
2013-01-17 14:46:33 +04:00
|
|
|
|
2014-12-17 18:11:40 +03:00
|
|
|
if (S_ISREG(st_buf->st_mode))
|
|
|
|
/* regular file mapping -- supported */;
|
|
|
|
else if (S_ISCHR(st_buf->st_mode) && (st_buf->st_rdev == DEVZERO))
|
|
|
|
/* devzero mapping -- also makes sense */;
|
|
|
|
else {
|
2014-12-17 18:10:54 +03:00
|
|
|
pr_err("Can't handle non-regular mapping on %d's map %#lx\n", pid, start);
|
2012-01-13 20:52:35 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* /dev/zero stands for anon-shared mapping
|
|
|
|
* otherwise it's some file mapping.
|
|
|
|
*/
|
2014-02-03 00:18:32 +04:00
|
|
|
if (is_anon_shmem_map(st_buf->st_dev)) {
|
2014-02-04 00:08:16 +04:00
|
|
|
if (!(vma_area->e->flags & MAP_SHARED))
|
2012-01-13 20:52:35 +04:00
|
|
|
goto err_bogus_mapping;
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags |= MAP_ANONYMOUS;
|
|
|
|
vma_area->e->status |= VMA_ANON_SHARED;
|
|
|
|
vma_area->e->shmid = st_buf->st_ino;
|
2012-02-14 20:19:49 +03:00
|
|
|
|
2014-10-26 01:00:00 +04:00
|
|
|
if (!strncmp(file_path, "/SYSV", 5)) {
|
2012-03-17 01:22:18 +04:00
|
|
|
pr_info("path: %s\n", file_path);
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->status |= VMA_AREA_SYSVIPC;
|
2012-02-14 20:19:49 +03:00
|
|
|
}
|
2012-01-13 20:52:35 +04:00
|
|
|
} else {
|
2014-02-04 00:08:16 +04:00
|
|
|
if (vma_area->e->flags & MAP_PRIVATE)
|
|
|
|
vma_area->e->status |= VMA_FILE_PRIVATE;
|
2012-01-13 20:52:35 +04:00
|
|
|
else
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->status |= VMA_FILE_SHARED;
|
2012-01-13 20:52:35 +04:00
|
|
|
}
|
2014-09-23 20:32:00 +04:00
|
|
|
|
|
|
|
if (get_fd_mntid(vma_area->vm_file_fd, &vma_area->mnt_id))
|
|
|
|
return -1;
|
2012-01-13 20:52:35 +04:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* No file but mapping -- anonymous one.
|
|
|
|
*/
|
2014-02-04 00:08:16 +04:00
|
|
|
if (vma_area->e->flags & MAP_SHARED) {
|
|
|
|
vma_area->e->status |= VMA_ANON_SHARED;
|
|
|
|
vma_area->e->shmid = vfi.ino;
|
2012-01-13 20:52:35 +04:00
|
|
|
} else {
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->status |= VMA_ANON_PRIVATE;
|
2012-01-13 20:52:35 +04:00
|
|
|
}
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->flags |= MAP_ANONYMOUS;
|
2012-01-13 20:52:35 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
vma_area = NULL;
|
2013-03-01 20:11:51 +04:00
|
|
|
ret = 0;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
|
|
|
err:
|
2014-09-19 17:31:25 +04:00
|
|
|
bclose(&f);
|
|
|
|
err_n:
|
2012-01-13 20:52:35 +04:00
|
|
|
if (map_files_dir)
|
|
|
|
closedir(map_files_dir);
|
|
|
|
|
|
|
|
xfree(vma_area);
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
err_bogus_mapping:
|
2013-01-16 19:20:08 +04:00
|
|
|
pr_err("Bogus mapping 0x%"PRIx64"-0x%"PRIx64" (flags: %#x vm_file_fd: %d)\n",
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->start, vma_area->e->end,
|
|
|
|
vma_area->e->flags, vma_area->vm_file_fd);
|
2012-01-13 20:52:35 +04:00
|
|
|
goto err;
|
2012-11-02 16:00:18 +03:00
|
|
|
|
|
|
|
err_bogus_mapfile:
|
|
|
|
pr_perror("Can't open %d's mapfile link %lx", pid, start);
|
|
|
|
goto err;
|
2012-01-13 20:52:35 +04:00
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
int parse_pid_stat(pid_t pid, struct proc_pid_stat *s)
|
2012-01-13 20:54:08 +04:00
|
|
|
{
|
2012-04-17 11:41:00 +04:00
|
|
|
char *tok, *p;
|
|
|
|
int fd;
|
2012-01-15 02:20:57 +04:00
|
|
|
int n;
|
2012-01-13 20:54:08 +04:00
|
|
|
|
2012-04-17 11:41:00 +04:00
|
|
|
fd = open_proc(pid, "stat");
|
|
|
|
if (fd < 0)
|
|
|
|
return -1;
|
|
|
|
|
2012-05-02 15:22:00 +04:00
|
|
|
n = read(fd, buf, BUF_SIZE);
|
2012-04-17 11:41:00 +04:00
|
|
|
if (n < 1) {
|
|
|
|
pr_err("stat for %d is corrupted\n", pid);
|
|
|
|
close(fd);
|
2012-01-13 20:54:08 +04:00
|
|
|
return -1;
|
2012-04-17 11:41:00 +04:00
|
|
|
}
|
|
|
|
close(fd);
|
2012-01-13 20:54:08 +04:00
|
|
|
|
|
|
|
memset(s, 0, sizeof(*s));
|
2012-04-17 11:41:00 +04:00
|
|
|
|
|
|
|
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 "
|
2012-01-14 01:58:36 +04:00
|
|
|
"%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 "
|
2012-01-23 15:18:31 +04:00
|
|
|
"%lu %lu %lu %lu %lu %lu %lu %d",
|
2012-01-13 20:54:08 +04:00
|
|
|
&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,
|
2012-01-22 20:22:40 +04:00
|
|
|
&s->start_brk,
|
2012-01-23 15:18:31 +04:00
|
|
|
&s->arg_start,
|
|
|
|
&s->arg_end,
|
|
|
|
&s->env_start,
|
|
|
|
&s->env_end,
|
2012-01-22 20:22:40 +04:00
|
|
|
&s->exit_code);
|
2012-04-17 11:41:00 +04:00
|
|
|
if (n < 50)
|
|
|
|
goto err;
|
2012-01-14 01:48:29 +04:00
|
|
|
|
2012-01-13 20:54:08 +04:00
|
|
|
return 0;
|
2012-04-17 11:41:00 +04:00
|
|
|
|
|
|
|
err:
|
|
|
|
pr_err("Parsing %d's stat failed (#fields do not match)\n", pid);
|
|
|
|
return -1;
|
2012-01-13 20:54:08 +04:00
|
|
|
}
|
2012-01-27 21:37:13 +04:00
|
|
|
|
|
|
|
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);
|
2014-11-10 11:13:08 +03:00
|
|
|
if (*end)
|
2012-01-27 21:37:13 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
int parse_pid_status(pid_t pid, struct proc_status_creds *cr)
|
2012-01-27 21:37:13 +04:00
|
|
|
{
|
2014-11-10 11:13:08 +03:00
|
|
|
struct bfd f;
|
2012-01-27 21:37:13 +04:00
|
|
|
int done = 0;
|
2014-11-10 11:13:08 +03:00
|
|
|
int ret = -1;
|
|
|
|
char *str;
|
2012-01-27 21:37:13 +04:00
|
|
|
|
2014-11-10 11:13:08 +03:00
|
|
|
f.fd = open_proc(pid, "status");
|
|
|
|
if (f.fd < 0) {
|
2012-01-31 15:13:05 +04:00
|
|
|
pr_perror("Can't open proc status");
|
2012-01-27 21:37:13 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-11-10 11:13:08 +03:00
|
|
|
if (bfdopen(&f, O_RDONLY))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (done < 8 && (str = breadline(&f))) {
|
|
|
|
pr_debug("str: `%s'\n", str);
|
2014-11-01 01:02:23 +03:00
|
|
|
if (!strncmp(str, "State:", 6)) {
|
|
|
|
cr->state = str[7];
|
|
|
|
done++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strncmp(str, "PPid:", 5)) {
|
|
|
|
if (sscanf(str, "PPid:\t%d", &cr->ppid) != 1) {
|
|
|
|
pr_err("Unable to parse: %s", str);
|
|
|
|
goto err_parse;
|
|
|
|
}
|
|
|
|
done++;
|
|
|
|
}
|
|
|
|
|
2012-01-27 21:37:13 +04:00
|
|
|
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++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-10 11:13:08 +03:00
|
|
|
if (done == 8)
|
|
|
|
ret = 0;
|
|
|
|
|
2012-01-27 21:37:13 +04:00
|
|
|
err_parse:
|
2014-11-10 11:13:08 +03:00
|
|
|
if (ret)
|
2012-01-27 21:37:13 +04:00
|
|
|
pr_err("Error parsing proc status file\n");
|
2014-11-10 11:13:08 +03:00
|
|
|
bclose(&f);
|
|
|
|
return ret;
|
2012-01-27 21:37:13 +04:00
|
|
|
}
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2012-05-13 08:30:06 +04:00
|
|
|
struct opt2flag {
|
|
|
|
char *opt;
|
|
|
|
unsigned flag;
|
|
|
|
};
|
|
|
|
|
|
|
|
static int do_opt2flag(char *opt, unsigned *flags,
|
|
|
|
const struct opt2flag *opts, char *unknown)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char *end;
|
2014-09-10 00:17:46 +04:00
|
|
|
size_t uoff = 0;
|
2012-05-13 08:30:06 +04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-09-10 00:17:46 +04:00
|
|
|
strcpy(unknown + uoff, opt);
|
|
|
|
uoff += strlen(opt);
|
|
|
|
unknown[uoff] = ',';
|
|
|
|
uoff++;
|
2012-05-13 08:30:06 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!end) {
|
2014-09-10 00:17:46 +04:00
|
|
|
if (uoff)
|
|
|
|
uoff--;
|
2012-05-13 08:30:06 +04:00
|
|
|
if (unknown)
|
2014-09-10 00:17:46 +04:00
|
|
|
unknown[uoff] = '\0';
|
2012-05-13 08:30:06 +04:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:30 +04:00
|
|
|
static int parse_mnt_opt(char *str, struct mount_info *mi, int *off)
|
2012-05-13 08:30:06 +04:00
|
|
|
{
|
|
|
|
char *istr = str, *end;
|
|
|
|
|
|
|
|
while (1) {
|
|
|
|
end = strchr(str, ' ');
|
|
|
|
if (!end) {
|
2012-11-23 16:43:33 +04:00
|
|
|
pr_err("Error parsing mount options\n");
|
2012-05-13 08:30:06 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-06-27 20:57:30 +04:00
|
|
|
static int parse_mountinfo_ent(char *str, struct mount_info *new)
|
2012-05-13 03:41:56 +04:00
|
|
|
{
|
|
|
|
unsigned int kmaj, kmin;
|
2012-05-13 08:30:06 +04:00
|
|
|
int ret, n;
|
2012-06-27 20:57:29 +04:00
|
|
|
char *opt;
|
2012-08-09 16:27:30 +04:00
|
|
|
char *fstype;
|
2012-05-13 03:41:56 +04:00
|
|
|
|
2014-04-16 14:48:00 +04:00
|
|
|
new->mountpoint = xmalloc(PATH_MAX);
|
|
|
|
if (new->mountpoint == NULL)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
new->mountpoint[0] = '.';
|
|
|
|
ret = sscanf(str, "%i %i %u:%u %ms %s %ms %n",
|
2012-05-13 03:41:56 +04:00
|
|
|
&new->mnt_id, &new->parent_mnt_id,
|
2014-04-16 14:48:00 +04:00
|
|
|
&kmaj, &kmin, &new->root, new->mountpoint + 1,
|
2012-06-27 20:57:29 +04:00
|
|
|
&opt, &n);
|
2014-04-16 14:48:00 +04:00
|
|
|
if (ret != 7) {
|
|
|
|
xfree(new->mountpoint);
|
2012-05-13 03:41:56 +04:00
|
|
|
return -1;
|
2014-04-16 14:48:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
new->mountpoint = xrealloc(new->mountpoint, strlen(new->mountpoint) + 1);
|
2012-05-13 03:41:56 +04:00
|
|
|
|
|
|
|
new->s_dev = MKKDEV(kmaj, kmin);
|
2012-05-13 08:30:06 +04:00
|
|
|
new->flags = 0;
|
|
|
|
if (parse_mnt_flags(opt, &new->flags))
|
|
|
|
return -1;
|
|
|
|
|
2012-06-27 20:57:29 +04:00
|
|
|
free(opt); /* after %ms scanf */
|
|
|
|
|
2012-05-13 08:30:06 +04:00
|
|
|
str += n;
|
|
|
|
if (parse_mnt_opt(str, new, &n))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
str += n;
|
2012-08-09 16:27:30 +04:00
|
|
|
ret = sscanf(str, "%ms %ms %ms", &fstype, &new->source, &opt);
|
2012-05-13 08:30:06 +04:00
|
|
|
if (ret != 3)
|
|
|
|
return -1;
|
|
|
|
|
2013-12-03 22:49:30 +04:00
|
|
|
ret = -1;
|
2012-08-09 16:27:30 +04:00
|
|
|
new->fstype = find_fstype_by_name(fstype);
|
|
|
|
|
2013-04-05 01:44:27 +04:00
|
|
|
new->options = xmalloc(strlen(opt) + 1);
|
2012-06-27 20:57:29 +04:00
|
|
|
if (!new->options)
|
2013-11-29 13:34:29 +04:00
|
|
|
goto err;
|
2012-06-27 20:57:29 +04:00
|
|
|
|
2012-05-13 08:30:06 +04:00
|
|
|
if (parse_sb_opt(opt, &new->flags, new->options))
|
2013-11-29 13:34:29 +04:00
|
|
|
goto err;
|
2012-05-13 08:30:06 +04:00
|
|
|
|
2013-11-29 13:34:29 +04:00
|
|
|
ret = 0;
|
|
|
|
err:
|
2012-06-27 20:57:29 +04:00
|
|
|
free(opt);
|
2013-11-29 13:34:29 +04:00
|
|
|
free(fstype);
|
|
|
|
return ret;
|
2012-05-13 03:41:56 +04:00
|
|
|
}
|
|
|
|
|
2014-04-21 18:23:36 +04:00
|
|
|
struct mount_info *parse_mountinfo(pid_t pid, struct ns_id *nsid)
|
2012-05-04 13:38:00 +04:00
|
|
|
{
|
2012-06-27 20:57:30 +04:00
|
|
|
struct mount_info *list = NULL;
|
2012-05-13 00:07:11 +04:00
|
|
|
FILE *f;
|
2012-09-19 15:02:25 +04:00
|
|
|
char str[1024];
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2014-08-12 14:35:25 +04:00
|
|
|
f = fopen_proc(pid, "mountinfo");
|
2012-05-04 13:38:00 +04:00
|
|
|
if (!f) {
|
|
|
|
pr_perror("Can't open %d mountinfo", pid);
|
2012-05-13 00:07:11 +04:00
|
|
|
return NULL;
|
2012-05-04 13:38:00 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(str, sizeof(str), f)) {
|
2012-06-27 20:57:30 +04:00
|
|
|
struct mount_info *new;
|
2012-05-04 13:38:00 +04:00
|
|
|
int ret;
|
|
|
|
|
2013-04-10 01:26:59 +04:00
|
|
|
new = mnt_entry_alloc();
|
2012-05-13 00:07:11 +04:00
|
|
|
if (!new)
|
|
|
|
goto err;
|
2012-05-04 13:38:00 +04:00
|
|
|
|
2014-04-21 18:23:36 +04:00
|
|
|
new->nsid = nsid;
|
|
|
|
|
2013-04-10 01:26:59 +04:00
|
|
|
new->next = list;
|
|
|
|
list = new;
|
2012-06-27 20:57:34 +04:00
|
|
|
|
2012-05-13 03:41:56 +04:00
|
|
|
ret = parse_mountinfo_ent(str, new);
|
|
|
|
if (ret < 0) {
|
2012-05-04 13:38:00 +04:00
|
|
|
pr_err("Bad format in %d mountinfo\n", pid);
|
2012-05-13 00:07:11 +04:00
|
|
|
goto err;
|
2012-05-04 13:38:00 +04:00
|
|
|
}
|
|
|
|
|
2014-10-02 15:18:00 +04:00
|
|
|
pr_info("\ttype %s source %s mnt_id %#x s_dev %#x %s @ %s flags %#x options %s\n",
|
2013-12-04 19:51:25 +04:00
|
|
|
new->fstype->name, new->source,
|
2014-10-02 15:18:00 +04:00
|
|
|
new->mnt_id, new->s_dev, new->root, new->mountpoint,
|
2012-05-13 08:30:06 +04:00
|
|
|
new->flags, new->options);
|
2013-12-04 19:15:11 +04:00
|
|
|
|
2013-12-04 19:51:25 +04:00
|
|
|
if (new->fstype->parse) {
|
|
|
|
ret = new->fstype->parse(new);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Failed to parse FS specific data on %s\n",
|
|
|
|
new->mountpoint);
|
|
|
|
goto err;
|
|
|
|
}
|
2013-12-04 19:15:11 +04:00
|
|
|
}
|
2012-05-04 13:38:00 +04:00
|
|
|
}
|
|
|
|
out:
|
2012-05-13 00:07:11 +04:00
|
|
|
fclose(f);
|
|
|
|
return list;
|
|
|
|
|
|
|
|
err:
|
|
|
|
while (list) {
|
2012-06-27 20:57:30 +04:00
|
|
|
struct mount_info *next = list->next;
|
2013-04-10 01:26:59 +04:00
|
|
|
mnt_entry_free(list);
|
2012-05-13 00:07:11 +04:00
|
|
|
list = next;
|
|
|
|
}
|
|
|
|
goto out;
|
2012-05-04 13:38:00 +04:00
|
|
|
}
|
2012-07-11 09:22:38 +04:00
|
|
|
|
2012-07-11 09:35:36 +04:00
|
|
|
static char nybble(const char n)
|
|
|
|
{
|
2012-08-11 22:03:11 +04:00
|
|
|
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);
|
2012-08-11 21:36:03 +04:00
|
|
|
return 0;
|
2012-07-11 09:35:36 +04:00
|
|
|
}
|
|
|
|
|
2013-01-16 02:22:06 +04:00
|
|
|
static int alloc_fhandle(FhEntry *fh)
|
|
|
|
{
|
|
|
|
fh->n_handle = FH_ENTRY_SIZES__min_entries;
|
|
|
|
fh->handle = xmalloc(pb_repeated_size(fh, handle));
|
|
|
|
|
|
|
|
return fh->handle == NULL ? -1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void free_fhandle(FhEntry *fh)
|
|
|
|
{
|
2014-01-31 08:50:28 +04:00
|
|
|
if (fh->handle)
|
|
|
|
xfree(fh->handle);
|
2013-01-16 02:22:06 +04:00
|
|
|
}
|
|
|
|
|
2014-08-25 23:19:54 +04:00
|
|
|
void free_inotify_wd_entry(union fdinfo_entries *e)
|
|
|
|
{
|
|
|
|
free_fhandle(e->ify.e.f_handle);
|
|
|
|
xfree(e);
|
|
|
|
}
|
|
|
|
|
2014-08-25 23:19:56 +04:00
|
|
|
void free_fanotify_mark_entry(union fdinfo_entries *e)
|
|
|
|
{
|
|
|
|
if (e->ffy.e.ie)
|
|
|
|
free_fhandle(e->ffy.ie.f_handle);
|
|
|
|
xfree(e);
|
|
|
|
}
|
|
|
|
|
2014-08-25 23:19:58 +04:00
|
|
|
void free_event_poll_entry(union fdinfo_entries *e)
|
|
|
|
{
|
|
|
|
xfree(e);
|
|
|
|
}
|
|
|
|
|
2012-07-17 07:25:42 +04:00
|
|
|
static void parse_fhandle_encoded(char *tok, FhEntry *fh)
|
2012-07-11 09:35:36 +04:00
|
|
|
{
|
2012-07-17 07:25:42 +04:00
|
|
|
char *d = (char *)fh->handle;
|
2012-07-11 09:35:36 +04:00
|
|
|
int i = 0;
|
|
|
|
|
2012-07-17 07:25:42 +04:00
|
|
|
memzero(d, pb_repeated_size(fh, handle));
|
2012-07-11 09:35:36 +04:00
|
|
|
|
|
|
|
while (*tok == ' ')
|
|
|
|
tok++;
|
|
|
|
|
|
|
|
while (*tok) {
|
2012-07-17 07:25:42 +04:00
|
|
|
if (i >= pb_repeated_size(fh, handle))
|
2012-07-11 09:35:36 +04:00
|
|
|
break;
|
|
|
|
d[i++] = (nybble(tok[0]) << 4) | nybble(tok[1]);
|
|
|
|
if (tok[1])
|
|
|
|
tok += 2;
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-19 17:31:11 +04:00
|
|
|
static int parse_timerfd(struct bfd *f, char *str, TimerfdEntry *tfy)
|
2014-06-30 21:58:05 +04:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Format is
|
|
|
|
* clockid: 0
|
|
|
|
* ticks: 0
|
|
|
|
* settime flags: 01
|
|
|
|
* it_value: (0, 49406829)
|
|
|
|
* it_interval: (1, 0)
|
|
|
|
*/
|
2014-09-30 11:41:36 +04:00
|
|
|
if (sscanf(str, "clockid: %d", &tfy->clockid) != 1)
|
2014-06-30 21:58:05 +04:00
|
|
|
goto parse_err;
|
|
|
|
|
2014-09-19 17:31:11 +04:00
|
|
|
str = breadline(f);
|
2014-10-01 11:21:35 +04:00
|
|
|
if (IS_ERR_OR_NULL(str))
|
2014-06-30 21:58:05 +04:00
|
|
|
goto nodata;
|
2014-09-19 17:31:11 +04:00
|
|
|
if (sscanf(str, "ticks: %llu", (unsigned long long *)&tfy->ticks) != 1)
|
2014-06-30 21:58:05 +04:00
|
|
|
goto parse_err;
|
|
|
|
|
2014-09-19 17:31:11 +04:00
|
|
|
str = breadline(f);
|
2014-10-01 11:21:35 +04:00
|
|
|
if (IS_ERR_OR_NULL(str))
|
2014-06-30 21:58:05 +04:00
|
|
|
goto nodata;
|
2014-09-19 17:31:11 +04:00
|
|
|
if (sscanf(str, "settime flags: 0%o", &tfy->settime_flags) != 1)
|
2014-06-30 21:58:05 +04:00
|
|
|
goto parse_err;
|
|
|
|
|
2014-09-19 17:31:11 +04:00
|
|
|
str = breadline(f);
|
2014-10-01 11:21:35 +04:00
|
|
|
if (IS_ERR_OR_NULL(str))
|
2014-06-30 21:58:05 +04:00
|
|
|
goto nodata;
|
2014-09-19 17:31:11 +04:00
|
|
|
if (sscanf(str, "it_value: (%llu, %llu)",
|
2014-06-30 21:58:05 +04:00
|
|
|
(unsigned long long *)&tfy->vsec,
|
|
|
|
(unsigned long long *)&tfy->vnsec) != 2)
|
|
|
|
goto parse_err;
|
|
|
|
|
2014-09-19 17:31:11 +04:00
|
|
|
str = breadline(f);
|
2014-10-01 11:21:35 +04:00
|
|
|
if (IS_ERR_OR_NULL(str))
|
2014-06-30 21:58:05 +04:00
|
|
|
goto nodata;
|
2014-09-19 17:31:11 +04:00
|
|
|
if (sscanf(str, "it_interval: (%llu, %llu)",
|
2014-06-30 21:58:05 +04:00
|
|
|
(unsigned long long *)&tfy->isec,
|
|
|
|
(unsigned long long *)&tfy->insec) != 2)
|
|
|
|
goto parse_err;
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
parse_err:
|
|
|
|
return -1;
|
|
|
|
nodata:
|
|
|
|
pr_err("No data left in proc file while parsing timerfd\n");
|
|
|
|
goto parse_err;
|
|
|
|
}
|
|
|
|
|
2012-07-11 09:22:38 +04:00
|
|
|
#define fdinfo_field(str, field) !strncmp(str, field":", sizeof(field))
|
|
|
|
|
2014-09-17 00:38:00 +04:00
|
|
|
static int parse_fdinfo_pid_s(int pid, int fd, int type,
|
2012-07-11 09:22:38 +04:00
|
|
|
int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
|
|
|
|
{
|
2014-09-19 17:31:11 +04:00
|
|
|
struct bfd f;
|
|
|
|
char *str;
|
2012-08-01 07:41:19 +04:00
|
|
|
bool entry_met = false;
|
2013-04-12 02:17:00 +04:00
|
|
|
int ret = -1;
|
2012-07-11 09:22:38 +04:00
|
|
|
|
2014-09-19 17:31:11 +04:00
|
|
|
f.fd = open_proc(pid, "fdinfo/%d", fd);
|
|
|
|
if (f.fd < 0) {
|
|
|
|
pr_perror("Can't open fdinfo/%d to parse", fd);
|
2012-07-11 09:22:38 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-10-29 15:44:00 +04:00
|
|
|
if (bfdopen(&f, O_RDONLY))
|
2014-09-19 17:31:11 +04:00
|
|
|
return -1;
|
|
|
|
|
|
|
|
while (1) {
|
2012-07-11 09:22:38 +04:00
|
|
|
union fdinfo_entries entry;
|
|
|
|
|
2014-09-19 17:31:11 +04:00
|
|
|
str = breadline(&f);
|
|
|
|
if (!str)
|
|
|
|
break;
|
2014-10-01 11:21:35 +04:00
|
|
|
if (IS_ERR(str))
|
2014-09-19 17:31:11 +04:00
|
|
|
goto out;
|
|
|
|
|
2014-04-09 03:34:53 +04:00
|
|
|
if (fdinfo_field(str, "pos") ||
|
2014-04-09 03:34:54 +04:00
|
|
|
fdinfo_field(str, "flags") ||
|
|
|
|
fdinfo_field(str, "mnt_id")) {
|
2014-04-09 03:34:53 +04:00
|
|
|
unsigned long long val;
|
|
|
|
struct fdinfo_common *fdinfo = arg;
|
|
|
|
|
|
|
|
if (type != FD_TYPES__UND)
|
|
|
|
continue;
|
|
|
|
ret = sscanf(str, "%*s %lli", &val);
|
|
|
|
if (ret != 1)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
if (fdinfo_field(str, "pos"))
|
|
|
|
fdinfo->pos = val;
|
|
|
|
else if (fdinfo_field(str, "flags"))
|
|
|
|
fdinfo->flags = val;
|
2014-04-09 03:34:54 +04:00
|
|
|
else if (fdinfo_field(str, "mnt_id"))
|
|
|
|
fdinfo->mnt_id = val;
|
2014-04-09 03:34:53 +04:00
|
|
|
|
|
|
|
entry_met = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (type == FD_TYPES__UND)
|
2012-07-11 09:22:38 +04:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (fdinfo_field(str, "eventfd-count")) {
|
2012-07-17 07:24:54 +04:00
|
|
|
eventfd_file_entry__init(&entry.efd);
|
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
if (type != FD_TYPES__EVENTFD)
|
2012-07-11 09:22:38 +04:00
|
|
|
goto parse_err;
|
2013-01-16 19:20:08 +04:00
|
|
|
ret = sscanf(str, "eventfd-count: %"PRIx64,
|
2012-07-11 09:22:38 +04:00
|
|
|
&entry.efd.counter);
|
|
|
|
if (ret != 1)
|
|
|
|
goto parse_err;
|
|
|
|
ret = cb(&entry, arg);
|
|
|
|
if (ret)
|
2014-06-30 21:58:05 +04:00
|
|
|
goto out;
|
|
|
|
|
|
|
|
entry_met = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (fdinfo_field(str, "clockid")) {
|
|
|
|
timerfd_entry__init(&entry.tfy);
|
|
|
|
|
|
|
|
if (type != FD_TYPES__TIMERFD)
|
|
|
|
goto parse_err;
|
2014-09-19 17:31:11 +04:00
|
|
|
ret = parse_timerfd(&f, str, &entry.tfy);
|
2014-06-30 21:58:05 +04:00
|
|
|
if (ret)
|
|
|
|
goto parse_err;
|
|
|
|
ret = cb(&entry, arg);
|
|
|
|
if (ret)
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2012-08-01 07:41:19 +04:00
|
|
|
|
|
|
|
entry_met = true;
|
2012-07-11 09:22:38 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (fdinfo_field(str, "tfd")) {
|
2014-08-25 23:19:58 +04:00
|
|
|
union fdinfo_entries *e;
|
2012-07-17 07:25:40 +04:00
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
if (type != FD_TYPES__EVENTPOLL)
|
2012-07-11 09:22:38 +04:00
|
|
|
goto parse_err;
|
2014-08-25 23:19:58 +04:00
|
|
|
|
|
|
|
e = xmalloc(sizeof(union fdinfo_entries));
|
|
|
|
if (!e)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
eventpoll_tfd_entry__init(&e->epl.e);
|
|
|
|
|
2013-01-16 19:20:08 +04:00
|
|
|
ret = sscanf(str, "tfd: %d events: %x data: %"PRIx64,
|
2014-08-25 23:19:58 +04:00
|
|
|
&e->epl.e.tfd, &e->epl.e.events, &e->epl.e.data);
|
|
|
|
if (ret != 3) {
|
|
|
|
free_event_poll_entry(e);
|
2012-07-11 09:22:38 +04:00
|
|
|
goto parse_err;
|
2014-08-25 23:19:58 +04:00
|
|
|
}
|
|
|
|
ret = cb(e, arg);
|
2012-07-11 09:22:38 +04:00
|
|
|
if (ret)
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2012-08-01 07:41:19 +04:00
|
|
|
|
|
|
|
entry_met = true;
|
2012-07-11 09:22:38 +04:00
|
|
|
continue;
|
|
|
|
}
|
2012-08-02 12:25:18 +04:00
|
|
|
if (fdinfo_field(str, "sigmask")) {
|
|
|
|
signalfd_entry__init(&entry.sfd);
|
|
|
|
|
|
|
|
if (type != FD_TYPES__SIGNALFD)
|
|
|
|
goto parse_err;
|
|
|
|
ret = sscanf(str, "sigmask: %Lx",
|
|
|
|
(unsigned long long *)&entry.sfd.sigmask);
|
|
|
|
if (ret != 1)
|
|
|
|
goto parse_err;
|
|
|
|
ret = cb(&entry, arg);
|
|
|
|
if (ret)
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2012-08-02 12:25:18 +04:00
|
|
|
|
|
|
|
entry_met = true;
|
|
|
|
continue;
|
|
|
|
}
|
2013-01-14 20:48:10 +04:00
|
|
|
if (fdinfo_field(str, "fanotify flags")) {
|
|
|
|
struct fsnotify_params *p = arg;
|
|
|
|
|
|
|
|
if (type != FD_TYPES__FANOTIFY)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
ret = sscanf(str, "fanotify flags:%x event-flags:%x",
|
|
|
|
&p->faflags, &p->evflags);
|
|
|
|
if (ret != 2)
|
|
|
|
goto parse_err;
|
|
|
|
entry_met = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (fdinfo_field(str, "fanotify ino")) {
|
2014-08-25 23:19:56 +04:00
|
|
|
union fdinfo_entries *e;
|
2014-08-06 18:03:00 +04:00
|
|
|
int hoff = 0;
|
2013-01-14 20:48:10 +04:00
|
|
|
|
|
|
|
if (type != FD_TYPES__FANOTIFY)
|
|
|
|
goto parse_err;
|
|
|
|
|
2014-08-25 23:19:56 +04:00
|
|
|
e = xmalloc(sizeof(*e));
|
|
|
|
if (!e)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
fanotify_mark_entry__init(&e->ffy.e);
|
|
|
|
fanotify_inode_mark_entry__init(&e->ffy.ie);
|
|
|
|
fh_entry__init(&e->ffy.f_handle);
|
|
|
|
e->ffy.e.ie = &e->ffy.ie;
|
|
|
|
e->ffy.ie.f_handle = &e->ffy.f_handle;
|
2013-01-14 20:48:10 +04:00
|
|
|
|
|
|
|
ret = sscanf(str,
|
2013-01-16 19:20:08 +04:00
|
|
|
"fanotify ino:%"PRIx64" sdev:%x mflags:%x mask:%x ignored_mask:%x "
|
2013-01-14 20:48:10 +04:00
|
|
|
"fhandle-bytes:%x fhandle-type:%x f_handle: %n",
|
2014-08-25 23:19:56 +04:00
|
|
|
&e->ffy.ie.i_ino, &e->ffy.e.s_dev,
|
|
|
|
&e->ffy.e.mflags, &e->ffy.e.mask, &e->ffy.e.ignored_mask,
|
|
|
|
&e->ffy.f_handle.bytes, &e->ffy.f_handle.type,
|
2013-01-14 20:48:10 +04:00
|
|
|
&hoff);
|
2014-08-25 23:19:56 +04:00
|
|
|
if (ret != 7 || hoff == 0) {
|
|
|
|
free_fanotify_mark_entry(e);
|
2013-01-14 20:48:10 +04:00
|
|
|
goto parse_err;
|
2014-08-25 23:19:56 +04:00
|
|
|
}
|
2013-01-14 20:48:10 +04:00
|
|
|
|
2014-08-25 23:19:56 +04:00
|
|
|
if (alloc_fhandle(&e->ffy.f_handle)) {
|
|
|
|
free_fanotify_mark_entry(e);
|
2013-04-12 02:17:00 +04:00
|
|
|
ret = -1;
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2013-04-12 02:17:00 +04:00
|
|
|
}
|
2014-08-25 23:19:56 +04:00
|
|
|
parse_fhandle_encoded(str + hoff, &e->ffy.f_handle);
|
2013-01-14 20:48:10 +04:00
|
|
|
|
2014-08-25 23:19:56 +04:00
|
|
|
e->ffy.e.type = MARK_TYPE__INODE;
|
|
|
|
ret = cb(e, arg);
|
2013-01-14 20:48:10 +04:00
|
|
|
|
|
|
|
|
|
|
|
if (ret)
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2013-01-14 20:48:10 +04:00
|
|
|
|
|
|
|
entry_met = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (fdinfo_field(str, "fanotify mnt_id")) {
|
2014-08-25 23:19:56 +04:00
|
|
|
union fdinfo_entries *e;
|
2013-01-15 23:17:57 +04:00
|
|
|
|
2013-01-14 20:48:10 +04:00
|
|
|
if (type != FD_TYPES__FANOTIFY)
|
|
|
|
goto parse_err;
|
|
|
|
|
2014-08-25 23:19:56 +04:00
|
|
|
e = xmalloc(sizeof(*e));
|
|
|
|
if (!e)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
fanotify_mark_entry__init(&e->ffy.e);
|
|
|
|
fanotify_mount_mark_entry__init(&e->ffy.me);
|
|
|
|
e->ffy.e.me = &e->ffy.me;
|
2013-01-14 20:48:10 +04:00
|
|
|
|
|
|
|
ret = sscanf(str,
|
|
|
|
"fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x",
|
2014-08-25 23:19:56 +04:00
|
|
|
&e->ffy.e.me->mnt_id, &e->ffy.e.mflags,
|
|
|
|
&e->ffy.e.mask, &e->ffy.e.ignored_mask);
|
2013-01-14 20:48:10 +04:00
|
|
|
if (ret != 4)
|
|
|
|
goto parse_err;
|
|
|
|
|
2014-08-25 23:19:56 +04:00
|
|
|
e->ffy.e.type = MARK_TYPE__MOUNT;
|
|
|
|
ret = cb(e, arg);
|
2013-01-14 20:48:10 +04:00
|
|
|
if (ret)
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2013-01-14 20:48:10 +04:00
|
|
|
|
|
|
|
entry_met = true;
|
|
|
|
continue;
|
|
|
|
}
|
2012-07-11 09:35:36 +04:00
|
|
|
if (fdinfo_field(str, "inotify wd")) {
|
2014-08-25 23:19:54 +04:00
|
|
|
InotifyWdEntry *ify;
|
|
|
|
union fdinfo_entries *e;
|
2012-07-11 09:35:36 +04:00
|
|
|
int hoff;
|
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
if (type != FD_TYPES__INOTIFY)
|
2012-07-11 09:35:36 +04:00
|
|
|
goto parse_err;
|
2014-08-25 23:19:54 +04:00
|
|
|
|
|
|
|
e = xmalloc(sizeof(*e));
|
|
|
|
if (!e)
|
|
|
|
goto parse_err;
|
|
|
|
ify = &e->ify.e;
|
|
|
|
|
|
|
|
inotify_wd_entry__init(ify);
|
|
|
|
ify->f_handle = &e->ify.f_handle;
|
|
|
|
fh_entry__init(ify->f_handle);
|
|
|
|
|
2012-07-11 09:35:36 +04:00
|
|
|
ret = sscanf(str,
|
2013-01-16 19:20:08 +04:00
|
|
|
"inotify wd:%x ino:%"PRIx64" sdev:%x "
|
2012-12-04 19:28:45 +04:00
|
|
|
"mask:%x ignored_mask:%x "
|
|
|
|
"fhandle-bytes:%x fhandle-type:%x "
|
2012-07-11 09:35:36 +04:00
|
|
|
"f_handle: %n",
|
2014-08-25 23:19:54 +04:00
|
|
|
&ify->wd, &ify->i_ino, &ify->s_dev,
|
|
|
|
&ify->mask, &ify->ignored_mask,
|
|
|
|
&ify->f_handle->bytes, &ify->f_handle->type,
|
2012-07-11 09:35:36 +04:00
|
|
|
&hoff);
|
2014-08-25 23:19:54 +04:00
|
|
|
if (ret != 7) {
|
|
|
|
free_inotify_wd_entry(e);
|
2012-07-11 09:35:36 +04:00
|
|
|
goto parse_err;
|
2014-08-25 23:19:54 +04:00
|
|
|
}
|
2012-07-17 07:25:42 +04:00
|
|
|
|
2014-08-25 23:19:54 +04:00
|
|
|
if (alloc_fhandle(ify->f_handle)) {
|
|
|
|
free_inotify_wd_entry(e);
|
2013-04-12 02:17:00 +04:00
|
|
|
ret = -1;
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2013-04-12 02:17:00 +04:00
|
|
|
}
|
|
|
|
|
2014-08-25 23:19:54 +04:00
|
|
|
parse_fhandle_encoded(str + hoff, ify->f_handle);
|
2012-07-11 09:35:36 +04:00
|
|
|
|
2014-08-25 23:19:54 +04:00
|
|
|
ret = cb(e, arg);
|
2012-07-17 07:25:42 +04:00
|
|
|
|
2012-07-11 09:35:36 +04:00
|
|
|
if (ret)
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2012-08-01 07:41:19 +04:00
|
|
|
|
|
|
|
entry_met = true;
|
2012-07-11 09:35:36 +04:00
|
|
|
continue;
|
|
|
|
}
|
2012-07-11 09:22:38 +04:00
|
|
|
}
|
|
|
|
|
2013-04-29 16:10:50 +04:00
|
|
|
ret = 0;
|
2012-09-12 17:55:15 +04:00
|
|
|
if (entry_met)
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2012-09-12 17:55:15 +04:00
|
|
|
/*
|
2012-11-28 21:49:57 +04:00
|
|
|
* An eventpoll/inotify file may have no target fds set thus
|
2012-09-12 17:55:15 +04:00
|
|
|
* resulting in no tfd: lines in proc. This is normal.
|
|
|
|
*/
|
2012-11-28 21:49:57 +04:00
|
|
|
if (type == FD_TYPES__EVENTPOLL || type == FD_TYPES__INOTIFY)
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2012-07-11 09:22:38 +04:00
|
|
|
|
2012-09-12 17:55:15 +04:00
|
|
|
pr_err("No records of type %d found in fdinfo file\n", type);
|
2012-07-11 09:22:38 +04:00
|
|
|
parse_err:
|
2013-04-12 02:17:00 +04:00
|
|
|
ret = -1;
|
2013-05-02 22:44:24 +04:00
|
|
|
pr_perror("%s: error parsing [%s] for %d", __func__, str, type);
|
2013-04-29 16:10:50 +04:00
|
|
|
out:
|
2014-09-19 17:31:11 +04:00
|
|
|
bclose(&f);
|
2013-04-12 02:17:00 +04:00
|
|
|
return ret;
|
2012-07-11 09:22:38 +04:00
|
|
|
}
|
2013-01-17 16:09:32 +08:00
|
|
|
|
2014-01-30 13:41:53 +04:00
|
|
|
int parse_fdinfo_pid(int pid, int fd, int type,
|
|
|
|
int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
|
|
|
|
{
|
2014-09-17 00:38:00 +04:00
|
|
|
return parse_fdinfo_pid_s(pid, fd, type, cb, arg);
|
2014-01-30 13:41:53 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
int parse_fdinfo(int fd, int type,
|
|
|
|
int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
|
|
|
|
{
|
2014-09-17 00:38:00 +04:00
|
|
|
return parse_fdinfo_pid_s(PROC_SELF, fd, type, cb, arg);
|
2014-01-30 13:41:53 +04:00
|
|
|
}
|
|
|
|
|
2014-09-23 20:32:00 +04:00
|
|
|
int get_fd_mntid(int fd, int *mnt_id)
|
|
|
|
{
|
|
|
|
struct fdinfo_common fdinfo = { .mnt_id = -1};
|
|
|
|
|
|
|
|
if (parse_fdinfo(fd, FD_TYPES__UND, NULL, &fdinfo))
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
*mnt_id = fdinfo.mnt_id;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-01-17 16:09:32 +08:00
|
|
|
static int parse_file_lock_buf(char *buf, struct file_lock *fl,
|
|
|
|
bool is_blocked)
|
|
|
|
{
|
|
|
|
int num;
|
2014-09-02 17:04:11 +04:00
|
|
|
char fl_flag[10], fl_type[15], fl_option[10];
|
2013-01-17 16:09:32 +08:00
|
|
|
|
|
|
|
if (is_blocked) {
|
2013-10-11 18:37:14 +04:00
|
|
|
num = sscanf(buf, "%lld: -> %s %s %s %d %x:%x:%ld %lld %s",
|
2014-09-02 17:04:11 +04:00
|
|
|
&fl->fl_id, fl_flag, fl_type, fl_option,
|
2013-01-17 16:09:32 +08:00
|
|
|
&fl->fl_owner, &fl->maj, &fl->min, &fl->i_no,
|
|
|
|
&fl->start, fl->end);
|
|
|
|
} else {
|
2013-10-11 18:37:14 +04:00
|
|
|
num = sscanf(buf, "%lld:%s %s %s %d %x:%x:%ld %lld %s",
|
2014-09-02 17:04:11 +04:00
|
|
|
&fl->fl_id, fl_flag, fl_type, fl_option,
|
2013-01-17 16:09:32 +08:00
|
|
|
&fl->fl_owner, &fl->maj, &fl->min, &fl->i_no,
|
|
|
|
&fl->start, fl->end);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (num < 10) {
|
2013-10-11 18:37:14 +04:00
|
|
|
pr_err("Invalid file lock info (%d): %s", num, buf);
|
2013-01-17 16:09:32 +08:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2014-08-26 21:30:15 +04:00
|
|
|
if (!strcmp(fl_flag, "POSIX"))
|
|
|
|
fl->fl_kind = FL_POSIX;
|
|
|
|
else if (!strcmp(fl_flag, "FLOCK"))
|
|
|
|
fl->fl_kind = FL_FLOCK;
|
|
|
|
else
|
|
|
|
fl->fl_kind = FL_UNKNOWN;
|
|
|
|
|
2014-09-02 17:04:11 +04:00
|
|
|
if (!strcmp(fl_type, "MSNFS")) {
|
|
|
|
fl->fl_ltype |= LOCK_MAND;
|
|
|
|
|
|
|
|
if (!strcmp(fl_option, "READ")) {
|
|
|
|
fl->fl_ltype |= LOCK_READ;
|
|
|
|
} else if (!strcmp(fl_option, "RW")) {
|
|
|
|
fl->fl_ltype |= LOCK_RW;
|
|
|
|
} else if (!strcmp(fl_option, "WRITE")) {
|
|
|
|
fl->fl_ltype |= LOCK_WRITE;
|
|
|
|
} else {
|
|
|
|
pr_err("Unknown lock option!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (!strcmp(fl_option, "UNLCK")) {
|
|
|
|
fl->fl_ltype |= F_UNLCK;
|
|
|
|
} else if (!strcmp(fl_option, "WRITE")) {
|
|
|
|
fl->fl_ltype |= F_WRLCK;
|
|
|
|
} else if (!strcmp(fl_option, "READ")) {
|
|
|
|
fl->fl_ltype |= F_RDLCK;
|
|
|
|
} else {
|
|
|
|
pr_err("Unknown lock option!\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-17 16:09:32 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int parse_file_locks(void)
|
|
|
|
{
|
|
|
|
struct file_lock *fl;
|
|
|
|
|
|
|
|
FILE *fl_locks;
|
|
|
|
int ret = 0;
|
2013-02-06 21:05:53 +04:00
|
|
|
bool is_blocked;
|
2013-01-17 16:09:32 +08:00
|
|
|
|
2014-09-17 16:41:00 +04:00
|
|
|
fl_locks = fopen_proc(PROC_GEN, "locks");
|
2013-01-17 16:09:32 +08:00
|
|
|
if (!fl_locks) {
|
|
|
|
pr_perror("Can't open file locks file!");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(buf, BUF_SIZE, fl_locks)) {
|
2013-02-06 21:05:53 +04:00
|
|
|
is_blocked = strstr(buf, "->") != NULL;
|
2013-01-17 16:09:32 +08:00
|
|
|
|
|
|
|
fl = alloc_file_lock();
|
|
|
|
if (!fl) {
|
|
|
|
pr_perror("Alloc file lock failed!");
|
|
|
|
ret = -1;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parse_file_lock_buf(buf, fl, is_blocked)) {
|
|
|
|
xfree(fl);
|
|
|
|
ret = -1;
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-11-05 17:20:00 +04:00
|
|
|
pr_info("lockinfo: %lld:%d %x %d %02x:%02x:%ld %lld %s\n",
|
|
|
|
fl->fl_id, fl->fl_kind, fl->fl_ltype,
|
|
|
|
fl->fl_owner, fl->maj, fl->min, fl->i_no,
|
|
|
|
fl->start, fl->end);
|
|
|
|
|
|
|
|
|
2014-09-02 19:52:41 +04:00
|
|
|
if (fl->fl_kind == FL_UNKNOWN) {
|
|
|
|
pr_err("Unknown file lock!\n");
|
|
|
|
ret = -1;
|
|
|
|
xfree(fl);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2014-11-05 17:20:00 +04:00
|
|
|
if (is_blocked) {
|
2013-01-17 16:09:32 +08:00
|
|
|
/*
|
2014-11-05 17:20:00 +04:00
|
|
|
* All target processes are stopped in this moment and
|
|
|
|
* can't wait any locks.
|
2013-01-17 16:09:32 +08:00
|
|
|
*/
|
2014-11-05 17:20:00 +04:00
|
|
|
pr_debug("Skip blocked processes\n");
|
2013-01-17 16:09:32 +08:00
|
|
|
xfree(fl);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-11-05 17:20:00 +04:00
|
|
|
if ((fl->fl_kind == FL_POSIX) &&
|
|
|
|
!pid_in_pstree(fl->fl_owner)) {
|
2013-01-17 16:09:32 +08:00
|
|
|
/*
|
2014-11-05 17:20:00 +04:00
|
|
|
* We only care about tasks which are taken
|
|
|
|
* into dump, so we only collect file locks
|
|
|
|
* belong to these tasks.
|
2013-01-17 16:09:32 +08:00
|
|
|
*/
|
2013-05-16 21:00:42 +08:00
|
|
|
xfree(fl);
|
2014-11-05 17:20:00 +04:00
|
|
|
continue;
|
2013-01-17 16:09:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
list_add_tail(&fl->list, &file_lock_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
err:
|
|
|
|
fclose(fl_locks);
|
|
|
|
return ret;
|
|
|
|
}
|
2013-06-27 23:32:20 +04:00
|
|
|
|
2014-04-15 21:59:21 +04:00
|
|
|
void free_posix_timers(struct proc_posix_timers_stat *st)
|
|
|
|
{
|
|
|
|
while (!list_empty(&st->timers)) {
|
|
|
|
struct proc_posix_timer *timer;
|
|
|
|
timer = list_first_entry(&st->timers, struct proc_posix_timer, list);
|
|
|
|
list_del(&timer->list);
|
|
|
|
xfree(timer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-27 23:32:20 +04:00
|
|
|
int parse_posix_timers(pid_t pid, struct proc_posix_timers_stat *args)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
int pid_t;
|
|
|
|
|
2014-09-19 17:31:52 +04:00
|
|
|
struct bfd f;
|
|
|
|
char *s;
|
2013-06-27 23:32:20 +04:00
|
|
|
char sigpid[7];
|
|
|
|
char tidpid[4];
|
|
|
|
|
|
|
|
struct proc_posix_timer *timer = NULL;
|
|
|
|
|
|
|
|
INIT_LIST_HEAD(&args->timers);
|
|
|
|
args->timer_n = 0;
|
|
|
|
|
2014-09-19 17:31:52 +04:00
|
|
|
f.fd = open_proc(pid, "timers");
|
|
|
|
if (f.fd < 0) {
|
2013-06-27 23:32:20 +04:00
|
|
|
pr_perror("Can't open posix timers file!");
|
2013-07-02 20:25:14 +04:00
|
|
|
return -1;
|
2013-06-27 23:32:20 +04:00
|
|
|
}
|
|
|
|
|
2014-10-29 15:44:00 +04:00
|
|
|
if (bfdopen(&f, O_RDONLY))
|
2014-09-19 17:31:52 +04:00
|
|
|
return -1;
|
|
|
|
|
2013-06-27 23:32:20 +04:00
|
|
|
while (1) {
|
2014-02-18 16:53:00 +04:00
|
|
|
char pbuf[17]; /* 16 + eol */
|
2014-09-19 17:31:52 +04:00
|
|
|
|
2014-10-31 17:50:50 +03:00
|
|
|
if (!(s = breadline(&f)))
|
|
|
|
goto out;
|
|
|
|
|
2013-06-27 23:32:20 +04:00
|
|
|
timer = xzalloc(sizeof(struct proc_posix_timer));
|
2013-07-03 13:36:08 +04:00
|
|
|
if (timer == NULL)
|
|
|
|
goto err;
|
2013-06-27 23:32:20 +04:00
|
|
|
|
2014-09-19 17:31:52 +04:00
|
|
|
if (sscanf(s, "ID: %ld",
|
|
|
|
&timer->spt.it_id) != 1)
|
2014-10-31 17:50:50 +03:00
|
|
|
goto errf;
|
2014-09-19 17:31:52 +04:00
|
|
|
if (!(s = breadline(&f)))
|
2014-10-31 17:50:50 +03:00
|
|
|
goto errf;
|
2014-09-19 17:31:52 +04:00
|
|
|
if (sscanf(s, "signal: %d/%16s",
|
|
|
|
&timer->spt.si_signo, pbuf) != 2)
|
2014-10-31 17:50:50 +03:00
|
|
|
goto errf;
|
2014-09-19 17:31:52 +04:00
|
|
|
if (!(s = breadline(&f)))
|
2014-10-31 17:50:50 +03:00
|
|
|
goto errf;
|
2014-09-19 17:31:52 +04:00
|
|
|
if (sscanf(s, "notify: %6[a-z]/%3[a-z].%d\n",
|
|
|
|
sigpid, tidpid, &pid_t) != 3)
|
2014-10-31 17:50:50 +03:00
|
|
|
goto errf;
|
2014-09-19 17:31:52 +04:00
|
|
|
if (!(s = breadline(&f)))
|
2014-10-31 17:50:50 +03:00
|
|
|
goto errf;
|
2014-09-19 17:31:52 +04:00
|
|
|
if (sscanf(s, "ClockID: %d\n",
|
|
|
|
&timer->spt.clock_id) != 1)
|
2014-10-31 17:50:50 +03:00
|
|
|
goto errf;
|
2013-06-27 23:32:20 +04:00
|
|
|
|
2014-02-18 16:53:00 +04:00
|
|
|
timer->spt.sival_ptr = NULL;
|
|
|
|
if (sscanf(pbuf, "%p", &timer->spt.sival_ptr) != 1 &&
|
|
|
|
strcmp(pbuf, "(null)")) {
|
|
|
|
pr_err("Unable to parse '%s'\n", pbuf);
|
2014-09-19 17:31:52 +04:00
|
|
|
goto errf;
|
2014-02-18 16:53:00 +04:00
|
|
|
}
|
|
|
|
|
2013-06-27 23:32:20 +04:00
|
|
|
if ( tidpid[0] == 't') {
|
|
|
|
timer->spt.it_sigev_notify = SIGEV_THREAD_ID;
|
|
|
|
} else {
|
|
|
|
switch (sigpid[0]) {
|
|
|
|
case 's' :
|
|
|
|
timer->spt.it_sigev_notify = SIGEV_SIGNAL;
|
|
|
|
break;
|
|
|
|
case 't' :
|
|
|
|
timer->spt.it_sigev_notify = SIGEV_THREAD;
|
|
|
|
break;
|
|
|
|
default :
|
|
|
|
timer->spt.it_sigev_notify = SIGEV_NONE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
list_add(&timer->list, &args->timers);
|
|
|
|
timer = NULL;
|
|
|
|
args->timer_n++;
|
|
|
|
}
|
2014-09-19 17:31:52 +04:00
|
|
|
|
|
|
|
errf:
|
|
|
|
xfree(timer);
|
2013-07-03 13:36:08 +04:00
|
|
|
err:
|
2014-04-15 21:59:21 +04:00
|
|
|
free_posix_timers(args);
|
2013-06-27 23:32:20 +04:00
|
|
|
pr_perror("Parse error in posix timers proc file!");
|
|
|
|
ret = -1;
|
2013-07-03 13:36:08 +04:00
|
|
|
out:
|
2014-09-19 17:31:52 +04:00
|
|
|
bclose(&f);
|
2013-06-27 23:32:20 +04:00
|
|
|
return ret;
|
|
|
|
}
|
2013-09-23 14:33:30 +04:00
|
|
|
|
|
|
|
int parse_threads(int pid, struct pid **_t, int *_n)
|
|
|
|
{
|
|
|
|
struct dirent *de;
|
|
|
|
DIR *dir;
|
|
|
|
struct pid *t = NULL;
|
|
|
|
int nr = 1;
|
|
|
|
|
2013-09-23 14:33:31 +04:00
|
|
|
if (*_t)
|
|
|
|
t = *_t;
|
|
|
|
|
2013-09-23 14:33:30 +04:00
|
|
|
dir = opendir_proc(pid, "task");
|
|
|
|
if (!dir)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
while ((de = readdir(dir))) {
|
|
|
|
struct pid *tmp;
|
|
|
|
|
|
|
|
/* We expect numbers only here */
|
|
|
|
if (de->d_name[0] == '.')
|
|
|
|
continue;
|
|
|
|
|
2013-09-23 14:33:31 +04:00
|
|
|
if (*_t == NULL) {
|
|
|
|
tmp = xrealloc(t, nr * sizeof(struct pid));
|
|
|
|
if (!tmp) {
|
|
|
|
xfree(t);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
t = tmp;
|
|
|
|
t[nr - 1].virt = -1;
|
2013-09-23 14:33:30 +04:00
|
|
|
}
|
|
|
|
t[nr - 1].real = atoi(de->d_name);
|
|
|
|
nr++;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
|
2013-09-23 14:33:31 +04:00
|
|
|
if (*_t == NULL) {
|
|
|
|
*_t = t;
|
|
|
|
*_n = nr - 1;
|
|
|
|
} else
|
|
|
|
BUG_ON(nr - 1 != *_n);
|
2013-09-23 14:33:30 +04:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2014-05-08 16:30:30 +04:00
|
|
|
|
|
|
|
int parse_task_cgroup(int pid, struct list_head *retl, unsigned int *n)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
FILE *f;
|
|
|
|
|
|
|
|
f = fopen_proc(pid, "cgroup");
|
|
|
|
while (fgets(buf, BUF_SIZE, f)) {
|
|
|
|
struct cg_ctl *ncc, *cc;
|
2014-08-07 10:54:00 +04:00
|
|
|
char *name, *path = NULL, *e;
|
2014-05-08 16:30:30 +04:00
|
|
|
|
|
|
|
ret = -1;
|
|
|
|
ncc = xmalloc(sizeof(*cc));
|
|
|
|
if (!ncc)
|
|
|
|
goto err;
|
|
|
|
|
2014-07-15 17:04:11 +04:00
|
|
|
/*
|
|
|
|
* Typical output (':' is a separator here)
|
|
|
|
*
|
|
|
|
* 4:cpu,cpuacct:/
|
|
|
|
* 3:cpuset:/
|
|
|
|
* 2:name=systemd:/user.slice/user-1000.slice/session-1.scope
|
|
|
|
*/
|
2014-09-16 16:03:00 +04:00
|
|
|
name = strchr(buf, ':');
|
2014-08-06 17:13:00 +04:00
|
|
|
if (name)
|
2014-09-16 16:03:00 +04:00
|
|
|
path = strchr(++name, ':');
|
2014-07-15 17:04:11 +04:00
|
|
|
if (!name || !path) {
|
|
|
|
pr_err("Failed parsing cgroup %s\n", buf);
|
|
|
|
xfree(ncc);
|
|
|
|
goto err;
|
|
|
|
}
|
2014-05-08 16:30:30 +04:00
|
|
|
e = strchr(name, '\n');
|
|
|
|
*path++ = '\0';
|
|
|
|
if (e)
|
|
|
|
*e = '\0';
|
|
|
|
|
|
|
|
ncc->name = xstrdup(name);
|
|
|
|
ncc->path = xstrdup(path);
|
2014-06-25 01:28:00 +04:00
|
|
|
if (!ncc->name || !ncc->path) {
|
2014-05-08 16:30:30 +04:00
|
|
|
xfree(ncc->name);
|
|
|
|
xfree(ncc->path);
|
|
|
|
xfree(ncc);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_for_each_entry(cc, retl, l)
|
2014-09-16 00:17:00 +04:00
|
|
|
if (strcmp(cc->name, name) >= 0)
|
2014-05-08 16:30:30 +04:00
|
|
|
break;
|
|
|
|
|
|
|
|
list_add_tail(&ncc->l, &cc->l);
|
|
|
|
(*n)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
err:
|
|
|
|
put_ctls(retl);
|
|
|
|
fclose(f);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void put_ctls(struct list_head *l)
|
|
|
|
{
|
|
|
|
struct cg_ctl *c, *n;
|
|
|
|
|
|
|
|
list_for_each_entry_safe(c, n, l, l) {
|
|
|
|
xfree(c->name);
|
|
|
|
xfree(c->path);
|
|
|
|
xfree(c);
|
|
|
|
}
|
|
|
|
}
|
2014-07-10 17:00:28 +04:00
|
|
|
|
|
|
|
|
|
|
|
/* Parse and create all the real controllers. This does not include things with
|
|
|
|
* the "name=" prefix, e.g. systemd.
|
|
|
|
*/
|
|
|
|
int parse_cgroups(struct list_head *cgroups, unsigned int *n_cgroups)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
char buf[1024], name[1024];
|
|
|
|
int heirarchy, ret = 0;
|
|
|
|
struct cg_controller *cur = NULL;
|
|
|
|
|
2014-09-17 16:41:00 +04:00
|
|
|
f = fopen_proc(PROC_GEN, "cgroups");
|
2014-07-10 17:00:28 +04:00
|
|
|
if (!f) {
|
|
|
|
pr_perror("failed opening /proc/cgroups");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* throw away the header */
|
|
|
|
if (!fgets(buf, 1024, f)) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(buf, 1024, f)) {
|
|
|
|
char *n;
|
|
|
|
char found = 0;
|
|
|
|
|
|
|
|
sscanf(buf, "%s %d", name, &heirarchy);
|
|
|
|
list_for_each_entry(cur, cgroups, l) {
|
|
|
|
if (cur->heirarchy == heirarchy) {
|
|
|
|
void *m;
|
|
|
|
|
|
|
|
found = 1;
|
|
|
|
cur->n_controllers++;
|
|
|
|
m = xrealloc(cur->controllers, sizeof(char *) * cur->n_controllers);
|
|
|
|
if (!m) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
cur->controllers = m;
|
|
|
|
if (!cur->controllers) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = xstrdup(name);
|
|
|
|
if (!n) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
cur->controllers[cur->n_controllers-1] = n;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!found) {
|
|
|
|
struct cg_controller *nc = new_controller(name, heirarchy);
|
|
|
|
if (!nc) {
|
|
|
|
ret = -1;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
list_add_tail(&nc->l, &cur->l);
|
|
|
|
(*n_cgroups)++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
fclose(f);
|
|
|
|
return ret;
|
|
|
|
}
|
2014-08-19 22:31:07 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* AUFS callback function to "fix up" the root pathname.
|
|
|
|
* See sysfs_parse.c for details.
|
|
|
|
*/
|
|
|
|
int aufs_parse(struct mount_info *new)
|
|
|
|
{
|
2014-08-26 14:54:15 -07:00
|
|
|
int ret = 0;
|
2014-08-19 22:31:07 -07:00
|
|
|
|
2014-08-26 14:54:15 -07:00
|
|
|
if (!strcmp(new->mountpoint, "./")) {
|
|
|
|
opts.aufs = true;
|
|
|
|
ret = parse_aufs_branches(new);
|
2014-08-19 22:31:07 -07:00
|
|
|
}
|
|
|
|
|
2014-08-26 14:54:15 -07:00
|
|
|
return ret;
|
2014-08-19 22:31:07 -07:00
|
|
|
}
|