gentoo: use /var/run for new /run compatibility
[util-vserver.git] / src / vserver-stat.c
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     WRITE_MSG(2, "vc_get_task_xid(");
550     WRITE_STR(2, pid);
551     WRITE_MSG(2, "): ");
552     WRITE_STR(2, strerror(err));
553     WRITE_MSG(2, "\n");
554
555     return 0;
556   }
557   
558   memcpy(buffer,     "/proc/", 6); idx  = 6;
559   memcpy(buffer+idx, pid,      l); idx += l;
560   memcpy(buffer+idx, "/stat",  6);
561         
562     // open the /proc/#/stat file
563   if ((fd = open(buffer, O_RDONLY, 0)) == -1)
564     return NULL;
565     // put the file in a buffer
566   if (read(fd, buffer, sizeof(buffer)) < 1)
567     return NULL;
568
569   close(fd);
570
571   p   = strchr(buffer, ')');            // go after the PID (process_name)
572   for (idx = 0; idx<12 && *p!='\0'; ++p)
573     if ((*p)==' ') ++idx;
574
575   process.utime  = toMsec(strtol(p,   &p, 10));
576   process.stime  = toMsec(strtol(p+1, &p, 10));
577   process.cutime = toMsec(strtol(p+1, &p, 10));
578   process.cstime = toMsec(strtol(p+1, &p, 10));
579
580   for (idx = 0; idx<5 && *p!='\0'; ++p)
581     if ((*p)==' ') ++idx;
582
583   process.start_time = toMsec(strtol(p,   &p, 10));
584   process.VmSize     = strtol(p+1, &p, 10);
585   process.VmRSS      = strtol(p+1, &p, 10);
586
587   //printf("pid=%s, start_time=%llu\n", pid, process.start_time);
588   return &process;
589 }
590
591 static size_t
592 fillUintZero(char *buf, unsigned long val, size_t cnt)
593 {
594   size_t        l;
595   
596   l = utilvserver_fmt_ulong(buf, val);
597   if (l<cnt) {
598     memmove(buf+cnt-l, buf, l);
599     memset(buf, '0', cnt-l);
600   }
601   buf[cnt] = '\0';
602
603   return cnt;
604 }
605
606 static void
607 shortenMem(char *buf, unsigned long val)
608 {
609   char const *  SUFFIXES[] = { " ", "K", "M", "G", "T", "+" };
610   char          tmp[16];
611   char const *  suffix = "+";
612   size_t        i, l;
613   unsigned int  mod = 0;
614
615   for (i=0; i<6; ++i) {
616     if (val<1000) {
617       suffix = SUFFIXES[i];
618       break;
619     }
620     mod   = 10*(val & 1023)/1024;
621     val >>= 10;
622   }
623
624   if (val >9999) val=9999;
625   if (val>=1000) mod=0;
626
627   l = utilvserver_fmt_ulong(tmp, val);
628   if (mod!=0) {
629     tmp[l++] = '.';
630     l += utilvserver_fmt_ulong(tmp+l, mod);
631   }
632   i = 7-l-strlen(suffix);
633   
634   memcpy(buf+i,   tmp, l);
635   memcpy(buf+i+l, suffix, strlen(suffix));
636 }
637
638 static void
639 shortenTime(char *buf, uint64_t t)
640 {
641   char          tmp[32];
642   char          *ptr = tmp;
643
644   unsigned long hh, mm, ss, ms;
645
646   ms = t % 1000;
647   t /= 1000;
648
649   ss = t%60;
650   t /= 60;
651   mm = t%60;
652   t /= 60;
653   hh = t%24;
654   t /= 24;
655
656   if (t>999*999) {
657     memcpy(ptr, "INVALID", 7);
658     ptr   += 7;
659   }
660   else if (t>999) {
661     ptr   += utilvserver_fmt_ulong(ptr, t/365);
662     *ptr++ = 'y';
663     ptr   += fillUintZero(ptr, t%365, 2);
664     *ptr++ = 'd';
665     ptr   += fillUintZero(ptr, hh, 2);
666   }    
667   else if (t>0) {
668     ptr   += utilvserver_fmt_ulong(ptr, t);
669     *ptr++ = 'd';
670     ptr   += fillUintZero(ptr, hh, 2);
671     *ptr++ = 'h';
672     ptr   += fillUintZero(ptr, mm, 2);
673   }
674   else if (hh>0) {
675     ptr   += utilvserver_fmt_ulong(ptr, hh);
676     *ptr++ = 'h';
677     ptr   += fillUintZero(ptr, mm, 2);
678     *ptr++ = 'm';
679     ptr   += fillUintZero(ptr, ss, 2);    
680   }
681   else {
682     ptr   += utilvserver_fmt_ulong(ptr, mm);
683     *ptr++ = 'm';
684     ptr   += fillUintZero(ptr, ss, 2);
685     *ptr++ = 's';
686     ptr   += fillUintZero(ptr, ms, 2);
687   }
688
689   *ptr = ' ';
690   memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
691 }
692
693 static char *
694 formatName(char *dst, vcCfgStyle style, char const *name)
695 {
696   size_t                len;
697   
698   if (name==0) name = "";
699   len = strlen(name);
700
701   switch (style) {
702     case vcCFG_LEGACY   :
703       len    = MIN(len, 18);
704       *dst++ = '[';
705       memcpy(dst, name, len);
706       dst   += len;
707       *dst++ = ']';
708       break;
709
710     default             :
711       len    = MIN(len, 20);
712       memcpy(dst, name, len);
713       dst   += len;
714       break;
715   }
716
717   return dst;
718 }
719
720 static void
721 showContexts(struct Vector const *vec)
722 {
723   uint64_t                      uptime  = getUptime();
724   struct XidData const *        ptr     = Vector_begin_const(vec);
725   struct XidData const * const  end_ptr = Vector_end_const(vec);
726   
727
728   WRITE_MSG(1, "CTX   PROC    VSZ    RSS  userTIME   sysTIME    UPTIME NAME\n");
729   for (; ptr<end_ptr; ++ptr) {
730     char        buf[sizeof(xid_t)*3 + 512];
731     char        tmp[sizeof(int)*3 + 2];
732     size_t      l;
733
734     memset(buf, ' ', sizeof(buf));
735     l = utilvserver_fmt_long(buf, ptr->xid);
736     l = utilvserver_fmt_long(tmp, ptr->process_count);
737     memcpy(buf+10-l, tmp, l);
738
739     shortenMem (buf+10, ptr->VmSize_total);
740     shortenMem (buf+17, ptr->VmRSS_total*pagesize);
741     shortenTime(buf+24, ptr->utime_total);
742     shortenTime(buf+34, ptr->stime_total);
743     //printf("%llu, %llu\n", uptime, ptr->start_time_oldest);
744     shortenTime(buf+44, uptime - ptr->start_time_oldest);
745
746     formatName(buf+55, ptr->cfgstyle, ptr->name)[0] = '\0';
747
748     Vwrite(1, buf, strlen(buf));
749     Vwrite(1, "\n", 1);
750   }
751 }
752
753 static void
754 fillName(void *obj_v, void UNUSED * a)
755 {
756   struct XidData *      obj = obj_v;
757
758   switch (obj->xid) {
759     case 0              :
760       obj->cfgstyle = vcCFG_NONE;
761       obj->name     = strdup("root server");
762       break;
763
764     case 1              :
765       obj->cfgstyle = vcCFG_NONE;
766       obj->name     = strdup("monitoring server");
767       break;
768
769     default             : {
770       char *            cfgpath;
771
772       obj->cfgstyle  = vcCFG_AUTO;
773
774       if ((cfgpath   = vc_getVserverByCtx(obj->xid, &obj->cfgstyle, 0))==0 ||
775           (obj->name = vc_getVserverName(cfgpath, obj->cfgstyle))==0) {
776         obj->name     = 0;
777         obj->cfgstyle = vcCFG_NONE;
778       }
779
780       free(cfgpath);
781
782       break;
783     }
784   }
785 }
786
787 static void UNUSED
788 freeXidData(void *obj_v, void UNUSED * a)
789 {
790   struct XidData *      obj = obj_v;
791
792   free(const_cast(char *)(obj->name));
793 }
794
795 int main(int argc, char **argv)
796 {
797   DIR *                 proc_dir;
798   struct dirent*        dir_entry;
799   pid_t                 my_pid;
800   struct Vector         xid_data;
801   char const *          errptr;
802
803   while (1) {
804     int         c = getopt_long(argc, argv, "+O:", CMDLINE_OPTIONS, 0);
805     if (c==-1) break;
806
807     switch (c) {
808       case CMD_HELP     :  showHelp(argv[0]);
809       case CMD_VERSION  :  showVersion();
810       case 'O'          :  break;
811       default           :
812         WRITE_MSG(2, "Try '");
813         WRITE_STR(2, argv[0]);
814         WRITE_MSG(2, " --help' for more information.\n");
815         return EXIT_FAILURE;
816         break;
817     }
818   }
819     
820   if (optind!=argc) {
821     WRITE_MSG(2, "Unknown parameter, use '--help' for more information\n");
822     return EXIT_FAILURE;
823   }
824
825   if (hertz==0x42)    initHertz();
826   if (pagesize==0x42) initPageSize();
827   
828   Vector_init(&xid_data, sizeof(struct XidData));
829
830   if (vc_isSupported(vcFEATURE_VSTAT)) {
831     unsigned long xid;
832     Echdir(PROC_VIRT_DIR_NAME);
833     proc_dir = Eopendir(".");
834     while ((dir_entry = readdir(proc_dir)) != NULL) {
835       if (!isNumberUnsigned(dir_entry->d_name, &xid, false))
836         continue;
837
838       registerXidVstat(&xid_data, xid);
839     }
840     closedir(proc_dir);
841   }
842   else {
843     void (*handler)(struct Vector *vec, struct process_info *process);
844
845     my_pid = getpid();
846
847     if (!switchToWatchXid(&errptr)) {
848       perror(errptr);
849       exit(1);
850     }
851
852     if (access("/proc/uptime",R_OK)==-1 && errno==ENOENT)
853       WRITE_MSG(2,
854               "WARNING: can not access /proc/uptime. Usually, this is caused by\n"
855               "         procfs-security. Please read the FAQ for more details\n"
856               "         http://linux-vserver.org/Proc-Security\n");
857
858     if (vc_isSupported(vcFEATURE_MEMCG))
859       handler = registerXidCgroups;
860     else
861       handler = registerXid;
862
863     Echdir(PROC_DIR_NAME);
864     proc_dir = Eopendir(".");
865     while ((dir_entry = readdir(proc_dir)) != NULL)
866     {
867       // select only process file
868       if (!isdigit(*dir_entry->d_name))
869         continue;
870
871       if (atoi(dir_entry->d_name) != my_pid) {
872         struct process_info *   info = get_process_info(dir_entry->d_name);
873         if (info)
874           handler(&xid_data, info);
875       }
876     }
877     closedir(proc_dir);
878   }
879
880   Vector_foreach(&xid_data, fillName, 0);
881
882     // output the ctx_list      
883   showContexts(&xid_data);
884
885 #ifndef NDEBUG
886   Vector_foreach(&xid_data, freeXidData, 0);
887   Vector_free(&xid_data);
888 #endif
889   
890   return 0;
891 }