Add more functionality to the os module
authorBrian White <mscdex@mscdex.net>
Wed, 22 Dec 2010 18:55:47 +0000 (13:55 -0500)
committerRyan Dahl <ry@tinyclouds.org>
Wed, 22 Dec 2010 19:01:25 +0000 (11:01 -0800)
14 files changed:
doc/api/os.markdown
lib/os.js
src/node.cc
src/node_os.cc
src/platform.h
src/platform_cygwin.cc
src/platform_darwin.cc
src/platform_darwin_proctitle.cc
src/platform_freebsd.cc
src/platform_linux.cc
src/platform_openbsd.cc [new file with mode: 0644]
test/simple/test-os-hostname.js [deleted file]
test/simple/test-os.js [new file with mode: 0644]
wscript

index 4429bdd..3c4e1b4 100644 (file)
@@ -2,6 +2,101 @@
 
 Use `require('os')` to access this module.
 
-### os.getHostname()
+### os.hostname()
 
 Returns the hostname of the operating system.
+
+### os.type()
+
+Returns the operating system name.
+
+### os.release()
+
+Returns the operating system release.
+
+### os.uptime()
+
+Returns the system uptime in seconds.
+
+### os.loadavg()
+
+Returns an array containing the 1, 5, and 15 minute load averages.
+
+### os.totalmem()
+
+Returns the total amount of system memory in bytes.
+
+### os.freemem()
+
+Returns the amount of free system memory in bytes.
+
+### os.cpus()
+
+Returns an array of objects containing information about each CPU/core installed: model, speed (in MHz), and times (an object containing the number of CPU ticks spent in: user, nice, sys, idle, and irq).
+
+Example inspection of os.cpus:
+
+    [ { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
+        speed: 2926,
+        times:
+         { user: 252020,
+           nice: 0,
+           sys: 30340,
+           idle: 1070356870,
+           irq: 0 } },
+      { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
+        speed: 2926,
+        times:
+         { user: 306960,
+           nice: 0,
+           sys: 26980,
+           idle: 1071569080,
+           irq: 0 } },
+      { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
+        speed: 2926,
+        times:
+         { user: 248450,
+           nice: 0,
+           sys: 21750,
+           idle: 1070919370,
+           irq: 0 } },
+      { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
+        speed: 2926,
+        times:
+         { user: 256880,
+           nice: 0,
+           sys: 19430,
+           idle: 1070905480,
+           irq: 20 } },
+      { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
+        speed: 2926,
+        times:
+         { user: 511580,
+           nice: 20,
+           sys: 40900,
+           idle: 1070842510,
+           irq: 0 } },
+      { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
+        speed: 2926,
+        times:
+         { user: 291660,
+           nice: 0,
+           sys: 34360,
+           idle: 1070888000,
+           irq: 10 } },
+      { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
+        speed: 2926,
+        times:
+         { user: 308260,
+           nice: 0,
+           sys: 55410,
+           idle: 1071129970,
+           irq: 880 } },
+      { model: 'Intel(R) Core(TM) i7 CPU         860  @ 2.80GHz',
+        speed: 2926,
+        times:
+         { user: 266450,
+           nice: 1480,
+           sys: 34920,
+           idle: 1072572010,
+           irq: 30 } } ]
\ No newline at end of file
index 14a582c..5b1bb22 100644 (file)
--- a/lib/os.js
+++ b/lib/os.js
@@ -1,3 +1,10 @@
 var binding = process.binding('os');
 
