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>
|
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"
|
2012-01-14 01:48:29 +04:00
|
|
|
|
2012-01-13 20:52:35 +04:00
|
|
|
#include "proc_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"
|
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;
|
|
|
|
|
|
|
|
cpuinfo = fopen("/proc/cpuinfo", "r");
|
|
|
|
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
|
|
|
{
|
2013-04-15 13:02:09 +04:00
|
|
|
return kerndat_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];
|
|
|
|
|
|
|
|
if (!mfd)
|
|
|
|
return 0;
|
|
|
|
|
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-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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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-02-14 16:46:15 +04:00
|
|
|
static char smaps_buf[PAGE_SIZE];
|
|
|
|
|
2013-03-01 20:11:51 +04:00
|
|
|
int parse_smaps(pid_t pid, struct vm_area_list *vma_area_list, bool use_map_files)
|
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;
|
2012-05-10 21:07:00 +04:00
|
|
|
FILE *smaps = NULL;
|
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);
|
|
|
|
|
2012-05-10 21:07:00 +04:00
|
|
|
smaps = fopen_proc(pid, "smaps");
|
|
|
|
if (!smaps)
|
2012-01-13 20:52:35 +04:00
|
|
|
goto err;
|
|
|
|
|
2014-02-14 16:46:15 +04:00
|
|
|
setvbuf(smaps, smaps_buf, _IOFBF, sizeof(smaps_buf));
|
|
|
|
|
2012-01-13 20:52:35 +04:00
|
|
|
if (use_map_files) {
|
2012-02-17 01:39:36 +04:00
|
|
|
map_files_dir = opendir_proc(pid, "map_files");
|
2012-02-17 01:39:35 +04:00
|
|
|
if (!map_files_dir) /* old kernel? */
|
2012-01-13 20:52:35 +04:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
2013-05-21 14:37:52 +04:00
|
|
|
while (1) {
|
2012-01-13 20:52:35 +04:00
|
|
|
int num;
|
2012-02-14 20:19:49 +03:00
|
|
|
char file_path[6];
|
2013-05-21 14:37:52 +04:00
|
|
|
bool eof;
|
2012-01-13 20:52:35 +04:00
|
|
|
|
2013-05-21 14:37:52 +04:00
|
|
|
eof = (fgets(buf, BUF_SIZE, smaps) == NULL);
|
|
|
|
|
|
|
|
if (!eof && !is_vma_range_fmt(buf)) {
|
2012-05-10 21:07:00 +04:00
|
|
|
if (!strncmp(buf, "Nonlinear", 9)) {
|
|
|
|
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;
|
2012-10-26 00:16:05 +04:00
|
|
|
} else if (!strncmp(buf, "VmFlags: ", 9)) {
|
|
|
|
BUG_ON(!vma_area);
|
|
|
|
if (parse_vmflags(&buf[9], vma_area))
|
|
|
|
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
|
|
|
|
|
|
|
memset(file_path, 0, 6);
|
2013-10-14 13:20:35 +04:00
|
|
|
num = sscanf(buf, "%lx-%lx %c%c%c%c %lx %x:%x %lu %5s",
|
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) {
|
2012-11-23 16:43:33 +04:00
|
|
|
pr_err("Can't parse: %s\n", buf);
|
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;
|
2013-09-23 17:51:46 +04:00
|
|
|
} else if (strstr(buf, "[vsyscall]") || strstr(buf, "[vectors]")) {
|
2014-02-04 00:08:16 +04:00
|
|
|
vma_area->e->status |= VMA_AREA_VSYSCALL;
|
2012-05-02 15:22:00 +04:00
|
|
|
} else if (strstr(buf, "[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
|
|
|
|
} else if (strstr(buf, "[vvar]")) {
|
|
|
|
#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
|
2012-05-02 15:22:00 +04:00
|
|
|
} else if (strstr(buf, "[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-02-03 00:18:32 +04:00
|
|
|
vma_area->st = prev->st;
|
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-02-03 00:18:32 +04:00
|
|
|
st_buf = vma_area->st = xmalloc(sizeof(*st_buf));
|
|
|
|
if (!st_buf)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
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-02-03 00:18:32 +04:00
|
|
|
if (!S_ISREG(st_buf->st_mode) &&
|
|
|
|
!(S_ISCHR(st_buf->st_mode) && st_buf->st_rdev == DEVZERO)) {
|
2012-01-31 15:31:23 +04:00
|
|
|
pr_err("Can't handle non-regular mapping on %d's map %lu\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
|
|
|
|
|
|
|
if (!strcmp(file_path, "/SYSV")) {
|
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
|
|
|
}
|
|
|
|
} 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:
|
2012-05-10 21:07:00 +04:00
|
|
|
if (smaps)
|
|
|
|
fclose(smaps);
|
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_small(pid_t pid, struct proc_pid_stat_small *s)
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
{
|
2012-04-17 11:41:00 +04:00
|
|
|
char *tok, *p;
|
|
|
|
int fd;
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
int n;
|
|
|
|
|
2012-04-17 11:41:00 +04:00
|
|
|
fd = open_proc(pid, "stat");
|
|
|
|
if (fd < 0)
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
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);
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
return -1;
|
|
|
|
}
|
2012-04-17 11:41:00 +04:00
|
|
|
close(fd);
|
|
|
|
|
|
|
|
memset(s, 0, sizeof(*s));
|
|
|
|
|
|
|
|
tok = strchr(buf, ' ');
|
|
|
|
if (!tok)
|
|
|
|
goto err;
|
|
|
|
*tok++ = '\0';
|
|
|
|
if (*tok != '(')
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
s->pid = atoi(buf);
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +04:00
|
|
|
|
2012-04-17 11:41:00 +04:00
|
|
|
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;
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +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;
|
ctrools: Rewrite task/threads stopping engine is back
This commit brings the former "Rewrite task/threads stopping engine"
commit back. Handling it separately is too complex so better try
to handle it in-place.
Note some tests might fault, it's expected.
---
Stopping tasks with STOP and proceeding with SEIZE is actually excessive --
the SEIZE if enough. Moreover, just killing a task with STOP is also racy,
since task should be given some time to come to sleep before its proc
can be parsed.
Rewrite all this code to SEIZE task and all its threads from the very beginning.
With this we can distinguish stopped task state and migrate it properly (not
supported now, need to implement).
This thing however has one BIG problem -- after we SEIZE-d a task we should
seize
it's threads, but we should do it in a loop -- reading /proc/pid/task and
seizing
them again and again, until the contents of this dir stops changing (not done
now).
Besides, after we seized a task and all its threads we cannot scan it's children
list once -- task can get reparented to init and any task's child can call clone
with CLONE_PARENT flag thus repopulating the children list of the already seized
task (not done also)
This patch is ugly, yes, but splitting it doesn't help to review it much, sorry
:(
Signed-off-by: Pavel Emelyanov <xemul@parallels.com>
Signed-off-by: Cyrill Gorcunov <gorcunov@openvz.org>
2012-02-01 19:45:31 +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);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
{
|
|
|
|
int done = 0;
|
|
|
|
FILE *f;
|
|
|
|
char str[64];
|
|
|
|
|
2012-02-17 01:39:36 +04:00
|
|
|
f = fopen_proc(pid, "status");
|
2012-01-27 21:37:13 +04:00
|
|
|
if (f == NULL) {
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
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;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
|
|
snprintf(str, sizeof(str), "/proc/%d/mountinfo", pid);
|
|
|
|
f = fopen(str, "r");
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2013-12-04 19:51:25 +04:00
|
|
|
pr_info("\ttype %s source %s %x %s @ %s flags %x options %s\n",
|
|
|
|
new->fstype->name, new->source,
|
2012-05-13 08:30:06 +04:00
|
|
|
new->s_dev, new->root, new->mountpoint,
|
|
|
|
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
|
|
|
}
|
|
|
|
|
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-06-30 21:58:05 +04:00
|
|
|
static int parse_timerfd(FILE *f, char *buf, size_t size, TimerfdEntry *tfy)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Format is
|
|
|
|
* clockid: 0
|
|
|
|
* ticks: 0
|
|
|
|
* settime flags: 01
|
|
|
|
* it_value: (0, 49406829)
|
|
|
|
* it_interval: (1, 0)
|
|
|
|
*/
|
|
|
|
if (sscanf(buf, "clockid: %d", &tfy->clockid) != 1)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
if (!fgets(buf, size, f))
|
|
|
|
goto nodata;
|
|
|
|
if (sscanf(buf, "ticks: %llu", (unsigned long long *)&tfy->ticks) != 1)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
if (!fgets(buf, size, f))
|
|
|
|
goto nodata;
|
|
|
|
if (sscanf(buf, "settime flags: 0%o", &tfy->settime_flags) != 1)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
if (!fgets(buf, size, f))
|
|
|
|
goto nodata;
|
|
|
|
if (sscanf(buf, "it_value: (%llu, %llu)",
|
|
|
|
(unsigned long long *)&tfy->vsec,
|
|
|
|
(unsigned long long *)&tfy->vnsec) != 2)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
if (!fgets(buf, size, f))
|
|
|
|
goto nodata;
|
|
|
|
if (sscanf(buf, "it_interval: (%llu, %llu)",
|
|
|
|
(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-01-30 13:41:53 +04:00
|
|
|
static int parse_fdinfo_pid_s(char *pid, int fd, int type,
|
2012-07-11 09:22:38 +04:00
|
|
|
int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
|
|
|
|
{
|
|
|
|
FILE *f;
|
|
|
|
char str[256];
|
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-01-30 13:41:53 +04:00
|
|
|
sprintf(str, "/proc/%s/fdinfo/%d", pid, fd);
|
2012-07-11 09:22:38 +04:00
|
|
|
f = fopen(str, "r");
|
|
|
|
if (!f) {
|
2014-01-30 13:41:53 +04:00
|
|
|
pr_perror("Can't open %s to parse", str);
|
2012-07-11 09:22:38 +04:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
while (fgets(str, sizeof(str), f)) {
|
|
|
|
union fdinfo_entries entry;
|
|
|
|
|
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;
|
|
|
|
ret = parse_timerfd(f, str, sizeof(str), &entry.tfy);
|
|
|
|
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")) {
|
2012-07-17 07:25:40 +04:00
|
|
|
eventpoll_tfd_entry__init(&entry.epl);
|
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
if (type != FD_TYPES__EVENTPOLL)
|
2012-07-11 09:22:38 +04:00
|
|
|
goto parse_err;
|
2013-01-16 19:20:08 +04:00
|
|
|
ret = sscanf(str, "tfd: %d events: %x data: %"PRIx64,
|
2012-07-11 09:22:38 +04:00
|
|
|
&entry.epl.tfd, &entry.epl.events, &entry.epl.data);
|
|
|
|
if (ret != 3)
|
|
|
|
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;
|
|
|
|
}
|
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")) {
|
2013-01-15 23:17:57 +04:00
|
|
|
FanotifyInodeMarkEntry ie = FANOTIFY_INODE_MARK_ENTRY__INIT;
|
2013-01-14 20:48:10 +04:00
|
|
|
FhEntry f_handle = FH_ENTRY__INIT;
|
|
|
|
int hoff;
|
|
|
|
|
|
|
|
if (type != FD_TYPES__FANOTIFY)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
fanotify_mark_entry__init(&entry.ffy);
|
2013-01-15 23:17:57 +04:00
|
|
|
ie.f_handle = &f_handle;
|
|
|
|
entry.ffy.ie = &ie;
|
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",
|
2013-01-15 23:17:57 +04:00
|
|
|
&ie.i_ino, &entry.ffy.s_dev,
|
2013-01-14 20:48:10 +04:00
|
|
|
&entry.ffy.mflags, &entry.ffy.mask, &entry.ffy.ignored_mask,
|
2013-01-15 23:17:57 +04:00
|
|
|
&f_handle.bytes, &f_handle.type,
|
2013-01-14 20:48:10 +04:00
|
|
|
&hoff);
|
|
|
|
if (ret != 7)
|
|
|
|
goto parse_err;
|
|
|
|
|
2013-04-12 02:17:00 +04:00
|
|
|
if (alloc_fhandle(&f_handle)) {
|
|
|
|
ret = -1;
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2013-04-12 02:17:00 +04:00
|
|
|
}
|
2013-01-15 23:17:57 +04:00
|
|
|
parse_fhandle_encoded(str + hoff, &f_handle);
|
2013-01-14 20:48:10 +04:00
|
|
|
|
|
|
|
entry.ffy.type = MARK_TYPE__INODE;
|
|
|
|
ret = cb(&entry, arg);
|
|
|
|
|
2013-01-16 02:22:06 +04:00
|
|
|
free_fhandle(&f_handle);
|
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")) {
|
2013-01-15 23:17:57 +04:00
|
|
|
FanotifyMountMarkEntry me = FANOTIFY_MOUNT_MARK_ENTRY__INIT;
|
|
|
|
|
2013-01-14 20:48:10 +04:00
|
|
|
if (type != FD_TYPES__FANOTIFY)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
fanotify_mark_entry__init(&entry.ffy);
|
2013-01-15 23:17:57 +04:00
|
|
|
entry.ffy.me = &me;
|
2013-01-14 20:48:10 +04:00
|
|
|
|
|
|
|
ret = sscanf(str,
|
|
|
|
"fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x",
|
2013-01-15 23:17:57 +04:00
|
|
|
&me.mnt_id, &entry.ffy.mflags,
|
2013-01-14 20:48:10 +04:00
|
|
|
&entry.ffy.mask, &entry.ffy.ignored_mask);
|
|
|
|
if (ret != 4)
|
|
|
|
goto parse_err;
|
|
|
|
|
|
|
|
entry.ffy.type = MARK_TYPE__MOUNT;
|
|
|
|
ret = cb(&entry, arg);
|
|
|
|
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")) {
|
2012-07-17 07:25:42 +04:00
|
|
|
FhEntry f_handle = FH_ENTRY__INIT;
|
2012-07-11 09:35:36 +04:00
|
|
|
int hoff;
|
|
|
|
|
2012-07-17 07:25:42 +04:00
|
|
|
inotify_wd_entry__init(&entry.ify);
|
|
|
|
entry.ify.f_handle = &f_handle;
|
|
|
|
|
2012-07-19 10:18:37 +04:00
|
|
|
if (type != FD_TYPES__INOTIFY)
|
2012-07-11 09:35:36 +04:00
|
|
|
goto parse_err;
|
|
|
|
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",
|
|
|
|
&entry.ify.wd, &entry.ify.i_ino, &entry.ify.s_dev,
|
|
|
|
&entry.ify.mask, &entry.ify.ignored_mask,
|
2012-07-17 07:25:42 +04:00
|
|
|
&entry.ify.f_handle->bytes, &entry.ify.f_handle->type,
|
2012-07-11 09:35:36 +04:00
|
|
|
&hoff);
|
|
|
|
if (ret != 7)
|
|
|
|
goto parse_err;
|
2012-07-17 07:25:42 +04:00
|
|
|
|
2013-04-12 02:17:00 +04:00
|
|
|
if (alloc_fhandle(&f_handle)) {
|
|
|
|
ret = -1;
|
2013-04-29 16:10:50 +04:00
|
|
|
goto out;
|
2013-04-12 02:17:00 +04:00
|
|
|
}
|
|
|
|
|
2012-07-17 07:25:42 +04:00
|
|
|
parse_fhandle_encoded(str + hoff, entry.ify.f_handle);
|
2012-07-11 09:35:36 +04:00
|
|
|
|
|
|
|
ret = cb(&entry, arg);
|
2012-07-17 07:25:42 +04:00
|
|
|
|
2013-01-16 02:22:06 +04:00
|
|
|
free_fhandle(&f_handle);
|
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:
|
2013-04-12 02:17:00 +04:00
|
|
|
fclose(f);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
char pid_s[10];
|
|
|
|
|
|
|
|
sprintf(pid_s, "%d", pid);
|
|
|
|
return parse_fdinfo_pid_s(pid_s, fd, type, cb, arg);
|
|
|
|
}
|
|
|
|
|
|
|
|
int parse_fdinfo(int fd, int type,
|
|
|
|
int (*cb)(union fdinfo_entries *e, void *arg), void *arg)
|
|
|
|
{
|
|
|
|
return parse_fdinfo_pid_s("self", fd, type, cb, arg);
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
if (is_blocked) {
|
2013-10-11 18:37:14 +04:00
|
|
|
num = sscanf(buf, "%lld: -> %s %s %s %d %x:%x:%ld %lld %s",
|
2013-01-17 16:09:32 +08:00
|
|
|
&fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
|
|
|
|
&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",
|
2013-01-17 16:09:32 +08:00
|
|
|
&fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
|
|
|
|
&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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
fl_locks = fopen("/proc/locks", "r");
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!pid_in_pstree(fl->fl_owner)) {
|
|
|
|
/*
|
|
|
|
* We only care about tasks which are taken
|
|
|
|
* into dump, so we only collect file locks
|
|
|
|
* belong to these tasks.
|
|
|
|
*/
|
|
|
|
xfree(fl);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (is_blocked) {
|
|
|
|
/*
|
|
|
|
* Here the task is in the pstree.
|
|
|
|
* If it is blocked on a flock, when we try to
|
|
|
|
* ptrace-seize it, the kernel will unblock task
|
|
|
|
* from flock and will stop it in another place.
|
|
|
|
* So in dumping, a blocked file lock should never
|
|
|
|
* be here.
|
|
|
|
*/
|
|
|
|
pr_perror("We have a blocked file lock!");
|
|
|
|
ret = -1;
|
2013-05-16 21:00:42 +08:00
|
|
|
xfree(fl);
|
2013-01-17 16:09:32 +08:00
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_info("lockinfo: %lld:%s %s %s %d %02x:%02x:%ld %lld %s\n",
|
|
|
|
fl->fl_id, fl->fl_flag, fl->fl_type, fl->fl_option,
|
|
|
|
fl->fl_owner, fl->maj, fl->min, fl->i_no,
|
|
|
|
fl->start, fl->end);
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
FILE * file;
|
2013-07-03 13:36:08 +04:00
|
|
|
|
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;
|
|
|
|
|
2013-07-01 16:26:30 +04:00
|
|
|
file = fopen_proc(pid, "timers");
|
2013-06-27 23:32:20 +04:00
|
|
|
if (file == NULL) {
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
while (1) {
|
2014-02-18 16:53:00 +04:00
|
|
|
char pbuf[17]; /* 16 + eol */
|
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
|
|
|
|
2013-07-03 13:36:08 +04:00
|
|
|
ret = fscanf(file, "ID: %ld\n"
|
2014-02-18 16:53:00 +04:00
|
|
|
"signal: %d/%16s\n"
|
2013-07-03 13:36:08 +04:00
|
|
|
"notify: %6[a-z]/%3[a-z].%d\n"
|
|
|
|
"ClockID: %d\n",
|
|
|
|
&timer->spt.it_id,
|
2014-02-18 16:53:00 +04:00
|
|
|
&timer->spt.si_signo, pbuf,
|
2013-07-03 13:36:08 +04:00
|
|
|
sigpid, tidpid, &pid_t,
|
|
|
|
&timer->spt.clock_id);
|
|
|
|
if (ret != 7) {
|
|
|
|
ret = 0;
|
2013-07-08 00:18:38 +04:00
|
|
|
xfree(timer);
|
2013-07-03 13:36:08 +04:00
|
|
|
if (feof(file))
|
|
|
|
goto out;
|
|
|
|
goto err;
|
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);
|
|
|
|
xfree(timer);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
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++;
|
|
|
|
}
|
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:
|
|
|
|
fclose(file);
|
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;
|
|
|
|
char *name, *path, *e;
|
|
|
|
|
|
|
|
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-05-08 16:30:30 +04:00
|
|
|
name = strchr(buf, ':') + 1;
|
|
|
|
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-07-10 17:00:28 +04:00
|
|
|
if (strcmp(cc->name, name) >= 0 && strcmp(cc->path, path) >= 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;
|
|
|
|
|
|
|
|
f = fopen("/proc/cgroups", "r");
|
|
|
|
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;
|
|
|
|
}
|