diff --git a/include/uv.h b/include/uv.h index 37bdf9cf..ef1c6916 100644 --- a/include/uv.h +++ b/include/uv.h @@ -177,6 +177,8 @@ typedef struct uv_async_s uv_async_t; typedef struct uv_getaddrinfo_s uv_getaddrinfo_t; typedef struct uv_process_s uv_process_t; typedef struct uv_counters_s uv_counters_t; +typedef struct uv_cpu_info_s uv_cpu_info_t; +typedef struct uv_interface_address_s uv_interface_address_t; /* Request types */ typedef struct uv_req_s uv_req_t; typedef struct uv_shutdown_s uv_shutdown_t; @@ -1036,7 +1038,48 @@ UV_EXTERN int uv_queue_work(uv_loop_t* loop, uv_work_t* req, uv_work_cb work_cb, uv_after_work_cb after_work_cb); +struct uv_cpu_info_s { + char* model; + int speed; + struct uv_cpu_times_s { + uint64_t user; + uint64_t nice; + uint64_t sys; + uint64_t idle; + uint64_t irq; + } cpu_times; +}; +struct uv_interface_address_s { + char* name; + int is_internal; + union { + struct sockaddr_in address4; + struct sockaddr_in6 address6; + } address; +}; + +UV_EXTERN char** uv_setup_args(int argc, char** argv); +UV_EXTERN uv_err_t uv_get_process_title(char* buffer, size_t size); +UV_EXTERN uv_err_t uv_set_process_title(const char* title); +UV_EXTERN uv_err_t uv_resident_set_memory(size_t* rss); +UV_EXTERN uv_err_t uv_uptime(double* uptime); + +/* + * This allocates cpu_infos array, and sets count. The array + * is freed using uv_free_cpu_info(). + */ +UV_EXTERN uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count); +UV_EXTERN void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count); + +/* + * This allocates addresses array, and sets count. The array + * is freed using uv_free_interface_addresses(). + */ +UV_EXTERN uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count); +UV_EXTERN void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count); /* * File System Methods. diff --git a/src/unix/darwin.c b/src/unix/darwin.c index 675e0ad4..47104be8 100644 --- a/src/unix/darwin.c +++ b/src/unix/darwin.c @@ -25,6 +25,9 @@ #include #include +#include +#include + #include #include #include @@ -34,6 +37,9 @@ #include /* sysconf */ +static char *process_title; + + uint64_t uv_hrtime() { uint64_t time; Nanoseconds enano; @@ -71,6 +77,7 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } + uint64_t uv_get_free_memory(void) { vm_statistics_data_t info; mach_msg_type_number_t count = sizeof(info) / sizeof(integer_t); @@ -83,6 +90,7 @@ uint64_t uv_get_free_memory(void) { return (uint64_t) info.free_count * sysconf(_SC_PAGESIZE); } + uint64_t uv_get_total_memory(void) { uint64_t info; int which[] = {CTL_HW, HW_MEMSIZE}; @@ -95,6 +103,7 @@ uint64_t uv_get_total_memory(void) { return (uint64_t) info; } + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -106,3 +115,206 @@ void uv_loadavg(double avg[3]) { avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } + + +char** uv_setup_args(int argc, char** argv) { + process_title = argc ? strdup(argv[0]) : NULL; + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + /* TODO implement me */ + return uv__new_artificial_error(UV_ENOSYS); +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title) { + strncpy(buffer, process_title, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + struct task_basic_info t_info; + mach_msg_type_number_t t_info_count = TASK_BASIC_INFO_COUNT; + + int r = task_info(mach_task_self(), + TASK_BASIC_INFO, + (task_info_t)&t_info, + &t_info_count); + + if (r != KERN_SUCCESS) { + return uv__new_sys_error(errno); + } + + *rss = t_info.resident_size; + + return uv_ok_; +} + + +uv_err_t uv_uptime(double* uptime) { + 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 uv__new_sys_error(errno); + } + now = time(NULL); + + *uptime = (double)(now - info.tv_sec); + + return uv_ok_; +} + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + unsigned int ticks = (unsigned int)sysconf(_SC_CLK_TCK), + multiplier = ((uint64_t)1000L / ticks); + char model[512]; + uint64_t cpuspeed; + size_t size; + unsigned int i; + natural_t numcpus; + mach_msg_type_number_t msg_type; + processor_cpu_load_info_data_t *info; + uv_cpu_info_t* cpu_info; + + size = sizeof(model); + if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + size = sizeof(cpuspeed); + if (sysctlbyname("hw.cpufrequency", &cpuspeed, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + + if (host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &numcpus, + (processor_info_array_t*)&info, + &msg_type) != KERN_SUCCESS) { + return uv__new_sys_error(errno); + } + + *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = numcpus; + + for (i = 0; i < numcpus; i++) { + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(info[i].cpu_ticks[0]) * multiplier; + cpu_info->cpu_times.nice = (uint64_t)(info[i].cpu_ticks[3]) * multiplier; + cpu_info->cpu_times.sys = (uint64_t)(info[i].cpu_ticks[1]) * multiplier; + cpu_info->cpu_times.idle = (uint64_t)(info[i].cpu_ticks[2]) * multiplier; + cpu_info->cpu_times.irq = 0; + + cpu_info->model = strdup(model); + cpu_info->speed = cpuspeed/1000000; + } + vm_deallocate(mach_task_self(), (vm_address_t)info, msg_type); + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + struct ifaddrs *addrs, *ent; + char ip[INET6_ADDRSTRLEN]; + uv_interface_address_t* address; + + if (getifaddrs(&addrs) != 0) { + return uv__new_sys_error(errno); + } + + *count = 0; + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) || + (ent->ifa_addr == NULL) || + (ent->ifa_addr->sa_family == AF_LINK)) { + continue; + } + + (*count)++; + } + + *addresses = (uv_interface_address_t*) + malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + address = *addresses; + + 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; + } + + /* + * On Mac OS X getifaddrs returns information related to Mac Addresses for + * various devices, such as firewire, etc. These are not relevant here. + */ + if (ent->ifa_addr->sa_family == AF_LINK) { + continue; + } + + address->name = strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); + } + + address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; + + address++; + } + + freeifaddrs(addrs); + + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + free(addresses[i].name); + } + + free(addresses); +} diff --git a/src/unix/freebsd.c b/src/unix/freebsd.c index a519f86d..fb26d4a8 100644 --- a/src/unix/freebsd.c +++ b/src/unix/freebsd.c @@ -35,6 +35,9 @@ #define NANOSEC 1000000000 +static char *process_title; + + uint64_t uv_hrtime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); @@ -72,6 +75,7 @@ int uv_exepath(char* buffer, size_t* size) { return 0; } + uint64_t uv_get_free_memory(void) { int freecount; size_t size = sizeof(freecount); @@ -84,6 +88,7 @@ uint64_t uv_get_free_memory(void) { } + uint64_t uv_get_total_memory(void) { unsigned long info; int which[] = {CTL_HW, HW_PHYSMEM}; @@ -97,6 +102,7 @@ uint64_t uv_get_total_memory(void) { return (uint64_t) info; } + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -108,3 +114,161 @@ void uv_loadavg(double avg[3]) { avg[1] = (double) info.ldavg[1] / info.fscale; avg[2] = (double) info.ldavg[2] / info.fscale; } + + +char** uv_setup_args(int argc, char** argv) { + process_title = argc ? strdup(argv[0]) : NULL; + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + if (process_title) free(process_title); + process_title = strdup(title); + setproctitle(title); + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title) { + strncpy(buffer, process_title, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + kvm_t *kd = NULL; + struct kinfo_proc *kinfo = NULL; + pid_t pid; + int nprocs; + size_t page_size = getpagesize(); + + pid = getpid(); + + kd = kvm_open(NULL, _PATH_DEVNULL, NULL, O_RDONLY, "kvm_open"); + if (kd == NULL) goto error; + + kinfo = kvm_getprocs(kd, KERN_PROC_PID, pid, &nprocs); + if (kinfo == NULL) goto error; + + *rss = kinfo->ki_rssize * page_size; + + kvm_close(kd); + + return uv_ok_; + +error: + if (kd) kvm_close(kd); + return uv__new_sys_error(errno); +} + + +uv_err_t uv_uptime(double* uptime) { + 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 uv__new_sys_error(errno); + } + + now = time(NULL); + + *uptime = (double*)(now - info.tv_sec); + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + 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; + uv_cpu_info_t* cpu_info; + + size = sizeof(model); + if (sysctlbyname("hw.model", &model, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + size = sizeof(numcpus); + if (sysctlbyname("hw.ncpu", &numcpus, &size, NULL, 0) < 0) { + return uv__new_sys_error(errno); + } + + *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = numcpus; + + size = sizeof(cpuspeed); + if (sysctlbyname("hw.clockrate", &cpuspeed, &size, NULL, 0) < 0) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + // 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) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + size = maxcpus * CPUSTATES * sizeof(long); + long cp_times[size]; + if (sysctlbyname("kern.cp_times", &cp_times, &size, NULL, 0) < 0) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + + for (int i = 0; i < numcpus; i++) { + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(cp_times[CP_USER+cur]) * multiplier); + cpu_info->cpu_times.nice = (uint64_t)(cp_times[CP_NICE+cur]) * multiplier); + cpu_info->cpu_times.sys = (uint64_t)(cp_times[CP_SYS+cur]) * multiplier); + cpu_info->cpu_times.idle = (uint64_t)(cp_times[CP_IDLE+cur]) * multiplier); + cpu_info->cpu_times.irq = (uint64_t)(cp_times[CP_INTR+cur]) * multiplier); + + cpu_info->model = strdup(model); + cpu_info->speed = cpuspeed; + + cur+=CPUSTATES; + } + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].brand); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + /* TODO: implement */ + *addresses = NULL; + *count = 0; + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { +} diff --git a/src/unix/linux.c b/src/unix/linux.c index 4b796903..d841c49a 100644 --- a/src/unix/linux.c +++ b/src/unix/linux.c @@ -22,12 +22,16 @@ #include "internal.h" #include +#include #include #include #include #include +#include +#include #include +#include #include #include #include @@ -36,6 +40,14 @@ #define NANOSEC 1000000000 +static char buf[MAXPATHLEN + 1]; + +static struct { + char *str; + size_t len; +} process_title; + + /* Don't look aghast, this is exactly how glibc's basename() works. */ static char* basename_r(const char* path) { char* s = strrchr(path, '/'); @@ -209,3 +221,354 @@ void uv__fs_event_destroy(uv_fs_event_t* handle) { free(handle->filename); handle->filename = NULL; } + + +char** uv_setup_args(int argc, char** argv) { + char **new_argv; + char **new_env; + size_t size; + int envc; + char *s; + int i; + + for (envc = 0; environ[envc]; envc++); + + s = envc ? environ[envc - 1] : argv[argc - 1]; + + process_title.str = argv[0]; + process_title.len = s + strlen(s) + 1 - argv[0]; + + size = process_title.len; + size += (argc + 1) * sizeof(char **); + size += (envc + 1) * sizeof(char **); + + if ((s = (char *) malloc(size)) == NULL) { + process_title.str = NULL; + process_title.len = 0; + return argv; + } + + new_argv = (char **) s; + new_env = new_argv + argc + 1; + s = (char *) (new_env + envc + 1); + memcpy(s, process_title.str, process_title.len); + + for (i = 0; i < argc; i++) + new_argv[i] = s + (argv[i] - argv[0]); + new_argv[argc] = NULL; + + s += environ[0] - argv[0]; + + for (i = 0; i < envc; i++) + new_env[i] = s + (environ[i] - environ[0]); + new_env[envc] = NULL; + + environ = new_env; + return new_argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + /* No need to terminate, last char is always '\0'. */ + if (process_title.len) + strncpy(process_title.str, title, process_title.len - 1); + + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title.str) { + strncpy(buffer, process_title.str, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + FILE* f; + int itmp; + char ctmp; + size_t page_size = getpagesize(); + char *cbuf; + int foundExeEnd; + + f = fopen("/proc/self/stat", "r"); + if (!f) return uv__new_sys_error(errno); + + /* PID */ + if (fscanf(f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Exec file */ + cbuf = buf; + foundExeEnd = 0; + if (fscanf (f, "%c", cbuf++) == 0) goto error; + while (1) { + if (fscanf(f, "%c", cbuf) == 0) goto error; + if (*cbuf == ')') { + foundExeEnd = 1; + } else if (foundExeEnd && *cbuf == ' ') { + *cbuf = 0; + break; + } + + cbuf++; + } + /* State */ + if (fscanf (f, "%c ", &ctmp) == 0) goto error; /* coverity[secure_coding] */ + /* Parent process */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Process group */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Session id */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* TTY */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* TTY owner process group */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Flags */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Minor faults (no memory page) */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Minor faults, children */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Major faults (memory page faults) */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Major faults, children */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* utime */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* stime */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* utime, children */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* stime, children */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* jiffies remaining in current time slice */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* 'nice' value */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* jiffies until next timeout */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* jiffies until next SIGALRM */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* start time (jiffies since system boot) */ + if (fscanf (f, "%d ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + + /* Virtual memory size */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + + /* Resident set size */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + *rss = (size_t) itmp * page_size; + + /* rlim */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Start of text */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* End of text */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + /* Start of stack */ + if (fscanf (f, "%u ", &itmp) == 0) goto error; /* coverity[secure_coding] */ + + fclose (f); + return uv_ok_; + +error: + fclose (f); + return uv__new_sys_error(errno); +} + + +uv_err_t uv_uptime(double* uptime) { +#ifdef CLOCK_MONOTONIC + struct timespec now; + if (0 == clock_gettime(CLOCK_MONOTONIC, &now)) { + *uptime = now.tv_sec; + *uptime += (double)now.tv_nsec / 1000000000.0; + return uv_ok_; + } + return uv__new_sys_error(errno); +#else + struct sysinfo info; + if (sysinfo(&info) < 0) { + return uv__new_sys_error(errno); + } + *uptime = (double)info.uptime; + return uv_ok_; +#endif +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + 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; + uv_cpu_info_t* cpu_info; + + 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); + } + + *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = numcpus; + + cpu_info = *cpu_infos; + + if (fpStat) { + while (fgets(line, 511, fpStat) != NULL) { + if (strncmp(line, "cpu ", 4) == 0) { + continue; + } else if (strncmp(line, "cpu", 3) != 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); + } + + cpu_info->cpu_times.user = ticks_user * multiplier; + cpu_info->cpu_times.nice = ticks_nice * multiplier; + cpu_info->cpu_times.sys = ticks_sys * multiplier; + cpu_info->cpu_times.idle = ticks_idle * multiplier; + cpu_info->cpu_times.irq = ticks_intr * multiplier; + + cpu_info->model = strdup(model); + cpu_info->speed = cpuspeed; + + cpu_info++; + } + fclose(fpStat); + } + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + struct ifaddrs *addrs, *ent; + char ip[INET6_ADDRSTRLEN]; + uv_interface_address_t* address; + + if (getifaddrs(&addrs) != 0) { + return uv__new_sys_error(errno); + } + + *count = 0; + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) || + (ent->ifa_addr == NULL) || + (ent->ifa_addr->sa_family == PF_PACKET)) { + continue; + } + + (*count)++; + } + + *addresses = (uv_interface_address_t*) + malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + address = *addresses; + + 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; + } + + /* + * On Linux getifaddrs returns information related to the raw underlying + * devices. We're not interested in this information. + */ + if (ent->ifa_addr->sa_family == PF_PACKET) { + continue; + } + + address->name = strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); + } + + address->is_internal = ent->ifa_flags & IFF_LOOPBACK ? 1 : 0; + + address++; + } + + freeifaddrs(addrs); + + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + free(addresses[i].name); + } + + free(addresses); +} diff --git a/src/unix/openbsd.c b/src/unix/openbsd.c index 55f8ceb0..80e934cc 100644 --- a/src/unix/openbsd.c +++ b/src/unix/openbsd.c @@ -33,12 +33,16 @@ #define NANOSEC 1000000000 +static char *process_title; + + uint64_t uv_hrtime(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return (ts.tv_sec * NANOSEC + ts.tv_nsec); } + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); @@ -51,6 +55,7 @@ void uv_loadavg(double avg[3]) { avg[2] = (double) info.ldavg[2] / info.fscale; } + int uv_exepath(char* buffer, size_t* size) { int mib[4]; char **argsbuf = NULL; @@ -98,6 +103,7 @@ out: return status; } + uint64_t uv_get_free_memory(void) { struct uvmexp info; size_t size = sizeof(info); @@ -110,6 +116,7 @@ uint64_t uv_get_free_memory(void) { return (uint64_t) info.free * sysconf(_SC_PAGESIZE); } + uint64_t uv_get_total_memory(void) { uint64_t info; int which[] = {CTL_HW, HW_PHYSMEM64}; @@ -121,3 +128,160 @@ uint64_t uv_get_total_memory(void) { return (uint64_t) info; } + + +char** uv_setup_args(int argc, char** argv) { + process_title = argc ? strdup(argv[0]) : NULL; + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + if (process_title) free(process_title); + process_title = strdup(title); + setproctitle(title); + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (process_title) { + strncpy(buffer, process_title, size); + } else { + if (size > 0) { + buffer[0] = '\0'; + } + } + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + 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; + + kvm_close(kd); + + return uv_ok_; + +error: + if (kd) kvm_close(kd); + return uv__new_sys_error(errno); +} + + +uv_err_t uv_uptime(double* uptime) { + 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 uv__new_sys_error(errno); + } + + now = time(NULL); + + *uptime = (double)(now - info.tv_sec); + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + 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; + uv_cpu_info_t* cpu_info; + + 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; + } + + *cpu_infos = (uv_cpu_info_t*)malloc(numcpus * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = numcpus; + + which[1] = HW_CPUSPEED; + size = sizeof(cpuspeed); + if (sysctl(which, 2, &cpuspeed, &size, NULL, 0) < 0) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + + 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) { + free(*cpu_infos); + return uv__new_sys_error(errno); + } + + cpu_info = &(*cpu_infos)[i]; + + cpu_info->cpu_times.user = (uint64_t)(info[CP_USER]) * multiplier); + cpu_info->cpu_times.nice = ((uint64_t)(info[CP_NICE]) * multiplier); + cpu_info->cpu_times.sys = (uint64_t)(info[CP_SYS]) * multiplier)); + cpu_info->cpu_times.idle = (uint64_t)(info[CP_IDLE]) * multiplier)); + cpu_info->cpu_times.irq = (uint64_t)(info[CP_INTR]) * multiplier)); + + cpu_info->model = strdup(model); + cpu_info->speed = cpuspeed; + } + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].brand); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + /* TODO: implement */ + *addresses = NULL; + *count = 0; + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { +} diff --git a/src/unix/sunos.c b/src/unix/sunos.c index cf9f162a..0fd4e42f 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -28,6 +28,11 @@ #include #include +#ifdef SUNOS_HAVE_IFADDRS +# include +#endif +#include + #include #include #include @@ -38,6 +43,19 @@ # include #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 + +#if (PROCFS_FILE_OFFSET_BITS_HACK - 0 == 1) +#define _FILE_OFFSET_BITS 64 +#endif + uint64_t uv_hrtime() { return (gethrtime()); @@ -198,3 +216,252 @@ void uv__fs_event_destroy(uv_fs_event_t* handle) { } #endif /* HAVE_PORTS_FS */ + + +char** uv_setup_args(int argc, char** argv) { + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + return uv_ok_; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + if (size > 0) { + buffer[0] = '\0'; + } + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + pid_t pid = getpid(); + psinfo_t psinfo; + char pidpath[1024]; + FILE *f; + + sprintf(pidpath, "/proc/%d/psinfo", (int)pid); + + f = fopen(pidpath, "r"); + if (!f) return uv__new_sys_error(errno); + + if (fread(&psinfo, sizeof(psinfo_t), 1, f) != 1) { + fclose (f); + return uv__new_sys_error(errno); + } + + /* XXX correct? */ + + *rss = (size_t) psinfo.pr_rssize * 1024; + + fclose (f); + + return uv_ok_; +} + + +uv_err_t uv_uptime(double* uptime) { + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + + long hz = sysconf(_SC_CLK_TCK); + + if ((kc = kstat_open()) == NULL) + return uv__new_sys_error(errno); + + ksp = kstat_lookup(kc, (char *)"unix", 0, (char *)"system_misc"); + + if (kstat_read(kc, ksp, NULL) == -1) { + *uptime = -1; + } else { + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clk_intr"); + *uptime = knp->value.ul / hz; + } + + kstat_close(kc); + + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + int lookup_instance; + kstat_ctl_t *kc; + kstat_t *ksp; + kstat_named_t *knp; + uv_cpu_info_t* cpu_info; + + if ((kc = kstat_open()) == NULL) { + return uv__new_sys_error(errno); + } + + /* Get count of cpus */ + lookup_instance = 0; + while ((ksp = kstat_lookup(kc, (char *)"cpu_info", lookup_instance, NULL))) { + lookup_instance++; + } + + *cpu_infos = (uv_cpu_info_t*) + malloc(lookup_instance * sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + *count = lookup_instance; + + cpu_info = *cpu_infos; + lookup_instance = 0; + while ((ksp = kstat_lookup(kc, (char *)"cpu_info", lookup_instance, NULL))) { + 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(). + */ + cpu_info->speed = 0; + cpu_info->model = NULL; + } else { + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"clock_MHz"); + assert(knp->data_type == KSTAT_DATA_INT32); + cpu_info->speed = knp->value.i32; + + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"brand"); + assert(knp->data_type == KSTAT_DATA_STRING); + cpu_info->model = KSTAT_NAMED_STR_PTR(knp); + } + + lookup_instance++; + cpu_info++; + } + + cpu_info = *cpu_infos; + lookup_instance = 0; + while ((ksp = kstat_lookup(kc, (char *)"cpu", lookup_instance, (char *)"sys"))){ + + if (kstat_read(kc, ksp, NULL) == -1) { + cpu_info->cpu_times.user = 0; + cpu_info->cpu_times.nice = 0; + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + } else { + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_user"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.user = knp->value.ui64; + + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_kernel"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.sys = knp->value.ui64; + + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"cpu_ticks_idle"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.idle = knp->value.ui64; + + knp = (kstat_named_t *) kstat_data_lookup(ksp, (char *)"intr"); + assert(knp->data_type == KSTAT_DATA_UINT64); + cpu_info->cpu_times.irq = knp->value.ui64; + cpu_info->cpu_times.nice = 0; + } + + lookup_instance++; + cpu_info++; + } + + kstat_close(kc); + + return uv_ok_; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { +#ifndef SUNOS_HAVE_IFADDRS + return uv__new_artificial_error(UV_ENOSYS); +#else + struct ifaddrs *addrs, *ent; + char ip[INET6_ADDRSTRLEN]; + uv_interface_address_t* address; + + if (getifaddrs(&addrs) != 0) { + return uv__new_sys_error(errno); + } + + *count = 0; + + /* Count the number of interfaces */ + for (ent = addrs; ent != NULL; ent = ent->ifa_next) { + if (!(ent->ifa_flags & IFF_UP && ent->ifa_flags & IFF_RUNNING) || + (ent->ifa_addr == NULL) || + (ent->ifa_addr->sa_family == PF_PACKET)) { + continue; + } + + (*count)++; + } + + *addresses = (uv_interface_address_t*) + malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + return uv__new_artificial_error(UV_ENOMEM); + } + + address = *addresses; + + 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; + } + + address->name = strdup(ent->ifa_name); + + if (ent->ifa_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)ent->ifa_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)ent->ifa_addr); + } + + address->is_internal = ent->ifa_flags & IFF_PRIVATE || ent->ifa_flags & + IFF_LOOPBACK ? 1 : 0; + + address++; + } + + freeifaddrs(addrs); + + return uv_ok_; +#endif /* SUNOS_HAVE_IFADDRS */ +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + free(addresses[i].name); + } + + free(addresses); +} diff --git a/src/win/util.c b/src/win/util.c index fcdeafc7..2b0053af 100644 --- a/src/win/util.c +++ b/src/win/util.c @@ -22,11 +22,30 @@ #include #include #include +#include #include #include "uv.h" #include "internal.h" #include "tlhelp32.h" +#include "psapi.h" +#include "iphlpapi.h" + + +/* + * Max title length; the only thing MSDN tells us about the maximum length + * of the console title is that it is smaller than 64K. However in practice + * it is much smaller, and there is no way to figure out what the exact length + * of the title is or can be, at least not on XP. To make it even more + * annoying, GetConsoleTitle failes when the buffer to be read into is bigger + * than the actual maximum length. So we make a conservative guess here; + * just don't put the novel you're writing in the title, unless the plot + * survives truncation. + */ +#define MAX_TITLE_LENGTH 8192 + + +static char *process_title; int uv_utf16_to_utf8(const wchar_t* utf16Buffer, size_t utf16Size, @@ -236,3 +255,314 @@ int uv_parent_pid() { CloseHandle(handle); return parent_pid; } + + +char** uv_setup_args(int argc, char** argv) { + return argv; +} + + +uv_err_t uv_set_process_title(const char* title) { + uv_err_t err; + int length; + wchar_t* title_w = NULL; + + /* Find out how big the buffer for the wide-char title must be */ + length = uv_utf8_to_utf16(title, NULL, 0); + if (!length) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + /* Convert to wide-char string */ + title_w = (wchar_t*)malloc(sizeof(wchar_t) * length); + if (!title_w) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + length = uv_utf8_to_utf16(title, title_w, length); + if (!length) { + err = uv__new_sys_error(GetLastError()); + goto done; + }; + + /* If the title must be truncated insert a \0 terminator there */ + if (length > MAX_TITLE_LENGTH) { + title_w[MAX_TITLE_LENGTH - 1] = L'\0'; + } + + if (!SetConsoleTitleW(title_w)) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + free(process_title); + process_title = strdup(title); + + err = uv_ok_; + +done: + free(title_w); + return err; +} + + +static int uv__get_process_title() { + wchar_t title_w[MAX_TITLE_LENGTH]; + int length; + + if (!GetConsoleTitleW(title_w, sizeof(title_w) / sizeof(WCHAR))) { + return -1; + } + + /* Find out what the size of the buffer is that we need */ + length = uv_utf16_to_utf8(title_w, -1, NULL, 0); + if (!length) { + return -1; + } + + assert(!process_title); + process_title = (char*)malloc(length); + if (!process_title) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + /* Do utf16 -> utf8 conversion here */ + if (!uv_utf16_to_utf8(title_w, -1, process_title, length)) { + free(process_title); + return -1; + } + + return 0; +} + + +uv_err_t uv_get_process_title(char* buffer, size_t size) { + /* + * If the process_title was never read before nor explicitly set, + * we must query it with getConsoleTitleW + */ + if (!process_title && uv__get_process_title() == -1) { + return uv__new_sys_error(GetLastError()); + } + + assert(process_title); + strncpy(buffer, process_title, size); + + return uv_ok_; +} + + +uv_err_t uv_resident_set_memory(size_t* rss) { + HANDLE current_process; + PROCESS_MEMORY_COUNTERS pmc; + + current_process = GetCurrentProcess(); + + if (!GetProcessMemoryInfo(current_process, &pmc, sizeof(pmc))) { + return uv__new_sys_error(GetLastError()); + } + + *rss = pmc.WorkingSetSize; + + return uv_ok_; +} + + +uv_err_t uv_uptime(double* uptime) { + *uptime = (double)GetTickCount()/1000.0; + return uv_ok_; +} + + +uv_err_t uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + uv_err_t err; + char key[128]; + HKEY processor_key = NULL; + DWORD cpu_speed = 0; + DWORD cpu_speed_length = sizeof(cpu_speed); + char cpu_brand[256]; + DWORD cpu_brand_length = sizeof(cpu_brand); + SYSTEM_INFO system_info; + uv_cpu_info_t* cpu_info; + unsigned int i; + + GetSystemInfo(&system_info); + + *cpu_infos = (uv_cpu_info_t*)malloc(system_info.dwNumberOfProcessors * + sizeof(uv_cpu_info_t)); + if (!(*cpu_infos)) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + *count = 0; + + for (i = 0; i < system_info.dwNumberOfProcessors; i++) { + _snprintf(key, sizeof(key), "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\%d", i); + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_QUERY_VALUE, + &processor_key) != ERROR_SUCCESS) { + if (i == 0) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + continue; + } + + if (RegQueryValueEx(processor_key, "~MHz", NULL, NULL, + (LPBYTE)&cpu_speed, &cpu_speed_length) + != ERROR_SUCCESS) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + if (RegQueryValueEx(processor_key, "ProcessorNameString", NULL, NULL, + (LPBYTE)&cpu_brand, &cpu_brand_length) + != ERROR_SUCCESS) { + err = uv__new_sys_error(GetLastError()); + goto done; + } + + RegCloseKey(processor_key); + processor_key = NULL; + + cpu_info = &(*cpu_infos)[i]; + + /* $TODO: find times on windows */ + cpu_info->cpu_times.user = 0; + cpu_info->cpu_times.nice = 0; + cpu_info->cpu_times.sys = 0; + cpu_info->cpu_times.idle = 0; + cpu_info->cpu_times.irq = 0; + + cpu_info->model = strdup(cpu_brand); + cpu_info->speed = cpu_speed; + + (*count)++; + } + + err = uv_ok_; + +done: + if (processor_key) { + RegCloseKey(processor_key); + } + + if (err.code != UV_OK) { + free(*cpu_infos); + *cpu_infos = NULL; + *count = 0; + } + + return err; +} + + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) { + free(cpu_infos[i].model); + } + + free(cpu_infos); +} + + +uv_err_t uv_interface_addresses(uv_interface_address_t** addresses, + int* count) { + unsigned long size = 0; + IP_ADAPTER_ADDRESSES* adapter_addresses; + IP_ADAPTER_ADDRESSES* adapter_address; + uv_interface_address_t* address; + struct sockaddr* sock_addr; + int length; + + if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &size) + != ERROR_BUFFER_OVERFLOW) { + return uv__new_sys_error(GetLastError()); + } + + adapter_addresses = (IP_ADAPTER_ADDRESSES*)malloc(size); + if (!adapter_addresses) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (GetAdaptersAddresses(AF_UNSPEC, 0, NULL, adapter_addresses, &size) + != ERROR_SUCCESS) { + return uv__new_sys_error(GetLastError()); + } + + /* Count the number of interfaces */ + *count = 0; + + for (adapter_address = adapter_addresses; + adapter_address != NULL; + adapter_address = adapter_address->Next) { + (*count)++; + } + + *addresses = (uv_interface_address_t*) + malloc(*count * sizeof(uv_interface_address_t)); + if (!(*addresses)) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + address = *addresses; + + for (adapter_address = adapter_addresses; + adapter_address != NULL; + adapter_address = adapter_address->Next, + address++) { + + /* Convert FriendlyName to utf8 */ + length = uv_utf16_to_utf8(adapter_address->FriendlyName, -1, NULL, 0); + if (!length) { + address->name = NULL; + address->address.address4 = uv_addr_ip4_any_; + continue; + } + + address->name = (char*)malloc(length); + if (!address->name) { + uv_fatal_error(ERROR_OUTOFMEMORY, "malloc"); + } + + if (!uv_utf16_to_utf8(adapter_address->FriendlyName, -1, address->name, + length)) { + free(address->name); + address->name = NULL; + address->address.address4 = uv_addr_ip4_any_; + continue; + } + + if (adapter_address->FirstUnicastAddress) { + sock_addr = adapter_address->FirstUnicastAddress->Address.lpSockaddr; + if (sock_addr->sa_family == AF_INET6) { + address->address.address6 = *((struct sockaddr_in6 *)sock_addr); + } else { + address->address.address4 = *((struct sockaddr_in *)sock_addr); + } + } + + address->is_internal = + adapter_address->IfType == IF_TYPE_SOFTWARE_LOOPBACK ? 1 : 0; + } + + free(adapter_addresses); + + return uv_ok_; +} + + +void uv_free_interface_addresses(uv_interface_address_t* addresses, + int count) { + int i; + + for (i = 0; i < count; i++) { + free(addresses[i].name); + } + + free(addresses); +} diff --git a/test/run-tests.c b/test/run-tests.c index 1d8b0bcf..9082ff85 100644 --- a/test/run-tests.c +++ b/test/run-tests.c @@ -38,6 +38,8 @@ static int maybe_run_test(int argc, char **argv); int main(int argc, char **argv) { platform_init(argc, argv); + argv = uv_setup_args(argc, argv); + switch (argc) { case 1: return run_tests(TEST_TIMEOUT, 0); case 2: return maybe_run_test(argc, argv); diff --git a/test/runner.c b/test/runner.c index fdd81684..8c7bd7f0 100644 --- a/test/runner.c +++ b/test/runner.c @@ -191,8 +191,10 @@ out: } /* Show error and output from processes if the test failed. */ - if (status != 0) { - LOGF("\n`%s` failed: %s\n", test, errmsg); + if (status != 0 || task->show_output) { + if (status != 0) { + LOGF("\n`%s` failed: %s\n", test, errmsg); + } for (i = 0; i < process_count; i++) { switch (process_output_size(&processes[i])) { diff --git a/test/runner.h b/test/runner.h index 3b93ffe9..5cee695e 100644 --- a/test/runner.h +++ b/test/runner.h @@ -40,6 +40,7 @@ typedef struct { char *process_name; int (*main)(); int is_helper; + int show_output; } task_entry_t, bench_entry_t; @@ -57,19 +58,22 @@ typedef struct { int run_test_##name(); #define TEST_ENTRY(name) \ - { #name, #name, &run_test_##name, 0 }, + { #name, #name, &run_test_##name, 0, 0 }, + +#define TEST_OUTPUT_ENTRY(name) \ + { #name, #name, &run_test_##name, 0, 1 }, #define BENCHMARK_DECLARE(name) \ int run_benchmark_##name(); #define BENCHMARK_ENTRY(name) \ - { #name, #name, &run_benchmark_##name, 0 }, + { #name, #name, &run_benchmark_##name, 0, 0 }, #define HELPER_DECLARE(name) \ int run_helper_##name(); #define HELPER_ENTRY(task_name, name) \ - { #task_name, #name, &run_helper_##name, 1 }, + { #task_name, #name, &run_helper_##name, 1, 0 }, #define TEST_HELPER HELPER_ENTRY #define BENCHMARK_HELPER HELPER_ENTRY diff --git a/test/test-list.h b/test/test-list.h index 96e9d84c..d031eb27 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -19,6 +19,7 @@ * IN THE SOFTWARE. */ +TEST_DECLARE (platform_output) TEST_DECLARE (tty) TEST_DECLARE (stdio_over_pipes) TEST_DECLARE (ipc_listen_before_write) @@ -77,6 +78,7 @@ TEST_DECLARE (check_ref) TEST_DECLARE (unref_in_prepare_cb) TEST_DECLARE (async) TEST_DECLARE (get_currentexe) +TEST_DECLARE (process_title) TEST_DECLARE (cwd_and_chdir) TEST_DECLARE (get_memory) TEST_DECLARE (hrtime) @@ -136,6 +138,8 @@ HELPER_DECLARE (pipe_echo_server) TASK_LIST_START + TEST_OUTPUT_ENTRY (platform_output) + TEST_ENTRY (pipe_connect_bad_name) TEST_ENTRY (tty) @@ -224,6 +228,8 @@ TASK_LIST_START TEST_ENTRY (get_currentexe) + TEST_ENTRY (process_title) + TEST_ENTRY (cwd_and_chdir) TEST_ENTRY (get_memory) diff --git a/test/test-platform-output.c b/test/test-platform-output.c new file mode 100644 index 00000000..99c0551b --- /dev/null +++ b/test/test-platform-output.c @@ -0,0 +1,83 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * 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 "uv.h" +#include "task.h" +#include + + +TEST_IMPL(platform_output) { + char buffer[512]; + size_t rss; + double uptime; + uv_cpu_info_t* cpus; + uv_interface_address_t* interfaces; + int count; + int i; + uv_err_t err; + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(UV_OK == err.code); + fprintf(stderr, "uv_get_process_title: %s\n", buffer); + + err = uv_resident_set_memory(&rss); + ASSERT(UV_OK == err.code); + fprintf(stderr, "uv_resident_set_memory: %d\n", rss); + + err = uv_uptime(&uptime); + ASSERT(UV_OK == err.code); + fprintf(stderr, "uv_uptime: %f\n", uptime); + + err = uv_cpu_info(&cpus, &count); + ASSERT(UV_OK == err.code); + + fprintf(stderr, "uv_cpu_info:\n"); + for (i = 0; i < count; i++) { + fprintf(stderr, " model: %s\n", cpus[i].model); + fprintf(stderr, " speed: %d\n", cpus[i].speed); + fprintf(stderr, " times.sys: %llu\n", cpus[i].cpu_times.sys); + fprintf(stderr, " times.user: %llu\n", cpus[i].cpu_times.user); + fprintf(stderr, " times.idle: %llu\n", cpus[i].cpu_times.idle); + fprintf(stderr, " times.irq: %llu\n", cpus[i].cpu_times.irq); + fprintf(stderr, " times.nice: %llu\n", cpus[i].cpu_times.nice); + } + uv_free_cpu_info(cpus, count); + + err = uv_interface_addresses(&interfaces, &count); + ASSERT(UV_OK == err.code); + + fprintf(stderr, "uv_interface_addresses:\n"); + for (i = 0; i < count; i++) { + fprintf(stderr, " name: %s\n", interfaces[i].name); + fprintf(stderr, " internal: %d\n", interfaces[i].is_internal); + + if (interfaces[i].address.address4.sin_family == AF_INET) { + uv_ip4_name(&interfaces[i].address.address4, buffer, sizeof(buffer)); + } else if (interfaces[i].address.address4.sin_family == AF_INET6) { + uv_ip6_name(&interfaces[i].address.address6, buffer, sizeof(buffer)); + } + + fprintf(stderr, " address: %s\n", buffer); + } + uv_free_interface_addresses(interfaces, count); + + return 0; +} diff --git a/test/test-process-title.c b/test/test-process-title.c new file mode 100644 index 00000000..59fceda3 --- /dev/null +++ b/test/test-process-title.c @@ -0,0 +1,42 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * + * 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 "uv.h" +#include "task.h" +#include + +TEST_IMPL(process_title) { + char buffer[512]; + uv_err_t err; + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(UV_OK == err.code); + + err = uv_set_process_title("new title"); + ASSERT(UV_OK == err.code); + + err = uv_get_process_title(buffer, sizeof(buffer)); + ASSERT(UV_OK == err.code); + + ASSERT(strcmp(buffer, "new title") == 0); + + return 0; +} diff --git a/uv.gyp b/uv.gyp index b2eef605..3ed20ec6 100644 --- a/uv.gyp +++ b/uv.gyp @@ -157,6 +157,8 @@ 'link_settings': { 'libraries': [ '-lws2_32.lib', + '-lpsapi.lib', + '-liphlpapi.lib' ], }, }, { # Not Windows i.e. POSIX @@ -300,6 +302,8 @@ 'test/test-ping-pong.c', 'test/test-pipe-bind-error.c', 'test/test-pipe-connect-error.c', + 'test/test-platform-output.c', + 'test/test-process-title.c', 'test/test-ref.c', 'test/test-shutdown-eof.c', 'test/test-spawn.c',