-exports.getHostname = binding.getHostname;
+exports.hostname = binding.getHostname;
+exports.loadavg = binding.getLoadAvg;
+exports.uptime = binding.getUptime;
+exports.freemem = binding.getFreeMem;
+exports.totalmem = binding.getTotalMem;
+exports.cpus = binding.getCPUs;
+exports.type = binding.getOSType;
+exports.release = binding.getOSRelease;
\ No newline at end of file
index 8039d79..9d84055 100644 (file)
@@ -1186,7 +1186,7 @@ static void CheckStatus(EV_P_ ev_timer *watcher, int revents) {
 
   // check memory
   size_t rss, vsize;
-  if (!ev_is_active(&gc_idle) && OS::GetMemory(&rss, &vsize) == 0) {
+  if (!ev_is_active(&gc_idle) && Platform::GetMemory(&rss, &vsize) == 0) {
     if (rss > 1024*1024*128) {
       // larger than 128 megs, just start the idle watcher
       ev_idle_start(EV_A_ &gc_idle);
@@ -1211,7 +1211,7 @@ v8::Handle<v8::Value> MemoryUsage(const v8::Arguments& args) {
 
   size_t rss, vsize;
 
-  int r = OS::GetMemory(&rss, &vsize);
+  int r = Platform::GetMemory(&rss, &vsize);
 
   if (r != 0) {
     return ThrowException(Exception::Error(String::New(strerror(errno))));
@@ -1519,7 +1519,7 @@ static Handle<Value> ProcessTitleGetter(Local<String> property,
                                         const AccessorInfo& info) {
   HandleScope scope;
   int len;
-  const char *s = OS::GetProcessTitle(&len);
+  const char *s = Platform::GetProcessTitle(&len);
   return scope.Close(s ? String::New(s, len) : String::Empty());
 }
 
@@ -1529,7 +1529,7 @@ static void ProcessTitleSetter(Local<String> property,
                                const AccessorInfo& info) {
   HandleScope scope;
   String::Utf8Value title(value->ToString());
-  OS::SetProcessTitle(*title);
+  Platform::SetProcessTitle(*title);
 }
 
 
@@ -1685,7 +1685,7 @@ static void Load(int argc, char *argv[]) {
 
   size_t size = 2*PATH_MAX;
   char execPath[size];
-  if (OS::GetExecutablePath(execPath, &size) != 0) {
+  if (Platform::GetExecutablePath(execPath, &size) != 0) {
     // as a last ditch effort, fallback on argv[0] ?
     process->Set(String::NewSymbol("execPath"), String::New(argv[0]));
   } else {
@@ -1874,7 +1874,7 @@ static int RegisterSignalHandler(int signal, void (*handler)(int)) {
 
 int Start(int argc, char *argv[]) {
   // Hack aroung with the argv pointer. Used for process.title = "blah".
-  argv = node::OS::SetupArgs(argc, argv);
+  argv = node::Platform::SetupArgs(argc, argv);
 
   // Parse a few arguments which are specific to Node.
   node::ParseArgs(&argc, argv);
index 68b0895..ad7b71a 100644 (file)
@@ -3,8 +3,12 @@
 #include <node.h>
 #include <v8.h>
 
+#include "platform.h"
+
 #include <errno.h>
-#include <unistd.h>  // gethostname
+#include <unistd.h>  // gethostname, sysconf
+#include <sys/param.h>  // sysctl
+#include <sys/sysctl.h>  // sysctl
 
 namespace node {
 
@@ -13,19 +17,108 @@ using namespace v8;
 static Handle<Value> GetHostname(const Arguments& args) {
   HandleScope scope;
   char s[255];
-  int r = gethostname(s, 255);
 
-  if (r < 0) {
-    return ThrowException(ErrnoException(errno, "gethostname"));
+  if (gethostname(s, 255) < 0) {
+    return Undefined();
   }
 
   return scope.Close(String::New(s));
 }
 
+static Handle<Value> GetOSType(const Arguments& args) {
+  HandleScope scope;
+  char type[256];
+  static int which[] = {CTL_KERN, KERN_OSTYPE};
+  size_t size = sizeof(type);
+
+  if (sysctl(which, 2, &type, &size, NULL, 0) < 0) {
+    return Undefined();
+  }
+
+  return scope.Close(String::New(type));
+}
+
+static Handle<Value> GetOSRelease(const Arguments& args) {
+  HandleScope scope;
+  char release[256];
+  static int which[] = {CTL_KERN, KERN_OSRELEASE};
+  size_t size = sizeof(release);
+
+  if (sysctl(which, 2, &release, &size, NULL, 0) < 0) {
+    return Undefined();
+  }
+
+  return scope.Close(String::New(release));
+}
+
+static Handle<Value> GetCPUInfo(const Arguments& args) {
+  HandleScope scope;
+  Local<Array> cpus;
+  int r = Platform::GetCPUInfo(&cpus);
+
+  if (r < 0) {
+    return Undefined();
+  }
+
+  return scope.Close(cpus);
+}
+
+static Handle<Value> GetFreeMemory(const Arguments& args) {
+  HandleScope scope;
+  double amount = Platform::GetFreeMemory();
+
+  if (amount < 0) {
+    return Undefined();
+  }
+
+  return scope.Close(Number::New(amount));
+}
+
+static Handle<Value> GetTotalMemory(const Arguments& args) {
+  HandleScope scope;
+  double amount = Platform::GetTotalMemory();
+
+  if (amount < 0) {
+    return Undefined();
+  }
+
+  return scope.Close(Number::New(amount));
+}
+
+static Handle<Value> GetUptime(const Arguments& args) {
+  HandleScope scope;
+  double uptime = Platform::GetUptime();
+
+  if (uptime < 0) {
+    return Undefined();
+  }
+
+  return scope.Close(Number::New(uptime));
+}
+
+static Handle<Value> GetLoadAvg(const Arguments& args) {
+  HandleScope scope;
+  Local<Array> loads = Array::New(3);
+  int r = Platform::GetLoadAvg(&loads);
+
+  if (r < 0) {
+    return Undefined();
+  }
+
+  return scope.Close(loads);
+}
+
 void OS::Initialize(v8::Handle<v8::Object> target) {
   HandleScope scope;
 
   NODE_SET_METHOD(target, "getHostname", GetHostname);
+  NODE_SET_METHOD(target, "getLoadAvg", GetLoadAvg);
+  NODE_SET_METHOD(target, "getUptime", GetUptime);
+  NODE_SET_METHOD(target, "getTotalMem", GetTotalMemory);
+  NODE_SET_METHOD(target, "getFreeMem", GetFreeMemory);
+  NODE_SET_METHOD(target, "getCPUs", GetCPUInfo);
+  NODE_SET_METHOD(target, "getOSType", GetOSType);
+  NODE_SET_METHOD(target, "getOSRelease", GetOSRelease);
 }
 
 
index f025230..bf7cd52 100644 (file)
@@ -1,9 +1,11 @@
 #ifndef NODE_PLATFORM_H_
 #define NODE_PLATFORM_H_
 
+#include <v8.h>
+
 namespace node {
 
-class OS {
+class Platform {
  public:
   static char** SetupArgs(int argc, char *argv[]);
   static void SetProcessTitle(char *title);
@@ -11,6 +13,11 @@ class OS {
 
   static int GetMemory(size_t *rss, size_t *vsize);
   static int GetExecutablePath(char* buffer, size_t* size);
+  static int GetCPUInfo(v8::Local<v8::Array> *cpus);
+  static double GetFreeMemory();
+  static double GetTotalMemory();
+  static double GetUptime();
+  static int GetLoadAvg(v8::Local<v8::Array> *loads);
 };
 
 
index 4fcade8..de0a3ba 100644 (file)
@@ -1,13 +1,22 @@
 #include "node.h"
 #include "platform.h"
 
+#include <v8.h>
+
 #include <sys/param.h> // for MAXPATHLEN
-#include <unistd.h> // getpagesize
+#include <sys/sysctl.h>
+#include <sys/sysinfo.h>
+#include <unistd.h> // getpagesize, sysconf
+#include <stdio.h> // sscanf, snprintf
+#include <string.h>
+
 #include <windows.h>
 
 
 namespace node {
 
+using namespace v8;
+
 static char buf[MAXPATHLEN + 1];
 static char *process_title = NULL;
 
@@ -30,12 +39,12 @@ static void _winapi_perror(const char* prefix = NULL) {
 }
 
 
-char** OS::SetupArgs(int argc, char *argv[]) {
+char** Platform::SetupArgs(int argc, char *argv[]) {
   return argv;
 }
 
 
-void OS::SetProcessTitle(char *title) {
+void Platform::SetProcessTitle(char *title) {
   // We need to convert _title_ to UTF-16 first, because that's what windows uses internally.
   // It would be more efficient to use the UTF-16 value that we can obtain from v8,
   // but it's not accessible from here.
@@ -139,7 +148,7 @@ static inline char* _getProcessTitle() {
 }
 
 
-const char* OS::GetProcessTitle(int *len) {
+const char* Platform::GetProcessTitle(int *len) {
   // If the process_title was never read before nor explicitly set,
   // we must query it with getConsoleTitleW
   if (!process_title) {
@@ -156,7 +165,7 @@ const char* OS::GetProcessTitle(int *len) {
 }
 
 
-int OS::GetMemory(size_t *rss, size_t *vsize) {
+int Platform::GetMemory(size_t *rss, size_t *vsize) {
   FILE *f = fopen("/proc/self/stat", "r");
   if (!f) return -1;
 
@@ -236,11 +245,118 @@ error:
 }
 
 
-int OS::GetExecutablePath(char* buffer, size_t* size) {
+int Platform::GetExecutablePath(char* buffer, size_t* size) {
   *size = readlink("/proc/self/exe", buffer, *size - 1);
   if (*size <= 0) return -1;
   buffer[*size] = '\0';
   return 0;
 }
 
+int Platform::GetCPUInfo(Local<Array> *cpus) {
+  Local<Object> cpuinfo;
+  Local<Object> cputimes;
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks), cpuspeed;
+  int numcpus = 0, i = 0;
+  unsigned long long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr;
+  char line[512], speedPath[256], model[512];
+  FILE *fpStat = fopen("/proc/stat", "r");
+  FILE *fpModel = fopen("/proc/cpuinfo", "r");
+  FILE *fpSpeed;
+
+  if (fpModel) {
+    while (fgets(line, 511, fpModel) != NULL) {
+      if (strncmp(line, "model name", 10) == 0) {
+        numcpus++;
+        if (numcpus == 1) {
+          char *p = strchr(line, ':') + 2;
+          strcpy(model, p);
+          model[strlen(model)-1] = 0;
+        }
+      } else if (strncmp(line, "cpu MHz", 7) == 0) {
+        if (numcpus == 1) {
+          sscanf(line, "%*s %*s : %u", &cpuspeed);
+        }
+      }
+    }
+    fclose(fpModel);
+  }
+
+  *cpus = Array::New(numcpus);
+
+  if (fpStat) {
+    while (fgets(line, 511, fpStat) != NULL) {
+      if (strncmp(line, "cpu ", 4) == 0)
+        continue;
+      else if (strncmp(line, "intr ", 5) == 0)
+        break;
+      sscanf(line, "%*s %llu %llu %llu %llu %*llu %llu",
+             &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr);
+      snprintf(speedPath, sizeof(speedPath),
+               "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
+      fpSpeed = fopen(speedPath, "r");
+      if (fpSpeed) {
+        if (fgets(line, 511, fpSpeed) != NULL) {
+          sscanf(line, "%u", &cpuspeed);
+          cpuspeed /= 1000;
+        }
+        fclose(fpSpeed);
+      }
+      cpuinfo = Object::New();
+      cputimes = Object::New();
+      cputimes->Set(String::New("user"), Number::New(ticks_user * multiplier));
+      cputimes->Set(String::New("nice"), Number::New(ticks_nice * multiplier));
+      cputimes->Set(String::New("sys"), Number::New(ticks_sys * multiplier));
+      cputimes->Set(String::New("idle"), Number::New(ticks_idle * multiplier));
+      cputimes->Set(String::New("irq"), Number::New(ticks_intr * multiplier));
+
+      cpuinfo->Set(String::New("model"), String::New(model));
+      cpuinfo->Set(String::New("speed"), Number::New(cpuspeed));
+
+      cpuinfo->Set(String::New("times"), cputimes);
+      (*cpus)->Set(i++, cpuinfo);
+    }
+    fclose(fpStat);
+  }
+
+  return 0;
+}
+
+double Platform::GetFreeMemory() {
+  double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
+  double pages = static_cast<double>(sysconf(_SC_AVPHYS_PAGES));
+
+  return static_cast<double>(pages * pagesize);
+}
+
+double Platform::GetTotalMemory() {
+  double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
+  double pages = static_cast<double>(sysconf(_SC_PHYS_PAGES));
+
+  return pages * pagesize;
+}
+
+double Platform::GetUptime() {
+  struct sysinfo info;
+
+  if (sysinfo(&info) < 0) {
+    return -1;
+  }
+
+  return static_cast<double>(info.uptime);
+}
+
+int Platform::GetLoadAvg(Local<Array> *loads) {
+  struct sysinfo info;
+
+  if (sysinfo(&info) < 0) {
+    return -1;
+  }
+  (*loads)->Set(0, Number::New(static_cast<double>(info.loads[0]) / 65536.0));
+  (*loads)->Set(1, Number::New(static_cast<double>(info.loads[1]) / 65536.0));
+  (*loads)->Set(2, Number::New(static_cast<double>(info.loads[2]) / 65536.0));
+
+  return 0;
+}
+
 }  // namespace node
index f02dc49..828d511 100644 (file)
@@ -1,27 +1,38 @@
 #include "node.h"
 #include "platform.h"
 
+#include <v8.h>
+
 #include <mach/task.h>
-#include <mach/mach_init.h>
+#include <mach/mach.h>
+#include <mach/mach_host.h>
 #include <mach-o/dyld.h> /* _NSGetExecutablePath */
 #include <limits.h> /* PATH_MAX */
 
+#include <unistd.h>  // sysconf
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <time.h>
+
 namespace node {
+
+using namespace v8;
+
 static char *process_title;
 
-char** OS::SetupArgs(int argc, char *argv[]) {
+char** Platform::SetupArgs(int argc, char *argv[]) {
   process_title = argc ? strdup(argv[0]) : NULL;
   return argv;
 }
 
 
-// OS::SetProcessTitle implemented in platform_darwin_proctitle.cc
+// Platform::SetProcessTitle implemented in platform_darwin_proctitle.cc
 }  // namespace node
 #include "platform_darwin_proctitle.cc"
 namespace node {
 
 
-const char* OS::GetProcessTitle(int *len) {
+const char* Platform::GetProcessTitle(int *len) {
   if (process_title) {
     *len = strlen(process_title);
     return process_title;
@@ -32,7 +43,7 @@ const char* OS::GetProcessTitle(int *len) {
 
 // Researched by Tim Becker and Michael Knight
 // http://blog.kuriositaet.de/?p=257
-int OS::GetMemory(size_t *rss, size_t *vsize) {
+int Platform::GetMemory(size_t *rss, size_t *vsize) {
   struct task_basic_info t_info;
   mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT;
 
@@ -50,7 +61,7 @@ int OS::GetMemory(size_t *rss, size_t *vsize) {
 }
 
 
-int OS::GetExecutablePath(char* buffer, size_t* size) {
+int Platform::GetExecutablePath(char* buffer, size_t* size) {
   uint32_t usize = *size;
   int result = _NSGetExecutablePath(buffer, &usize);
   if (result) return result;
@@ -68,4 +79,112 @@ int OS::GetExecutablePath(char* buffer, size_t* size) {
   return 0;
 }
 
+int Platform::GetCPUInfo(Local<Array> *cpus) {
+  Local<Object> cpuinfo;
+  Local<Object> cputimes;
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks);
+  char model[512];
+  uint64_t cpuspeed;
+  size_t size;
+
+  size = sizeof(model);
+  if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  size = sizeof(cpuspeed);
+  if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0) < 0) {
+    return -1;
+  }
+
+  natural_t numcpus;
+  mach_msg_type_number_t count;
+  processor_cpu_load_info_data_t *info;
+  if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus,
+                          reinterpret_cast<processor_info_array_t*>(&info),
+                          &count) != KERN_SUCCESS) {
+    return -1;
+  }
+  *cpus = Array::New(numcpus);
+  for (int i = 0; i < numcpus; i++) {
+    cpuinfo = Object::New();
+    cputimes = Object::New();
+    cputimes->Set(String::New("user"),
+                  Number::New((uint64_t)(info[i].cpu_ticks[0]) * multiplier));
+    cputimes->Set(String::New("nice"),
+                  Number::New((uint64_t)(info[i].cpu_ticks[3]) * multiplier));
+    cputimes->Set(String::New("sys"),
+                  Number::New((uint64_t)(info[i].cpu_ticks[1]) * multiplier));
+    cputimes->Set(String::New("idle"),
+                  Number::New((uint64_t)(info[i].cpu_ticks[2]) * multiplier));
+    cputimes->Set(String::New("irq"), Number::New(0));
+
+    cpuinfo->Set(String::New("model"), String::New(model));
+    cpuinfo->Set(String::New("speed"), Number::New(cpuspeed/1000000));
+
+    cpuinfo->Set(String::New("times"), cputimes);
+    (*cpus)->Set(i, cpuinfo);
+  }
+  vm_deallocate(mach_task_self(), (vm_address_t)info, count);
+
+  return 0;
+}
+
+double Platform::GetFreeMemory() {
+  double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
+  vm_statistics_data_t info;
+  mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t);
+
+  if (host_statistics(mach_host_self(), HOST_VM_INFO,
+                      (host_info_t)&info, &count) != KERN_SUCCESS) {
+    return -1;
+  }
+
+  return (static_cast<double>(info.free_count)) * pagesize;
+}
+
+double Platform::GetTotalMemory() {
+  uint64_t info;
+  static int which[] = {CTL_HW, HW_MEMSIZE};
+  size_t size = sizeof(info);
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+
+  return static_cast<double>(info);
+}
+
+double Platform::GetUptime() {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  now = time(NULL);
+
+  return static_cast<double>(now - info.tv_sec);
+}
+
+int Platform::GetLoadAvg(Local<Array> *loads) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  (*loads)->Set(0, Number::New(static_cast<double>(info.ldavg[0])
+                               / static_cast<double>(info.fscale)));
+  (*loads)->Set(1, Number::New(static_cast<double>(info.ldavg[1])
+                               / static_cast<double>(info.fscale)));
+  (*loads)->Set(2, Number::New(static_cast<double>(info.ldavg[2])
+                               / static_cast<double>(info.fscale)));
+
+  return 0;
+}
+
 }  // namespace node
index 6c58ae1..e6a1ddd 100644 (file)
@@ -38,7 +38,7 @@
 
 namespace node {
 
-void OS::SetProcessTitle(char *title) {
+void Platform::SetProcessTitle(char *title) {
   static int symbol_lookup_status = 0; // 1=ok, 2=unavailable
   if (symbol_lookup_status == 2) {
     // feature is unavailable
index 0c8b259..6eb5ca2 100644 (file)
@@ -1,33 +1,41 @@
 #include "node.h"
 #include "platform.h"
 
+#include <v8.h>
+
 #include <stdlib.h>
 #include <kvm.h>
 #include <sys/param.h>
 #include <sys/sysctl.h>
 #include <sys/user.h>
+#include <sys/dkstat.h>
+#include <vm/vm_param.h>
 #include <string.h>
 #include <paths.h>
 #include <fcntl.h>
 #include <unistd.h>
+#include <time.h>
 
 
 namespace node {
+
+using namespace v8;
+
 static char *process_title;
 
-char** OS::SetupArgs(int argc, char *argv[]) {
+char** Platform::SetupArgs(int argc, char *argv[]) {
   process_title = argc ? strdup(argv[0]) : NULL;
   return argv;
 }
 
 
-void OS::SetProcessTitle(char *title) {
+void Platform::SetProcessTitle(char *title) {
   if (process_title) free(process_title);
   process_title = strdup(title);
   setproctitle(title);
 }
 
-const char* OS::GetProcessTitle(int *len) {
+const char* Platform::GetProcessTitle(int *len) {
   if (process_title) {
     *len = strlen(process_title);
     return process_title;
@@ -36,7 +44,7 @@ const char* OS::GetProcessTitle(int *len) {
   return NULL;
 }
 
-int OS::GetMemory(size_t *rss, size_t *vsize) {
+int Platform::GetMemory(size_t *rss, size_t *vsize) {
   kvm_t *kd = NULL;
   struct kinfo_proc *kinfo = NULL;
   pid_t pid;
@@ -64,7 +72,7 @@ error:
 }
 
 
-int OS::GetExecutablePath(char* buffer, size_t* size) {
+int Platform::GetExecutablePath(char* buffer, size_t* size) {
   int mib[4];
   mib[0] = CTL_KERN;
   mib[1] = KERN_PROC;
@@ -78,4 +86,125 @@ int OS::GetExecutablePath(char* buffer, size_t* size) {
   return 0;
 }
 
+int Platform::GetCPUInfo(Local<Array> *cpus) {
+  Local<Object> cpuinfo;
+  Local<Object> cputimes;
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks), cpuspeed, maxcpus,
+               cur = 0;
+  char model[512];
+  int numcpus;
+  size_t size;
+
+  size = sizeof(model);
+  if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  size = sizeof(numcpus);
+  if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0) < 0) {
+    return -1;
+  }
+
+  *cpus = Array::New(numcpus);
+
+  size = sizeof(cpuspeed);
+  if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  // kern.cp_times on FreeBSD i386 gives an array up to maxcpus instead of ncpu
+  size = sizeof(maxcpus);
+  if (sysctlbyname("kern.smp.maxcpus", &maxcpus, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  size = maxcpus * CPUSTATES * sizeof(long);
+  long cp_times[size];
+  if (sysctlbyname("kern.cp_times", &cp_times, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  for (int i = 0; i < numcpus; i++) {
+    cpuinfo = Object::New();
+    cputimes = Object::New();
+    cputimes->Set(String::New("user"),
+                  Number::New((uint64_t)(cp_times[CP_USER+cur]) * multiplier));
+    cputimes->Set(String::New("nice"),
+                  Number::New((uint64_t)(cp_times[CP_NICE+cur]) * multiplier));
+    cputimes->Set(String::New("sys"),
+                  Number::New((uint64_t)(cp_times[CP_SYS+cur]) * multiplier));
+    cputimes->Set(String::New("idle"),
+                  Number::New((uint64_t)(cp_times[CP_IDLE+cur]) * multiplier));
+    cputimes->Set(String::New("irq"),
+                  Number::New((uint64_t)(cp_times[CP_INTR+cur]) * multiplier));
+
+    cpuinfo->Set(String::New("model"), String::New(model));
+    cpuinfo->Set(String::New("speed"), Number::New(cpuspeed));
+
+    cpuinfo->Set(String::New("times"), cputimes);
+    (*cpus)->Set(i, cpuinfo);
+    cur+=CPUSTATES;
+  }
+
+  return 0;
+}
+
+double Platform::GetFreeMemory() {
+  double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
+  unsigned long info;
+  size_t size = sizeof(info);
+
+  if (sysctlbyname("vm.stats.vm.v_free_count", &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+
+  return (static_cast<double>(info)) * pagesize;
+}
+
+double Platform::GetTotalMemory() {
+#if defined(HW_PHYSMEM64)
+  uint64_t info;
+  static int which[] = {CTL_HW, HW_PHYSMEM64};
+#else
+  unsigned int info;
+  static int which[] = {CTL_HW, HW_PHYSMEM};
+#endif
+  size_t size = sizeof(info);
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+
+  return static_cast<double>(info);
+}
+
+double Platform::GetUptime() {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  now = time(NULL);
+
+  return static_cast<double>(now - info.tv_sec);
+}
+
+int Platform::GetLoadAvg(Local<Array> *loads) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  (*loads)->Set(0, Number::New(static_cast<double>(info.ldavg[0])
+                               / static_cast<double>(info.fscale)));
+  (*loads)->Set(1, Number::New(static_cast<double>(info.ldavg[1])
+                               / static_cast<double>(info.fscale)));
+  (*loads)->Set(2, Number::New(static_cast<double>(info.ldavg[2])
+                               / static_cast<double>(info.fscale)));
+
+  return 0;
+}
+
 }  // namespace node
index 9c4ccc4..00732a0 100644 (file)
@@ -1,8 +1,13 @@
 #include "node.h"
 #include "platform.h"
 
+#include <v8.h>
+
 #include <sys/param.h> // for MAXPATHLEN
-#include <unistd.h> // getpagesize
+#include <sys/sysctl.h>
+#include <sys/sysinfo.h>
+#include <unistd.h> // getpagesize, sysconf
+#include <stdio.h> // sscanf, snprintf
 
 /* SetProcessTitle */
 #include <sys/prctl.h>
 #include <stdlib.h> // free
 #include <string.h> // strdup
 
-
 namespace node {
 
+using namespace v8;
+
 static char buf[MAXPATHLEN + 1];
 static char *process_title;
 
 
-char** OS::SetupArgs(int argc, char *argv[]) {
+char** Platform::SetupArgs(int argc, char *argv[]) {
   process_title = strdup(argv[0]);
   return argv;
 }
 
 
-void OS::SetProcessTitle(char *title) {
+void Platform::SetProcessTitle(char *title) {
   if (process_title) free(process_title);
   process_title = strdup(title);
   prctl(PR_SET_NAME, process_title);
 }
 
 
-const char* OS::GetProcessTitle(int *len) {
+const char* Platform::GetProcessTitle(int *len) {
   if (process_title) {
     *len = strlen(process_title);
     return process_title;
@@ -40,7 +46,7 @@ const char* OS::GetProcessTitle(int *len) {
 }
 
 
-int OS::GetMemory(size_t *rss, size_t *vsize) {
+int Platform::GetMemory(size_t *rss, size_t *vsize) {
   FILE *f = fopen("/proc/self/stat", "r");
   if (!f) return -1;
 
@@ -135,11 +141,119 @@ error:
 }
 
 
-int OS::GetExecutablePath(char* buffer, size_t* size) {
+int Platform::GetExecutablePath(char* buffer, size_t* size) {
   *size = readlink("/proc/self/exe", buffer, *size - 1);
   if (*size <= 0) return -1;
   buffer[*size] = '\0';
   return 0;
 }
 
+int Platform::GetCPUInfo(Local<Array> *cpus) {
+  HandleScope scope;
+  Local<Object> cpuinfo;
+  Local<Object> cputimes;
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks), cpuspeed;
+  int numcpus = 0, i = 0;
+  unsigned long long ticks_user, ticks_sys, ticks_idle, ticks_nice, ticks_intr;
+  char line[512], speedPath[256], model[512];
+  FILE *fpStat = fopen("/proc/stat", "r");
+  FILE *fpModel = fopen("/proc/cpuinfo", "r");
+  FILE *fpSpeed;
+
+  if (fpModel) {
+    while (fgets(line, 511, fpModel) != NULL) {
+      if (strncmp(line, "model name", 10) == 0) {
+        numcpus++;
+        if (numcpus == 1) {
+          char *p = strchr(line, ':') + 2;
+          strcpy(model, p);
+          model[strlen(model)-1] = 0;
+        }
+      } else if (strncmp(line, "cpu MHz", 7) == 0) {
+        if (numcpus == 1) {
+          sscanf(line, "%*s %*s : %u", &cpuspeed);
+        }
+      }
+    }
+    fclose(fpModel);
+  }
+
+  *cpus = Array::New(numcpus);
+
+  if (fpStat) {
+    while (fgets(line, 511, fpStat) != NULL) {
+      if (strncmp(line, "cpu ", 4) == 0)
+        continue;
+      else if (strncmp(line, "intr ", 5) == 0)
+        break;
+      sscanf(line, "%*s %llu %llu %llu %llu %*llu %llu",
+             &ticks_user, &ticks_nice, &ticks_sys, &ticks_idle, &ticks_intr);
+      snprintf(speedPath, sizeof(speedPath),
+               "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
+      fpSpeed = fopen(speedPath, "r");
+      if (fpSpeed) {
+        if (fgets(line, 511, fpSpeed) != NULL) {
+          sscanf(line, "%u", &cpuspeed);
+          cpuspeed /= 1000;
+        }
+        fclose(fpSpeed);
+      }
+      cpuinfo = Object::New();
+      cputimes = Object::New();
+      cputimes->Set(String::New("user"), Number::New(ticks_user * multiplier));
+      cputimes->Set(String::New("nice"), Number::New(ticks_nice * multiplier));
+      cputimes->Set(String::New("sys"), Number::New(ticks_sys * multiplier));
+      cputimes->Set(String::New("idle"), Number::New(ticks_idle * multiplier));
+      cputimes->Set(String::New("irq"), Number::New(ticks_intr * multiplier));
+
+      cpuinfo->Set(String::New("model"), String::New(model));
+      cpuinfo->Set(String::New("speed"), Number::New(cpuspeed));
+
+      cpuinfo->Set(String::New("times"), cputimes);
+      (*cpus)->Set(i++, cpuinfo);
+    }
+    fclose(fpStat);
+  }
+
+  return 0;
+}
+
+double Platform::GetFreeMemory() {
+  double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
+  double pages = static_cast<double>(sysconf(_SC_AVPHYS_PAGES));
+
+  return static_cast<double>(pages * pagesize);
+}
+
+double Platform::GetTotalMemory() {
+  double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
+  double pages = static_cast<double>(sysconf(_SC_PHYS_PAGES));
+
+  return pages * pagesize;
+}
+
+double Platform::GetUptime() {
+  struct sysinfo info;
+
+  if (sysinfo(&info) < 0) {
+    return -1;
+  }
+
+  return static_cast<double>(info.uptime);
+}
+
+int Platform::GetLoadAvg(Local<Array> *loads) {
+  struct sysinfo info;
+
+  if (sysinfo(&info) < 0) {
+    return -1;
+  }
+  (*loads)->Set(0, Number::New(static_cast<double>(info.loads[0]) / 65536.0));
+  (*loads)->Set(1, Number::New(static_cast<double>(info.loads[1]) / 65536.0));
+  (*loads)->Set(2, Number::New(static_cast<double>(info.loads[2]) / 65536.0));
+
+  return 0;
+}
+
 }  // namespace node
diff --git a/src/platform_openbsd.cc b/src/platform_openbsd.cc
new file mode 100644 (file)
index 0000000..b8a3209
--- /dev/null
@@ -0,0 +1,201 @@
+#include "node.h"
+#include "platform.h"
+
+#include <v8.h>
+
+#include <stdlib.h>
+#include <kvm.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/user.h>
+#include <sys/dkstat.h>
+#include <uvm/uvm_param.h>
+#include <string.h>
+#include <paths.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <stdio.h>
+namespace node {
+
+using namespace v8;
+
+static char *process_title;
+
+char** Platform::SetupArgs(int argc, char *argv[]) {
+  process_title = argc ? strdup(argv[0]) : NULL;
+  return argv;
+}
+
+
+void Platform::SetProcessTitle(char *title) {
+  if (process_title) free(process_title);
+  process_title = strdup(title);
+  setproctitle(title);
+}
+
+const char* Platform::GetProcessTitle(int *len) {
+  if (process_title) {
+    *len = strlen(process_title);
+    return process_title;
+  }
+  *len = 0;
+  return NULL;
+}
+
+int Platform::GetMemory(size_t *rss, size_t *vsize) {
+  kvm_t *kd = NULL;
+  struct kinfo_proc2 *kinfo = NULL;
+  pid_t pid;
+  int nprocs, max_size = sizeof(struct kinfo_proc2);
+  size_t page_size = getpagesize();
+
+  pid = getpid();
+
+  kd = kvm_open(NULL, _PATH_MEM, NULL, O_RDONLY, "kvm_open");
+  if (kd == NULL) goto error;
+
+  kinfo = kvm_getproc2(kd, KERN_PROC_PID, pid, max_size, &nprocs);
+  if (kinfo == NULL) goto error;
+
+  *rss = kinfo->p_vm_rssize * page_size;
+  *vsize = kinfo->p_uru_ixrss;
+
+  kvm_close(kd);
+
+  return 0;
+
+error:
+  if (kd) kvm_close(kd);
+  return -1;
+}
+
+
+int Platform::GetExecutablePath(char* buffer, size_t* size) {
+  *size = 0;
+  return -1;
+}
+
+int Platform::GetCPUInfo(Local<Array> *cpus) {
+  Local<Object> cpuinfo;
+  Local<Object> cputimes;
+  unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK),
+               multiplier = ((uint64_t)1000L / ticks), cpuspeed;
+  uint64_t info[CPUSTATES];
+  char model[512];
+  int numcpus = 1;
+  static int which[] = {CTL_HW, HW_MODEL, NULL};
+  size_t size;
+
+  size = sizeof(model);
+  if (sysctl(which, 2, &model, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  which[1] = HW_NCPU;
+  size = sizeof(numcpus);
+  if (sysctl(which, 2, &numcpus, &size, NULL, 0) < 0) {
+    return -1;
+  }
+
+  *cpus = Array::New(numcpus);
+
+  which[1] = HW_CPUSPEED;
+  size = sizeof(cpuspeed);
+  if (sysctl(which, 2, &cpuspeed, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  size = sizeof(info);
+  which[0] = CTL_KERN;
+  which[1] = KERN_CPTIME2;
+  for (int i = 0; i < numcpus; i++) {
+    which[2] = i;
+    size = sizeof(info);
+    if (sysctl(which, 3, &info, &size, NULL, 0) < 0) {
+      return -1;
+    }
+    cpuinfo = Object::New();
+    cputimes = Object::New();
+    cputimes->Set(String::New("user"),
+                  Number::New((uint64_t)(info[CP_USER]) * multiplier));
+    cputimes->Set(String::New("nice"),
+                  Number::New((uint64_t)(info[CP_NICE]) * multiplier));
+    cputimes->Set(String::New("sys"),
+                  Number::New((uint64_t)(info[CP_SYS]) * multiplier));
+    cputimes->Set(String::New("idle"),
+                  Number::New((uint64_t)(info[CP_IDLE]) * multiplier));
+    cputimes->Set(String::New("irq"),
+                  Number::New((uint64_t)(info[CP_INTR]) * multiplier));
+
+    cpuinfo->Set(String::New("model"), String::New(model));
+    cpuinfo->Set(String::New("speed"), Number::New(cpuspeed));
+
+    cpuinfo->Set(String::New("times"), cputimes);
+    (*cpus)->Set(i, cpuinfo);
+  }
+  return 0;
+}
+
+double Platform::GetFreeMemory() {
+  double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
+  struct uvmexp info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_VM, VM_UVMEXP};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+
+  return static_cast<double>(info.free) * pagesize;
+}
+
+double Platform::GetTotalMemory() {
+#if defined(HW_PHYSMEM64)
+  uint64_t info;
+  static int which[] = {CTL_HW, HW_PHYSMEM64};
+#else
+  unsigned int info;
+  static int which[] = {CTL_HW, HW_PHYSMEM};
+#endif
+  size_t size = sizeof(info);
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+
+  return static_cast<double>(info);
+}
+
+double Platform::GetUptime() {
+  time_t now;
+  struct timeval info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_KERN, KERN_BOOTTIME};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  now = time(NULL);
+
+  return static_cast<double>(now - info.tv_sec);
+}
+
+int Platform::GetLoadAvg(Local<Array> *loads) {
+  struct loadavg info;
+  size_t size = sizeof(info);
+  static int which[] = {CTL_VM, VM_LOADAVG};
+
+  if (sysctl(which, 2, &info, &size, NULL, 0) < 0) {
+    return -1;
+  }
+  (*loads)->Set(0, Number::New(static_cast<double>(info.ldavg[0])
+                               / static_cast<double>(info.fscale)));
+  (*loads)->Set(1, Number::New(static_cast<double>(info.ldavg[1])
+                               / static_cast<double>(info.fscale)));
+  (*loads)->Set(2, Number::New(static_cast<double>(info.ldavg[2])
+                               / static_cast<double>(info.fscale)));
+
+  return 0;
+}
+
+}  // namespace node
diff --git a/test/simple/test-os-hostname.js b/test/simple/test-os-hostname.js
deleted file mode 100644 (file)
index b323622..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-var common = require('../common');
-var assert = require('assert');
-var os = require('os');
-
-assert.ok(os.getHostname().length > 0);
diff --git a/test/simple/test-os.js b/test/simple/test-os.js
new file mode 100644 (file)
index 0000000..25cabab
--- /dev/null
@@ -0,0 +1,12 @@
+var common = require('../common');
+var assert = require('assert');
+var os = require('os');
+
+assert.ok(os.hostname().length > 0);
+assert.ok(os.loadavg().length > 0);
+assert.ok(os.uptime() > 0);
+assert.ok(os.freemem() > 0);
+assert.ok(os.totalmem() > 0);
+assert.ok(os.cpus().length > 0);
+assert.ok(os.type().length > 0);
+assert.ok(os.release().length > 0);
\ No newline at end of file
diff --git a/wscript b/wscript
index d0d9477..7f39031 100644 (file)
--- a/wscript
+++ b/wscript
@@ -197,7 +197,7 @@ def configure(conf):
     conf.env.append_value("CCFLAGS", "-rdynamic")
     conf.env.append_value("LINKFLAGS_DL", "-rdynamic")
 
-  if sys.platform.startswith("freebsd"):
+  if sys.platform.startswith("freebsd") or sys.platform.startswith("openbsd"):
     conf.check(lib='kvm', uselib_store='KVM')
 
   #if Options.options.debug: