3 // Copyright (C) 2003 Enrico Scholz <enrico.scholz@informatik.tu-chemnitz.de>
4 // based on vserver-stat.cc by Guillaum Dallaire and Jacques Gelinas
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)
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.
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.
28 #include <ensc_vector/vector.h>
35 #include <sys/types.h>
44 #include <sys/param.h>
46 #define ENSC_WRAPPERS_DIRENT 1
47 #define ENSC_WRAPPERS_VSERVER 1
48 #define ENSC_WRAPPERS_FCNTL 1
49 #define ENSC_WRAPPERS_UNISTD 1
52 #define PROC_DIR_NAME "/proc"
53 #define CTX_DIR_NAME "/var/run/vservers/"
54 #define CTX_NAME_MAX_LEN 50
56 int wrapper_exit_code = 1;
59 #define AT_CLKTCK 17 /* frequency of times() */
62 static unsigned long hertz =0x42;
63 static unsigned long pagesize=0x42;
71 uint64_t start_time_oldest;
72 uint64_t stime_total, utime_total;
80 long VmSize; // number of pages of virtual memory
81 long VmRSS; // resident set size from /proc/#/stat
82 uint64_t start_time; // start time of process -- milliseconds since 1-1-70
83 uint64_t stime, utime; // kernel & user-mode CPU time accumulated by process
84 uint64_t cstime, cutime; // cumulative time of process and reaped children
89 enum { tpUNSET, tpCTX, tpPID } type;
92 unsigned int interval;
99 #define CMD_HELP 0x1000
100 #define CMD_VERSION 0x1001
103 CMDLINE_OPTIONS[] = {
104 { "help", no_argument, 0, CMD_HELP },
105 { "version", no_argument, 0, CMD_VERSION },
106 { "sort", required_argument, 0, 'O' },
111 showHelp(char const *cmd)
113 WRITE_MSG(1, "Usage: ");
117 "Show informations about all the active context.\n\n"
118 " CTX# Context number\n"
119 " #0 = root context\n"
120 " #1 = monitoring context\n"
121 " PROC QTY Quantity of processes in each context\n"
122 " VSZ Number of pages of virtual memory\n"
123 " RSS Resident set size\n"
124 " userTIME User-mode CPU time accumulated\n"
125 " sysTIME Kernel-mode CPU time accumulated\n"
126 " UPTIME Uptime/context\n"
127 " NAME Virtual server name\n"
136 "vserver-stat " VERSION " -- show virtual context statistics\n"
137 "This program is part of " PACKAGE_STRING "\n\n"
138 "Copyright (C) 2003,2005 Enrico Scholz\n"
139 VERSION_COPYRIGHT_DISCLAIMER);
144 // return uptime (in ms) from /proc/uptime
155 // open the /proc/uptime file
156 fd = EopenD("/proc/uptime", O_RDONLY, 0);
157 len = Eread(fd, buffer, sizeof buffer);
159 if (len==sizeof(buffer)) {
160 WRITE_MSG(2, "Too much data in /proc/uptime; aborting...\n");
165 while (len>0 && buffer[len-1]=='\n') --len;
168 secs = strtol(buffer, &errptr, 10);
169 if (*errptr!='.') errptr = buffer;
172 switch (strlen(errptr+1)) {
173 case 0 : mult = 1000; break;
174 case 1 : mult = 100; break;
175 case 2 : mult = 10; break;
176 case 3 : mult = 1; break;
177 default : mult = 0; break;
179 msecs = strtol(errptr+1, &errptr, 10) * mult;
182 if ((*errptr!='\0' && *errptr!=' ') || errptr==buffer) {
183 WRITE_MSG(2, "Bad data in /proc/uptime\n");
187 return secs*1000 + msecs;
191 cmpData(void const *xid_v, void const *map_v)
193 xid_t const * const xid = xid_v;
194 struct XidData const * const map = map_v;
195 int res = *xid - map->xid;
201 registerXid(struct Vector *vec, struct process_info *process)
205 res = Vector_search(vec, &process->s_context, cmpData);
207 res = Vector_insert(vec, &process->s_context, cmpData);
208 res->xid = process->s_context;
209 res->process_count = 0;
210 res->VmSize_total = 0;
211 res->VmRSS_total = 0;
212 res->utime_total = 0;
213 res->stime_total = 0;
214 res->start_time_oldest = process->start_time;
217 ++res->process_count;
218 res->VmSize_total += process->VmSize;
219 res->VmRSS_total += process->VmRSS;
220 res->utime_total += process->utime + process->cutime;
221 res->stime_total += process->stime + process->cstime;
223 res->start_time_oldest = MIN(res->start_time_oldest, process->start_time);
226 static inline uint64_t
229 return v*1000llu/hertz;
233 // shamelessly stolen from procps...
235 find_elf_note(unsigned long findme){
236 unsigned long *ep = (unsigned long *)environ;
239 if(ep[0]==findme) return ep[1];
242 return (unsigned long)(-1);
245 static void initHertz() __attribute__((__constructor__));
246 static void initPageSize() __attribute__((__constructor__));
251 hertz = find_elf_note(AT_CLKTCK);
252 if (hertz==(unsigned long)(-1))
253 hertz = sysconf(_SC_CLK_TCK);
259 pagesize = sysconf(_SC_PAGESIZE);
262 // open the process's status file to get the ctx number, and other stat
263 struct process_info *
264 get_process_info(char *pid)
269 size_t idx, l=strlen(pid);
270 static struct process_info process;
273 process.s_context = vc_get_task_xid(atoi(pid));
275 # warning Compiling in debug-code
276 process.s_context = random()%6;
279 if (process.s_context==VC_NOCTX) {
281 WRITE_MSG(2, "vc_get_task_xid(");
284 WRITE_STR(2, strerror(err));
290 memcpy(buffer, "/proc/", 6); idx = 6;
291 memcpy(buffer+idx, pid, l); idx += l;
292 memcpy(buffer+idx, "/stat", 6);
294 // open the /proc/#/stat file
295 if ((fd = open(buffer, O_RDONLY, 0)) == -1)
297 // put the file in a buffer
298 if (read(fd, buffer, sizeof(buffer)) < 1)
303 p = strchr(buffer, ')'); // go after the PID (process_name)
304 for (idx = 0; idx<12 && *p!='\0'; ++p)
305 if ((*p)==' ') ++idx;
307 process.utime = toMsec(strtol(p, &p, 10));
308 process.stime = toMsec(strtol(p+1, &p, 10));
309 process.cutime = toMsec(strtol(p+1, &p, 10));
310 process.cstime = toMsec(strtol(p+1, &p, 10));
312 for (idx = 0; idx<5 && *p!='\0'; ++p)
313 if ((*p)==' ') ++idx;
315 process.start_time = toMsec(strtol(p, &p, 10));
316 process.VmSize = strtol(p+1, &p, 10);
317 process.VmRSS = strtol(p+1, &p, 10);
319 //printf("pid=%s, start_time=%llu\n", pid, process.start_time);
324 fillUintZero(char *buf, unsigned long val, size_t cnt)
328 l = utilvserver_fmt_ulong(buf, val);
330 memmove(buf+cnt-l, buf, l);
331 memset(buf, '0', cnt-l);
339 shortenMem(char *buf, unsigned long val)
341 char const * SUFFIXES[] = { " ", "K", "M", "G", "T", "+" };
343 char const * suffix = "+";
345 unsigned int mod = 0;
347 for (i=0; i<6; ++i) {
349 suffix = SUFFIXES[i];
352 mod = 10*(val & 1023)/1024;
356 if (val >9999) val=9999;
357 if (val>=1000) mod=0;
359 l = utilvserver_fmt_ulong(tmp, val);
362 l += utilvserver_fmt_ulong(tmp+l, mod);
364 i = 7-l-strlen(suffix);
366 memcpy(buf+i, tmp, l);
367 memcpy(buf+i+l, suffix, strlen(suffix));
371 shortenTime(char *buf, uint64_t t)
376 unsigned long hh, mm, ss, ms;
389 memcpy(ptr, "INVALID", 7);
393 ptr += utilvserver_fmt_ulong(ptr, t/365);
395 ptr += fillUintZero(ptr, t%365, 2);
397 ptr += fillUintZero(ptr, hh, 2);
400 ptr += utilvserver_fmt_ulong(ptr, t);
402 ptr += fillUintZero(ptr, hh, 2);
404 ptr += fillUintZero(ptr, mm, 2);
407 ptr += utilvserver_fmt_ulong(ptr, hh);
409 ptr += fillUintZero(ptr, mm, 2);
411 ptr += fillUintZero(ptr, ss, 2);
414 ptr += utilvserver_fmt_ulong(ptr, mm);
416 ptr += fillUintZero(ptr, ss, 2);
418 ptr += fillUintZero(ptr, ms, 2);
422 memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
426 formatName(char *dst, vcCfgStyle style, char const *name)
430 if (name==0) name = "";
437 memcpy(dst, name, len);
444 memcpy(dst, name, len);
453 showContexts(struct Vector const *vec)
455 uint64_t uptime = getUptime();
456 struct XidData const * ptr = Vector_begin_const(vec);
457 struct XidData const * const end_ptr = Vector_end_const(vec);
460 WRITE_MSG(1, "CTX PROC VSZ RSS userTIME sysTIME UPTIME NAME\n");
461 for (; ptr<end_ptr; ++ptr) {
462 char buf[sizeof(xid_t)*3 + 512];
463 char tmp[sizeof(int)*3 + 2];
466 memset(buf, ' ', sizeof(buf));
467 l = utilvserver_fmt_long(buf, ptr->xid);
468 l = utilvserver_fmt_long(tmp, ptr->process_count);
469 memcpy(buf+10-l, tmp, l);
471 shortenMem (buf+10, ptr->VmSize_total);
472 shortenMem (buf+17, ptr->VmRSS_total*pagesize);
473 shortenTime(buf+24, ptr->utime_total);
474 shortenTime(buf+34, ptr->stime_total);
475 //printf("%llu, %llu\n", uptime, ptr->start_time_oldest);
476 shortenTime(buf+44, uptime - ptr->start_time_oldest);
478 formatName(buf+55, ptr->cfgstyle, ptr->name)[0] = '\0';
480 Vwrite(1, buf, strlen(buf));
486 fillName(void *obj_v, void UNUSED * a)
488 struct XidData * obj = obj_v;
492 obj->cfgstyle = vcCFG_NONE;
493 obj->name = strdup("root server");
497 obj->cfgstyle = vcCFG_NONE;
498 obj->name = strdup("monitoring server");
504 obj->cfgstyle = vcCFG_AUTO;
506 if ((cfgpath = vc_getVserverByCtx(obj->xid, &obj->cfgstyle, 0))==0 ||
507 (obj->name = vc_getVserverName(cfgpath, obj->cfgstyle))==0) {
509 obj->cfgstyle = vcCFG_NONE;
520 freeXidData(void *obj_v, void UNUSED * a)
522 struct XidData * obj = obj_v;
524 free(const_cast(char *)(obj->name));
527 int main(int argc, char **argv)
530 struct dirent* dir_entry;
532 struct Vector xid_data;
536 int c = getopt_long(argc, argv, "+O:", CMDLINE_OPTIONS, 0);
540 case CMD_HELP : showHelp(argv[0]);
541 case CMD_VERSION : showVersion();
544 WRITE_MSG(2, "Try '");
545 WRITE_STR(2, argv[0]);
546 WRITE_MSG(2, " --help\" for more information.\n");
553 WRITE_MSG(2, "Unknown parameter, use '--help' for more information\n");
557 if (hertz==0x42) initHertz();
558 if (pagesize==0x42) initPageSize();
562 if (!switchToWatchXid(&errptr)) {
567 if (access("/proc/uptime",R_OK)==-1 && errno==ENOENT)
569 "WARNING: can not access /proc/uptime. Usually, this is caused by\n"
570 " procfs-security. Please read the FAQ for more details\n"
571 " http://www.linux-vserver.org/index.php?page=Linux-Vserver+FAQ\n");
573 Vector_init(&xid_data, sizeof(struct XidData));
575 Echdir(PROC_DIR_NAME);
576 proc_dir = Eopendir(".");
577 while ((dir_entry = readdir(proc_dir)) != NULL)
579 // select only process file
580 if (!isdigit(*dir_entry->d_name))
583 if (atoi(dir_entry->d_name) != my_pid) {
584 struct process_info * info = get_process_info(dir_entry->d_name);
586 registerXid(&xid_data, info);
591 Vector_foreach(&xid_data, fillName, 0);
593 // output the ctx_list
594 showContexts(&xid_data);
597 Vector_foreach(&xid_data, freeXidData, 0);
598 Vector_free(&xid_data);