vshost-util-vserver

Build script and sources for util-vserver.
git clone https://ccx.te2000.cz/git/vshost-util-vserver
Log | Files | Refs

vserver-stat.c (21515B)


      1 // $Id$
      2 
      3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
      4 // based on vserver-stat.cc by Guillaum Dallaire and Jacques Gelinas
      5 //  
      6 // This program is free software; you can redistribute it and/or modify
      7 // it under the terms of the GNU General Public License as published by
      8 // the Free Software Foundation; either version 2, or (at your option)
      9 // any later version.
     10 //  
     11 // This program is distributed in the hope that it will be useful,
     12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 // GNU General Public License for more details.
     15 //  
     16 // You should have received a copy of the GNU General Public License
     17 // along with this program; if not, write to the Free Software
     18 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
     19 
     20 #ifdef HAVE_CONFIG_H
     21 #  include <config.h>
     22 #endif
     23 
     24 #include "vserver.h"
     25 #include "util.h"
     26 #include "internal.h"
     27 #include "pathconfig.h"
     28 
     29 #include <ensc_vector/vector.h>
     30 
     31 #include <stdio.h>
     32 #include <stdlib.h>
     33 #include <unistd.h>
     34 #include <fcntl.h>
     35 #include <ctype.h>
     36 #include <sys/types.h>
     37 #include <sys/stat.h>
     38 #include <dirent.h>
     39 #include <string.h>
     40 #include <errno.h>
     41 #include <syscall.h>
     42 #include <time.h>
     43 #include <stdbool.h>
     44 #include <getopt.h>
     45 #include <sys/param.h>
     46 #include <sys/resource.h>
     47 
     48 #define ENSC_WRAPPERS_DIRENT	1
     49 #define ENSC_WRAPPERS_VSERVER	1
     50 #define ENSC_WRAPPERS_FCNTL	1
     51 #define ENSC_WRAPPERS_UNISTD	1
     52 #include "wrappers.h"
     53 
     54 #define PROC_DIR_NAME "/proc"
     55 #define PROC_VIRT_DIR_NAME "/proc/virtual"
     56 #define CTX_DIR_NAME "/var/run/vservers/"
     57 #define CTX_NAME_MAX_LEN 50
     58 
     59 int	wrapper_exit_code = 1;
     60 
     61 #ifndef AT_CLKTCK
     62 #define AT_CLKTCK       17    /* frequency of times() */
     63 #endif
     64 
     65 static unsigned long	hertz   =0x42;
     66 static unsigned long	pagesize=0x42;
     67 
     68 struct XidData
     69 {
     70     xid_t		xid;
     71     unsigned int	process_count;
     72     uint64_t		VmSize_total;
     73     uint64_t		VmRSS_total;
     74     uint64_t		start_time_oldest;
     75     uint64_t		stime_total, utime_total;
     76 
     77     vcCfgStyle		cfgstyle;
     78     char const *	name;
     79 };
     80 
     81 struct process_info
     82 {
     83 	long 		VmSize;		// number of pages of virtual memory
     84 	long 		VmRSS;		// resident set size from /proc/#/stat
     85 	uint64_t	start_time;	// start time of process -- milliseconds since 1-1-70
     86         uint64_t	stime, utime;	// kernel & user-mode CPU time accumulated by process
     87 	uint64_t	cstime, cutime;	// cumulative time of process and reaped children
     88 	xid_t		s_context;
     89 };
     90 
     91 struct ArgInfo {
     92     enum { tpUNSET, tpCTX, tpPID }	type;
     93     xid_t		ctx;
     94     pid_t		pid;
     95     unsigned int	interval;
     96     bool		shutdown;
     97     bool		omit_init;
     98     size_t		argc;
     99     char * const *	argv;
    100 };
    101 
    102 #define CMD_HELP		0x1000
    103 #define CMD_VERSION		0x1001
    104 
    105 struct option const
    106 CMDLINE_OPTIONS[] = {
    107   { "help",     no_argument,  0, CMD_HELP },
    108   { "version",  no_argument,  0, CMD_VERSION },
    109   { "sort",     required_argument, 0, 'O' },
    110   {0,0,0,0}
    111 };
    112 
    113 static void
    114 showHelp(char const *cmd)
    115 {
    116   WRITE_MSG(1, "Usage:  ");
    117   WRITE_STR(1, cmd);
    118   WRITE_MSG(1,
    119 	    "\n"
    120 	    "Show information about all active contexts.\n\n"
    121 	    "	CTX#		Context number\n"
    122 	    "			#0 = root context\n"
    123 	    "			#1 = monitoring context\n"
    124 	    "	PROC QTY	Quantity of processes in each context\n"
    125 	    "	VSZ		Number of pages of virtual memory\n"
    126 	    "	RSS		Resident set size\n"
    127 	    "	userTIME	User-mode CPU time accumulated\n"
    128 	    "	sysTIME		Kernel-mode CPU time accumulated\n"
    129 	    "	UPTIME		Uptime/context\n"
    130 	    "	NAME		Virtual server name\n"
    131 	    "\n");
    132   exit(0);
    133 }
    134 
    135 static void
    136 showVersion()
    137 {
    138   WRITE_MSG(1,
    139 	    "vserver-stat " VERSION " -- show virtual context statistics\n"
    140 	    "This program is part of " PACKAGE_STRING "\n\n"
    141 	    "Copyright (C) 2003,2005 Enrico Scholz\n"
    142 	    VERSION_COPYRIGHT_DISCLAIMER);
    143   exit(0);
    144 }
    145 
    146 
    147 // return uptime (in ms) from /proc/uptime
    148 static uint64_t
    149 getUptime()
    150 {
    151   int		fd;
    152   char		buffer[64];
    153   char *	errptr;
    154   size_t	len;
    155   uint64_t	secs;
    156   uint32_t	msecs=0;
    157 
    158     // open the /proc/uptime file
    159   fd  = EopenD("/proc/uptime", O_RDONLY, 0);
    160   len = Eread(fd, buffer, sizeof buffer);
    161 
    162   if (len==sizeof(buffer)) {
    163     WRITE_MSG(2, "Too much data in /proc/uptime; aborting...\n");
    164     exit(1);
    165   }
    166   Eclose(fd);
    167 
    168   while (len>0 && buffer[len-1]=='\n') --len;
    169   buffer[len] = '\0';
    170 
    171   secs = strtol(buffer, &errptr, 10);
    172   if (*errptr!='.') errptr = buffer;
    173   else {
    174     unsigned int	mult;
    175     switch (strlen(errptr+1)) {
    176       case 0	:  mult = 1000; break;
    177       case 1	:  mult = 100;  break;
    178       case 2	:  mult = 10;   break;
    179       case 3	:  mult = 1;    break;
    180       default	:  mult = 0;    break;
    181     }
    182     msecs = strtol(errptr+1, &errptr, 10) * mult;
    183   }
    184 
    185   if ((*errptr!='\0' && *errptr!=' ') || errptr==buffer) {
    186     WRITE_MSG(2, "Bad data in /proc/uptime\n");
    187     exit(1);
    188   }
    189 
    190   return secs*1000 + msecs;
    191 }
    192 
    193 static inline uint64_t
    194 toMsec(uint64_t v)
    195 {
    196   return v*1000llu/hertz;
    197 }
    198 
    199 static int
    200 cmpData(void const *xid_v, void const *map_v)
    201 {
    202   xid_t const * const			xid = xid_v;
    203   struct XidData const * const		map = map_v;
    204   int	res = *xid - map->xid;
    205 
    206   return res;
    207 }
    208 
    209 static void
    210 registerXid(struct Vector *vec, struct process_info *process)
    211 {
    212   struct XidData	*res;
    213 
    214   res = Vector_search(vec, &process->s_context, cmpData);
    215   if (res==0) {
    216     res = Vector_insert(vec, &process->s_context, cmpData);
    217     res->xid           = process->s_context;
    218     res->process_count = 0;
    219     res->VmSize_total  = 0;
    220     res->VmRSS_total   = 0;
    221     res->utime_total   = 0;
    222     res->stime_total   = 0;
    223     res->start_time_oldest = process->start_time;
    224   }
    225 
    226   ++res->process_count;
    227   res->VmSize_total += process->VmSize;
    228   res->VmRSS_total  += process->VmRSS;
    229   res->utime_total  += process->utime + process->cutime;
    230   res->stime_total  += process->stime + process->cstime;
    231 
    232   res->start_time_oldest = MIN(res->start_time_oldest, process->start_time);
    233 }
    234 
    235 static void
    236 registerXidVstat(struct Vector *vec, unsigned long xid_l)
    237 {
    238   xid_t			xid = (xid_t) xid_l;
    239   struct XidData	*res;
    240   struct vc_rlimit_stat	limit[3];
    241   struct vc_virt_stat	vstat;
    242   struct vc_sched_info	sched;
    243   int			cpu;
    244 
    245   res = Vector_search(vec, &xid, cmpData);
    246   if (res!=0) {
    247     WRITE_MSG(2, "Duplicate xid found?!\n");
    248     return;
    249   }
    250   if (vc_virt_stat(xid, &vstat) == -1) {
    251     perror("vc_virt_stat()");
    252     return;
    253   }
    254   if (vc_rlimit_stat(xid, RLIMIT_NPROC, &limit[0]) == -1) {
    255     perror("vc_rlimit_stat(RLIMIT_NRPOC)");
    256     return;
    257   }
    258   if (vc_rlimit_stat(xid, RLIMIT_AS, &limit[1]) == -1) {
    259     perror("vc_rlimit_stat(RLIMIT_AS)");
    260     return;
    261   }
    262   if (vc_rlimit_stat(xid, RLIMIT_RSS, &limit[2]) == -1) {
    263     perror("vc_rlimit_stat(RLIMIT_RSS)");
    264     return;
    265   }
    266 
    267   res			= Vector_insert(vec, &xid, cmpData);
    268   res->xid		= xid;
    269 
    270   res->process_count	= limit[0].value;
    271   res->VmSize_total	= limit[1].value * pagesize;
    272   res->VmRSS_total	= limit[2].value;
    273   res->start_time_oldest= getUptime() - vstat.uptime/1000000;
    274 
    275   res->utime_total	= 0;
    276   res->stime_total	= 0;
    277   // XXX: arbitrary CPU limit.
    278   for (cpu = 0; cpu < 1024; cpu++) {
    279     sched.cpu_id = cpu;
    280     sched.bucket_id = 0;
    281     if (vc_sched_info(xid, &sched) == -1)
    282       break;
    283 
    284     res->utime_total	+= sched.user_msec;
    285     res->stime_total	+= sched.sys_msec;
    286   }
    287 }
    288 
    289 static void
    290 registerXidCgroups(struct Vector *vec, struct process_info *process)
    291 {
    292   xid_t				xid = (xid_t) process->s_context;
    293   struct XidData		*res;
    294 
    295   switch (vc_getXIDType(xid)) {
    296     case vcTYPE_STATIC:
    297     case vcTYPE_DYNAMIC:
    298       break;
    299     default:
    300       return;
    301   }
    302 
    303   res = Vector_search(vec, &xid, cmpData);
    304   if (res == 0) {
    305     struct vc_rlimit_stat	limit;
    306     struct vc_virt_stat		vstat;
    307     struct vc_sched_info	sched;
    308     int				cpu;
    309     char			vhi_name[65],
    310 				filename[128],
    311 				cgroup[129],
    312 				name[129],
    313 				buf[30];
    314     int				fd;
    315     ssize_t			cgroup_len, name_len;
    316     unsigned long long		rss = 0;
    317     char			*endptr;
    318     ssize_t			len;
    319     unsigned long long		stime_total, utime_total;
    320     int				per_ss = 0;
    321     FILE			*fp;
    322 
    323 
    324     if (vc_virt_stat(xid, &vstat) == -1) {
    325       perror("vc_virt_stat()");
    326       return;
    327     }
    328     if (vc_rlimit_stat(xid, RLIMIT_NPROC, &limit) == -1) {
    329       perror("vc_rlimit_stat(RLIMIT_NRPOC)");
    330       return;
    331     }
    332     if (vc_get_vhi_name(xid, vcVHI_CONTEXT, vhi_name, sizeof(vhi_name)) == -1) {
    333       perror("vc_get_vhi_name(CONTEXT)");
    334       return;
    335     }
    336 
    337     if ((fd = open(DEFAULTCONFDIR "/cgroup/mnt", O_RDONLY)) == -1) {
    338       if (utilvserver_isDirectory("/sys/fs/cgroup/memory", false)) {
    339         strcpy(cgroup, "/sys/fs/cgroup/");
    340         cgroup_len = sizeof("/sys/fs/cgroup");
    341         per_ss = 1;
    342       } else {
    343         strcpy(cgroup, "/dev/cgroup/");
    344         cgroup_len = sizeof("/dev/cgroup");
    345       }
    346     }
    347     else {
    348       cgroup_len = read(fd, cgroup, sizeof(cgroup));
    349       if (cgroup_len == -1) {
    350         perror("read(cgroup/mnt)");
    351         return;
    352       }
    353       if (cgroup_len > 0) {
    354 	close(fd);
    355 	while (cgroup[cgroup_len - 1] == '\n' || cgroup[cgroup_len - 1] == '\r')
    356 	  cgroup_len--;
    357 	cgroup[cgroup_len] = '/';
    358 	cgroup_len += 1;
    359 	cgroup[cgroup_len] = 0;
    360       }
    361     }
    362 
    363     if ((fd = open(DEFAULTCONFDIR "/cgroup/base", O_RDONLY)) != -1) {
    364       len = read(fd, cgroup + cgroup_len, sizeof(cgroup) - cgroup_len);
    365       if (len == -1) {
    366         perror("read(cgroup/base)");
    367         return;
    368       }
    369       close(fd);
    370       if (len > 0) {
    371 	while (cgroup[cgroup_len + len - 1] == '\n' || cgroup[cgroup_len + len - 1] == '\r')
    372 	  len--;
    373 	cgroup_len += len;
    374 	if (cgroup[cgroup_len - 1] != '/') {
    375 	  cgroup[cgroup_len] = '/';
    376 	  cgroup_len += 1;
    377 	}
    378 	cgroup[cgroup_len] = 0;
    379       }
    380     }
    381 
    382     if (access(DEFAULTCONFDIR "/cgroup/per-ss", F_OK) == 0)
    383       per_ss = 1;
    384 
    385     len = strlen(vhi_name);
    386     if ((len + sizeof("/cgroup/name")) >= sizeof(filename)) {
    387       WRITE_MSG(2, "too long context name: ");
    388       WRITE_STR(2, vhi_name);
    389       WRITE_MSG(2, "\n");
    390       return;
    391     }
    392     strcpy(filename, vhi_name);
    393     strcpy(filename + len, "/cgroup/name");
    394 
    395     if ((fd = open(filename, O_RDONLY)) == -1) {
    396       char *dir = strrchr(vhi_name, '/');
    397       if (dir == NULL) {
    398         WRITE_MSG(2, "invalid context name: ");
    399         WRITE_STR(2, vhi_name);
    400         WRITE_MSG(2, "\n");
    401         return;
    402       }
    403       name_len = strlen(dir);
    404       if (name_len >= sizeof(name)) {
    405         WRITE_MSG(2, "cgroup name too long: ");
    406         WRITE_STR(2, dir);
    407         WRITE_MSG(2, "\n");
    408         return;
    409       }
    410       strcpy(name, dir);
    411     }
    412     else {
    413       name_len = read(fd, name, sizeof(name));
    414       if (name_len == -1) {
    415         perror("read(cgroup/name)");
    416         return;
    417       }
    418       if (name_len > 0) {
    419 	while (name[name_len - 1] == '\n' || name[name_len - 1] == '\r')
    420 	  name_len--;
    421 	name[name_len] = '\0';
    422       }
    423       close(fd);
    424     }
    425 
    426     if ((cgroup_len + name_len + sizeof("/memory/memory.stat")) > sizeof(filename)) {
    427       WRITE_MSG(2, "cgroup name too long: ");
    428       WRITE_STR(2, cgroup);
    429       WRITE_MSG(2, "\n");
    430       return;
    431     }
    432     snprintf(filename, sizeof(filename), "%s%s%s/memory.stat", cgroup, (per_ss ? "/memory" : ""), name);
    433 
    434     if ((fp = fopen(filename, "r")) == NULL)
    435       perror("open(memory.stat)");
    436     else {
    437       unsigned long long _rss = 0, _mapped_file = 0;
    438       while (fgets(buf, sizeof(buf), fp) != NULL) {
    439 	if (strncmp(buf, "rss ", 4) == 0) {
    440 	  if ((_rss = strtoull(buf + 4, &endptr, 0)) == ULLONG_MAX ||
    441 	      (*endptr != '\n' && *endptr != '\0')) {
    442 	    perror("strtoull(memory.stat:rss)");
    443 	    return;
    444 	  }
    445 	}
    446         else if (strncmp(buf, "mapped_file ", 12) == 0) {
    447 	  if ((_mapped_file = strtoull(buf + 12, &endptr, 0)) == ULLONG_MAX ||
    448 	      (*endptr != '\n' && *endptr != '\0')) {
    449 	    perror("strtoull(memory.stat:mapped_file)");
    450 	    return;
    451 	  }
    452 	}
    453       }
    454       rss = _rss + _mapped_file;
    455     }
    456 
    457     snprintf(filename, sizeof(filename), "%s%s%s/cpuacct.stat", cgroup, (per_ss ? "/cpuacct" : ""), name);
    458 
    459     if ((fd = open(filename, O_RDONLY)) == -1) {
    460       utime_total	= 0;
    461       stime_total	= 0;
    462       // XXX: arbitrary CPU limit.
    463       for (cpu = 0; cpu < 1024; cpu++) {
    464 	sched.cpu_id = cpu;
    465 	sched.bucket_id = 0;
    466 	if (vc_sched_info(xid, &sched) == -1)
    467 	  break;
    468 
    469 	utime_total	+= sched.user_msec;
    470 	stime_total	+= sched.sys_msec;
    471       }
    472     }
    473     else {
    474       if (read(fd, buf, sizeof(buf)) == -1) {
    475 	perror("read(cpuacct.stat)");
    476 	return;
    477       }
    478       close(fd);
    479 
    480       if (sscanf(buf, "user %llu\nsystem %llu\n", &utime_total, &stime_total) != 2) {
    481 	perror("sscanf(cpuacct.stat)");
    482 	return;
    483       }
    484     }
    485 
    486     res			= Vector_insert(vec, &xid, cmpData);
    487     res->xid		= xid;
    488 
    489     res->process_count	= limit.value;
    490     res->VmRSS_total	= rss / 4096;
    491     res->start_time_oldest= getUptime() - vstat.uptime/1000000;
    492 
    493     res->utime_total	= toMsec(utime_total);
    494     res->stime_total	= toMsec(stime_total);
    495   }
    496   
    497   res->VmSize_total	+= process->VmSize;
    498 }
    499 
    500 
    501 // shamelessly stolen from procps...
    502 static unsigned long
    503 find_elf_note(unsigned long findme){
    504   unsigned long *ep = (unsigned long *)environ;
    505   while(*ep++);
    506   while(*ep){
    507     if(ep[0]==findme) return ep[1];
    508     ep+=2;
    509   }
    510   return (unsigned long)(-1);
    511 }
    512 
    513 static void initHertz()	   __attribute__((__constructor__));
    514 static void initPageSize() __attribute__((__constructor__));
    515 
    516 static void
    517 initHertz()
    518 {
    519   hertz = find_elf_note(AT_CLKTCK);
    520   if (hertz==(unsigned long)(-1))
    521     hertz = sysconf(_SC_CLK_TCK);
    522 }
    523 
    524 static void
    525 initPageSize()
    526 {
    527   pagesize = sysconf(_SC_PAGESIZE);
    528 }
    529 
    530 // open the process's status file to get the ctx number, and other stat
    531 struct process_info *
    532 get_process_info(char *pid)
    533 {
    534   int 				fd;
    535   char				buffer[1024];
    536   char				*p;
    537   size_t			idx, l=strlen(pid);
    538   static struct process_info	process;
    539 
    540 #if 1
    541   process.s_context = vc_get_task_xid(atoi(pid));
    542 #else
    543 #  warning Compiling in debug-code
    544   process.s_context = random()%6;
    545 #endif
    546 
    547   if (process.s_context==VC_NOCTX) {
    548     int		err=errno;
    549     if (err != ESRCH) {
    550       WRITE_MSG(2, "vc_get_task_xid(");
    551       WRITE_STR(2, pid);
    552       WRITE_MSG(2, "): ");
    553       WRITE_STR(2, strerror(err));
    554       WRITE_MSG(2, "\n");
    555     }
    556 
    557     return 0;
    558   }
    559   
    560   memcpy(buffer,     "/proc/", 6); idx  = 6;
    561   memcpy(buffer+idx, pid,      l); idx += l;
    562   memcpy(buffer+idx, "/stat",  6);
    563 	
    564     // open the /proc/#/stat file
    565   if ((fd = open(buffer, O_RDONLY, 0)) == -1)
    566     return NULL;
    567     // put the file in a buffer
    568   if (read(fd, buffer, sizeof(buffer)) < 1)
    569     return NULL;
    570 
    571   close(fd);
    572 
    573   p   = strchr(buffer, ')');		// go after the PID (process_name)
    574   for (idx = 0; idx<12 && *p!='\0'; ++p)
    575     if ((*p)==' ') ++idx;
    576 
    577   process.utime  = toMsec(strtol(p,   &p, 10));
    578   process.stime  = toMsec(strtol(p+1, &p, 10));
    579   process.cutime = toMsec(strtol(p+1, &p, 10));
    580   process.cstime = toMsec(strtol(p+1, &p, 10));
    581 
    582   for (idx = 0; idx<5 && *p!='\0'; ++p)
    583     if ((*p)==' ') ++idx;
    584 
    585   process.start_time = toMsec(strtol(p,   &p, 10));
    586   process.VmSize     = strtol(p+1, &p, 10);
    587   process.VmRSS      = strtol(p+1, &p, 10);
    588 
    589   //printf("pid=%s, start_time=%llu\n", pid, process.start_time);
    590   return &process;
    591 }
    592 
    593 static size_t
    594 fillUintZero(char *buf, unsigned long val, size_t cnt)
    595 {
    596   size_t	l;
    597   
    598   l = utilvserver_fmt_ulong(buf, val);
    599   if (l<cnt) {
    600     memmove(buf+cnt-l, buf, l);
    601     memset(buf, '0', cnt-l);
    602   }
    603   buf[cnt] = '\0';
    604 
    605   return cnt;
    606 }
    607 
    608 static void
    609 shortenMem(char *buf, unsigned long val)
    610 {
    611   char const *	SUFFIXES[] = { " ", "K", "M", "G", "T", "+" };
    612   char		tmp[16];
    613   char const *	suffix = "+";
    614   size_t	i, l;
    615   unsigned int	mod = 0;
    616 
    617   for (i=0; i<6; ++i) {
    618     if (val<1000) {
    619       suffix = SUFFIXES[i];
    620       break;
    621     }
    622     mod   = 10*(val & 1023)/1024;
    623     val >>= 10;
    624   }
    625 
    626   if (val >9999) val=9999;
    627   if (val>=1000) mod=0;
    628 
    629   l = utilvserver_fmt_ulong(tmp, val);
    630   if (mod!=0) {
    631     tmp[l++] = '.';
    632     l += utilvserver_fmt_ulong(tmp+l, mod);
    633   }
    634   i = 7-l-strlen(suffix);
    635   
    636   memcpy(buf+i,   tmp, l);
    637   memcpy(buf+i+l, suffix, strlen(suffix));
    638 }
    639 
    640 static void
    641 shortenTime(char *buf, uint64_t t)
    642 {
    643   char		tmp[32];
    644   char		*ptr = tmp;
    645 
    646   unsigned long	hh, mm, ss, ms;
    647 
    648   ms = t % 1000;
    649   t /= 1000;
    650 
    651   ss = t%60;
    652   t /= 60;
    653   mm = t%60;
    654   t /= 60;
    655   hh = t%24;
    656   t /= 24;
    657 
    658   if (t>999*999) {
    659     memcpy(ptr, "INVALID", 7);
    660     ptr   += 7;
    661   }
    662   else if (t>999) {
    663     ptr   += utilvserver_fmt_ulong(ptr, t/365);
    664     *ptr++ = 'y';
    665     ptr   += fillUintZero(ptr, t%365, 2);
    666     *ptr++ = 'd';
    667     ptr   += fillUintZero(ptr, hh, 2);
    668   }    
    669   else if (t>0) {
    670     ptr   += utilvserver_fmt_ulong(ptr, t);
    671     *ptr++ = 'd';
    672     ptr   += fillUintZero(ptr, hh, 2);
    673     *ptr++ = 'h';
    674     ptr   += fillUintZero(ptr, mm, 2);
    675   }
    676   else if (hh>0) {
    677     ptr   += utilvserver_fmt_ulong(ptr, hh);
    678     *ptr++ = 'h';
    679     ptr   += fillUintZero(ptr, mm, 2);
    680     *ptr++ = 'm';
    681     ptr   += fillUintZero(ptr, ss, 2);    
    682   }
    683   else {
    684     ptr   += utilvserver_fmt_ulong(ptr, mm);
    685     *ptr++ = 'm';
    686     ptr   += fillUintZero(ptr, ss, 2);
    687     *ptr++ = 's';
    688     ptr   += fillUintZero(ptr, ms, 2);
    689   }
    690 
    691   *ptr = ' ';
    692   memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
    693 }
    694 
    695 static char *
    696 formatName(char *dst, vcCfgStyle style, char const *name)
    697 {
    698   size_t		len;
    699   
    700   if (name==0) name = "";
    701   len = strlen(name);
    702 
    703   switch (style) {
    704     case vcCFG_LEGACY	:
    705       len    = MIN(len, 18);
    706       *dst++ = '[';
    707       memcpy(dst, name, len);
    708       dst   += len;
    709       *dst++ = ']';
    710       break;
    711 
    712     default		:
    713       len    = MIN(len, 20);
    714       memcpy(dst, name, len);
    715       dst   += len;
    716       break;
    717   }
    718 
    719   return dst;
    720 }
    721 
    722 static void
    723 showContexts(struct Vector const *vec)
    724 {
    725   uint64_t			uptime  = getUptime();
    726   struct XidData const *	ptr     = Vector_begin_const(vec);
    727   struct XidData const * const	end_ptr = Vector_end_const(vec);
    728   
    729 
    730   WRITE_MSG(1, "CTX   PROC    VSZ    RSS  userTIME   sysTIME    UPTIME NAME\n");
    731   for (; ptr<end_ptr; ++ptr) {
    732     char	buf[sizeof(xid_t)*3 + 512];
    733     char	tmp[sizeof(int)*3 + 2];
    734     size_t	l;
    735 
    736     memset(buf, ' ', sizeof(buf));
    737     l = utilvserver_fmt_long(buf, ptr->xid);
    738     l = utilvserver_fmt_long(tmp, ptr->process_count);
    739     memcpy(buf+10-l, tmp, l);
    740 
    741     shortenMem (buf+10, ptr->VmSize_total);
    742     shortenMem (buf+17, ptr->VmRSS_total*pagesize);
    743     shortenTime(buf+24, ptr->utime_total);
    744     shortenTime(buf+34, ptr->stime_total);
    745     //printf("%llu, %llu\n", uptime, ptr->start_time_oldest);
    746     shortenTime(buf+44, uptime - ptr->start_time_oldest);
    747 
    748     formatName(buf+55, ptr->cfgstyle, ptr->name)[0] = '\0';
    749 
    750     Vwrite(1, buf, strlen(buf));
    751     Vwrite(1, "\n", 1);
    752   }
    753 }
    754 
    755 static void
    756 fillName(void *obj_v, void UNUSED * a)
    757 {
    758   struct XidData *	obj = obj_v;
    759 
    760   switch (obj->xid) {
    761     case 0		:
    762       obj->cfgstyle = vcCFG_NONE;
    763       obj->name     = strdup("root server");
    764       break;
    765 
    766     case 1		:
    767       obj->cfgstyle = vcCFG_NONE;
    768       obj->name     = strdup("monitoring server");
    769       break;
    770 
    771     default		: {
    772       char *		cfgpath;
    773 
    774       obj->cfgstyle  = vcCFG_AUTO;
    775 
    776       if ((cfgpath   = vc_getVserverByCtx(obj->xid, &obj->cfgstyle, 0))==0 ||
    777 	  (obj->name = vc_getVserverName(cfgpath, obj->cfgstyle))==0) {
    778 	obj->name     = 0;
    779 	obj->cfgstyle = vcCFG_NONE;
    780       }
    781 
    782       free(cfgpath);
    783 
    784       break;
    785     }
    786   }
    787 }
    788 
    789 static void UNUSED
    790 freeXidData(void *obj_v, void UNUSED * a)
    791 {
    792   struct XidData *	obj = obj_v;
    793 
    794   free(const_cast(char *)(obj->name));
    795 }
    796 
    797 int main(int argc, char **argv)
    798 {
    799   DIR *			proc_dir;
    800   struct dirent*	dir_entry;
    801   pid_t			my_pid;
    802   struct Vector		xid_data;
    803   char const *		errptr;
    804 
    805   while (1) {
    806     int		c = getopt_long(argc, argv, "+O:", CMDLINE_OPTIONS, 0);
    807     if (c==-1) break;
    808 
    809     switch (c) {
    810       case CMD_HELP	:  showHelp(argv[0]);
    811       case CMD_VERSION	:  showVersion();
    812       case 'O'		:  break;
    813       default		:
    814 	WRITE_MSG(2, "Try '");
    815 	WRITE_STR(2, argv[0]);
    816 	WRITE_MSG(2, " --help' for more information.\n");
    817 	return EXIT_FAILURE;
    818 	break;
    819     }
    820   }
    821     
    822   if (optind!=argc) {
    823     WRITE_MSG(2, "Unknown parameter, use '--help' for more information\n");
    824     return EXIT_FAILURE;
    825   }
    826 
    827   if (hertz==0x42)    initHertz();
    828   if (pagesize==0x42) initPageSize();
    829   
    830   Vector_init(&xid_data, sizeof(struct XidData));
    831 
    832   if (vc_isSupported(vcFEATURE_VSTAT)) {
    833     unsigned long xid;
    834     Echdir(PROC_VIRT_DIR_NAME);
    835     proc_dir = Eopendir(".");
    836     while ((dir_entry = readdir(proc_dir)) != NULL) {
    837       if (!isNumberUnsigned(dir_entry->d_name, &xid, false))
    838 	continue;
    839 
    840       registerXidVstat(&xid_data, xid);
    841     }
    842     closedir(proc_dir);
    843   }
    844   else {
    845     void (*handler)(struct Vector *vec, struct process_info *process);
    846 
    847     my_pid = getpid();
    848 
    849     if (!switchToWatchXid(&errptr)) {
    850       perror(errptr);
    851       exit(1);
    852     }
    853 
    854     if (access("/proc/uptime",R_OK)==-1 && errno==ENOENT)
    855       WRITE_MSG(2,
    856 	      "WARNING: can not access /proc/uptime. Usually, this is caused by\n"
    857 	      "         procfs-security. Please read the FAQ for more details\n"
    858 	      "         http://linux-vserver.org/Proc-Security\n");
    859 
    860     if (vc_isSupported(vcFEATURE_MEMCG))
    861       handler = registerXidCgroups;
    862     else
    863       handler = registerXid;
    864 
    865     Echdir(PROC_DIR_NAME);
    866     proc_dir = Eopendir(".");
    867     while ((dir_entry = readdir(proc_dir)) != NULL)
    868     {
    869       // select only process file
    870       if (!isdigit(*dir_entry->d_name))
    871         continue;
    872 
    873       if (atoi(dir_entry->d_name) != my_pid) {
    874 	struct process_info *	info = get_process_info(dir_entry->d_name);
    875 	if (info)
    876 	  handler(&xid_data, info);
    877       }
    878     }
    879     closedir(proc_dir);
    880   }
    881 
    882   Vector_foreach(&xid_data, fillName, 0);
    883 
    884     // output the ctx_list	
    885   showContexts(&xid_data);
    886 
    887 #ifndef NDEBUG
    888   Vector_foreach(&xid_data, freeXidData, 0);
    889   Vector_free(&xid_data);
    890 #endif
    891   
    892   return 0;
    893 }