s!/etc/slackware-release!/etc/slackware-version! (reported by bubulak)
[util-vserver.git] / util-vserver / src / vserver-stat.c
index d4c6dc4..6d78f9d 100644 (file)
 // 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
@@ -38,6 +25,8 @@
 #include "util.h"
 #include "internal.h"
 
+#include <ensc_vector/vector.h>
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
@@ -51,6 +40,8 @@
 #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
 
 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
 {
-       xid_t 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
-       xid_t 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;
@@ -99,12 +96,23 @@ struct ArgInfo {
     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"
@@ -118,7 +126,7 @@ showHelp(int fd, char const *cmd, int res)
            "   UPTIME          Uptime/context\n"
            "   NAME            Virtual server name\n"
            "\n");
-  exit(res);
+  exit(0);
 }
 
 static void
@@ -127,7 +135,7 @@ showVersion()
   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);
 }
@@ -141,11 +149,11 @@ getUptime()
   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)) {
@@ -159,88 +167,101 @@ getUptime()
 
   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 **ptr, struct process_info *process)
+static inline uint64_t
+toMsec(uint64_t v)
 {
-  for (;;) {
-    if (*ptr==0 || (*ptr)->ctx > process->s_context) {
-      *ptr = insert_ctx(process->s_context, *ptr);
-      break;
-    }
+  return v*1000llu/hertz;
+}
 
-    if ((*ptr)->ctx == process->s_context)
-      break;
 
-    ptr = &(*ptr)->next;
+// 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;
   }
-
-  add_ctx(*ptr, process);
+  return (unsigned long)(-1);
 }
 
-// free mem
+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];
@@ -248,7 +269,12 @@ struct process_info *get_process_info(char *pid)
   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
 
   if (process.s_context==VC_NOCTX) {
     int                err=errno;
@@ -257,6 +283,8 @@ struct process_info *get_process_info(char *pid)
     WRITE_MSG(2, "): ");
     WRITE_STR(2, strerror(err));
     WRITE_MSG(2, "\n");
+
+    return 0;
   }
   
   memcpy(buffer,     "/proc/", 6); idx  = 6;
@@ -276,23 +304,22 @@ struct process_info *get_process_info(char *pid)
   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)
 {
@@ -348,17 +375,28 @@ shortenTime(char *buf, uint64_t t)
 
   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);
@@ -384,82 +422,155 @@ shortenTime(char *buf, uint64_t t)
   memcpy(buf+10-(ptr-tmp), tmp, ptr-tmp);
 }
 
-void showContexts(struct ctx_list *list)
+static char *
+formatName(char *dst, vcCfgStyle style, char const *name)
+{
+  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();
+  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) {
+  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_long(buf, list->ctx);
-    l = utilvserver_fmt_long(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
-  if (vc_get_task_xid(0)!=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(".");
@@ -469,17 +580,23 @@ int main(int argc, char **argv)
     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;
 }