node/src/platform_sunos.cc

354 lines
9.0 KiB
C++

// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "node.h"
#include "platform.h"
#include <unistd.h> /* getpagesize() */
#include <stdlib.h> /* getexecname() */
#include <strings.h> /* strncpy() */
#include <kstat.h>
#include <errno.h>
#include <inttypes.h>
#include <sys/types.h>
#include <sys/loadavg.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#ifdef SUNOS_HAVE_IFADDRS
# include <ifaddrs.h>
#endif
#if (!defined(_LP64)) && (_FILE_OFFSET_BITS - 0 == 64)
#define PROCFS_FILE_OFFSET_BITS_HACK 1
#undef _FILE_OFFSET_BITS
#else
#define PROCFS_FILE_OFFSET_BITS_HACK 0
#endif
#include <procfs.h>
#if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1)
#define _FILE_OFFSET_BITS 64
#endif
namespace node {
using namespace v8;
double Platform::prog_start_time = Platform::GetUptime();
char** Platform::SetupArgs(int argc, char *argv[]) {
return argv;
}
void Platform::SetProcessTitle(char *title) {
;
}
const char* Platform::GetProcessTitle(int *len) {
*len = 0;
return NULL;
}
int Platform::GetMemory(size_t *rss, size_t *vsize) {
pid_t pid = getpid();
size_t page_size = getpagesize();
char pidpath[1024];
sprintf(pidpath, "/proc/%d/psinfo", pid);
psinfo_t psinfo;
FILE *f = fopen(pidpath, "r");
if (!f) return -1;
if (fread(&psinfo, sizeof(psinfo_t), 1, f) != 1) {
fclose (f);
return -1;
}
/* XXX correct? */
*vsize = (size_t) psinfo.pr_size * page_size;
*rss = (size_t) psinfo.pr_rssize * 1024;
fclose (f);
return 0;
}
static Handle<Value> data_named(kstat_named_t *knp) {
Handle<Value> val;
switch (knp->data_type) {
case KSTAT_DATA_CHAR:
val = Number::New(knp->value.c[0]);
break;
case KSTAT_DATA_INT32:
val = Number::New(knp->value.i32);
break;
case KSTAT_DATA_UINT32:
val = Number::New(knp->value.ui32);
break;
case KSTAT_DATA_INT64:
val = Number::New(knp->value.i64);
break;
case KSTAT_DATA_UINT64:
val = Number::New(knp->value.ui64);
break;
case KSTAT_DATA_STRING:
val = String::New(KSTAT_NAMED_STR_PTR(knp));
break;
default:
throw (String::New("unrecognized data type"));
}
return (val);
}
int Platform::GetCPUInfo(Local<Array> *cpus) {
HandleScope scope;
Local<Object> cpuinfo;
Local<Object> cputimes;
int lookup_instance;
kstat_ctl_t *kc;
kstat_t *ksp;
kstat_named_t *knp;
if ((kc = kstat_open()) == NULL)
throw "could not open kstat";
*cpus = Array::New();
lookup_instance = 0;
while (ksp = kstat_lookup(kc, (char *)"cpu_info", lookup_instance, NULL)){
cpuinfo = Object::New();
if (kstat_read(kc, ksp, NULL) == -1) {
/*
* It is deeply annoying, but some kstats can return errors
* under otherwise routine conditions. (ACPI is one
* offender; there are surely others.) To prevent these
* fouled kstats from completely ruining our day, we assign
* an "error" member to the return value that consists of
* the strerror().
*/
cpuinfo->Set(String::New("error"), String::New(strerror(errno)));
(*cpus)->Set(lookup_instance, cpuinfo);
} else {
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clock_MHz");
cpuinfo->Set(String::New("speed"), data_named(knp));
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"brand");
cpuinfo->Set(String::New("model"), data_named(knp));
(*cpus)->Set(lookup_instance, cpuinfo);
}
lookup_instance++;
}
lookup_instance = 0;
while (ksp = kstat_lookup(kc, (char *)"cpu", lookup_instance, (char *)"sys")){
cpuinfo = (*cpus)->Get(lookup_instance)->ToObject();
cputimes = Object::New();
if (kstat_read(kc, ksp, NULL) == -1) {
cputimes->Set(String::New("error"), String::New(strerror(errno)));
cpuinfo->Set(String::New("times"), cpuinfo);
} else {
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_kernel");
cputimes->Set(String::New("system"), data_named(knp));
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_user");
cputimes->Set(String::New("user"), data_named(knp));
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_idle");
cputimes->Set(String::New("idle"), data_named(knp));
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"intr");
cputimes->Set(String::New("irq"), data_named(knp));
cpuinfo->Set(String::New("times"), cputimes);
}
lookup_instance++;
}
kstat_close(kc);
return 0;
}
double Platform::GetFreeMemory() {
kstat_ctl_t *kc;
kstat_t *ksp;
kstat_named_t *knp;
double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
ulong_t freemem;
if((kc = kstat_open()) == NULL)
throw "could not open kstat";
ksp = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_pages");
if(kstat_read(kc, ksp, NULL) == -1){
throw "could not read kstat";
}
else {
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"freemem");
freemem = knp->value.ul;
}
kstat_close(kc);
return static_cast<double>(freemem)*pagesize;
}
double Platform::GetTotalMemory() {
double pagesize = static_cast<double>(sysconf(_SC_PAGESIZE));
double pages = static_cast<double>(sysconf(_SC_PHYS_PAGES));
return pagesize*pages;
}
double Platform::GetUptimeImpl() {
kstat_ctl_t *kc;
kstat_t *ksp;
kstat_named_t *knp;
long hz = sysconf(_SC_CLK_TCK);
ulong_t clk_intr;
if ((kc = kstat_open()) == NULL)
throw "could not open kstat";
ksp = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_misc");
if (kstat_read(kc, ksp, NULL) == -1) {
throw "unable to read kstat";
} else {
knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clk_intr");
clk_intr = knp->value.ul;
}
kstat_close(kc);
return static_cast<double>( clk_intr / hz );
}
int Platform::GetLoadAvg(Local<Array> *loads) {
HandleScope scope;
double loadavg[3];
(void) getloadavg(loadavg, 3);
(*loads)->Set(0, Number::New(loadavg[LOADAVG_1MIN]));
(*loads)->Set(1, Number::New(loadavg[LOADAVG_5MIN]));
(*loads)->Set(2, Number::New(loadavg[LOADAVG_15MIN]));
return 0;
}
Handle<Value> Platform::GetInterfaceAddresses() {
HandleScope scope;
#ifndef SUNOS_HAVE_IFADDRS
return ThrowException(Exception::Error(String::New(
"This version of sunos doesn't support getifaddrs")));
#else
struct ::ifaddrs *addrs, *ent;
struct ::sockaddr_in *in4;
struct ::sockaddr_in6 *in6;
char ip[INET6_ADDRSTRLEN];
Local<Object> ret, o;
Local<String> name, ipaddr, family;
Local<Array> ifarr;
if (getifaddrs(&addrs) != 0) {
return ThrowException(ErrnoException(errno, "getifaddrs"));
}
ret = Object::New();
for (ent = addrs; ent != NULL; ent = ent->ifa_next) {
bzero(&ip, sizeof (ip));
if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING)) {
continue;
}
if (ent->ifa_addr == NULL) {
continue;
}
name = String::New(ent->ifa_name);
if (ret->Has(name)) {
ifarr = Local<Array>::Cast(ret->Get(name));
} else {
ifarr = Array::New();
ret->Set(name, ifarr);
}
if (ent->ifa_addr->sa_family == AF_INET6) {
in6 = (struct sockaddr_in6 *)ent->ifa_addr;
inet_ntop(AF_INET6, &(in6->sin6_addr), ip, INET6_ADDRSTRLEN);
family = String::New("IPv6");
} else if (ent->ifa_addr->sa_family == AF_INET) {
in4 = (struct sockaddr_in *)ent->ifa_addr;
inet_ntop(AF_INET, &(in4->sin_addr), ip, INET6_ADDRSTRLEN);
family = String::New("IPv4");
} else {
(void) strlcpy(ip, "<unknown sa family>", INET6_ADDRSTRLEN);
family = String::New("<unknown>");
}
o = Object::New();
o->Set(String::New("address"), String::New(ip));
o->Set(String::New("family"), family);
o->Set(String::New("internal"), ent->ifa_flags & IFF_PRIVATE || ent->ifa_flags &
IFF_LOOPBACK ? True() : False());
ifarr->Set(ifarr->Length(), o);
}
freeifaddrs(addrs);
return scope.Close(ret);
#endif // SUNOS_HAVE_IFADDRS
}
} // namespace node