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