merge with 0.30.213
[util-vserver.git] / src / vserver-stat.c
1 // $Id: vserver-stat.c 2450 2007-01-10 19:27:56Z dhozac $
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
28 #include <ensc_vector/vector.h>
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <fcntl.h>
34 #include <ctype.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <dirent.h>
38 #include <string.h>
39 #include <errno.h>
40 #include <syscall.h>
41 #include <time.h>
42 #include <stdbool.h>
43 #include <getopt.h>
44 #include <sys/param.h>
45 #include <sys/resource.h>
46
47 #define ENSC_WRAPPERS_DIRENT    1
48 #define ENSC_WRAPPERS_VSERVER   1
49 #define ENSC_WRAPPERS_FCNTL     1
50 #define ENSC_WRAPPERS_UNISTD    1
51 #include "wrappers.h"
52
53 #define PROC_DIR_NAME "/proc"
54 #define PROC_VIRT_DIR_NAME "/proc/virtual"
55 #define CTX_DIR_NAME "/var/run/vservers/"
56 #define CTX_NAME_MAX_LEN 50
57
58 int     wrapper_exit_code = 1;
59
60 #ifndef AT_CLKTCK
61 #define AT_CLKTCK       17    /* frequency of times() */
62 #endif
63
64 static unsigned long    hertz   =0x42;
65 static unsigned long    pagesize=0x42;
66
67 struct XidData
68 {
69     xid_t               xid;
70     int                 process_count;
71     int                 VmSize_total;
72     int                 VmRSS_total;
73     uint64_t            start_time_oldest;
74     uint64_t            stime_total, utime_total;
75
76     vcCfgStyle          cfgstyle;
77     char const *        name;
78 };
79
80 struct process_info
81 {
82         long            VmSize;         // number of pages of virtual memory
83         long            VmRSS;          // resident set size from /proc/#/stat
84         uint64_t        start_time;     // start time of process -- milliseconds since 1-1-70
85         uint64_t        stime, utime;   // kernel & user-mode CPU time accumulated by process
86         uint64_t        cstime, cutime; // cumulative time of process and reaped children
87         xid_t           s_context;
88 };
89
90 struct ArgInfo {
91     enum { tpUNSET, tpCTX, tpPID }      type;
92     xid_t               ctx;
93     pid_t               pid;
94     unsigned int        interval;
95     bool                shutdown;
96     bool                omit_init;
97     size_t              argc;
98     char * const *      argv;
99 };
100
101 #define CMD_HELP                0x1000
102 #define CMD_VERSION             0x1001
103
104 struct option const
105 CMDLINE_OPTIONS[] = {
106   { "help",     no_argument,  0, CMD_HELP },
107   { "version",  no_argument,  0, CMD_VERSION },
108   { "sort",     required_argument, 0, 'O' },
109   {0,0,0,0}
110 };
111
112 static void
113 showHelp(char const *cmd)
114 {
115   WRITE_MSG(1, "Usage:  ");
116   WRITE_STR(1, cmd);
117   WRITE_MSG(1,
118             "\n"
119             "Show informations about all the active context.\n\n"
120             "   CTX#            Context number\n"
121             "                   #0 = root context\n"
122             "                   #1 = monitoring context\n"
123             "   PROC QTY        Quantity of processes in each context\n"
124             "   VSZ             Number of pages of virtual memory\n"
125             "   RSS             Resident set size\n"
126             "   userTIME        User-mode CPU time accumulated\n"
127             "   sysTIME         Kernel-mode CPU time accumulated\n"
128             "   UPTIME          Uptime/context\n"
129             "   NAME            Virtual server name\n"
130             "\n");
131   exit(0);
132 }
133
134 static void
135 showVersion()
136 {
137   WRITE_MSG(1,
138             "vserver-stat " VERSION " -- show virtual context statistics\n"
139             "This program is part of " PACKAGE_STRING "\n\n"
140             "Copyright (C) 2003,2005 Enrico Scholz\n"
141             VERSION_COPYRIGHT_DISCLAIMER);
142   exit(0);
143 }
144
145
146 // return uptime (in ms) from /proc/uptime
147 static uint64_t
148 getUptime()
149 {
150   int           fd;
151   char          buffer[64];
152   char *        errptr;
153   size_t        len;
154   uint64_t      secs;
155   uint32_t      msecs=0;
156
157     // open the /proc/uptime file
158   fd  = EopenD("/proc/uptime", O_RDONLY, 0);
159   len = Eread(fd, buffer, sizeof buffer);
160
161   if (len==sizeof(buffer)) {
162     WRITE_MSG(2, "Too much data in /proc/uptime; aborting...\n");
163     exit(1);
164   }
165   Eclose(fd);
166
167   while (len>0 && buffer[len-1]=='\n') --len;
168   buffer[len] = '\0';
169
170   secs = strtol(buffer, &errptr, 10);
171   if (*errptr!='.') errptr = buffer;
172   else {
173     unsigned int        mult;
174     switch (strlen(errptr+1)) {
175       case 0    :  mult = 1000; break;
176       case 1    :  mult = 100;  break;
177       case 2    :  mult = 10;   break;
178       case 3    :  mult = 1;    break;
179       default   :  mult = 0;    break;
180     }
181     msecs = strtol(errptr+1, &errptr, 10) * mult;
182   }
183
184   if ((*errptr!='\0' && *errptr!=' ') || errptr==buffer) {
185     WRITE_MSG(2, "Bad data in /proc/uptime\n");
186     exit(1);
187   }
188
189   return secs*1000 + msecs;
190 }
191
192 static int
193 cmpData(void const *xid_v, void const *map_v)
194 {
195   xid_t const * const                   xid = xid_v;
196   struct XidData const * const          map = map_v;
197   int   res = *xid - map->xid;
198
199   return res;
200 }
201
202 static void
203 registerXid(struct Vector *vec, struct process_info *process)
204 {
205   struct XidData        *res;
206
207   res = Vector_search(vec, &process->s_context, cmpData);
208   if (res==0) {
209     res = Vector_insert(vec, &process->s_context, cmpData);
210     res->xid           = process->s_context;
211     res->process_count = 0;
212     res->VmSize_total  = 0;
213     res->VmRSS_total   = 0;
214     res->utime_total   = 0;
215     res->stime_total   = 0;
216     res->start_time_oldest = process->start_time;
217   }
218
219   ++res->process_count;
220   res->VmSize_total += process->VmSize;
221   res->VmRSS_total  += process->VmRSS;
222   res->utime_total  += process->utime + process->cutime;
223   res->stime_total  += process->stime + process->cstime;
224
225   res->start_time_oldest = MIN(res->start_time_oldest, process->start_time);
226 }
227
228 static void
229 registerXidVstat(struct Vector *vec, unsigned long xid_l)
230 {
231   xid_t                 xid = (xid_t) xid_l;
232   struct XidData        *res;
233   struct vc_rlimit_stat limit[3];
234   struct vc_virt_stat   vstat;
235   struct vc_sched_info  sched;
236   int                   cpu;
237
238   res = Vector_search(vec, &xid, cmpData);
239   if (res!=0) {
240     WRITE_MSG(2, "Duplicate xid found?!\n");
241     return;
242   }
243   if (vc_virt_stat(xid, &vstat) == -1) {
244     perror("vc_virt_stat()");
245     return;
246   }
247   if (vc_rlimit_stat(xid, RLIMIT_NPROC, &limit[0]) == -1) {
248     perror("vc_rlimit_stat(RLIMIT_NRPOC)");
249     return;
250   }
251   if (vc_rlimit_stat(xid, RLIMIT_AS, &limit[1]) == -1) {
252     perror("vc_rlimit_stat(RLIMIT_AS)");
253     return;
254   }
255   if (vc_rlimit_stat(xid, RLIMIT_RSS, &limit[2]) == -1) {
256     perror("vc_rlimit_stat(RLIMIT_RSS)");
257     return;
258   }
259
260   res                   = Vector_insert(vec, &xid, cmpData);
261   res->xid              = xid;
262
263   res->process_count    = limit[0].value;
264   res->VmSize_total     = limit[1].value * pagesize;
265   res->VmRSS_total      = limit[2].value;
266   res->start_time_oldest= getUptime() - vstat.uptime/1000000;
267
268   res->utime_total      = 0;
269   res->stime_total      = 0;
270   // XXX: arbitrary CPU limit.
271   for (cpu = 0; cpu < 1024; cpu++) {
272     sched.cpu_id = cpu;
273     sched.bucket_id = 0;
274     if (vc_sched_info(xid, &sched) == -1)
275       break;
276
277     res->utime_total    += sched.user_msec;
278     res->stime_total    += sched.sys_msec;
279   }
280 }
281
282 static inline uint64_t
283 toMsec(uint64_t v)
284 {
285   return v*1000llu/hertz;
286 }
287
288
289 // shamelessly stolen from procps...
290 static unsigned long
291 find_elf_note(unsigned long findme){
292   unsigned long *ep = (unsigned long *)environ;
293   while(*ep++);
294   while(*ep){
295     if(ep[0]==findme) return ep[1];
296     ep+=2;
297   }
298   return (unsigned long)(-1);
299 }
300
301 static void initHertz()    __attribute__((__constructor__));
302 static void initPageSize() __attribute__((__constructor__));
303
304 static void
305 initHertz()
306 {
307   hertz = find_elf_note(AT_CLKTCK);
308   if (hertz==(unsigned long)(-1))
309     hertz = sysconf(_SC_CLK_TCK);
310 }
311
312 static void
313 initPageSize()
314 {
315   pagesize = sysconf(_SC_PAGESIZE);
316 }
317
318 // open the process's status file to get the ctx number, and other stat
319 struct process_info *
320 get_process_info(char *pid)
321 {
322   int                           fd;
323   char                          buffer[1024];
324   char                          *p;
325   size_t                        idx, l=strlen(pid);
326   static struct process_info    process;
327
328 #if 1
329   process.s_context = vc_get_task_xid(atoi(pid));
330 #else
331 #  warning Compiling in debug-code
332   process.s_context = random()%6;
333 #endif
334
335   if (process.s_context==VC_NOCTX) {
336     int         err=errno;
337     WRITE_MSG(2, "vc_get_task_xid(");
338     WRITE_STR(2, pid);
339     WRITE_MSG(2, "): ");
340     WRITE_STR(2, strerror(err));
341     WRITE_MSG(2, "\n");
342
343     return 0;
344   }
345   
346   memcpy(buffer,     "/proc/", 6); idx  = 6;
347   memcpy(buffer+idx, pid,      l); idx += l;
348   memcpy(buffer+idx, "/stat",  6);
349         
350     // open the /proc/#/stat file
351   if ((fd = open(buffer, O_RDONLY, 0)) == -1)
352     return NULL;
353     // put the file in a buffer
354   if (read(fd, buffer, sizeof(buffer)) < 1)
355     return NULL;
356
357   close(fd);
358
359   p   = strchr(buffer, ')');            // go after the PID (process_name)
360   for (idx = 0; idx<12 && *p!='\0'; ++p)
361     if ((*p)==' ') ++idx;
362
363   process.utime  = toMsec(strtol(p,   &p, 10));
364   process.stime  = toMsec(strtol(p+1, &p, 10));
365   process.cutime = toMsec(strtol(p+1, &p, 10));
366   process.cstime = toMsec(strtol(p+1, &p, 10));
367
368   for (idx = 0; idx<5 && *p!='\0'; ++p)
369     if ((*p)==' ') ++idx;
370
371   process.start_time = toMsec(strtol(p,   &p, 10));
372   process.VmSize     = strtol(p+1, &p, 10);
373   process.VmRSS      = strtol(p+1, &p, 10);
374
375   //printf("pid=%s, start_time=%llu\n", pid, process.start_time);
376   return &process;
377 }
378
379 static size_t
380 fillUintZero(char *buf, unsigned long val, size_t cnt)
381 {
382   size_t        l;
383   
384   l = utilvserver_fmt_ulong(buf, val);
385   if (l<cnt) {
386     memmove(buf+cnt-l, buf, l);
387     memset(buf, '0', cnt-l);
388   }
389   buf[cnt] = '\0';
390
391   return cnt;
392 }
393
394 static void
395 shortenMem(char *buf, unsigned long val)
396 {
397   char const *  SUFFIXES[] = { " ", "K", "M", "G", "T", "+" };
398   char          tmp[16];
399   char const *  suffix = "+";
400   size_t        i, l;
401   unsigned int  mod = 0;
402
403   for (i=0; i<6; ++i) {
404     if (val<1000) {
405       suffix = SUFFIXES[i];
406       break;
407     }
408     mod   = 10*(val & 1023)/1024;
409     val >>= 10;
410   }
411
412   if (val >9999) val=9999;
413   if (val>=1000) mod=0;
414
415   l = utilvserver_fmt_ulong(tmp, val);
416   if (mod!=0) {
417     tmp[l++] = '.';
418     l += utilvserver_fmt_ulong(tmp+l, mod);
419   }
420   i = 7-l-strlen(suffix);
421   
422   memcpy(buf+i,   tmp, l);
423   memcpy(buf+i+l, suffix, strlen(suffix));
424 }
425
426 static void
427 shortenTime(char *buf, uint64_t t)
428 {
429   char          tmp[32];
430   char          *ptr = tmp;
431
432   unsigned long hh, mm, ss, ms;
433
434   ms = t % 1000;
435   t /= 1000;
436
437   ss = t%60;
438   t /= 60;
439   mm = t%60;
440   t /= 60;
441   hh = t%24;
442   t /= 24;
443
444   if (t>999*999) {
445     memcpy(ptr, "INVALID", 7);
446     ptr   += 7;
447   }
448   else if (t>999) {
449     ptr   += utilvserver_fmt_ulong(ptr, t/365);
450     *ptr++ = 'y';
451     ptr   += fillUintZero(ptr, t%365, 2);
452     *ptr++ = 'd';
453     ptr   += fillUintZero(ptr, hh, 2);
454   }    
455   else if (t>0) {
456     ptr   += utilvserver_fmt_ulong(ptr, t);
457     *ptr++ = 'd';
458     ptr   += fillUintZero(ptr, hh, 2);
459     *ptr++ = 'h';
460     ptr   += fillUintZero(ptr, mm, 2);
461   }
462   else if (hh>0) {
463     ptr   += utilvserver_fmt_ulong(ptr, hh);
464     *ptr++ = 'h';
465     ptr   += fillUintZero(ptr, mm, 2);
466     *ptr++ = 'm';
467     ptr   += fillUintZero(ptr, ss, 2);    
468   }
469   else {
470     ptr   += utilvserver_fmt_ulong(ptr, mm);
471     *ptr++ = 'm';
472     ptr   += fillUintZero(ptr, ss, 2);
473     *ptr++ = 's';
474     ptr   += fillUintZero(ptr, ms, 2);
475   }
476
477   *ptr = ' ';
478   memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
479 }
480
481 static char *
482 formatName(char *dst, vcCfgStyle style, char const *name)
483 {
484   size_t                len;
485   
486   if (name==0) name = "";
487   len = strlen(name);
488
489   switch (style) {
490     case vcCFG_LEGACY   :
491       len    = MIN(len, 18);
492       *dst++ = '[';
493       memcpy(dst, name, len);
494       dst   += len;
495       *dst++ = ']';
496       break;
497
498     default             :
499       len    = MIN(len, 20);
500       memcpy(dst, name, len);
501       dst   += len;
502       break;
503   }
504
505   return dst;
506 }
507
508 static void
509 showContexts(struct Vector const *vec)
510 {
511   uint64_t                      uptime  = getUptime();
512   struct XidData const *        ptr     = Vector_begin_const(vec);
513   struct XidData const * const  end_ptr = Vector_end_const(vec);
514   
515
516   WRITE_MSG(1, "CTX   PROC    VSZ    RSS  userTIME   sysTIME    UPTIME NAME\n");
517   for (; ptr<end_ptr; ++ptr) {
518     char        buf[sizeof(xid_t)*3 + 512];
519     char        tmp[sizeof(int)*3 + 2];
520     size_t      l;
521
522     memset(buf, ' ', sizeof(buf));
523     l = utilvserver_fmt_long(buf, ptr->xid);
524     l = utilvserver_fmt_long(tmp, ptr->process_count);
525     memcpy(buf+10-l, tmp, l);
526
527     shortenMem (buf+10, ptr->VmSize_total);
528     shortenMem (buf+17, ptr->VmRSS_total*pagesize);
529     shortenTime(buf+24, ptr->utime_total);
530     shortenTime(buf+34, ptr->stime_total);
531     //printf("%llu, %llu\n", uptime, ptr->start_time_oldest);
532     shortenTime(buf+44, uptime - ptr->start_time_oldest);
533
534     formatName(buf+55, ptr->cfgstyle, ptr->name)[0] = '\0';
535
536     Vwrite(1, buf, strlen(buf));
537     Vwrite(1, "\n", 1);
538   }
539 }
540
541 static void
542 fillName(void *obj_v, void UNUSED * a)
543 {
544   struct XidData *      obj = obj_v;
545
546   switch (obj->xid) {
547     case 0              :
548       obj->cfgstyle = vcCFG_NONE;
549       obj->name     = strdup("root server");
550       break;
551
552     case 1              :
553       obj->cfgstyle = vcCFG_NONE;
554       obj->name     = strdup("monitoring server");
555       break;
556
557     default             : {
558       char *            cfgpath;
559
560       obj->cfgstyle  = vcCFG_AUTO;
561
562       if ((cfgpath   = vc_getVserverByCtx(obj->xid, &obj->cfgstyle, 0))==0 ||
563           (obj->name = vc_getVserverName(cfgpath, obj->cfgstyle))==0) {
564         obj->name     = 0;
565         obj->cfgstyle = vcCFG_NONE;
566       }
567
568       free(cfgpath);
569
570       break;
571     }
572   }
573 }
574
575 static void UNUSED
576 freeXidData(void *obj_v, void UNUSED * a)
577 {
578   struct XidData *      obj = obj_v;
579
580   free(const_cast(char *)(obj->name));
581 }
582
583 int main(int argc, char **argv)
584 {
585   DIR *                 proc_dir;
586   struct dirent*        dir_entry;
587   pid_t                 my_pid;
588   struct Vector         xid_data;
589   char const *          errptr;
590
591   while (1) {
592     int         c = getopt_long(argc, argv, "+O:", CMDLINE_OPTIONS, 0);
593     if (c==-1) break;
594
595     switch (c) {
596       case CMD_HELP     :  showHelp(argv[0]);
597       case CMD_VERSION  :  showVersion();
598       case 'O'          :  break;
599       default           :
600         WRITE_MSG(2, "Try '");
601         WRITE_STR(2, argv[0]);
602         WRITE_MSG(2, " --help' for more information.\n");
603         return EXIT_FAILURE;
604         break;
605     }
606   }
607     
608   if (optind!=argc) {
609     WRITE_MSG(2, "Unknown parameter, use '--help' for more information\n");
610     return EXIT_FAILURE;
611   }
612
613   if (hertz==0x42)    initHertz();
614   if (pagesize==0x42) initPageSize();
615   
616   Vector_init(&xid_data, sizeof(struct XidData));
617
618   if (vc_isSupported(vcFEATURE_VSTAT)) {
619     unsigned long xid;
620     Echdir(PROC_VIRT_DIR_NAME);
621     proc_dir = Eopendir(".");
622     while ((dir_entry = readdir(proc_dir)) != NULL) {
623       if (!isNumberUnsigned(dir_entry->d_name, &xid, false))
624         continue;
625
626       registerXidVstat(&xid_data, xid);
627     }
628     closedir(proc_dir);
629   }
630   else {
631     my_pid = getpid();
632
633     if (!switchToWatchXid(&errptr)) {
634       perror(errptr);
635       exit(1);
636     }
637
638     if (access("/proc/uptime",R_OK)==-1 && errno==ENOENT)
639       WRITE_MSG(2,
640               "WARNING: can not access /proc/uptime. Usually, this is caused by\n"
641               "         procfs-security. Please read the FAQ for more details\n"
642               "         http://linux-vserver.org/Proc-Security\n");
643
644     Echdir(PROC_DIR_NAME);
645     proc_dir = Eopendir(".");
646     while ((dir_entry = readdir(proc_dir)) != NULL)
647     {
648       // select only process file
649       if (!isdigit(*dir_entry->d_name))
650         continue;
651
652       if (atoi(dir_entry->d_name) != my_pid) {
653         struct process_info *   info = get_process_info(dir_entry->d_name);
654         if (info)
655           registerXid(&xid_data, info);
656       }
657     }
658     closedir(proc_dir);
659   }
660
661   Vector_foreach(&xid_data, fillName, 0);
662
663     // output the ctx_list      
664   showContexts(&xid_data);
665
666 #ifndef NDEBUG
667   Vector_foreach(&xid_data, freeXidData, 0);
668   Vector_free(&xid_data);
669 #endif
670   
671   return 0;
672 }