// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-/*
- vserver-stat help you to see all the active context currently in the kernel
- with some useful stat
-
- Changelog:
-
- 2003-01-08 Jacques Gelinas: Shows vserver description
- 2002-02-28 Jacques Gelinas: Use dynamic system call
- 2002-06-05 Martial Rioux : fix memory output error
- 2002-12-05 Martial Rioux : fix output glitch
- 2001-11-29 added uptime/ctx stat
- 2001-11-20 added vmsize, rss, stime and utime stat
-*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "vserver.h"
#include "util.h"
#include "internal.h"
-#include "wrappers.h"
-#include "wrappers-dirent.h"
-#include "wrappers-vserver.h"
+
+#include <ensc_vector/vector.h>
#include <stdio.h>
#include <stdlib.h>
#include <syscall.h>
#include <time.h>
#include <stdbool.h>
+#include <getopt.h>
+#include <sys/param.h>
+
+#define ENSC_WRAPPERS_DIRENT 1
+#define ENSC_WRAPPERS_VSERVER 1
+#define ENSC_WRAPPERS_FCNTL 1
+#define ENSC_WRAPPERS_UNISTD 1
+#include "wrappers.h"
#define PROC_DIR_NAME "/proc"
#define CTX_DIR_NAME "/var/run/vservers/"
int wrapper_exit_code = 1;
-struct ctx_list
+#ifndef AT_CLKTCK
+#define AT_CLKTCK 17 /* frequency of times() */
+#endif
+
+static unsigned long hertz =0x42;
+static unsigned long pagesize=0x42;
+
+struct XidData
{
- int ctx;
- int process_count;
- int VmSize_total;
- int VmRSS_total;
- long start_time_oldest;
- long stime_total, utime_total;
- char name[CTX_NAME_MAX_LEN];
- struct ctx_list *next;
-} *my_ctx_list;
+ xid_t xid;
+ int process_count;
+ int VmSize_total;
+ int VmRSS_total;
+ uint64_t start_time_oldest;
+ uint64_t stime_total, utime_total;
+
+ vcCfgStyle cfgstyle;
+ char const * name;
+};
struct process_info
{
- long VmSize; // number of pages of virtual memory
- long VmRSS; // resident set size from /proc/#/stat
- long start_time; // start time of process -- seconds since 1-1-70
- long stime, utime; // kernel & user-mode CPU time accumulated by process
- long cstime, cutime; // cumulative time of process and reaped children
- int s_context;
+ long VmSize; // number of pages of virtual memory
+ long VmRSS; // resident set size from /proc/#/stat
+ uint64_t start_time; // start time of process -- milliseconds since 1-1-70
+ uint64_t stime, utime; // kernel & user-mode CPU time accumulated by process
+ uint64_t cstime, cutime; // cumulative time of process and reaped children
+ xid_t s_context;
};
-char *process_name;
-
struct ArgInfo {
enum { tpUNSET, tpCTX, tpPID } type;
xid_t ctx;
char * const * argv;
};
+#define CMD_HELP 0x1000
+#define CMD_VERSION 0x1001
+
+struct option const
+CMDLINE_OPTIONS[] = {
+ { "help", no_argument, 0, CMD_HELP },
+ { "version", no_argument, 0, CMD_VERSION },
+ { "sort", required_argument, 0, 'O' },
+ {0,0,0,0}
+};
+
static void
-showHelp(int fd, char const *cmd, int res)
+showHelp(char const *cmd)
{
- WRITE_MSG(fd, "Usage: ");
- WRITE_STR(fd, cmd);
- WRITE_MSG(fd,
+ WRITE_MSG(1, "Usage: ");
+ WRITE_STR(1, cmd);
+ WRITE_MSG(1,
"\n"
"Show informations about all the active context.\n\n"
" CTX# Context number\n"
" UPTIME Uptime/context\n"
" NAME Virtual server name\n"
"\n");
- exit(res);
+ exit(0);
}
static void
WRITE_MSG(1,
"vserver-stat " VERSION " -- show virtual context statistics\n"
"This program is part of " PACKAGE_STRING "\n\n"
- "Copyright (C) 2003 Enrico Scholz\n"
+ "Copyright (C) 2003,2005 Enrico Scholz\n"
VERSION_COPYRIGHT_DISCLAIMER);
exit(0);
}
char buffer[64];
char * errptr;
size_t len;
- time_t secs;
+ uint64_t secs;
uint32_t msecs=0;
// open the /proc/uptime file
- fd = Eopen("/proc/uptime", O_RDONLY, 0);
+ fd = EopenD("/proc/uptime", O_RDONLY, 0);
len = Eread(fd, buffer, sizeof buffer);
if (len==sizeof(buffer)) {
secs = strtol(buffer, &errptr, 10);
if (*errptr!='.') errptr = buffer;
- else msecs = strtol(errptr+1, &errptr, 10);
+ else {
+ unsigned int mult;
+ switch (strlen(errptr+1)) {
+ case 0 : mult = 1000; break;
+ case 1 : mult = 100; break;
+ case 2 : mult = 10; break;
+ case 3 : mult = 1; break;
+ default : mult = 0; break;
+ }
+ msecs = strtol(errptr+1, &errptr, 10) * mult;
+ }
if ((*errptr!='\0' && *errptr!=' ') || errptr==buffer) {
WRITE_MSG(2, "Bad data in /proc/uptime\n");
exit(1);
}
- return secs*100 + msecs;
+ return secs*1000 + msecs;
}
-// insert a new record to the list
-static struct ctx_list *
-insert_ctx(int ctx, struct ctx_list *next)
+static int
+cmpData(void const *xid_v, void const *map_v)
{
- struct ctx_list *new;
-
- new = (struct ctx_list *)malloc(sizeof(struct ctx_list));
- new->ctx = ctx;
- new->process_count = 0;
- new->VmSize_total = 0;
- new->VmRSS_total = 0;
- new->utime_total = 0;
- new->stime_total = 0;
- new->start_time_oldest = 0;
- new->next = next;
- new->name[0] = '\0';
-
- return new;
+ xid_t const * const xid = xid_v;
+ struct XidData const * const map = map_v;
+ int res = *xid - map->xid;
+
+ return res;
}
-// compute the process info into the list
static void
-add_ctx(struct ctx_list *list, struct process_info *process)
+registerXid(struct Vector *vec, struct process_info *process)
{
- list->process_count ++;
- list->VmSize_total += process->VmSize;
- list->VmRSS_total += process->VmRSS;
- list->utime_total += process->utime + process->cutime;
- list->stime_total += process->stime + process->cstime;
-
- if (list->start_time_oldest == 0) // first entry
- list->start_time_oldest = process->start_time;
- else
- if (list->start_time_oldest > process->start_time)
- list->start_time_oldest = process->start_time;
+ struct XidData *res;
+
+ res = Vector_search(vec, &process->s_context, cmpData);
+ if (res==0) {
+ res = Vector_insert(vec, &process->s_context, cmpData);
+ res->xid = process->s_context;
+ res->process_count = 0;
+ res->VmSize_total = 0;
+ res->VmRSS_total = 0;
+ res->utime_total = 0;
+ res->stime_total = 0;
+ res->start_time_oldest = process->start_time;
+ }
+
+ ++res->process_count;
+ res->VmSize_total += process->VmSize;
+ res->VmRSS_total += process->VmRSS;
+ res->utime_total += process->utime + process->cutime;
+ res->stime_total += process->stime + process->cstime;
+
+ res->start_time_oldest = MIN(res->start_time_oldest, process->start_time);
}
-// increment the count number in the ctx record using ctx number
-static void
-count_ctx(struct ctx_list *list, struct process_info *process)
+static inline uint64_t
+toMsec(uint64_t v)
{
- struct ctx_list *prev = list;
-
- if (process == NULL) return;
-
- // search
- while(list != NULL)
- {
- // find
- if (list->ctx == process->s_context)
- {
- add_ctx(list, process);
- return;
- }
- // insert between
- if (list->ctx > process->s_context)
- {
- prev->next = insert_ctx(process->s_context, list);
- add_ctx(prev->next, process);
- return;
- }
- // ++
- prev = list;
- list = list->next;
- }
- // add at the end
- prev->next = insert_ctx(process->s_context, NULL);
- add_ctx(prev->next, process);
+ return v*1000llu/hertz;
}
-// free mem
+
+// shamelessly stolen from procps...
+static unsigned long
+find_elf_note(unsigned long findme){
+ unsigned long *ep = (unsigned long *)environ;
+ while(*ep++);
+ while(*ep){
+ if(ep[0]==findme) return ep[1];
+ ep+=2;
+ }
+ return (unsigned long)(-1);
+}
+
+static void initHertz() __attribute__((__constructor__));
+static void initPageSize() __attribute__((__constructor__));
+
static void
-free_ctx(struct ctx_list *list)
+initHertz()
{
- struct ctx_list *prev;
-
- for(;list != NULL; list = prev)
- {
- prev = list->next;
- free(list);
- }
+ hertz = find_elf_note(AT_CLKTCK);
+ if (hertz==(unsigned long)(-1))
+ hertz = sysconf(_SC_CLK_TCK);
}
+static void
+initPageSize()
+{
+ pagesize = sysconf(_SC_PAGESIZE);
+}
// open the process's status file to get the ctx number, and other stat
-struct process_info *get_process_info(char *pid)
+struct process_info *
+get_process_info(char *pid)
{
- int fd;
- char buffer[1024];
- char *p;
- size_t idx, l=strlen(pid);
- static struct process_info process;
+ int fd;
+ char buffer[1024];
+ char *p;
+ size_t idx, l=strlen(pid);
+ static struct process_info process;
+
+#if 1
+ process.s_context = vc_get_task_xid(atoi(pid));
+#else
+# warning Compiling in debug-code
+ process.s_context = random()%6;
+#endif
- process.s_context = vc_X_getctx(atoi(pid));
+ if (process.s_context==VC_NOCTX) {
+ int err=errno;
+ WRITE_MSG(2, "vc_get_task_xid(");
+ WRITE_STR(2, pid);
+ WRITE_MSG(2, "): ");
+ WRITE_STR(2, strerror(err));
+ WRITE_MSG(2, "\n");
+ return 0;
+ }
memcpy(buffer, "/proc/", 6); idx = 6;
memcpy(buffer+idx, pid, l); idx += l;
for (idx = 0; idx<12 && *p!='\0'; ++p)
if ((*p)==' ') ++idx;
- process.utime = strtol(p, &p, 10);
- process.stime = strtol(p+1, &p, 10);
- process.cutime = strtol(p+1, &p, 10);
- process.cstime = strtol(p+1, &p, 10);
+ process.utime = toMsec(strtol(p, &p, 10));
+ process.stime = toMsec(strtol(p+1, &p, 10));
+ process.cutime = toMsec(strtol(p+1, &p, 10));
+ process.cstime = toMsec(strtol(p+1, &p, 10));
for (idx = 0; idx<5 && *p!='\0'; ++p)
if ((*p)==' ') ++idx;
- process.start_time = strtol(p, &p, 10);
+ process.start_time = toMsec(strtol(p, &p, 10));
process.VmSize = strtol(p+1, &p, 10);
process.VmRSS = strtol(p+1, &p, 10);
-
+
+ //printf("pid=%s, start_time=%llu\n", pid, process.start_time);
return &process;
}
-
-
static size_t
fillUintZero(char *buf, unsigned long val, size_t cnt)
{
unsigned long hh, mm, ss, ms;
- ms = t % 100;
- t /= 100;
+ ms = t % 1000;
+ t /= 1000;
ss = t%60;
t /= 60;
mm = t%60;
t /= 60;
- hh = t%60;
+ hh = t%24;
t /= 24;
- if (t>0) {
+ if (t>999*999) {
+ memcpy(ptr, "INVALID", 7);
+ ptr += 7;
+ }
+ else if (t>999) {
+ ptr += utilvserver_fmt_ulong(ptr, t/365);
+ *ptr++ = 'y';
+ ptr += fillUintZero(ptr, t%365, 2);
+ *ptr++ = 'd';
+ ptr += fillUintZero(ptr, hh, 2);
+ }
+ else if (t>0) {
ptr += utilvserver_fmt_ulong(ptr, t);
*ptr++ = 'd';
ptr += fillUintZero(ptr, hh, 2);
memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
}
-void showContexts(struct ctx_list *list)
+static char *
+formatName(char *dst, vcCfgStyle style, char const *name)
{
- uint64_t uptime = getUptime();
+ size_t len;
+
+ if (name==0) name = "";
+ len = strlen(name);
+
+ switch (style) {
+ case vcCFG_LEGACY :
+ len = MIN(len, 18);
+ *dst++ = '[';
+ memcpy(dst, name, len);
+ dst += len;
+ *dst++ = ']';
+ break;
+
+ default :
+ len = MIN(len, 20);
+ memcpy(dst, name, len);
+ dst += len;
+ break;
+ }
+
+ return dst;
+}
+
+static void
+showContexts(struct Vector const *vec)
+{
+ uint64_t uptime = getUptime();
+ struct XidData const * ptr = Vector_begin_const(vec);
+ struct XidData const * const end_ptr = Vector_end_const(vec);
+
WRITE_MSG(1, "CTX PROC VSZ RSS userTIME sysTIME UPTIME NAME\n");
- while (list!=0) {
- char buf[512];
- char tmp[32];
+ for (; ptr<end_ptr; ++ptr) {
+ char buf[sizeof(xid_t)*3 + 512];
+ char tmp[sizeof(int)*3 + 2];
size_t l;
memset(buf, ' ', sizeof(buf));
- l = utilvserver_fmt_ulong(buf, list->ctx);
- l = utilvserver_fmt_ulong(tmp, list->process_count);
+ l = utilvserver_fmt_long(buf, ptr->xid);
+ l = utilvserver_fmt_long(tmp, ptr->process_count);
memcpy(buf+10-l, tmp, l);
- shortenMem (buf+10, list->VmSize_total);
- shortenMem (buf+17, list->VmRSS_total);
- shortenTime(buf+24, list->utime_total);
- shortenTime(buf+34, list->stime_total);
- shortenTime(buf+44, uptime - list->start_time_oldest);
-
- switch (list->ctx) {
- case 0 : strncpy(buf+55, "root server", 20); break;
- case 1 : strncpy(buf+55, "monitoring server", 20); break;
- default : {
- char * name = 0;
- char * cfgpath = 0;
- vcCfgStyle cfgstyle = vcCFG_AUTO;
-
- if ((cfgpath = vc_getVserverByCtx(list->ctx, &cfgstyle, 0))!=0 &&
- (name = vc_getVserverName(cfgpath, cfgstyle))!=0)
- strncpy(buf+55, name, 20);
- free(name);
- free(cfgpath);
+ shortenMem (buf+10, ptr->VmSize_total);
+ shortenMem (buf+17, ptr->VmRSS_total*pagesize);
+ shortenTime(buf+24, ptr->utime_total);
+ shortenTime(buf+34, ptr->stime_total);
+ //printf("%llu, %llu\n", uptime, ptr->start_time_oldest);
+ shortenTime(buf+44, uptime - ptr->start_time_oldest);
+
+ formatName(buf+55, ptr->cfgstyle, ptr->name)[0] = '\0';
+
+ Vwrite(1, buf, strlen(buf));
+ Vwrite(1, "\n", 1);
+ }
+}
+
+static void
+fillName(void *obj_v, void UNUSED * a)
+{
+ struct XidData * obj = obj_v;
+
+ switch (obj->xid) {
+ case 0 :
+ obj->cfgstyle = vcCFG_NONE;
+ obj->name = strdup("root server");
+ break;
+
+ case 1 :
+ obj->cfgstyle = vcCFG_NONE;
+ obj->name = strdup("monitoring server");
+ break;
+
+ default : {
+ char * cfgpath;
+
+ obj->cfgstyle = vcCFG_AUTO;
+
+ if ((cfgpath = vc_getVserverByCtx(obj->xid, &obj->cfgstyle, 0))==0 ||
+ (obj->name = vc_getVserverName(cfgpath, obj->cfgstyle))==0) {
+ obj->name = 0;
+ obj->cfgstyle = vcCFG_NONE;
}
- }
- write(1, buf, 80);
- write(1, "\n", 1);
+ free(cfgpath);
- list = list->next;
+ break;
+ }
}
}
-int main(int argc, char **argv)
+static void UNUSED
+freeXidData(void *obj_v, void UNUSED * a)
{
- DIR *proc_dir;
- struct dirent *dir_entry;
- pid_t my_pid;
+ struct XidData * obj = obj_v;
- // for error msg
- process_name = argv[0];
+ free(const_cast(char *)(obj->name));
+}
- if (argc==2) {
- if (strcmp(argv[1], "--help") ==0) showHelp(1, argv[0], 0);
- if (strcmp(argv[1], "--version")==0) showVersion();
+int main(int argc, char **argv)
+{
+ DIR * proc_dir;
+ struct dirent* dir_entry;
+ pid_t my_pid;
+ struct Vector xid_data;
+ char const * errptr;
+
+ while (1) {
+ int c = getopt_long(argc, argv, "+O:", CMDLINE_OPTIONS, 0);
+ if (c==-1) break;
+
+ switch (c) {
+ case CMD_HELP : showHelp(argv[0]);
+ case CMD_VERSION : showVersion();
+ case 'O' : break;
+ default :
+ WRITE_MSG(2, "Try '");
+ WRITE_STR(2, argv[0]);
+ WRITE_MSG(2, " --help\" for more information.\n");
+ return EXIT_FAILURE;
+ break;
+ }
}
- if (argc!=1) {
+
+ if (optind!=argc) {
WRITE_MSG(2, "Unknown parameter, use '--help' for more information\n");
return EXIT_FAILURE;
}
- // do not include own stat
+ if (hertz==0x42) initHertz();
+ if (pagesize==0x42) initPageSize();
+
my_pid = getpid();
-#if 1
- // try to switch in context 1
- Evc_new_s_context(1, 0,0);
-#endif
-
- // create the fist...
- my_ctx_list = insert_ctx(0, NULL);
- // init with the default name for the context 0
- strncpy(my_ctx_list->name, "root server", CTX_NAME_MAX_LEN);
+ if (!switchToWatchXid(&errptr)) {
+ perror(errptr);
+ exit(1);
+ }
+
+ if (access("/proc/uptime",R_OK)==-1 && errno==ENOENT)
+ WRITE_MSG(2,
+ "WARNING: can not access /proc/uptime. Usually, this is caused by\n"
+ " procfs-security. Please read the FAQ for more details\n"
+ " http://www.linux-vserver.org/index.php?page=Linux-Vserver+FAQ\n");
+ Vector_init(&xid_data, sizeof(struct XidData));
Echdir(PROC_DIR_NAME);
proc_dir = Eopendir(".");
if (!isdigit(*dir_entry->d_name))
continue;
- if (atoi(dir_entry->d_name) != my_pid)
- count_ctx(my_ctx_list, get_process_info(dir_entry->d_name));
-
+ if (atoi(dir_entry->d_name) != my_pid) {
+ struct process_info * info = get_process_info(dir_entry->d_name);
+ if (info)
+ registerXid(&xid_data, info);
+ }
}
closedir(proc_dir);
- // output the ctx_list
- showContexts(my_ctx_list);
+ Vector_foreach(&xid_data, fillName, 0);
- // free the ctx_list
- free_ctx(my_ctx_list);
+ // output the ctx_list
+ showContexts(&xid_data);
+#ifndef NDEBUG
+ Vector_foreach(&xid_data, freeXidData, 0);
+ Vector_free(&xid_data);
+#endif
+
return 0;
}