sigar/src/os/win32/win32_sigar.c

2257 lines
58 KiB
C
Raw Normal View History

2004-06-22 06:37:04 +08:00
#include "sigar.h"
#include "sigar_private.h"
#include "sigar_pdh.h"
#include "sigar_os.h"
#include "sigar_util.h"
#include <shellapi.h>
#define USING_WIDE_S(s) (s)->using_wide
#define USING_WIDE() USING_WIDE_S(sigar)
#define PERFBUF_SIZE 8192
#define PERF_TITLE_PROC 230
#define PERF_TITLE_PROC_KEY "230"
#define PERF_TITLE_CPU_KEY "238"
2004-12-05 09:18:57 +08:00
#define PERF_TITLE_DISK_KEY "236"
2004-06-22 06:37:04 +08:00
#define PERF_TITLE_CPU_USER 142
#define PERF_TITLE_CPU_IDLE 1746
#define PERF_TITLE_CPU_SYS 144
typedef enum {
PERF_IX_CPU_USER,
PERF_IX_CPU_IDLE,
PERF_IX_CPU_SYS,
PERF_IX_CPU_MAX
} perf_cpu_offsets_t;
#define PERF_TITLE_CPUTIME 6
#define PERF_TITLE_MEM_VSIZE 174
#define PERF_TITLE_MEM_SIZE 180
#define PERF_TITLE_MEM_PRIV 186
#define PERF_TITLE_THREAD_CNT 680
#define PERF_TITLE_HANDLE_CNT 952
#define PERF_TITLE_PID 784
#define PERF_TITLE_PPID 1410
#define PERF_TITLE_PRIORITY 682
#define PERF_TITLE_START_TIME 684
typedef enum {
PERF_IX_CPUTIME,
PERF_IX_MEM_VSIZE,
PERF_IX_MEM_SIZE,
PERF_IX_MEM_PRIV,
PERF_IX_THREAD_CNT,
PERF_IX_HANDLE_CNT,
PERF_IX_PID,
PERF_IX_PPID,
PERF_IX_PRIORITY,
PERF_IX_START_TIME,
PERF_IX_MAX
} perf_proc_offsets_t;
2004-12-05 09:18:57 +08:00
typedef enum {
PERF_IX_DISK_READ,
PERF_IX_DISK_WRITE,
2005-04-07 09:23:49 +08:00
PERF_IX_DISK_READ_BYTES,
PERF_IX_DISK_WRITE_BYTES,
PERF_IX_DISK_QUEUE,
2004-12-05 09:18:57 +08:00
PERF_IX_DISK_MAX
} perf_disk_offsets_t;
#define PERF_TITLE_DISK_READ 208
#define PERF_TITLE_DISK_WRITE 210
2005-04-07 09:23:49 +08:00
#define PERF_TITLE_DISK_READ_BYTES 220
#define PERF_TITLE_DISK_WRITE_BYTES 222
2005-04-07 10:11:00 +08:00
#define PERF_TITLE_DISK_QUEUE 1028
2004-12-05 09:18:57 +08:00
2004-06-22 06:37:04 +08:00
/*
* diff is:
* ExW -> ExA
* wcounter -> counter
*/
#define MyRegQueryValue() \
(USING_WIDE() ? \
RegQueryValueExW(sigar->handle, \
wcounter_key, NULL, &type, \
sigar->perfbuf, \
&bytes) : \
2004-06-22 06:37:04 +08:00
RegQueryValueExA(sigar->handle, \
counter_key, NULL, &type, \
sigar->perfbuf, \
&bytes))
2004-06-22 06:37:04 +08:00
#define PERF_VAL(ix) \
perf_offsets[ix] ? \
*((DWORD *)((BYTE *)counter_block + perf_offsets[ix])) : 0
2005-05-12 07:17:39 +08:00
/* 1/100ns units to seconds */
#define NS100_2SEC(t) ((t) / 10000000)
2005-05-12 07:37:22 +08:00
#define PERF_VAL_CPU(ix) \
NS100_2SEC(PERF_VAL(ix))
static PERF_OBJECT_TYPE *get_perf_object(sigar_t *sigar, char *counter_key,
DWORD *err)
2004-06-22 06:37:04 +08:00
{
DWORD retval, type, bytes;
2004-06-22 06:37:04 +08:00
WCHAR wcounter_key[MAX_PATH+1];
PERF_DATA_BLOCK *block;
PERF_OBJECT_TYPE *object;
2004-06-22 06:37:04 +08:00
*err = SIGAR_OK;
2004-06-22 06:37:04 +08:00
if (!sigar->perfbuf) {
2004-06-23 07:44:46 +08:00
sigar->perfbuf = malloc(PERFBUF_SIZE);
2004-06-22 06:37:04 +08:00
sigar->perfbuf_size = PERFBUF_SIZE;
}
if (USING_WIDE()) {
SIGAR_A2W(counter_key, wcounter_key, sizeof(wcounter_key));
}
bytes = sigar->perfbuf_size;
2004-06-22 06:37:04 +08:00
while ((retval = MyRegQueryValue()) != ERROR_SUCCESS) {
if (retval == ERROR_MORE_DATA) {
sigar->perfbuf_size += PERFBUF_SIZE;
bytes = sigar->perfbuf_size;
2004-06-23 07:44:46 +08:00
sigar->perfbuf =
realloc(sigar->perfbuf, sigar->perfbuf_size);
2004-06-22 06:37:04 +08:00
}
else {
*err = retval;
2004-06-22 06:37:04 +08:00
return NULL;
}
}
block = (PERF_DATA_BLOCK *)sigar->perfbuf;
object = PdhFirstObject(block);
2004-06-22 06:37:04 +08:00
/*
* only seen on windows 2003 server when pdh.dll
* functions are in use by the same process.
* confucius say what the fuck.
*/
if (object->NumInstances == PERF_NO_INSTANCES) {
2005-01-29 00:33:12 +08:00
int i;
2005-01-29 00:33:12 +08:00
for (i=0; i<block->NumObjectTypes; i++) {
if (object->NumInstances != PERF_NO_INSTANCES) {
return object;
}
object = PdhNextObject(object);
}
return NULL;
}
else {
return object;
}
2004-06-22 06:37:04 +08:00
}
static void get_sysinfo(sigar_t *sigar)
{
SYSTEM_INFO sysinfo;
GetSystemInfo(&sysinfo);
sigar->ncpu = sysinfo.dwNumberOfProcessors;
sigar->pagesize = sysinfo.dwPageSize;
}
/* for C# bindings */
SIGAR_DECLARE(sigar_t *) sigar_new(void)
{
sigar_t *sigar;
if (sigar_open(&sigar) != SIGAR_OK) {
return NULL;
}
return sigar;
}
int sigar_os_open(sigar_t **sigar)
{
LONG result;
HINSTANCE h;
OSVERSIONINFO version;
2004-06-22 06:37:04 +08:00
*sigar = malloc(sizeof(**sigar));
(*sigar)->machine = ""; /* local machine */
(*sigar)->using_wide = 0; /*XXX*/
(*sigar)->perfbuf = NULL;
(*sigar)->perfbuf_size = 0;
version.dwOSVersionInfoSize = sizeof(version);
GetVersionEx(&version);
2004-07-01 05:20:19 +08:00
2004-07-01 06:43:48 +08:00
/*
* 4 == NT 4.0
* 5 == 2000, XP, 2003 Server
*/
(*sigar)->winnt = (version.dwMajorVersion == 4);
2004-06-22 06:37:04 +08:00
if (USING_WIDE_S(*sigar)) {
WCHAR wmachine[MAX_PATH+1];
SIGAR_A2W((*sigar)->machine, wmachine, sizeof(wmachine));
result = RegConnectRegistryW(wmachine,
HKEY_PERFORMANCE_DATA,
&(*sigar)->handle);
}
else {
result = RegConnectRegistryA((*sigar)->machine,
HKEY_PERFORMANCE_DATA,
&(*sigar)->handle);
}
get_sysinfo(*sigar);
if ((h = LoadLibrary("iphlpapi.dll"))) {
(*sigar)->get_if_table =
(LPGETIFTABLE)GetProcAddress(h, "GetIfTable");
(*sigar)->get_ipforward_table =
(LPGETIPFORWARDTABLE)GetProcAddress(h, "GetIpForwardTable");
(*sigar)->get_tcp_table =
(LPGETTCPTABLE)GetProcAddress(h, "GetTcpTable");
(*sigar)->get_tcpx_table =
(LPGETTCPEXTABLE)GetProcAddress(h,
"AllocateAndGet"
"TcpExTableFromStack");
(*sigar)->get_udp_table =
(LPGETUDPTABLE)GetProcAddress(h, "GetUdpTable");
2005-03-20 11:20:28 +08:00
(*sigar)->get_udpx_table =
(LPGETUDPEXTABLE)GetProcAddress(h,
"AllocateAndGet"
"UdpExTableFromStack");
2005-07-12 06:40:31 +08:00
(*sigar)->get_net_params =
(LPNETPARAMS)GetProcAddress(h, "GetNetworkParams");
(*sigar)->ip_handle = h;
2004-06-22 06:37:04 +08:00
}
else {
(*sigar)->get_if_table = NULL;
(*sigar)->get_ipforward_table = NULL;
(*sigar)->ip_handle = NULL;
}
if ((h = LoadLibrary("Ntdll.dll"))) {
(*sigar)->get_ntsys_info =
(LPSYSINFO)GetProcAddress(h, "NtQuerySystemInformation");
(*sigar)->nt_handle = h;
}
else {
(*sigar)->get_ntsys_info = NULL;
(*sigar)->nt_handle = NULL;
2004-06-22 06:37:04 +08:00
}
if ((h = LoadLibrary("psapi.dll"))) {
(*sigar)->enum_modules =
(LPENUMMODULES)GetProcAddress(h, "EnumProcessModules");
(*sigar)->get_module_name =
(LPGETMODULENAME)GetProcAddress(h, "GetModuleFileNameExA");
(*sigar)->ps_handle = h;
}
else {
(*sigar)->ps_handle = NULL;
}
2004-06-22 06:37:04 +08:00
(*sigar)->pinfo.pid = -1;
(*sigar)->ws_version = 0;
(*sigar)->ncpu = 0;
(*sigar)->peb = NULL;
return result;
}
int sigar_os_close(sigar_t *sigar)
{
int retval;
if (sigar->perfbuf) {
free(sigar->perfbuf);
}
retval = RegCloseKey(sigar->handle);
if (sigar->ip_handle) {
FreeLibrary(sigar->ip_handle);
}
if (sigar->nt_handle) {
FreeLibrary(sigar->nt_handle);
}
if (sigar->ps_handle) {
FreeLibrary(sigar->ps_handle);
}
2004-06-22 06:37:04 +08:00
if (sigar->ws_version != 0) {
WSACleanup();
}
if (sigar->peb) {
free(sigar->peb);
}
free(sigar);
return retval;
}
2005-03-12 14:19:34 +08:00
char *sigar_os_error_string(sigar_t *sigar, int err)
2004-06-22 06:37:04 +08:00
{
switch (err) {
case SIGAR_NO_SUCH_PROCESS:
return "No such process";
break;
}
2004-06-22 06:37:04 +08:00
return NULL;
}
int sigar_wsa_init(sigar_t *sigar)
{
if (sigar->ws_version == 0) {
WSADATA data;
if (WSAStartup(MAKEWORD(2, 0), &data)) {
sigar->ws_error = WSAGetLastError();
WSACleanup();
return sigar->ws_error;
}
sigar->ws_version = data.wVersion;
}
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem)
{
MEMORYSTATUS memstat;
GlobalMemoryStatus(&memstat);
mem->total = memstat.dwTotalPhys;
mem->free = memstat.dwAvailPhys;
mem->shared = memstat.dwTotalVirtual - memstat.dwAvailVirtual;
mem->used = mem->total - mem->free;
sigar_mem_calc_ram(sigar, mem);
mem->actual_free = mem->free;
mem->actual_used = mem->used;
2004-06-22 06:37:04 +08:00
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap)
{
MEMORYSTATUS memstat;
GlobalMemoryStatus(&memstat);
swap->total = memstat.dwTotalPageFile;
swap->free = memstat.dwAvailPageFile;
swap->used = swap->total - swap->free;
return SIGAR_OK;
}
static PERF_INSTANCE_DEFINITION *get_cpu_instance(sigar_t *sigar,
DWORD *perf_offsets,
DWORD *num, DWORD *err)
2004-06-22 06:37:04 +08:00
{
PERF_OBJECT_TYPE *object = get_perf_object(sigar, "238", err);
2004-06-22 06:37:04 +08:00
PERF_INSTANCE_DEFINITION *inst;
PERF_COUNTER_DEFINITION *counter;
DWORD i;
if (!object) {
return NULL;
}
for (i=0, counter = PdhFirstCounter(object);
i<object->NumCounters;
i++, counter = PdhNextCounter(counter))
{
DWORD offset = counter->CounterOffset;
switch (counter->CounterNameTitleIndex) {
case PERF_TITLE_CPU_SYS:
perf_offsets[PERF_IX_CPU_SYS] = offset;
break;
case PERF_TITLE_CPU_USER:
perf_offsets[PERF_IX_CPU_USER] = offset;
break;
case PERF_TITLE_CPU_IDLE:
perf_offsets[PERF_IX_CPU_IDLE] = offset;
break;
}
}
if (num) {
*num = object->NumInstances;
}
return PdhFirstInstance(object);
}
static int get_idle_cpu(sigar_t *sigar, sigar_cpu_t *cpu,
DWORD idx,
PERF_COUNTER_BLOCK *counter_block,
DWORD *perf_offsets)
{
cpu->idle = 0;
if (perf_offsets[PERF_IX_CPU_IDLE]) {
2005-05-12 07:37:22 +08:00
cpu->idle = PERF_VAL_CPU(PERF_IX_CPU_IDLE);
}
else {
/* windows NT and 2000 do not have an Idle counter */
sigar_cpu_count(sigar);
if (sigar->get_ntsys_info) {
DWORD retval, num;
/* XXX unhardcode 16 */
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[16];
/* into the lungs of hell */
sigar->get_ntsys_info(SystemProcessorPerformanceInformation,
&info, sizeof(info), &retval);
if (!retval) {
return GetLastError();
}
num = retval/sizeof(info[0]);
if (idx == -1) {
int i;
for (i=0; i<num; i++) {
2005-05-12 07:17:39 +08:00
cpu->idle += NS100_2SEC(info[i].IdleTime.QuadPart);
}
}
else if (idx < num) {
2005-05-12 07:17:39 +08:00
cpu->idle = NS100_2SEC(info[idx].IdleTime.QuadPart);
}
else {
return ERROR_INVALID_DATA;
}
}
else {
return ERROR_INVALID_FUNCTION;
}
}
return SIGAR_OK;
}
static int sigar_cpu_perflib_get(sigar_t *sigar, sigar_cpu_t *cpu)
2004-06-22 06:37:04 +08:00
{
int status;
2004-06-22 06:37:04 +08:00
PERF_INSTANCE_DEFINITION *inst;
PERF_COUNTER_BLOCK *counter_block;
DWORD perf_offsets[PERF_IX_CPU_MAX], err;
2004-06-22 06:37:04 +08:00
SIGAR_ZERO(cpu);
memset(&perf_offsets, 0, sizeof(perf_offsets));
inst = get_cpu_instance(sigar, (DWORD*)&perf_offsets, 0, &err);
2004-06-22 06:37:04 +08:00
if (!inst) {
2004-08-28 07:06:46 +08:00
return err;
2004-06-22 06:37:04 +08:00
}
/* first instance is total, rest are per-cpu */
counter_block = PdhGetCounterBlock(inst);
2005-05-12 07:37:22 +08:00
cpu->sys = PERF_VAL_CPU(PERF_IX_CPU_SYS);
cpu->user = PERF_VAL_CPU(PERF_IX_CPU_USER);
status = get_idle_cpu(sigar, cpu, -1, counter_block, perf_offsets);
2004-06-22 06:37:04 +08:00
cpu->nice = 0; /* no nice here */
2004-11-22 09:51:34 +08:00
cpu->wait = 0; /*N/A?*/
cpu->total = cpu->sys + cpu->user + cpu->idle + cpu->wait;
if (status != SIGAR_OK) {
sigar_log_printf(sigar, SIGAR_LOG_WARN,
"unable to determine idle cpu time: %s",
sigar_strerror(sigar, status));
}
2004-06-22 06:37:04 +08:00
return SIGAR_OK;
}
static int sigar_cpu_ntsys_get(sigar_t *sigar, sigar_cpu_t *cpu)
{
DWORD retval, num;
int i;
/* XXX unhardcode 16 */
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[16];
/* into the lungs of hell */
sigar->get_ntsys_info(SystemProcessorPerformanceInformation,
&info, sizeof(info), &retval);
if (!retval) {
return GetLastError();
}
num = retval/sizeof(info[0]);
SIGAR_ZERO(cpu);
for (i=0; i<num; i++) {
2005-05-12 07:17:39 +08:00
cpu->idle += NS100_2SEC(info[i].IdleTime.QuadPart);
cpu->user += NS100_2SEC(info[i].UserTime.QuadPart);
cpu->sys += NS100_2SEC(info[i].KernelTime.QuadPart -
info[i].IdleTime.QuadPart);
cpu->total += cpu->idle + cpu->user + cpu->sys;
}
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu)
{
if (sigar->get_ntsys_info) {
return sigar_cpu_ntsys_get(sigar, cpu);
}
else {
return sigar_cpu_perflib_get(sigar, cpu);
}
}
static int sigar_cpu_list_perflib_get(sigar_t *sigar,
sigar_cpu_list_t *cpulist)
2004-06-22 06:37:04 +08:00
{
int status, i, j, hthread=0;
PERF_INSTANCE_DEFINITION *inst;
DWORD perf_offsets[PERF_IX_CPU_MAX], num, err;
2004-06-22 06:37:04 +08:00
memset(&perf_offsets, 0, sizeof(perf_offsets));
/* first instance is total, rest are per-cpu */
inst = get_cpu_instance(sigar, (DWORD*)&perf_offsets, &num, &err);
2004-06-22 06:37:04 +08:00
if (!inst) {
return err;
2004-06-22 06:37:04 +08:00
}
if (!sigar->winnt) {
/* skip Processor _Total instance (NT doesnt have one) */
--num;
inst = PdhNextInstance(inst);
}
2004-06-22 06:37:04 +08:00
sigar_cpu_count(sigar);
sigar_cpu_list_create(cpulist);
/*
* if hyper-threading was detected and ncpu is less than
* the number of counter instances, assume there is a counter
* for each logical processor.
* XXX assuming this is correct until have something to test on.
*/
if (sigar->ht_enabled && ((sigar->ncpu * sigar->lcpu) == num)) {
2004-06-22 06:37:04 +08:00
hthread = 1;
}
for (i=0; i<num; i++) {
PERF_COUNTER_BLOCK *counter_block;
sigar_cpu_t *cpu;
if (hthread && (i % sigar->lcpu)) {
2004-06-22 06:37:04 +08:00
/* merge times of logical processors */
cpu = &cpulist->data[cpulist->number-1];
}
else {
SIGAR_CPU_LIST_GROW(cpulist);
cpu = &cpulist->data[cpulist->number++];
SIGAR_ZERO(cpu);
}
counter_block = PdhGetCounterBlock(inst);
2005-05-12 07:37:22 +08:00
cpu->sys += PERF_VAL_CPU(PERF_IX_CPU_SYS);
cpu->user += PERF_VAL_CPU(PERF_IX_CPU_USER);
get_idle_cpu(sigar, cpu, i, counter_block, perf_offsets);
cpu->nice = cpu->wait = 0; /*N/A*/
/*XXX adding up too much here if xeon, but not using this atm*/
2004-06-22 06:37:04 +08:00
cpu->total += cpu->sys + cpu->user + cpu->idle;
inst = PdhNextInstance(inst);
2004-06-22 06:37:04 +08:00
}
return SIGAR_OK;
}
static int sigar_cpu_list_ntsys_get(sigar_t *sigar,
sigar_cpu_list_t *cpulist)
{
DWORD retval, num;
int status, i, j, hthread=0;
/* XXX unhardcode 16 */
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[16];
/* into the lungs of hell */
sigar->get_ntsys_info(SystemProcessorPerformanceInformation,
&info, sizeof(info), &retval);
if (!retval) {
return GetLastError();
}
num = retval/sizeof(info[0]);
sigar_cpu_count(sigar);
sigar_cpu_list_create(cpulist);
/*
* if hyper-threading was detected and ncpu is less than
* the number of counter instances, assume there is a counter
* for each logical processor.
* XXX assuming this is correct until have something to test on.
*/
if (sigar->ht_enabled && ((sigar->ncpu * sigar->lcpu) == num)) {
hthread = 1;
}
for (i=0; i<num; i++) {
sigar_cpu_t *cpu;
sigar_uint64_t idle, user, sys;
if (hthread && (i % sigar->lcpu)) {
/* merge times of logical processors */
cpu = &cpulist->data[cpulist->number-1];
}
else {
SIGAR_CPU_LIST_GROW(cpulist);
cpu = &cpulist->data[cpulist->number++];
SIGAR_ZERO(cpu);
}
2005-05-12 07:17:39 +08:00
idle = NS100_2SEC(info[i].IdleTime.QuadPart);
user = NS100_2SEC(info[i].UserTime.QuadPart);
sys = NS100_2SEC(info[i].KernelTime.QuadPart -
info[i].IdleTime.QuadPart);
cpu->idle += idle;
cpu->user += user;
cpu->sys += sys;
cpu->nice = cpu->wait = 0; /*N/A*/
cpu->total += idle + user + sys;
}
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_cpu_list_get(sigar_t *sigar,
sigar_cpu_list_t *cpulist)
{
if (sigar->get_ntsys_info) {
return sigar_cpu_list_ntsys_get(sigar, cpulist);
}
else {
return sigar_cpu_list_perflib_get(sigar, cpulist);
}
}
2004-06-22 06:37:04 +08:00
SIGAR_DECLARE(int) sigar_uptime_get(sigar_t *sigar,
sigar_uptime_t *uptime)
{
uptime->uptime = GetTickCount() / 1000;
return SIGAR_OK;
}
/*
* there is no api for this info.
* closest i've seen is enumerating the entire process table
* and calculating an average based on process times.
*/
SIGAR_DECLARE(int) sigar_loadavg_get(sigar_t *sigar,
sigar_loadavg_t *loadavg)
{
return SIGAR_ENOTIMPL;
}
#define get_process_object(sigar, err) \
get_perf_object(sigar, PERF_TITLE_PROC_KEY, err)
2004-06-22 06:37:04 +08:00
SIGAR_DECLARE(int) sigar_proc_list_get(sigar_t *sigar,
sigar_proc_list_t *proclist)
{
PERF_OBJECT_TYPE *object;
PERF_INSTANCE_DEFINITION *inst;
PERF_COUNTER_DEFINITION *counter;
DWORD i, err;
2004-06-22 06:37:04 +08:00
DWORD perf_offsets[PERF_IX_MAX];
perf_offsets[PERF_IX_PID] = 0;
object = get_process_object(sigar, &err);
2004-06-22 06:37:04 +08:00
if (!object) {
return err;
2004-06-22 06:37:04 +08:00
}
/*
* note we assume here:
* block->NumObjectTypes == 1
* object->ObjectNameTitleIndex == PERF_TITLE_PROC
*
* which should always be the case.
*/
for (i=0, counter = PdhFirstCounter(object);
i<object->NumCounters;
i++, counter = PdhNextCounter(counter))
{
DWORD offset = counter->CounterOffset;
switch (counter->CounterNameTitleIndex) {
case PERF_TITLE_PID:
perf_offsets[PERF_IX_PID] = offset;
break;
}
}
sigar_proc_list_create(proclist);
2004-06-22 06:37:04 +08:00
for (i=0, inst = PdhFirstInstance(object);
i<object->NumInstances;
i++, inst = PdhNextInstance(inst))
{
PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst);
DWORD pid = PERF_VAL(PERF_IX_PID);
if (pid == 0) {
continue; /* dont include the system Idle process */
}
2004-06-22 06:37:04 +08:00
SIGAR_PROC_LIST_GROW(proclist);
proclist->data[proclist->number++] = pid;
2004-06-22 06:37:04 +08:00
}
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_proc_stat_get(sigar_t *sigar,
sigar_proc_stat_t *procstat)
{
int status = /* XXX optimize */
sigar_proc_count(sigar, &procstat->total);
return status;
}
#define PROCESS_DAC (PROCESS_QUERY_INFORMATION|PROCESS_VM_READ)
static HANDLE open_process(sigar_pid_t pid)
{
return OpenProcess(PROCESS_DAC, 0, (DWORD)pid);
}
SIGAR_DECLARE(int) sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_mem_t *procmem)
{
int status = get_proc_info(sigar, pid);
sigar_win32_pinfo_t *pinfo = &sigar->pinfo;
if (status != SIGAR_OK) {
return status;
}
procmem->vsize = pinfo->vsize;
procmem->size = pinfo->size;
procmem->resident = SIGAR_FIELD_NOTIMPL;
procmem->share = SIGAR_FIELD_NOTIMPL;
procmem->rss = SIGAR_FIELD_NOTIMPL;
2004-06-22 06:37:04 +08:00
return SIGAR_OK;
}
#define TOKEN_DAC (STANDARD_RIGHTS_READ | READ_CONTROL | TOKEN_QUERY)
SIGAR_DECLARE(int)
sigar_proc_cred_name_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_cred_name_t *proccredname)
{
HANDLE proc, token;
DWORD len;
int success;
TOKEN_USER *user = NULL;
TOKEN_PRIMARY_GROUP *group = NULL;
SID_NAME_USE type;
char domain[SIGAR_CRED_NAME_MAX];
/* XXX cache lookup */
if (!(proc = open_process(pid))) {
return GetLastError();
}
if (!OpenProcessToken(proc, TOKEN_DAC, &token)) {
CloseHandle(proc);
return GetLastError();
}
CloseHandle(proc);
success =
!GetTokenInformation(token, TokenUser, NULL, 0, &len) &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(user = malloc(len)) &&
GetTokenInformation(token, TokenUser, user, len, &len);
if (success) {
DWORD domain_len = sizeof(domain);
DWORD user_len = sizeof(proccredname->user);
success = LookupAccountSid(NULL, user->User.Sid,
proccredname->user, &user_len,
domain, &domain_len, &type);
}
if (user != NULL) {
free(user);
}
if (!success) {
CloseHandle(token);
return GetLastError();
}
success =
!GetTokenInformation(token, TokenPrimaryGroup, NULL, 0, &len) &&
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
(group = malloc(len)) &&
GetTokenInformation(token, TokenPrimaryGroup, group, len, &len);
if (success) {
DWORD domain_len = sizeof(domain);
DWORD group_len = sizeof(proccredname->group);
success = LookupAccountSid(NULL, group->PrimaryGroup,
proccredname->group, &group_len,
domain, &domain_len, &type);
}
if (group != NULL) {
free(group);
}
CloseHandle(token);
if (!success) {
return GetLastError();
}
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_cred_t *proccred)
{
return SIGAR_ENOTIMPL;
}
#define FILETIME2SEC(ft) \
2005-05-12 07:17:39 +08:00
NS100_2SEC(((ft.dwHighDateTime << 32) | ft.dwLowDateTime))
2004-06-22 06:37:04 +08:00
SIGAR_DECLARE(int) sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_time_t *proctime)
{
HANDLE proc = open_process(pid);
FILETIME start_time, exit_time, system_time, user_time;
if (!proc) {
return GetLastError();
}
if (!GetProcessTimes(proc,
&start_time, &exit_time,
&system_time, &user_time))
{
return GetLastError();
}
CloseHandle(proc);
if (start_time.dwHighDateTime) {
proctime->start_time = FileTimeToTime(&start_time) / 1000;
}
else {
proctime->start_time = 0;
}
proctime->user = FILETIME2SEC(user_time);
proctime->sys = FILETIME2SEC(system_time);
proctime->total = proctime->user + proctime->sys;
2004-06-22 06:37:04 +08:00
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_state_t *procstate)
{
int status = get_proc_info(sigar, pid);
sigar_win32_pinfo_t *pinfo = &sigar->pinfo;
if (status != SIGAR_OK) {
return status;
}
memcpy(procstate->name, pinfo->name, sizeof(procstate->name));
procstate->state = pinfo->state;
procstate->ppid = pinfo->ppid;
procstate->priority = pinfo->priority;
procstate->nice = SIGAR_FIELD_NOTIMPL;
procstate->tty = SIGAR_FIELD_NOTIMPL;
2004-06-22 06:37:04 +08:00
return SIGAR_OK;
}
static int get_proc_info(sigar_t *sigar, sigar_pid_t pid)
{
PERF_OBJECT_TYPE *object;
PERF_INSTANCE_DEFINITION *inst;
PERF_COUNTER_DEFINITION *counter;
DWORD i, err;
2004-06-22 06:37:04 +08:00
DWORD perf_offsets[PERF_IX_MAX];
sigar_win32_pinfo_t *pinfo = &sigar->pinfo;
time_t timenow = time(NULL);
if (pinfo->pid == pid) {
if ((timenow - pinfo->mtime) < SIGAR_LAST_PROC_EXPIRE) {
return SIGAR_OK;
}
}
memset(&perf_offsets, 0, sizeof(perf_offsets));
object = get_process_object(sigar, &err);
if (object == NULL) {
return err;
}
2004-06-22 06:37:04 +08:00
pinfo->pid = pid;
pinfo->mtime = timenow;
2004-06-22 06:37:04 +08:00
/*
* note we assume here:
* block->NumObjectTypes == 1
* object->ObjectNameTitleIndex == PERF_TITLE_PROC
*
* which should always be the case.
*/
for (i=0, counter = PdhFirstCounter(object);
i<object->NumCounters;
i++, counter = PdhNextCounter(counter))
{
DWORD offset = counter->CounterOffset;
switch (counter->CounterNameTitleIndex) {
case PERF_TITLE_CPUTIME:
perf_offsets[PERF_IX_CPUTIME] = offset;
break;
case PERF_TITLE_MEM_VSIZE:
perf_offsets[PERF_IX_MEM_VSIZE] = offset;
break;
case PERF_TITLE_MEM_SIZE:
perf_offsets[PERF_IX_MEM_SIZE] = offset;
break;
case PERF_TITLE_MEM_PRIV:
perf_offsets[PERF_IX_MEM_PRIV] = offset;
break;
case PERF_TITLE_THREAD_CNT:
perf_offsets[PERF_IX_THREAD_CNT] = offset;
break;
case PERF_TITLE_HANDLE_CNT:
perf_offsets[PERF_IX_HANDLE_CNT] = offset;
break;
case PERF_TITLE_PID:
perf_offsets[PERF_IX_PID] = offset;
break;
case PERF_TITLE_PPID:
perf_offsets[PERF_IX_PPID] = offset;
break;
case PERF_TITLE_PRIORITY:
perf_offsets[PERF_IX_PRIORITY] = offset;
break;
case PERF_TITLE_START_TIME:
perf_offsets[PERF_IX_START_TIME] = offset;
break;
}
}
for (i=0, inst = PdhFirstInstance(object);
i<object->NumInstances;
i++, inst = PdhNextInstance(inst))
{
PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst);
sigar_pid_t this_pid = PERF_VAL(PERF_IX_PID);
if (this_pid != pid) {
continue;
}
pinfo->state = 'R'; /* XXX? */
SIGAR_W2A(PdhInstanceName(inst),
pinfo->name, sizeof(pinfo->name));
pinfo->size = PERF_VAL(PERF_IX_MEM_SIZE);
pinfo->vsize = PERF_VAL(PERF_IX_MEM_VSIZE);
pinfo->ppid = PERF_VAL(PERF_IX_PPID);
pinfo->priority = PERF_VAL(PERF_IX_PRIORITY);
pinfo->handles = PERF_VAL(PERF_IX_HANDLE_CNT);
return SIGAR_OK;
}
return SIGAR_NO_SUCH_PROCESS;
2004-06-22 06:37:04 +08:00
}
2004-07-29 05:47:14 +08:00
static int sigar_remote_proc_args_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_args_t *procargs)
{
int status;
2004-08-03 10:45:26 +08:00
char cmdline[SIGAR_CMDLINE_MAX], *ptr = cmdline, *arg;
2004-07-29 05:47:14 +08:00
HANDLE proc = open_process(pid);
if (!proc) {
return GetLastError();
}
status = sigar_proc_args_peb_get(sigar, proc, procargs);
2004-07-29 05:47:14 +08:00
CloseHandle(proc);
return status;
2004-07-29 05:47:14 +08:00
}
2004-06-22 06:37:04 +08:00
SIGAR_DECLARE(int) sigar_proc_args_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_args_t *procargs)
{
if (pid == sigar->pid) {
return sigar_parse_proc_args(sigar, NULL, procargs);
2004-06-22 06:37:04 +08:00
}
else {
return sigar_remote_proc_args_get(sigar, pid, procargs);
}
}
static int sigar_local_proc_env_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_env_t *procenv)
{
UCHAR *ptr, *env;
env = ptr = (UCHAR*)GetEnvironmentStrings();
while (*ptr) {
char *val;
int klen, vlen, status;
char key[128]; /* XXX is there a max key size? */
if (*ptr == '=') {
ptr += strlen(ptr)+1;
continue;
}
val = strchr(ptr, '=');
if (val == NULL) {
break; /*XXX*/
}
klen = val - ptr;
SIGAR_SSTRCPY(key, ptr);
key[klen] = '\0';
++val;
vlen = strlen(val);
status = procenv->env_getter(procenv->data,
key, klen, val, vlen);
if (status != SIGAR_OK) {
/* not an error; just stop iterating */
break;
}
ptr += klen + 1 + vlen + 1;
}
FreeEnvironmentStrings(env);
return SIGAR_OK;
}
static int sigar_remote_proc_env_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_env_t *procenv)
{
FARPROC rgetenv, fstrlen;
HANDLE proc, thread, kdll;
PVOID data=NULL;
const char *key;
char *value;
DWORD rv, thrid, bytes, datalen=0, size;
LPVOID addr;
if (!(kdll = GetModuleHandle("msvcrt.dll"))) {
return GetLastError();
}
if (!(rgetenv = GetProcAddress(kdll, "getenv"))) {
return GetLastError();
}
if (!(kdll = GetModuleHandle("kernel32.dll"))) {
return GetLastError();
}
if (!(fstrlen = GetProcAddress(kdll, "lstrlenA"))) {
return GetLastError();
}
if (!(proc = OpenProcess(MAXIMUM_ALLOWED, 0, (DWORD)pid))) {
return GetLastError();
}
key = procenv->key;
size = procenv->klen+1;
addr = VirtualAllocEx(proc, NULL, size,
MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (!addr) {
CloseHandle(proc);
return GetLastError();
}
if (!WriteProcessMemory(proc, addr, (char*)&key[0], size, 0)) {
VirtualFreeEx(proc, addr, size, 0);
CloseHandle(proc);
return GetLastError();
}
thread = CreateRemoteThread(proc, NULL, 0,
(LPTHREAD_START_ROUTINE)rgetenv,
addr, 0, &thrid);
if (!thread) {
VirtualFreeEx(proc, addr, size, 0);
CloseHandle(proc);
return GetLastError();
}
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, (LPDWORD)(&data));
CloseHandle(thread);
VirtualFreeEx(proc, addr, size, 0);
if (!data) {
CloseHandle(proc);
return SIGAR_OK;
}
thread = CreateRemoteThread(proc, NULL, 0,
(LPTHREAD_START_ROUTINE)fstrlen,
data, 0, &thrid);
if (!thread) {
CloseHandle(proc);
return GetLastError();
}
WaitForSingleObject(thread, INFINITE);
GetExitCodeThread(thread, &datalen);
CloseHandle(thread);
if (!datalen) {
CloseHandle(proc);
return GetLastError();
}
value = HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY,
datalen);
if (!value) {
CloseHandle(proc);
return GetLastError();
}
if (ReadProcessMemory(proc, data, value,
datalen+1, &bytes))
{
procenv->env_getter(procenv->data,
key, strlen(key),
value, bytes-1);
HeapFree(GetProcessHeap(), 0, value);
}
else {
CloseHandle(proc);
return GetLastError();
}
CloseHandle(proc);
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_env_t *procenv)
{
if (pid == sigar->pid) {
if (procenv->type == SIGAR_PROC_ENV_KEY) {
char value[32767]; /* max size from msdn docs */
DWORD retval =
GetEnvironmentVariable(procenv->key, value, sizeof(value));
if (retval == 0) {
if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
return SIGAR_OK;
}
return GetLastError();
}
else if (retval > sizeof(value)) {
/* XXX shouldnt happen */
return GetLastError();
}
procenv->env_getter(procenv->data,
procenv->key, procenv->klen,
value, retval);
return SIGAR_OK;
}
else {
return sigar_local_proc_env_get(sigar, pid, procenv);
}
}
else {
if (procenv->type == SIGAR_PROC_ENV_KEY) {
return sigar_remote_proc_env_get(sigar, pid, procenv);
}
else {
return SIGAR_ENOTIMPL;
}
}
}
SIGAR_DECLARE(int) sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_fd_t *procfd)
{
int status;
sigar_win32_pinfo_t *pinfo = &sigar->pinfo;
pinfo->pid = -1; /* force update */
if ((status = get_proc_info(sigar, pid)) != SIGAR_OK) {
return status;
}
procfd->total = pinfo->handles;
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_exe_t *procexe)
{
int status = SIGAR_OK;
HANDLE proc = open_process(pid);
if (!proc) {
return GetLastError();
}
2004-08-02 04:31:42 +08:00
status = sigar_proc_exe_peb_get(sigar, proc, procexe);
2004-08-02 04:42:18 +08:00
if (procexe->cwd[0] != '\0') {
/* strip trailing '\' */
int len = strlen(procexe->cwd);
if (procexe->cwd[len-1] == '\\') {
procexe->cwd[len-1] = '\0';
}
/* uppercase driver letter */
procexe->cwd[0] = toupper(procexe->cwd[0]);
/* e.g. C:\ */
strncpy(procexe->root, procexe->cwd, 3);
procexe->root[3] = '\0';
}
else {
procexe->root[0] = '\0';
}
if (procexe->name[0] != '\0') {
/* uppercase driver letter */
procexe->name[0] = toupper(procexe->name[0]);
}
2004-06-22 06:37:04 +08:00
return status;
}
SIGAR_DECLARE(int) sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid,
sigar_proc_modules_t *procmods)
2004-06-22 14:12:51 +08:00
{
HANDLE proc;
HMODULE modules[1024];
DWORD size = 0;
unsigned int i;
2004-06-22 14:12:51 +08:00
if (!sigar->ps_handle) {
return SIGAR_ENOTIMPL;
2004-06-22 14:12:51 +08:00
}
if (!(proc = open_process(pid))) {
2004-06-22 14:12:51 +08:00
return GetLastError();
}
if (!sigar->enum_modules(proc, modules, sizeof(modules), &size)) {
CloseHandle(proc);
2004-06-22 14:12:51 +08:00
return GetLastError();
}
for (i=0; i<(size/sizeof(HMODULE)); i++) {
int status;
char name[MAX_PATH];
2004-06-22 14:12:51 +08:00
if (!sigar->get_module_name(proc, modules[i], name, sizeof(name))) {
continue;
}
2004-06-22 14:12:51 +08:00
status = procmods->module_getter(procmods->data,
name, strlen(name));
2004-06-22 14:12:51 +08:00
if (status != SIGAR_OK) {
/* not an error; just stop iterating */
break;
}
}
2004-06-22 14:12:51 +08:00
CloseHandle(proc);
2004-06-22 14:12:51 +08:00
return SIGAR_OK;
}
2004-11-17 12:56:00 +08:00
#define FT2INT64(ft) \
((__int64)((__int64)(ft).dwHighDateTime << 32 | \
(__int64)(ft).dwLowDateTime))
SIGAR_DECLARE(int) sigar_thread_cpu_get(sigar_t *sigar,
sigar_uint64_t id,
sigar_thread_cpu_t *cpu)
{
FILETIME start, exit, sys, user;
DWORD retval;
if (id != 0) {
return SIGAR_ENOTIMPL;
}
retval = GetThreadTimes(GetCurrentThread(),
&start, &exit, &sys, &user);
if (retval == 0) {
return GetLastError();
}
cpu->user = FT2INT64(user) * 100;
cpu->sys = FT2INT64(sys) * 100;
cpu->total = (FT2INT64(user) + FT2INT64(sys)) * 100;
return SIGAR_OK;
}
2004-06-22 06:37:04 +08:00
int sigar_os_fs_type_get(sigar_file_system_t *fsp)
{
return fsp->type;
}
SIGAR_DECLARE(int) sigar_file_system_list_get(sigar_t *sigar,
sigar_file_system_list_t *fslist)
{
sigar_file_system_t *fsp;
char name[256];
char *ptr = name;
/* XXX: hmm, Find{First,Next}Volume not available in my sdk */
DWORD len = GetLogicalDriveStringsA(sizeof(name), name);
if (len == 0) {
return GetLastError();
}
sigar_file_system_list_create(fslist);
while (*ptr) {
DWORD flags, serialnum=0;
char fsname[1024];
UINT type;
fsname[0] = '\0';
GetVolumeInformation(ptr, NULL, 0, &serialnum, NULL,
&flags, fsname, sizeof(fsname));
type = GetDriveType(ptr);
if (!serialnum && (type == DRIVE_FIXED)) {
ptr += strlen(ptr)+1;
continue; /* ignore unformatted partitions */
}
SIGAR_FILE_SYSTEM_LIST_GROW(fslist);
fsp = &fslist->data[fslist->number++];
SIGAR_SSTRCPY(fsp->dir_name, ptr);
SIGAR_SSTRCPY(fsp->dev_name, ptr);
switch (type) {
case DRIVE_FIXED:
fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
break;
case DRIVE_REMOTE:
fsp->type = SIGAR_FSTYPE_NETWORK;
break;
case DRIVE_CDROM:
fsp->type = SIGAR_FSTYPE_CDROM;
break;
case DRIVE_RAMDISK:
fsp->type = SIGAR_FSTYPE_RAM_DISK;
break;
case DRIVE_REMOVABLE:
/* XXX */
default:
fsp->type = SIGAR_FSTYPE_NONE;
break;
}
/* we set fsp->type, just looking up sigar.c:fstype_names[type] */
sigar_fs_type_get(fsp);
if (*fsname == '\0') {
SIGAR_SSTRCPY(fsp->sys_type_name, fsp->type_name);
}
else {
SIGAR_SSTRCPY(fsp->sys_type_name, fsname); /* CDFS, NTFS, etc */
}
ptr += strlen(ptr)+1;
}
return SIGAR_OK;
}
2004-12-05 09:18:57 +08:00
static PERF_INSTANCE_DEFINITION *get_disk_instance(sigar_t *sigar,
DWORD *perf_offsets,
DWORD *num, DWORD *err)
{
PERF_OBJECT_TYPE *object = get_perf_object(sigar, "236", err);
PERF_INSTANCE_DEFINITION *inst;
PERF_COUNTER_DEFINITION *counter;
DWORD i;
if (!object) {
return NULL;
}
for (i=0, counter = PdhFirstCounter(object);
i<object->NumCounters;
i++, counter = PdhNextCounter(counter))
{
DWORD offset = counter->CounterOffset;
switch (counter->CounterNameTitleIndex) {
case PERF_TITLE_DISK_READ:
perf_offsets[PERF_IX_DISK_READ] = offset;
break;
case PERF_TITLE_DISK_WRITE:
perf_offsets[PERF_IX_DISK_WRITE] = offset;
break;
2005-04-07 09:23:49 +08:00
case PERF_TITLE_DISK_READ_BYTES:
perf_offsets[PERF_IX_DISK_READ_BYTES] = offset;
break;
case PERF_TITLE_DISK_WRITE_BYTES:
perf_offsets[PERF_IX_DISK_WRITE_BYTES] = offset;
break;
case PERF_TITLE_DISK_QUEUE:
perf_offsets[PERF_IX_DISK_QUEUE] = offset;
break;
2004-12-05 09:18:57 +08:00
}
}
if (num) {
*num = object->NumInstances;
}
return PdhFirstInstance(object);
}
static int get_disk_metrics(sigar_t *sigar,
const char *dirname,
sigar_file_system_usage_t *fsusage)
{
DWORD i, err;
PERF_OBJECT_TYPE *object =
get_perf_object(sigar, PERF_TITLE_DISK_KEY, &err);
2004-12-05 09:18:57 +08:00
PERF_INSTANCE_DEFINITION *inst;
PERF_COUNTER_DEFINITION *counter;
DWORD perf_offsets[PERF_IX_DISK_MAX];
if (!object) {
return err;
}
memset(&perf_offsets, 0, sizeof(perf_offsets));
inst = get_disk_instance(sigar, (DWORD*)&perf_offsets, 0, &err);
if (!inst) {
return err;
}
for (i=0, inst = PdhFirstInstance(object);
i<object->NumInstances;
i++, inst = PdhNextInstance(inst))
{
char drive[MAX_PATH];
PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst);
wchar_t *name = (wchar_t *)((BYTE *)inst + inst->NameOffset);
SIGAR_W2A(name, drive, sizeof(drive));
2005-04-28 02:54:42 +08:00
if (sigar_isdigit(*name)) {
char *ptr = strchr(drive, ' '); /* 2000 Server "0 C:" */
if (ptr) {
++ptr;
SIGAR_SSTRCPY(drive, ptr);
}
else {
/* XXX NT is a number only "0", how to map? */
}
}
2004-12-05 09:18:57 +08:00
if (strnEQ(drive, dirname, 2)) {
fsusage->disk_reads = PERF_VAL(PERF_IX_DISK_READ);
fsusage->disk_writes = PERF_VAL(PERF_IX_DISK_WRITE);
2005-04-07 09:23:49 +08:00
fsusage->disk_read_bytes = PERF_VAL(PERF_IX_DISK_READ_BYTES);
fsusage->disk_write_bytes = PERF_VAL(PERF_IX_DISK_WRITE_BYTES);
fsusage->disk_queue = PERF_VAL(PERF_IX_DISK_QUEUE);
2004-12-05 09:18:57 +08:00
return SIGAR_OK;
}
}
return ENOENT;
}
2004-06-22 06:37:04 +08:00
SIGAR_DECLARE(int)
sigar_file_system_usage_get(sigar_t *sigar,
const char *dirname,
sigar_file_system_usage_t *fsusage)
{
BOOL retval;
ULARGE_INTEGER avail, total, free;
2004-12-05 09:18:57 +08:00
int status;
2004-06-22 06:37:04 +08:00
/* prevent dialog box if A:\ drive is empty */
UINT errmode = SetErrorMode(SEM_FAILCRITICALERRORS);
retval = GetDiskFreeSpaceEx(dirname,
&avail, &total, &free);
/* restore previous error mode */
SetErrorMode(errmode);
if (!retval) {
return GetLastError();
}
fsusage->total = total.QuadPart / 1024;
fsusage->free = free.QuadPart / 1024;
fsusage->avail = avail.QuadPart / 1024;
2005-04-27 03:55:21 +08:00
fsusage->used = fsusage->total - fsusage->free;
2004-06-22 06:37:04 +08:00
fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage);
/* N/A */
fsusage->files = SIGAR_FIELD_NOTIMPL;
fsusage->free_files = SIGAR_FIELD_NOTIMPL;
2004-06-22 06:37:04 +08:00
2004-12-05 09:18:57 +08:00
status = get_disk_metrics(sigar, dirname, fsusage);
if (status != SIGAR_OK) {
SIGAR_DISK_STATS_NOTIMPL(fsusage);
2004-12-05 09:18:57 +08:00
}
2004-06-22 06:37:04 +08:00
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_cpu_info_list_get(sigar_t *sigar,
sigar_cpu_info_list_t *cpu_infos)
2004-06-22 06:37:04 +08:00
{
int i, status;
sigar_cpu_info_t *info;
sigar_cpu_count(sigar);
sigar_cpu_info_list_create(cpu_infos);
2004-06-22 06:37:04 +08:00
info = &cpu_infos->data[cpu_infos->number++];
status = sigar_cpu_info_get(sigar, info);
if (status != SIGAR_OK) {
return status;
}
if (sigar->ncpu > 1) {
for (i=1; i<sigar->ncpu; i++) {
SIGAR_CPU_INFO_LIST_GROW(cpu_infos);
2004-06-22 06:37:04 +08:00
memcpy(&cpu_infos->data[cpu_infos->number++],
info, sizeof(*info));
}
}
return SIGAR_OK;
}
2005-07-12 06:40:31 +08:00
SIGAR_DECLARE(int) sigar_net_info_get(sigar_t *sigar,
sigar_net_info_t *netinfo)
{
FIXED_INFO info;
ULONG len;
IP_ADDR_STRING *ip;
if (!sigar->get_net_params) {
return SIGAR_ENOTIMPL;
}
SIGAR_ZERO(netinfo);
len = sizeof(info);
if (sigar->get_net_params(&info, &len) != ERROR_SUCCESS) {
return GetLastError();
}
2005-07-12 06:44:13 +08:00
SIGAR_SSTRCPY(netinfo->host_name, info.HostName);
2005-07-12 06:40:31 +08:00
SIGAR_SSTRCPY(netinfo->domain_name, info.DomainName);
SIGAR_SSTRCPY(netinfo->primary_dns,
info.DnsServerList.IpAddress.String);
if ((ip = info.DnsServerList.Next)) {
SIGAR_SSTRCPY(netinfo->secondary_dns,
ip->IpAddress.String);
}
return SIGAR_OK;
}
2004-06-22 06:37:04 +08:00
SIGAR_DECLARE(int) sigar_net_route_list_get(sigar_t *sigar,
sigar_net_route_list_t *routelist)
{
char *buffer = NULL;
ULONG bufsize = 0;
DWORD rc, i;
MIB_IPFORWARDTABLE *ipt;
sigar_net_route_t *route;
if (!sigar->get_ipforward_table) {
return SIGAR_ENOTIMPL;
}
rc = (*(sigar->get_ipforward_table))((PMIB_IPFORWARDTABLE)buffer,
&bufsize, FALSE);
if (rc != ERROR_INSUFFICIENT_BUFFER) {
return GetLastError();
}
buffer = malloc(bufsize);
rc = (*(sigar->get_ipforward_table))((PMIB_IPFORWARDTABLE)buffer,
&bufsize, FALSE);
if (rc != NO_ERROR) {
free(buffer);
return GetLastError();
}
sigar_net_route_list_create(routelist);
routelist->size = routelist->number = 0;
ipt = (MIB_IPFORWARDTABLE *)buffer;
for (i=0; i<ipt->dwNumEntries; i++) {
MIB_IPFORWARDROW *ipr = ipt->table + i;
SIGAR_NET_ROUTE_LIST_GROW(routelist);
route = &routelist->data[routelist->number++];
SIGAR_ZERO(route); /* XXX: other fields */
route->destination = ipr->dwForwardDest;
route->mask = ipr->dwForwardMask;
route->gateway = ipr->dwForwardNextHop;
}
free(buffer);
return SIGAR_OK;
}
#define IFTYPE_LO 2
#define IFTYPE_ETH 3
static int get_iftype(const char *name, int *type, int *inst)
{
if (strnEQ(name, "eth", IFTYPE_ETH)) {
*type = IFTYPE_ETH;
}
else if (strnEQ(name, "lo", IFTYPE_LO)) {
*type = IFTYPE_LO;
}
else {
return EINVAL;
}
if (isdigit(*(name + *type))) {
*inst = atoi(name + *type);
}
else {
return EINVAL;
}
return SIGAR_OK;
}
SIGAR_DECLARE(int)
sigar_net_interface_stat_get(sigar_t *sigar, const char *name,
sigar_net_interface_stat_t *ifstat)
{
char *buffer = NULL;
ULONG buf_size = 0;
DWORD rc, i;
MIB_IFTABLE *ift;
MIB_IFROW *ifr;
DWORD lo=0, eth=0;
int status, type, inst;
if ((status = get_iftype(name, &type, &inst)) != SIGAR_OK) {
return status;
}
if (!sigar->get_if_table) {
return SIGAR_ENOTIMPL;
}
rc = (*(sigar->get_if_table))((PMIB_IFTABLE)buffer, &buf_size, FALSE);
if (rc != ERROR_INSUFFICIENT_BUFFER) {
return GetLastError();
}
buffer = malloc(buf_size);
rc = (*(sigar->get_if_table))((PMIB_IFTABLE)buffer, &buf_size, FALSE);
if (rc != NO_ERROR) {
free(buffer);
return GetLastError();
}
ift = (MIB_IFTABLE *)buffer;
for (i=0; i<ift->dwNumEntries; i++) {
ifr = ift->table + i;
if (!(ifr->dwOperStatus & MIB_IF_OPER_STATUS_OPERATIONAL)) {
continue;
}
if (ifr->dwType == MIB_IF_TYPE_LOOPBACK) {
if ((type == IFTYPE_LO) && (inst == lo)) {
break;
}
++lo;
}
else if (ifr->dwType == MIB_IF_TYPE_ETHERNET) {
if ((type == IFTYPE_ETH) && (inst == eth)) {
break;
}
++eth;
}
ifr = NULL;
}
if (!ifr) {
free(buffer);
return ENOENT;
}
ifstat->rx_bytes = ifr->dwInOctets;
ifstat->rx_packets = ifr->dwInUcastPkts + ifr->dwInNUcastPkts;
ifstat->rx_errors = ifr->dwInErrors;
ifstat->rx_dropped = ifr->dwInDiscards;
ifstat->rx_overruns = SIGAR_FIELD_NOTIMPL;
2005-05-14 08:02:33 +08:00
ifstat->rx_frame = SIGAR_FIELD_NOTIMPL;
2004-06-22 06:37:04 +08:00
ifstat->tx_bytes = ifr->dwOutOctets;
ifstat->tx_packets = ifr->dwOutUcastPkts + ifr->dwOutNUcastPkts;
ifstat->tx_errors = ifr->dwOutErrors;
ifstat->tx_dropped = ifr->dwOutDiscards;
ifstat->tx_overruns = SIGAR_FIELD_NOTIMPL;
ifstat->tx_collisions = SIGAR_FIELD_NOTIMPL;
ifstat->tx_carrier = SIGAR_FIELD_NOTIMPL;
2004-06-22 06:37:04 +08:00
free(buffer);
return SIGAR_OK;
}
static int get_iflist(sigar_t *sigar, char *buffer, DWORD buflen, DWORD *bytes)
{
SOCKET sock = INVALID_SOCKET;
DWORD rc;
int status = sigar_wsa_init(sigar);
if (status != SIGAR_OK) {
return status;
}
sock = WSASocket(PF_INET, SOCK_RAW, AF_INET, 0, 0, 0);
if (sock == INVALID_SOCKET) {
return WSAGetLastError();
}
rc = WSAIoctl(sock,
SIO_GET_INTERFACE_LIST,
NULL,
0,
(void *)buffer,
buflen,
bytes,
0,
0);
status = rc ? WSAGetLastError() : SIGAR_OK;
closesocket(sock);
return status;
}
#include <nb30.h>
static void hwaddr_lookup(sigar_net_interface_config_t *ifconfig, int num)
{
NCB ncb;
UCHAR rc;
struct {
ADAPTER_STATUS status;
NAME_BUFFER name[30];
} adapter;
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBRESET;
ncb.ncb_lana_num = num;
Netbios(&ncb);
memset(&ncb, 0, sizeof(ncb));
ncb.ncb_command = NCBASTAT;
ncb.ncb_lana_num = num;
/*
* http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netbios/netbios_1l82.asp
* mdsn docs claim this needs to be padded with spaces and
* suggest the following silly code:
* strcpy(ncb.ncb_callname, "* ");
*/
ncb.ncb_callname[0] = '*';
memset(&ncb.ncb_callname[1], ' ',
sizeof(ncb.ncb_callname)-1);
ncb.ncb_callname[sizeof(ncb.ncb_callname)] = '\0';
ncb.ncb_buffer = (unsigned char *)&adapter;
ncb.ncb_length = sizeof(adapter);
if ((rc = Netbios(&ncb)) == 0) {
sigar_hwaddr_format(ifconfig->hwaddr,
adapter.status.adapter_address);
}
else {
sigar_hwaddr_set_null(ifconfig);
}
}
SIGAR_DECLARE(int)
sigar_net_interface_config_get(sigar_t *sigar,
const char *name,
sigar_net_interface_config_t *ifconfig)
{
char buffer[8192];
DWORD i, num, bytes, inst;
DWORD lo=0, eth=0;
int status, type;
INTERFACE_INFO *if_info = NULL;
u_long flags;
/* win32 lacks socket ioctls to query given interface.
* so we loop through the list to find our made up ifname.
*/
status = get_iflist(sigar, buffer, sizeof(buffer), &bytes);
if (status != SIGAR_OK) {
return status;
}
num = bytes / sizeof(INTERFACE_INFO);
if ((status = get_iftype(name, &type, &inst)) != SIGAR_OK) {
return status;
}
for (i=0; i<num ; i++) {
if_info = ((INTERFACE_INFO *)buffer) + i;
if (if_info->iiFlags & IFF_LOOPBACK) {
if ((type == IFTYPE_LO) && (inst == lo)) {
break;
}
++lo;
}
else {
if ((type == IFTYPE_ETH) && (inst == eth)) {
break;
}
++eth;
}
if_info = NULL;
}
if (!if_info) {
return ENOENT;
}
SIGAR_ZERO(ifconfig);
SIGAR_SSTRCPY(ifconfig->name, name);
#define if_s_addr(a) \
((struct sockaddr_in *)&a)->sin_addr.s_addr
ifconfig->address = if_s_addr(if_info->iiAddress);
ifconfig->broadcast = if_s_addr(if_info->iiBroadcastAddress);
ifconfig->netmask = if_s_addr(if_info->iiNetmask);
flags = if_info->iiFlags;
if (flags & IFF_UP) {
ifconfig->flags |= SIGAR_IFF_UP|SIGAR_IFF_RUNNING;
}
if (flags & IFF_BROADCAST) {
ifconfig->flags |= SIGAR_IFF_BROADCAST;
}
if (flags & IFF_LOOPBACK) {
ifconfig->flags |= SIGAR_IFF_LOOPBACK;
ifconfig->destination = ifconfig->address;
ifconfig->broadcast = 0;
sigar_hwaddr_set_null(ifconfig);
}
else {
hwaddr_lookup(ifconfig, i);
}
if (flags & IFF_POINTTOPOINT) {
ifconfig->flags |= SIGAR_IFF_POINTOPOINT;
}
if (flags & IFF_MULTICAST) {
ifconfig->flags |= SIGAR_IFF_MULTICAST;
}
return SIGAR_OK;
}
/*
* win32 interface list does not include a name.
* and the name from GetIfList() is the name of card
* including vendor name, etc. so we use 'eth' for ethernet
* interfaces and 'lo' for loopback.
*/
#define ETH "eth"
#define LO "lo"
SIGAR_DECLARE(int)
sigar_net_interface_list_get(sigar_t *sigar,
sigar_net_interface_list_t *iflist)
{
char eth[56], lo[56];
int ethcnt=0, locnt=0;
char buffer[8192];
DWORD i, num, bytes;
int status;
status = get_iflist(sigar, buffer, sizeof(buffer), &bytes);
if (status != SIGAR_OK) {
return status;
}
num = bytes / sizeof(INTERFACE_INFO);
iflist->number = 0;
iflist->size = num;
iflist->data =
2004-06-23 07:44:46 +08:00
malloc(sizeof(*(iflist->data)) * iflist->size);
2004-06-22 06:37:04 +08:00
for (i=0; i<num ; i++) {
INTERFACE_INFO *if_info = ((INTERFACE_INFO *)buffer) + i;
char *name;
if (if_info->iiFlags & IFF_LOOPBACK) {
sprintf(lo, LO "%d", locnt++);
name = strdup(lo);
}
else {
/* XXX: assuming ethernet here */
sprintf(eth, ETH "%d", ethcnt++);
name = strdup(eth);
}
iflist->data[iflist->number++] = name;
}
return SIGAR_OK;
}
2005-05-07 14:00:55 +08:00
#define IS_TCP_SERVER(state, flags) \
((flags & SIGAR_NETCONN_SERVER) && (state == MIB_TCP_STATE_LISTEN))
#define IS_TCP_CLIENT(state, flags) \
((flags & SIGAR_NETCONN_CLIENT) && (state != MIB_TCP_STATE_LISTEN))
2004-06-22 06:37:04 +08:00
static int net_conn_get_tcp(sigar_t *sigar,
sigar_net_connection_list_t *connlist,
int flags)
{
int status;
DWORD rc, size, i;
PMIB_TCPTABLE tcp;
size = 0;
rc = sigar->get_tcp_table(NULL, &size, FALSE);
if (rc != ERROR_INSUFFICIENT_BUFFER) {
return GetLastError();
}
2004-06-23 07:44:46 +08:00
tcp = malloc(size);
2004-06-22 06:37:04 +08:00
rc = sigar->get_tcp_table(tcp, &size, FALSE);
if (rc) {
free(tcp);
return GetLastError();
}
for (i = 0; i < tcp->dwNumEntries; i++) {
sigar_net_connection_t conn;
DWORD state = tcp->table[i].dwState;
2005-05-07 14:00:55 +08:00
if (!(IS_TCP_SERVER(state, flags) ||
IS_TCP_CLIENT(state, flags)))
{
continue;
2004-06-22 06:37:04 +08:00
}
conn.local_port = htons((WORD)tcp->table[i].dwLocalPort);
conn.remote_port = htons((WORD)tcp->table[i].dwRemotePort);
conn.type = SIGAR_NETCONN_TCP;
2005-05-13 23:16:49 +08:00
sigar_inet_ntoa(sigar, tcp->table[i].dwLocalAddr,
conn.local_address);
2004-06-22 06:37:04 +08:00
2005-05-13 23:16:49 +08:00
sigar_inet_ntoa(sigar, tcp->table[i].dwRemoteAddr,
conn.remote_address);
2004-06-22 06:37:04 +08:00
2005-03-12 12:49:38 +08:00
conn.send_queue = conn.receive_queue = SIGAR_FIELD_NOTIMPL;
2005-03-11 12:45:09 +08:00
switch (state) {
case MIB_TCP_STATE_CLOSED:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_CLOSE;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_LISTEN:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_LISTEN;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_SYN_SENT:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_SYN_SENT;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_SYN_RCVD:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_SYN_RECV;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_ESTAB:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_ESTABLISHED;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_FIN_WAIT1:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_FIN_WAIT1;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_FIN_WAIT2:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_FIN_WAIT2;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_CLOSE_WAIT:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_CLOSE_WAIT;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_CLOSING:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_CLOSING;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_LAST_ACK:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_LAST_ACK;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_TIME_WAIT:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_TIME_WAIT;
2005-03-11 12:45:09 +08:00
break;
case MIB_TCP_STATE_DELETE_TCB:
default:
2005-03-12 01:11:30 +08:00
conn.state = SIGAR_TCP_UNKNOWN;
2005-03-11 12:45:09 +08:00
break;
}
2004-06-22 06:37:04 +08:00
SIGAR_NET_CONNLIST_GROW(connlist);
memcpy(&connlist->data[connlist->number++],
&conn, sizeof(conn));
}
free(tcp);
return SIGAR_OK;
}
2005-05-07 14:00:55 +08:00
#define IS_UDP_SERVER(conn, flags) \
((flags & SIGAR_NETCONN_SERVER) && !conn.remote_port)
#define IS_UDP_CLIENT(state, flags) \
((flags & SIGAR_NETCONN_CLIENT) && conn.remote_port)
2004-06-22 06:37:04 +08:00
static int net_conn_get_udp(sigar_t *sigar,
sigar_net_connection_list_t *connlist,
int flags)
{
int status;
DWORD rc, size, i;
PMIB_UDPTABLE udp;
size = 0;
rc = sigar->get_udp_table(NULL, &size, FALSE);
if (rc != ERROR_INSUFFICIENT_BUFFER) {
return GetLastError();
}
2004-06-23 07:44:46 +08:00
udp = malloc(size);
2004-06-22 06:37:04 +08:00
rc = sigar->get_udp_table(udp, &size, FALSE);
if (rc) {
free(udp);
return GetLastError();
}
for (i = 0; i < udp->dwNumEntries; i++) {
sigar_net_connection_t conn;
2005-05-07 14:00:55 +08:00
if (!(IS_UDP_SERVER(conn, flags) ||
IS_UDP_CLIENT(conn, flags)))
2004-06-22 06:37:04 +08:00
{
continue;
}
conn.local_port = htons((WORD)udp->table[i].dwLocalPort);
conn.remote_port = 0;
2004-06-22 06:37:04 +08:00
conn.type = SIGAR_NETCONN_UDP;
2005-05-13 23:16:49 +08:00
sigar_inet_ntoa(sigar, udp->table[i].dwLocalAddr,
conn.local_address);
2004-06-22 06:37:04 +08:00
2005-05-09 01:23:24 +08:00
SIGAR_SSTRCPY(conn.remote_address, "0.0.0.0");
2004-06-22 06:37:04 +08:00
2005-03-12 12:49:38 +08:00
conn.send_queue = conn.receive_queue = SIGAR_FIELD_NOTIMPL;
2004-06-22 06:37:04 +08:00
SIGAR_NET_CONNLIST_GROW(connlist);
memcpy(&connlist->data[connlist->number++],
&conn, sizeof(conn));
}
free(udp);
return SIGAR_OK;
}
SIGAR_DECLARE(int)
sigar_net_connection_list_get(sigar_t *sigar,
sigar_net_connection_list_t *connlist,
int flags)
{
int status;
sigar_net_connection_list_create(connlist);
if (flags & SIGAR_NETCONN_TCP) {
status = net_conn_get_tcp(sigar, connlist, flags);
if (status != SIGAR_OK) {
return status;
}
}
if (flags & SIGAR_NETCONN_UDP) {
status = net_conn_get_udp(sigar, connlist, flags);
if (status != SIGAR_OK) {
return status;
}
}
return SIGAR_OK;
}
SIGAR_DECLARE(int) sigar_proc_port_get(sigar_t *sigar,
int protocol,
2004-06-22 06:37:04 +08:00
unsigned long port,
sigar_pid_t *pid)
{
DWORD rc, i;
2005-03-20 11:20:28 +08:00
if (protocol == SIGAR_NETCONN_TCP) {
PMIB_TCPEXTABLE tcp;
2004-06-22 06:37:04 +08:00
2005-03-20 11:20:28 +08:00
if (!sigar->get_tcpx_table) {
return SIGAR_ENOTIMPL;
}
2005-03-16 11:49:54 +08:00
2005-03-20 11:20:28 +08:00
rc = sigar->get_tcpx_table(&tcp, FALSE, GetProcessHeap(),
2, 2);
2004-06-22 06:37:04 +08:00
2005-03-20 11:20:28 +08:00
if (rc) {
return GetLastError();
}
for (i=0; i<tcp->dwNumEntries; i++) {
if (tcp->table[i].dwState != MIB_TCP_STATE_LISTEN) {
continue;
}
if (htons((WORD)tcp->table[i].dwLocalPort) != port) {
continue;
}
*pid = tcp->table[i].dwProcessId;
return SIGAR_OK;
}
2004-06-22 06:37:04 +08:00
}
2005-03-20 11:20:28 +08:00
else if (protocol == SIGAR_NETCONN_UDP) {
PMIB_UDPEXTABLE udp;
2004-06-22 06:37:04 +08:00
2005-03-20 11:20:28 +08:00
if (!sigar->get_udpx_table) {
return SIGAR_ENOTIMPL;
2004-06-22 06:37:04 +08:00
}
2005-03-20 11:20:28 +08:00
rc = sigar->get_udpx_table(&udp, FALSE, GetProcessHeap(),
2, 2);
if (rc) {
return GetLastError();
2004-06-22 06:37:04 +08:00
}
2005-03-20 11:20:28 +08:00
for (i=0; i<udp->dwNumEntries; i++) {
if (htons((WORD)udp->table[i].dwLocalPort) != port) {
continue;
}
2004-06-22 06:37:04 +08:00
2005-03-20 11:20:28 +08:00
*pid = udp->table[i].dwProcessId;
return SIGAR_OK;
}
}
else {
return SIGAR_ENOTIMPL;
2004-06-22 06:37:04 +08:00
}
return ENOENT;
}