e3ead787d9ebb1696eba27d0987a3b8c5ac531d0
[util-vserver.git] / 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 /*
21         vserver-stat help you to see all the active context currently in the kernel
22         with some useful stat
23
24         Changelog:
25
26         2003-01-08 Jacques Gelinas: Shows vserver description
27         2002-02-28 Jacques Gelinas: Use dynamic system call
28         2002-06-05 Martial Rioux : fix memory output error
29         2002-12-05 Martial Rioux : fix output glitch
30         2001-11-29 added uptime/ctx stat
31         2001-11-20 added vmsize, rss, stime and utime stat
32 */
33 #ifdef HAVE_CONFIG_H
34 #  include <config.h>
35 #endif
36
37 #include "vserver.h"
38 #include "util.h"
39 #include "internal.h"
40
41 #include <ensc_vector/vector.h>
42
43 #include <stdio.h>
44 #include <stdlib.h>
45 #include <unistd.h>
46 #include <fcntl.h>
47 #include <ctype.h>
48 #include <sys/types.h>
49 #include <sys/stat.h>
50 #include <dirent.h>
51 #include <string.h>
52 #include <errno.h>
53 #include <syscall.h>
54 #include <time.h>
55 #include <stdbool.h>
56
57 #define ENSC_WRAPPERS_DIRENT    1
58 #define ENSC_WRAPPERS_VSERVER   1
59 #define ENSC_WRAPPERS_FCNTL     1
60 #define ENSC_WRAPPERS_UNISTD    1
61 #include "wrappers.h"
62
63 #define PROC_DIR_NAME "/proc"
64 #define CTX_DIR_NAME "/var/run/vservers/"
65 #define CTX_NAME_MAX_LEN 50
66
67 int     wrapper_exit_code = 1;
68
69 struct XidData
70 {
71     xid_t       xid;
72     int         process_count;
73     int         VmSize_total;
74     int         VmRSS_total;
75     long        start_time_oldest;
76     long        stime_total, utime_total;
77 };
78
79 struct process_info
80 {
81         long VmSize;            // number of pages of virtual memory
82         long VmRSS;             // resident set size from /proc/#/stat
83         long start_time;        // start time of process -- seconds since 1-1-70
84         long stime, utime;      // kernel & user-mode CPU time accumulated by process
85         long cstime, cutime;    // cumulative time of process and reaped children
86         xid_t s_context;
87 };
88
89 struct ArgInfo {
90     enum { tpUNSET, tpCTX, tpPID }      type;
91     xid_t               ctx;
92     pid_t               pid;
93     unsigned int        interval;
94     bool                shutdown;
95     bool                omit_init;
96     size_t              argc;
97     char * const *      argv;
98 };
99
100 static void
101 showHelp(int fd, char const *cmd, int res)
102 {
103   WRITE_MSG(fd, "Usage:  ");
104   WRITE_STR(fd, cmd);
105   WRITE_MSG(fd,
106             "\n"
107             "Show informations about all the active context.\n\n"
108             "   CTX#            Context number\n"
109             "                   #0 = root context\n"
110             "                   #1 = monitoring context\n"
111             "   PROC QTY        Quantity of processes in each context\n"
112             "   VSZ             Number of pages of virtual memory\n"
113             "   RSS             Resident set size\n"
114             "   userTIME        User-mode CPU time accumulated\n"
115             "   sysTIME         Kernel-mode CPU time accumulated\n"
116             "   UPTIME          Uptime/context\n"
117             "   NAME            Virtual server name\n"
118             "\n");
119   exit(res);
120 }
121
122 static void
123 showVersion()
124 {
125   WRITE_MSG(1,
126             "vserver-stat " VERSION " -- show virtual context statistics\n"
127             "This program is part of " PACKAGE_STRING "\n\n"
128             "Copyright (C) 2003 Enrico Scholz\n"
129             VERSION_COPYRIGHT_DISCLAIMER);
130   exit(0);
131 }
132
133
134 // return uptime (in ms) from /proc/uptime
135 static uint64_t
136 getUptime()
137 {
138   int           fd;
139   char          buffer[64];
140   char *        errptr;
141   size_t        len;
142   time_t        secs;
143   uint32_t      msecs=0;
144
145     // open the /proc/uptime file
146   fd  = EopenD("/proc/uptime", O_RDONLY, 0);
147   len = Eread(fd, buffer, sizeof buffer);
148
149   if (len==sizeof(buffer)) {
150     WRITE_MSG(2, "Too much data in /proc/uptime; aborting...\n");
151     exit(1);
152   }
153   Eclose(fd);
154
155   while (len>0 && buffer[len-1]=='\n') --len;
156   buffer[len] = '\0';
157
158   secs = strtol(buffer, &errptr, 10);
159   if (*errptr!='.') errptr = buffer;
160   else              msecs = strtol(errptr+1, &errptr, 10);
161
162   if ((*errptr!='\0' && *errptr!=' ') || errptr==buffer) {
163     WRITE_MSG(2, "Bad data in /proc/uptime\n");
164     exit(1);
165   }
166
167   return secs*100 + msecs;
168 }
169
170 static int
171 cmpData(void const *xid_v, void const *map_v)
172 {
173   xid_t const * const                   xid = xid_v;
174   struct XidData const * const          map = map_v;
175   int   res = *xid - map->xid;
176
177   return res;
178 }
179
180 static void
181 registerXid(struct Vector *vec, struct process_info *process)
182 {
183   struct XidData        *res;
184
185   res = Vector_search(vec, &process->s_context, cmpData);
186   if (res==0) {
187     res = Vector_insert(vec, &process->s_context, cmpData);
188     res->xid           = process->s_context;
189     res->process_count = 0;
190     res->VmSize_total  = 0;
191     res->VmRSS_total   = 0;
192     res->utime_total   = 0;
193     res->stime_total   = 0;
194     res->start_time_oldest = 0;
195   }
196
197   ++res->process_count;
198   res->VmSize_total += process->VmSize;
199   res->VmRSS_total  += process->VmRSS;
200   res->utime_total  += process->utime + process->cutime;
201   res->stime_total  += process->stime + process->cstime;
202
203   if (res->start_time_oldest == 0) // first entry
204     res->start_time_oldest = process->start_time;
205   else
206     if (res->start_time_oldest > process->start_time)
207       res->start_time_oldest = process->start_time;
208 }
209
210 // open the process's status file to get the ctx number, and other stat
211 struct process_info *
212 get_process_info(char *pid)
213 {
214   int                           fd;
215   char                          buffer[1024];
216   char                          *p;
217   size_t                        idx, l=strlen(pid);
218   static struct process_info    process;
219
220 #if 1
221   process.s_context = vc_get_task_xid(atoi(pid));
222 #else
223 #  warning Compiling in debug-code
224   process.s_context = random()%6;
225 #endif
226
227   if (process.s_context==VC_NOCTX) {
228     int         err=errno;
229     WRITE_MSG(2, "vc_get_task_xid(");
230     WRITE_STR(2, pid);
231     WRITE_MSG(2, "): ");
232     WRITE_STR(2, strerror(err));
233     WRITE_MSG(2, "\n");
234
235     return 0;
236   }
237   
238   memcpy(buffer,     "/proc/", 6); idx  = 6;
239   memcpy(buffer+idx, pid,      l); idx += l;
240   memcpy(buffer+idx, "/stat",  6);
241         
242     // open the /proc/#/stat file
243   if ((fd = open(buffer, O_RDONLY, 0)) == -1)
244     return NULL;
245     // put the file in a buffer
246   if (read(fd, buffer, sizeof(buffer)) < 1)
247     return NULL;
248
249   close(fd);
250
251   p   = strchr(buffer, ')');            // go after the PID (process_name)
252   for (idx = 0; idx<12 && *p!='\0'; ++p)
253     if ((*p)==' ') ++idx;
254
255   process.utime  = strtol(p,   &p, 10);
256   process.stime  = strtol(p+1, &p, 10);
257   process.cutime = strtol(p+1, &p, 10);
258   process.cstime = strtol(p+1, &p, 10);
259
260   for (idx = 0; idx<5 && *p!='\0'; ++p)
261     if ((*p)==' ') ++idx;
262
263   process.start_time = strtol(p,   &p, 10);
264   process.VmSize     = strtol(p+1, &p, 10);
265   process.VmRSS      = strtol(p+1, &p, 10);
266         
267   return &process;
268 }
269
270 static size_t
271 fillUintZero(char *buf, unsigned long val, size_t cnt)
272 {
273   size_t        l;
274   
275   l = utilvserver_fmt_ulong(buf, val);
276   if (l<cnt) {
277     memmove(buf+cnt-l, buf, l);
278     memset(buf, '0', cnt-l);
279   }
280   buf[cnt] = '\0';
281
282   return cnt;
283 }
284
285 static void
286 shortenMem(char *buf, unsigned long val)
287 {
288   char const *  SUFFIXES[] = { " ", "K", "M", "G", "T", "+" };
289   char          tmp[16];
290   char const *  suffix = "+";
291   size_t        i, l;
292   unsigned int  mod = 0;
293
294   for (i=0; i<6; ++i) {
295     if (val<1000) {
296       suffix = SUFFIXES[i];
297       break;
298     }
299     mod   = 10*(val & 1023)/1024;
300     val >>= 10;
301   }
302
303   if (val >9999) val=9999;
304   if (val>=1000) mod=0;
305
306   l = utilvserver_fmt_ulong(tmp, val);
307   if (mod!=0) {
308     tmp[l++] = '.';
309     l += utilvserver_fmt_ulong(tmp+l, mod);
310   }
311   i = 7-l-strlen(suffix);
312   
313   memcpy(buf+i,   tmp, l);
314   memcpy(buf+i+l, suffix, strlen(suffix));
315 }
316
317 static void
318 shortenTime(char *buf, uint64_t t)
319 {
320   char          tmp[32];
321   char          *ptr = tmp;
322
323   unsigned long hh, mm, ss, ms;
324
325   ms = t % 100;
326   t /= 100;
327
328   ss = t%60;
329   t /= 60;
330   mm = t%60;
331   t /= 60;
332   hh = t%60;
333   t /= 24;
334
335   if (t>0) {
336     ptr   += utilvserver_fmt_ulong(ptr, t);
337     *ptr++ = 'd';
338     ptr   += fillUintZero(ptr, hh, 2);
339     *ptr++ = 'h';
340     ptr   += fillUintZero(ptr, mm, 2);
341   }
342   else if (hh>0) {
343     ptr   += utilvserver_fmt_ulong(ptr, hh);
344     *ptr++ = 'h';
345     ptr   += fillUintZero(ptr, mm, 2);
346     *ptr++ = 'm';
347     ptr   += fillUintZero(ptr, ss, 2);    
348   }
349   else {
350     ptr   += utilvserver_fmt_ulong(ptr, mm);
351     *ptr++ = 'm';
352     ptr   += fillUintZero(ptr, ss, 2);
353     *ptr++ = 's';
354     ptr   += fillUintZero(ptr, ms, 2);
355   }
356
357   *ptr = ' ';
358   memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
359 }
360
361 static void
362 showContexts(struct Vector *vec)
363 {
364   uint64_t      uptime = getUptime();
365   struct XidData const *        ptr     = Vector_begin_const(vec);
366   struct XidData const * const  end_ptr = Vector_end_const(vec);
367   
368
369   WRITE_MSG(1, "CTX   PROC    VSZ    RSS  userTIME   sysTIME    UPTIME NAME\n");
370   for (; ptr<end_ptr; ++ptr) {
371     char        buf[sizeof(xid_t)*3 + 512];
372     char        tmp[sizeof(int)*3 + 2];
373     size_t      l;
374
375     memset(buf, ' ', sizeof(buf));
376     l = utilvserver_fmt_long(buf, ptr->xid);
377     l = utilvserver_fmt_long(tmp, ptr->process_count);
378     memcpy(buf+10-l, tmp, l);
379
380     shortenMem (buf+10, ptr->VmSize_total);
381     shortenMem (buf+17, ptr->VmRSS_total);
382     shortenTime(buf+24, ptr->utime_total);
383     shortenTime(buf+34, ptr->stime_total);
384     shortenTime(buf+44, uptime - ptr->start_time_oldest);
385
386     switch (ptr->xid) {
387       case 0            :  strncpy(buf+55, "root server",       20); break;
388       case 1            :  strncpy(buf+55, "monitoring server", 20); break;
389       default           : {
390         char *          name     = 0;
391         char *          cfgpath  = 0;
392         vcCfgStyle      cfgstyle = vcCFG_AUTO;
393
394         if ((cfgpath = vc_getVserverByCtx(ptr->xid, &cfgstyle, 0))!=0 &&
395             (name    = vc_getVserverName(cfgpath, cfgstyle))!=0)
396           strncpy(buf+55, name, 20);
397         free(name);
398         free(cfgpath);
399       }
400     }
401
402     write(1, buf, 80);
403     write(1, "\n", 1);
404   }
405 }
406
407 int main(int argc, char **argv)
408 {
409   DIR *                 proc_dir;
410   struct dirent*        dir_entry;
411   pid_t                 my_pid;
412   struct Vector         xid_data;
413
414   if (argc==2) {
415     if (strcmp(argv[1], "--help")   ==0) showHelp(1, argv[0], 0);
416     if (strcmp(argv[1], "--version")==0) showVersion();
417   }
418   if (argc!=1) {
419     WRITE_MSG(2, "Unknown parameter, use '--help' for more information\n");
420     return EXIT_FAILURE;
421   }
422
423     // do not include own stat
424   my_pid = getpid();
425
426 #if 1
427     // try to switch in context 1
428   if (vc_get_task_xid(0)!=1)
429     Evc_new_s_context(1, 0,0);
430 #else
431 #  warning Compiling in debug-code
432 #endif
433
434   Vector_init(&xid_data, sizeof(struct XidData));
435
436   Echdir(PROC_DIR_NAME);
437   proc_dir = Eopendir(".");
438   while ((dir_entry = readdir(proc_dir)) != NULL)
439   {
440       // select only process file
441     if (!isdigit(*dir_entry->d_name))
442       continue;
443
444     if (atoi(dir_entry->d_name) != my_pid) {
445       struct process_info *     info = get_process_info(dir_entry->d_name);
446       if (info)
447         registerXid(&xid_data, info);
448     }
449   }
450   closedir(proc_dir);
451
452     // output the ctx_list      
453   showContexts(&xid_data);
454
455 #ifndef NDEBUG
456   Vector_free(&xid_data);
457 #endif
458   
459   return 0;
460 }