#include "vserver.h"
#include "util.h"
#include "internal.h"
+#include "pathconfig.h"
#include <ensc_vector/vector.h>
#include <stdbool.h>
#include <getopt.h>
#include <sys/param.h>
+#include <sys/resource.h>
#define ENSC_WRAPPERS_DIRENT 1
#define ENSC_WRAPPERS_VSERVER 1
#include "wrappers.h"
#define PROC_DIR_NAME "/proc"
+#define PROC_VIRT_DIR_NAME "/proc/virtual"
#define CTX_DIR_NAME "/var/run/vservers/"
#define CTX_NAME_MAX_LEN 50
struct XidData
{
xid_t xid;
- int process_count;
- int VmSize_total;
- int VmRSS_total;
+ unsigned int process_count;
+ uint64_t VmSize_total;
+ uint64_t VmRSS_total;
uint64_t start_time_oldest;
uint64_t stime_total, utime_total;
WRITE_STR(1, cmd);
WRITE_MSG(1,
"\n"
- "Show informations about all the active context.\n\n"
+ "Show information about all active contexts.\n\n"
" CTX# Context number\n"
" #0 = root context\n"
" #1 = monitoring context\n"
return secs*1000 + msecs;
}
+static inline uint64_t
+toMsec(uint64_t v)
+{
+ return v*1000llu/hertz;
+}
+
static int
cmpData(void const *xid_v, void const *map_v)
{
res->start_time_oldest = MIN(res->start_time_oldest, process->start_time);
}
-static inline uint64_t
-toMsec(uint64_t v)
+static void
+registerXidVstat(struct Vector *vec, unsigned long xid_l)
{
- return v*1000llu/hertz;
+ xid_t xid = (xid_t) xid_l;
+ struct XidData *res;
+ struct vc_rlimit_stat limit[3];
+ struct vc_virt_stat vstat;
+ struct vc_sched_info sched;
+ int cpu;
+
+ res = Vector_search(vec, &xid, cmpData);
+ if (res!=0) {
+ WRITE_MSG(2, "Duplicate xid found?!\n");
+ return;
+ }
+ if (vc_virt_stat(xid, &vstat) == -1) {
+ perror("vc_virt_stat()");
+ return;
+ }
+ if (vc_rlimit_stat(xid, RLIMIT_NPROC, &limit[0]) == -1) {
+ perror("vc_rlimit_stat(RLIMIT_NRPOC)");
+ return;
+ }
+ if (vc_rlimit_stat(xid, RLIMIT_AS, &limit[1]) == -1) {
+ perror("vc_rlimit_stat(RLIMIT_AS)");
+ return;
+ }
+ if (vc_rlimit_stat(xid, RLIMIT_RSS, &limit[2]) == -1) {
+ perror("vc_rlimit_stat(RLIMIT_RSS)");
+ return;
+ }
+
+ res = Vector_insert(vec, &xid, cmpData);
+ res->xid = xid;
+
+ res->process_count = limit[0].value;
+ res->VmSize_total = limit[1].value * pagesize;
+ res->VmRSS_total = limit[2].value;
+ res->start_time_oldest= getUptime() - vstat.uptime/1000000;
+
+ res->utime_total = 0;
+ res->stime_total = 0;
+ // XXX: arbitrary CPU limit.
+ for (cpu = 0; cpu < 1024; cpu++) {
+ sched.cpu_id = cpu;
+ sched.bucket_id = 0;
+ if (vc_sched_info(xid, &sched) == -1)
+ break;
+
+ res->utime_total += sched.user_msec;
+ res->stime_total += sched.sys_msec;
+ }
+}
+
+static void
+registerXidCgroups(struct Vector *vec, struct process_info *process)
+{
+ xid_t xid = (xid_t) process->s_context;
+ struct XidData *res;
+
+ switch (vc_getXIDType(xid)) {
+ case vcTYPE_STATIC:
+ case vcTYPE_DYNAMIC:
+ break;
+ default:
+ return;
+ }
+
+ res = Vector_search(vec, &xid, cmpData);
+ if (res == 0) {
+ struct vc_rlimit_stat limit;
+ struct vc_virt_stat vstat;
+ struct vc_sched_info sched;
+ int cpu;
+ char vhi_name[65],
+ filename[128],
+ cgroup[129],
+ name[129],
+ buf[30];
+ int fd;
+ ssize_t cgroup_len, name_len;
+ unsigned long long rss = 0;
+ char *endptr;
+ size_t len;
+ uint64_t stime_total, utime_total;
+ int per_ss = 0;
+
+
+ if (vc_virt_stat(xid, &vstat) == -1) {
+ perror("vc_virt_stat()");
+ return;
+ }
+ if (vc_rlimit_stat(xid, RLIMIT_NPROC, &limit) == -1) {
+ perror("vc_rlimit_stat(RLIMIT_NRPOC)");
+ return;
+ }
+ if (vc_get_vhi_name(xid, vcVHI_CONTEXT, vhi_name, sizeof(vhi_name)) == -1) {
+ perror("vc_get_vhi_name(CONTEXT)");
+ return;
+ }
+
+ if ((fd = open(DEFAULTCONFDIR "/cgroup/mnt", O_RDONLY)) == -1) {
+ strcpy(cgroup, "/dev/cgroup/");
+ cgroup_len = sizeof("/dev/cgroup");
+ }
+ else {
+ cgroup_len = read(fd, cgroup, sizeof(cgroup));
+ if (cgroup_len == -1) {
+ perror("read(cgroup/mnt)");
+ return;
+ }
+ if (cgroup_len > 0) {
+ close(fd);
+ while (cgroup[cgroup_len - 1] == '\n' || cgroup[cgroup_len - 1] == '\r')
+ cgroup_len--;
+ cgroup[cgroup_len] = '/';
+ cgroup_len += 1;
+ cgroup[cgroup_len] = 0;
+ }
+ }
+
+ if ((fd = open(DEFAULTCONFDIR "/cgroup/base", O_RDONLY)) != -1) {
+ len = read(fd, cgroup + cgroup_len, sizeof(cgroup) - cgroup_len);
+ if (len == -1) {
+ perror("read(cgroup/base)");
+ return;
+ }
+ close(fd);
+ if (len > 0) {
+ while (cgroup[cgroup_len + len - 1] == '\n' || cgroup[cgroup_len + len - 1] == '\r')
+ len--;
+ cgroup_len += len;
+ if (cgroup[cgroup_len - 1] != '/') {
+ cgroup[cgroup_len] = '/';
+ cgroup_len += 1;
+ }
+ cgroup[cgroup_len] = 0;
+ }
+ }
+
+ if (access(DEFAULTCONFDIR "/cgroup/per-ss", F_OK) == 0)
+ per_ss = 1;
+
+ len = strlen(vhi_name);
+ if ((len + sizeof("/cgroup/name")) >= sizeof(filename)) {
+ WRITE_MSG(2, "too long context name: ");
+ WRITE_STR(2, vhi_name);
+ WRITE_MSG(2, "\n");
+ return;
+ }
+ strcpy(filename, vhi_name);
+ strcpy(filename + len, "/cgroup/name");
+
+ if ((fd = open(filename, O_RDONLY)) == -1) {
+ char *dir = strrchr(vhi_name, '/');
+ if (dir == NULL) {
+ WRITE_MSG(2, "invalid context name: ");
+ WRITE_STR(2, dir);
+ WRITE_MSG(2, "\n");
+ return;
+ }
+ name_len = strlen(dir);
+ if (name_len >= sizeof(name)) {
+ WRITE_MSG(2, "cgroup name too long: ");
+ WRITE_STR(2, dir);
+ WRITE_MSG(2, "\n");
+ return;
+ }
+ strcpy(name, dir);
+ }
+ else {
+ name_len = read(fd, name, sizeof(name));
+ if (name_len == -1) {
+ perror("read(cgroup/name)");
+ return;
+ }
+ if (name_len > 0) {
+ while (name[name_len - 1] == '\n' || name[name_len - 1] == '\r')
+ name_len--;
+ name[name_len] = '\0';
+ }
+ close(fd);
+ }
+
+ if ((cgroup_len + name_len + sizeof("/memory/memory.usage_in_bytes")) > sizeof(filename)) {
+ WRITE_MSG(2, "cgroup name too long: ");
+ WRITE_STR(2, cgroup);
+ WRITE_MSG(2, "\n");
+ return;
+ }
+ snprintf(filename, sizeof(filename), "%s%s%s/memory.usage_in_bytes", cgroup, (per_ss ? "/memory" : ""), name);
+
+ if ((fd = open(filename, O_RDONLY)) == -1)
+ perror("open(memory.usage_in_bytes)");
+ else {
+ if (read(fd, buf, sizeof(buf)) == -1) {
+ perror("read(memory.usage_in_bytes)");
+ return;
+ }
+ close(fd);
+ if ((rss = strtoull(buf, &endptr, 0)) == ULLONG_MAX ||
+ (*endptr != '\n' && *endptr != '\0')) {
+ perror("strtoull(memory.usage_in_bytes)");
+ return;
+ }
+ }
+
+ snprintf(filename, sizeof(filename), "%s%s%s/cpuacct.stat", cgroup, (per_ss ? "/cpuacct" : ""), name);
+
+ if ((fd = open(filename, O_RDONLY)) == -1) {
+ utime_total = 0;
+ stime_total = 0;
+ // XXX: arbitrary CPU limit.
+ for (cpu = 0; cpu < 1024; cpu++) {
+ sched.cpu_id = cpu;
+ sched.bucket_id = 0;
+ if (vc_sched_info(xid, &sched) == -1)
+ break;
+
+ utime_total += sched.user_msec;
+ stime_total += sched.sys_msec;
+ }
+ }
+ else {
+ if (read(fd, buf, sizeof(buf)) == -1) {
+ perror("read(cpuacct.stat)");
+ return;
+ }
+ close(fd);
+
+ if (sscanf(buf, "user %llu\nsystem %llu\n", &utime_total, &stime_total) != 2) {
+ perror("sscanf(cpuacct.stat)");
+ return;
+ }
+ }
+
+ res = Vector_insert(vec, &xid, cmpData);
+ res->xid = xid;
+
+ res->process_count = limit.value;
+ res->VmRSS_total = rss / 4096;
+ res->start_time_oldest= getUptime() - vstat.uptime/1000000;
+
+ res->utime_total = toMsec(utime_total);
+ res->stime_total = toMsec(stime_total);
+ }
+
+ res->VmSize_total += process->VmSize;
}
default :
WRITE_MSG(2, "Try '");
WRITE_STR(2, argv[0]);
- WRITE_MSG(2, " --help\" for more information.\n");
+ WRITE_MSG(2, " --help' for more information.\n");
return EXIT_FAILURE;
break;
}
if (hertz==0x42) initHertz();
if (pagesize==0x42) initPageSize();
- my_pid = getpid();
+ Vector_init(&xid_data, sizeof(struct XidData));
- if (!switchToWatchXid(&errptr)) {
- perror(errptr);
- exit(1);
+ if (vc_isSupported(vcFEATURE_VSTAT)) {
+ unsigned long xid;
+ Echdir(PROC_VIRT_DIR_NAME);
+ proc_dir = Eopendir(".");
+ while ((dir_entry = readdir(proc_dir)) != NULL) {
+ if (!isNumberUnsigned(dir_entry->d_name, &xid, false))
+ continue;
+
+ registerXidVstat(&xid_data, xid);
+ }
+ closedir(proc_dir);
}
+ else {
+ void (*handler)(struct Vector *vec, struct process_info *process);
+
+ my_pid = getpid();
- if (access("/proc/uptime",R_OK)==-1 && errno==ENOENT)
- WRITE_MSG(2,
+ if (!switchToWatchXid(&errptr)) {
+ perror(errptr);
+ exit(1);
+ }
+
+ if (access("/proc/uptime",R_OK)==-1 && errno==ENOENT)
+ WRITE_MSG(2,
"WARNING: can not access /proc/uptime. Usually, this is caused by\n"
" procfs-security. Please read the FAQ for more details\n"
- " http://www.linux-vserver.org/index.php?page=Linux-Vserver+FAQ\n");
+ " http://linux-vserver.org/Proc-Security\n");
- Vector_init(&xid_data, sizeof(struct XidData));
+ if (vc_isSupported(vcFEATURE_MEMCG))
+ handler = registerXidCgroups;
+ else
+ handler = registerXid;
- Echdir(PROC_DIR_NAME);
- proc_dir = Eopendir(".");
- while ((dir_entry = readdir(proc_dir)) != NULL)
- {
+ Echdir(PROC_DIR_NAME);
+ proc_dir = Eopendir(".");
+ while ((dir_entry = readdir(proc_dir)) != NULL)
+ {
// select only process file
- if (!isdigit(*dir_entry->d_name))
- continue;
+ if (!isdigit(*dir_entry->d_name))
+ continue;
- if (atoi(dir_entry->d_name) != my_pid) {
- struct process_info * info = get_process_info(dir_entry->d_name);
- if (info)
- registerXid(&xid_data, info);
+ if (atoi(dir_entry->d_name) != my_pid) {
+ struct process_info * info = get_process_info(dir_entry->d_name);
+ if (info)
+ handler(&xid_data, info);
+ }
}
+ closedir(proc_dir);
}
- closedir(proc_dir);
Vector_foreach(&xid_data, fillName, 0);