Fix types
[util-vserver.git] / src / vserver-stat.c
index 4927141..636c6b7 100644 (file)
@@ -24,6 +24,7 @@
 #include "vserver.h"
 #include "util.h"
 #include "internal.h"
+#include "pathconfig.h"
 
 #include <ensc_vector/vector.h>
 
@@ -42,6 +43,7 @@
 #include <stdbool.h>
 #include <getopt.h>
 #include <sys/param.h>
+#include <sys/resource.h>
 
 #define ENSC_WRAPPERS_DIRENT   1
 #define ENSC_WRAPPERS_VSERVER  1
@@ -50,6 +52,7 @@
 #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
 
@@ -65,9 +68,9 @@ static unsigned long  pagesize=0x42;
 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;
 
@@ -114,7 +117,7 @@ showHelp(char const *cmd)
   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"
@@ -187,6 +190,12 @@ getUptime()
   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)
 {
@@ -223,10 +232,254 @@ registerXid(struct Vector *vec, struct process_info *process)
   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;
+    ssize_t                    len;
+    unsigned long long         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;
 }
 
 
@@ -543,7 +796,7 @@ int main(int argc, char **argv)
       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;
     }
@@ -557,36 +810,57 @@ int main(int argc, char **argv)
   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://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);