This commit was generated by cvs2svn to compensate for changes in r120,
[util-vserver.git] / src / vserver-stat.c
1 // $Id: vserver-stat.c,v 1.1.4.1 2003/10/14 00:45:04 ensc Exp $
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
34 #ifdef HAVE_CONFIG_H
35 #  include <config.h>
36 #endif
37 #include "compat.h"
38
39 #include "vserver.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
54 #define PROC_DIR_NAME "/proc"
55 #define CTX_DIR_NAME "/var/run/vservers/"
56 #define CTX_NAME_MAX_LEN 50
57
58 struct ctx_list
59 {
60         int ctx;
61         int process_count;
62         int VmSize_total;
63         int VmRSS_total;
64         long start_time_oldest;
65         long stime_total, utime_total;
66         char name[CTX_NAME_MAX_LEN];
67         struct ctx_list *next;
68 } *my_ctx_list;
69
70 struct process_info
71 {
72         long VmSize;            // number of pages of virtual memory
73         long VmRSS;             // resident set size from /proc/#/stat
74         long start_time;        // start time of process -- seconds since 1-1-70
75         long stime, utime;      // kernel & user-mode CPU time accumulated by process
76         long cstime, cutime;    // cumulative time of process and reaped children
77         int s_context;
78 };
79
80 char *process_name;
81
82 void usage()
83 {
84         fprintf(stderr, "%s: from vserver kit version %s\n", process_name, VERSION);
85         fprintf(stderr, "(no argument needed)\n\n");
86         fprintf(stderr, "Show informations about all the active context.\n\n");
87         fprintf(stderr, "       CTX#            Context number\n");
88         fprintf(stderr, "                       #0 = root context\n");
89         fprintf(stderr, "                       #1 = monitoring context\n");
90         fprintf(stderr, "       PROC QTY        Quantity of processes in each context\n");
91         fprintf(stderr, "       VSZ             Number of pages of virtual memory\n");
92         fprintf(stderr, "       RSS             Resident set size\n");
93         fprintf(stderr, "       userTIME        User-mode CPU time accumulated\n");
94         fprintf(stderr, "       sysTIME         Kernel-mode CPU time accumulated\n");
95         fprintf(stderr, "       UPTIME          Uptime/context\n");
96         fprintf(stderr, "       NAME            Virtual server name\n");
97         fprintf(stderr, "\n");
98
99 }
100
101 // return uptime (in ms) from /proc/uptime
102 long get_uptime()
103 {
104         int fd;
105         double up;
106         char buffer[64];
107
108         // open the /proc/uptime file
109         if ((fd = open("/proc/uptime", O_RDONLY, 0)) == -1)
110                 return 0;
111
112         if (read(fd, buffer, sizeof(buffer)) < 1)
113                 return 0;
114
115         close(fd);
116
117         if (sscanf(buffer, "%lf", &up) < 1)
118         {
119                 fprintf(stderr, "%s: bad data in /proc/uptime\n", process_name);
120                 return 0;
121         }
122
123         return up * 100;
124 }
125
126 // insert a new record to the list
127 struct ctx_list *insert_ctx(int ctx, struct ctx_list *next)
128 {
129         struct ctx_list *new;
130
131         new = (struct ctx_list *)malloc(sizeof(struct ctx_list));
132         new->ctx = ctx;
133         new->process_count = 0;
134         new->VmSize_total = 0;
135         new->VmRSS_total = 0;
136         new->utime_total = 0;
137         new->stime_total = 0;
138         new->start_time_oldest = 0;
139         new->next = next;
140         new->name[0] = '\0';
141
142         return new;
143 }
144
145 // find the ctx record with the ctx number
146 struct ctx_list *find_ctx(struct ctx_list *list, int ctx)
147 {
148         // very simple search engine...
149         while(list != NULL)
150         {
151                 // find
152                 if (list->ctx == ctx)
153                 {
154                         return list;
155                 }
156                 list = list->next;
157         }
158         return NULL;
159 }
160
161 // compute the process info into the list
162 void add_ctx(struct ctx_list *list, struct process_info *process)
163 {
164         list->process_count ++;
165         list->VmSize_total += process->VmSize;
166         list->VmRSS_total += process->VmRSS;
167         list->utime_total += process->utime + process->cutime;
168         list->stime_total += process->stime + process->cstime;
169
170         if (list->start_time_oldest == 0) // first entry
171                 list->start_time_oldest = process->start_time;
172         else
173                 if (list->start_time_oldest > process->start_time)
174                         list->start_time_oldest = process->start_time;
175 }
176
177 // increment the count number in the ctx record using ctx number
178 void count_ctx(struct ctx_list *list, struct process_info *process)
179 {
180         struct ctx_list *prev = list;
181
182         if (process == NULL) return;
183
184         // search
185         while(list != NULL)
186         {
187                 // find
188                 if (list->ctx == process->s_context)
189                 {
190                         add_ctx(list, process);
191                         return;
192                 }
193                 // insert between
194                 if (list->ctx > process->s_context)
195                 {
196                         prev->next = insert_ctx(process->s_context, list);
197                         add_ctx(prev->next, process);
198                         return;
199                 }
200                 // ++
201                 prev = list;
202                 list = list->next;
203         }
204         // add at the end
205         prev->next = insert_ctx(process->s_context, NULL);
206         add_ctx(prev->next, process);
207 }
208
209 // free mem
210 void free_ctx(struct ctx_list *list)
211 {
212         struct ctx_list *prev;
213
214         for(;list != NULL; list = prev)
215         {
216                 prev = list->next;              
217                 free(list);
218         }
219 }
220
221 /*
222         Read the vserver description
223 */
224 static void read_description(
225         const char *name,               // Vserver name
226         char descrip[1000])
227 {
228         char conf[PATH_MAX];
229         FILE *fin;
230         descrip[0] = '\0';
231         snprintf (conf,sizeof(conf)-1,"/etc/vservers/%s.conf",name);
232         fin = fopen (conf,"r");
233         if (fin != NULL){
234                 char line[1000];
235                 while (fgets(line,sizeof(line)-1,fin)!=NULL){
236                         if (line[0] == '#'){
237                                 char *pt = line+1;
238                                 while (isspace(*pt)) pt++;
239                                 if (strncmp(pt,"Description:",12)==0){
240                                         int last;
241                                         pt += 12;
242                                         while (isspace(*pt)) pt++;
243                                         strcpy (descrip,pt);
244                                         last = strlen(descrip)-1;
245                                         if (last >=0 && descrip[last] == '\n'){
246                                                 descrip[last] = '\0';
247                                         }
248                                 }
249                         }
250                 }
251                 fclose (fin);
252         }
253 }
254
255 // show the ctx_list with name from /var/run/servers/*.ctx
256 void show_ctx(struct ctx_list *list)
257 {
258         // fill the ctx_list using the /var/run/servers/*.ctx file(s)
259          __extension__ int bind_ctx_name(struct ctx_list *list)
260         {
261                 // fetch the context number in /var/run/vservers/'filename'
262                 int fetch_ctx_number(char *filename)
263                 {
264                         int fd;
265                         int ctx;
266                         char buf[25];
267
268                         // open file
269                         if ((fd = open(filename, O_RDONLY, 0)) == -1)
270                                 return -1;
271                         // put the file in a small buffer
272                         if (read(fd, buf, sizeof(buf)) < 1)
273                                 return -1;
274
275                         close(fd);
276
277                         sscanf(buf, "S_CONTEXT=%d", &ctx);
278                         return ctx;
279                 }
280
281                 /* begin bind_ctx_name */
282
283                 DIR *ctx_dir;
284                 struct dirent *dir_entry;
285                 char *p;
286                 char ctx_name[CTX_NAME_MAX_LEN];
287                 struct ctx_list *ctx;
288                 int ctx_number;
289
290                 // open the /var/run/vservers directory
291                 if ((ctx_dir = opendir(CTX_DIR_NAME)) == NULL)
292                 {
293                         fprintf(stderr, "%s: in openning %s: %s\n", process_name, CTX_DIR_NAME, strerror(errno));
294                         return -1;
295                 }
296         
297                 chdir(CTX_DIR_NAME);
298                 while ((dir_entry = readdir(ctx_dir)) != NULL)
299                 {
300                         strncpy(ctx_name, dir_entry->d_name, sizeof(ctx_name));
301                         p = strstr(ctx_name, ".ctx");
302                         if (p != NULL) // make sure that it is a .ctx file..
303                         {
304                                 *p = '\0'; // remove the .ctx in the file name
305                                 if ((ctx_number = fetch_ctx_number(dir_entry->d_name)) > 1)
306                                 {
307                                         if ((ctx = find_ctx(list, ctx_number)) != NULL)
308                                                 strncpy(ctx->name, ctx_name, CTX_NAME_MAX_LEN);
309                                 }
310                         }
311                         // else fprintf(stderr, "invalid file %s in %s\n", dir_entry->d_name, CTX_DIR_NAME);
312                 }
313                 closedir(ctx_dir);      
314                 return 0;
315         }
316
317          __extension__ char *convert_time(unsigned t, char *str)
318         {
319                 unsigned hh, mm, ss, ms;
320
321                 ms = t % 100;
322                 t /= 100;
323
324                 ss = t%60;
325                 t /= 60;
326                 mm = t%60;
327                 t /= 60;
328                 hh = t%60;
329                 t /= 24;
330
331                 if (t > 0)      // day > 0
332                 {
333                                 snprintf(str, 25, "%3.ud%02uh%02u", t, (hh%12) ? hh%12 : 12, mm);
334                 }
335                 else
336                 {
337                         if (hh > 0) // hour > 0
338                                 snprintf(str, 25, " %2.uh%02um%02u", hh, mm, ss);
339                         else
340                         {
341                                 snprintf(str, 25, " %2.um%02u.%02u", mm, ss, ms);
342                         }
343                 }
344                 return str;
345         }
346
347          __extension__ char *convert_mem(unsigned long total, char *str)
348         {
349                 // Byte
350                 if (total < 1024)
351                 {
352                         snprintf(str, 25, "%luB", total);
353                         return str;
354                 }
355
356                 total >>= 10; // kByte
357                 if (total < 1024)
358                 {
359                         snprintf(str, 25, "%lukB", total);
360                         return str;
361                 }
362
363                 total >>= 10; // MByte
364                 if (total < 1024)
365                 {
366                         snprintf(str, 25, "%luMB", total);
367                         return str;
368                 }
369
370                 total >>= 10; // GByte
371                 if (total < 1024)
372                 {
373                         snprintf(str, 25, "%luGB", total);
374                         return str;
375                 }
376                 total >>= 10; // TByte
377                 snprintf(str, 25, "%luTB", total);
378                 return str;
379         }
380
381         /* begin show_ctx */
382         char utime[25], stime[25], ctx_uptime[25];
383         char vmsize[25], vmrss[25];
384         long uptime = get_uptime();
385
386         // now we have all the active context, fetch the name
387         // from /var/run/vservers/*.ctx
388         bind_ctx_name(list);
389
390         printf("CTX  PROC    VSZ    RSS  userTIME   sysTIME    UPTIME NAME     DESCRIPTION\n");
391         while(list != NULL)
392         {
393                 char descrip[1000];
394                 if (list->ctx == 1)
395                         strncpy(list->name, "monitoring server", CTX_NAME_MAX_LEN);
396
397                 read_description (list->name,descrip);
398
399                 printf("%-4d %4d %6s %6s %9s %9s %9s %-8s %s\n", list->ctx, list->process_count,
400                         convert_mem(list->VmSize_total, vmsize), convert_mem(list->VmRSS_total, vmrss),
401                         convert_time(list->utime_total, utime), convert_time(list->stime_total, stime), convert_time(uptime - list->start_time_oldest, ctx_uptime)
402                         , list->name,descrip);
403                 list = list->next;
404         }
405 }
406
407 // open the process's status file to get the ctx number, and other stat
408 struct process_info *get_process_info(char *pid)
409 {
410         int fd;
411         char buffer[1024];
412         char *p;
413         static struct process_info process;
414
415         // open the proc/#/status file
416         snprintf(buffer, sizeof(buffer),  "/proc/%s/status", pid);
417         if ((fd = open(buffer, O_RDONLY, 0)) == -1)
418                 return NULL;
419         // put the file in a buffer
420         if (read(fd, buffer, sizeof(buffer)) < 1)
421                 return NULL;
422
423         close(fd);
424
425         // find the s_context entry
426         if ((p = strstr(buffer, "s_context:")) == NULL)
427                 return NULL;
428
429         sscanf(p, "s_context: %d", &process.s_context);
430
431         // open the /proc/#/stat file
432         snprintf(buffer, sizeof(buffer),  "/proc/%s/stat", pid);
433         if ((fd = open(buffer, O_RDONLY, 0)) == -1)
434                 return NULL;
435         // put the file in a buffer
436         if (read(fd, buffer, sizeof(buffer)) < 1)
437                 return NULL;
438
439         close(fd);
440
441         p = strchr(buffer, ')');                // go after the PID (process_name)
442         sscanf(p + 2,
443                 "%*s "
444                 "%*s %*s %*s %*s %*s "
445                 "%*s %*s %*s %*s %*s %ld %ld "
446                 "%ld %ld %*s %*s %*s %*s "
447                 "%ld %ld "
448                 "%ld ", &process.utime, &process.stime,
449                         &process.cutime, &process.cstime,
450                         &process.start_time,
451                         &process.VmSize, &process.VmRSS);
452
453         return &process;
454 }
455
456 int main(int argc, char **argv)
457 {
458         DIR *proc_dir;
459         struct dirent *dir_entry;
460         pid_t my_pid;
461
462         // for error msg
463         process_name = argv[0];
464
465         if (argc > 1)
466         {
467                 usage();
468                 return 0;
469         }
470
471         // do not include own stat
472         my_pid = getpid();
473
474         // try to switch in context 1
475         if (vc_new_s_context(1,0, 0) < 0)
476         {
477                 fprintf(stderr, "%s: unable to switch in context security #1\n", process_name);
478                 return -1;
479         }
480
481         // create the fist...
482         my_ctx_list = insert_ctx(0, NULL);
483         // init with the default name for the context 0
484         strncpy(my_ctx_list->name, "root server", CTX_NAME_MAX_LEN);
485
486         // open the /proc dir
487         if ((proc_dir = opendir(PROC_DIR_NAME)) == NULL)
488         {
489                 fprintf(stderr, "%s: %s\n", process_name, strerror(errno));
490                 return -1;
491         }
492         
493         chdir(PROC_DIR_NAME);
494         while ((dir_entry = readdir(proc_dir)) != NULL)
495         {
496                 // select only process file
497                 if (!isdigit(*dir_entry->d_name))
498                         continue;
499
500                 if (atoi(dir_entry->d_name) != my_pid)
501                         count_ctx(my_ctx_list, get_process_info(dir_entry->d_name));
502                 
503         }
504         closedir(proc_dir);
505
506         // output the ctx_list  
507         show_ctx(my_ctx_list);
508
509         // free the ctx_list
510         free_ctx(my_ctx_list);
511
512         return 0;
513 }