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