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