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