1/*
2 * Copyright (c) 2004-2009 Hyperic, Inc.
3 * Copyright (c) 2009 SpringSource, Inc.
4 * Copyright (c) 2009-2010 VMware, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include "sigar.h"
20#include "sigar_private.h"
21#include "sigar_pdh.h"
22#include "sigar_os.h"
23#include "sigar_util.h"
24#include "sigar_format.h"
25#include <shellapi.h>
26#include <assert.h>
27
28#define USING_WIDE_S(s) (s)->using_wide
29#define USING_WIDE()    USING_WIDE_S(sigar)
30
31#define PERFBUF_SIZE 8192
32
33#define PERF_TITLE_PROC       230
34#define PERF_TITLE_SYS_KEY   "2"
35#define PERF_TITLE_MEM_KEY   "4"
36#define PERF_TITLE_PROC_KEY  "230"
37#define PERF_TITLE_CPU_KEY   "238"
38#define PERF_TITLE_DISK_KEY  "236"
39
40#define PERF_TITLE_CPU_USER    142
41#define PERF_TITLE_CPU_IDLE    1746
42#define PERF_TITLE_CPU_SYS     144
43#define PERF_TITLE_CPU_IRQ     698
44
45typedef enum {
46    PERF_IX_CPU_USER,
47    PERF_IX_CPU_IDLE,
48    PERF_IX_CPU_SYS,
49    PERF_IX_CPU_IRQ,
50    PERF_IX_CPU_MAX
51} perf_cpu_offsets_t;
52
53#define PERF_TITLE_CPUTIME    6
54#define PERF_TITLE_PAGE_FAULTS 28
55#define PERF_TITLE_MEM_VSIZE  174
56#define PERF_TITLE_MEM_SIZE   180
57#define PERF_TITLE_THREAD_CNT 680
58#define PERF_TITLE_HANDLE_CNT 952
59#define PERF_TITLE_PID        784
60#define PERF_TITLE_PPID       1410
61#define PERF_TITLE_PRIORITY   682
62#define PERF_TITLE_START_TIME 684
63
64typedef enum {
65    PERF_IX_CPUTIME,
66    PERF_IX_PAGE_FAULTS,
67    PERF_IX_MEM_VSIZE,
68    PERF_IX_MEM_SIZE,
69    PERF_IX_THREAD_CNT,
70    PERF_IX_HANDLE_CNT,
71    PERF_IX_PID,
72    PERF_IX_PPID,
73    PERF_IX_PRIORITY,
74    PERF_IX_START_TIME,
75    PERF_IX_MAX
76} perf_proc_offsets_t;
77
78typedef enum {
79    PERF_IX_DISK_TIME,
80    PERF_IX_DISK_READ_TIME,
81    PERF_IX_DISK_WRITE_TIME,
82    PERF_IX_DISK_READ,
83    PERF_IX_DISK_WRITE,
84    PERF_IX_DISK_READ_BYTES,
85    PERF_IX_DISK_WRITE_BYTES,
86    PERF_IX_DISK_QUEUE,
87    PERF_IX_DISK_MAX
88} perf_disk_offsets_t;
89
90#define PERF_TITLE_DISK_TIME 200 /* % Disk Time */
91#define PERF_TITLE_DISK_READ_TIME 202 /* % Disk Read Time */
92#define PERF_TITLE_DISK_WRITE_TIME 204 /* % Disk Write Time */
93#define PERF_TITLE_DISK_READ  214 /* Disk Reads/sec */
94#define PERF_TITLE_DISK_WRITE 216 /* Disk Writes/sec */
95#define PERF_TITLE_DISK_READ_BYTES  220 /* Disk Read Bytes/sec */
96#define PERF_TITLE_DISK_WRITE_BYTES 222 /* Disk Write Bytes/sec */
97#define PERF_TITLE_DISK_QUEUE 198 /* Current Disk Queue Length */
98
99/*
100 * diff is:
101 *   ExW      -> ExA
102 *   wcounter -> counter
103 */
104#define MyRegQueryValue() \
105    (USING_WIDE() ? \
106        RegQueryValueExW(sigar->handle, \
107                         wcounter_key, NULL, &type, \
108                         sigar->perfbuf, \
109                         &bytes) : \
110        RegQueryValueExA(sigar->handle, \
111                         counter_key, NULL, &type, \
112                         sigar->perfbuf, \
113                         &bytes))
114
115#define PERF_VAL(ix) \
116    perf_offsets[ix] ? \
117        *((DWORD *)((BYTE *)counter_block + perf_offsets[ix])) : 0
118
119#define PERF_VAL64(ix) \
120    perf_offsets[ix] ? \
121        *((sigar_uint64_t *)((BYTE *)counter_block + perf_offsets[ix])) : 0
122
123/* 1/100ns units to milliseconds */
124#define NS100_2MSEC(t) ((t) / 10000)
125
126#define PERF_VAL_CPU(ix) \
127    NS100_2MSEC(PERF_VAL(ix))
128
129#define MS_LOOPBACK_ADAPTER "Microsoft Loopback Adapter"
130#define NETIF_LA "la"
131
132sigar_uint64_t sigar_FileTimeToTime(FILETIME *ft)
133{
134    sigar_uint64_t time;
135    time = ft->dwHighDateTime;
136    time = time << 32;
137    time |= ft->dwLowDateTime;
138    time /= 10;
139    time -= EPOCH_DELTA;
140    return time;
141}
142
143static DWORD perfbuf_init(sigar_t *sigar)
144{
145    if (!sigar->perfbuf) {
146        sigar->perfbuf = malloc(PERFBUF_SIZE);
147        sigar->perfbuf_size = PERFBUF_SIZE;
148    }
149
150    return sigar->perfbuf_size;
151}
152
153static DWORD perfbuf_grow(sigar_t *sigar)
154{
155    sigar->perfbuf_size += PERFBUF_SIZE;
156
157    sigar->perfbuf =
158        realloc(sigar->perfbuf, sigar->perfbuf_size);
159
160    return sigar->perfbuf_size;
161}
162
163static char *get_counter_name(char *key)
164{
165    if (strEQ(key, PERF_TITLE_MEM_KEY)) {
166        return "Memory";
167    }
168    else if (strEQ(key, PERF_TITLE_PROC_KEY)) {
169        return "Process";
170    }
171    else if (strEQ(key, PERF_TITLE_CPU_KEY)) {
172        return "Processor";
173    }
174    else if (strEQ(key, PERF_TITLE_DISK_KEY)) {
175        return "LogicalDisk";
176    }
177    else {
178        return key;
179    }
180}
181
182static PERF_OBJECT_TYPE *get_perf_object_inst(sigar_t *sigar,
183                                              char *counter_key,
184                                              DWORD inst, DWORD *err)
185{
186    DWORD retval, type, bytes;
187    WCHAR wcounter_key[MAX_PATH+1];
188    PERF_DATA_BLOCK *block;
189    PERF_OBJECT_TYPE *object;
190
191    *err = SIGAR_OK;
192
193    if (USING_WIDE()) {
194        SIGAR_A2W(counter_key, wcounter_key, sizeof(wcounter_key));
195    }
196
197    bytes = perfbuf_init(sigar);
198
199    while ((retval = MyRegQueryValue()) != ERROR_SUCCESS) {
200        if (retval == ERROR_MORE_DATA) {
201            bytes = perfbuf_grow(sigar);
202        }
203        else {
204            *err = retval;
205            return NULL;
206        }
207    }
208
209    block = (PERF_DATA_BLOCK *)sigar->perfbuf;
210    if (block->NumObjectTypes == 0) {
211        counter_key = get_counter_name(counter_key);
212        sigar_strerror_printf(sigar, "No %s counters defined (disabled?)",
213                              counter_key);
214        *err = -1;
215        return NULL;
216    }
217    object = PdhFirstObject(block);
218
219    /*
220     * only seen on windows 2003 server when pdh.dll
221     * functions are in use by the same process.
222     * confucius say what the fuck.
223     */
224    if (inst && (object->NumInstances == PERF_NO_INSTANCES)) {
225        int i;
226
227        for (i=0; i<block->NumObjectTypes; i++) {
228            if (object->NumInstances != PERF_NO_INSTANCES) {
229                return object;
230            }
231            object = PdhNextObject(object);
232        }
233        return NULL;
234    }
235    else {
236        return object;
237    }
238}
239
240#define get_perf_object(sigar, counter_key, err) \
241    get_perf_object_inst(sigar, counter_key, 1, err)
242
243static int get_mem_counters(sigar_t *sigar, sigar_swap_t *swap, sigar_mem_t *mem)
244{
245    DWORD status;
246    PERF_OBJECT_TYPE *object =
247        get_perf_object_inst(sigar, PERF_TITLE_MEM_KEY, 0, &status);
248    PERF_COUNTER_DEFINITION *counter;
249    BYTE *data;
250    DWORD i;
251
252    if (!object) {
253        return status;
254    }
255
256    data = (BYTE *)((BYTE *)object + object->DefinitionLength);
257
258    for (i=0, counter = PdhFirstCounter(object);
259         i<object->NumCounters;
260         i++, counter = PdhNextCounter(counter))
261    {
262        DWORD offset = counter->CounterOffset;
263
264        switch (counter->CounterNameTitleIndex) {
265          case 48: /* "Pages Output/sec" */
266            if (swap) swap->page_out = *((DWORD *)(data + offset));
267            break;
268          case 76: /* "System Cache Resident Bytes" aka file cache */
269            if (mem) {
270                sigar_uint64_t kern = *((DWORD *)(data + offset));
271                mem->actual_free = mem->free + kern;
272                mem->actual_used = mem->used - kern;
273                return SIGAR_OK;
274            }
275          case 822: /* "Pages Input/sec" */
276            if (swap) swap->page_in = *((DWORD *)(data + offset));
277            break;
278          default:
279            continue;
280        }
281    }
282
283    return SIGAR_OK;
284}
285
286static void get_sysinfo(sigar_t *sigar)
287{
288    SYSTEM_INFO sysinfo;
289
290    GetSystemInfo(&sysinfo);
291
292    sigar->ncpu = sysinfo.dwNumberOfProcessors;
293    sigar->pagesize = sysinfo.dwPageSize;
294}
295
296/* for C# bindings */
297SIGAR_DECLARE(sigar_t *) sigar_new(void)
298{
299    sigar_t *sigar;
300    if (sigar_open(&sigar) != SIGAR_OK) {
301        return NULL;
302    }
303    return sigar;
304}
305
306static sigar_wtsapi_t sigar_wtsapi = {
307    "wtsapi32.dll",
308    NULL,
309    { "WTSEnumerateSessionsA", NULL },
310    { "WTSFreeMemory", NULL },
311    { "WTSQuerySessionInformationA", NULL },
312    { NULL, NULL }
313};
314
315static sigar_iphlpapi_t sigar_iphlpapi = {
316    "iphlpapi.dll",
317    NULL,
318    { "GetIpForwardTable", NULL },
319    { "GetIpAddrTable", NULL },
320    { "GetIfTable", NULL },
321    { "GetIfEntry", NULL },
322    { "GetNumberOfInterfaces", NULL },
323    { "GetTcpTable", NULL },
324    { "GetUdpTable", NULL },
325    { "AllocateAndGetTcpExTableFromStack", NULL },
326    { "AllocateAndGetUdpExTableFromStack", NULL },
327    { "GetTcpStatistics", NULL },
328    { "GetNetworkParams", NULL },
329    { "GetAdaptersInfo", NULL },
330    { "GetAdaptersAddresses", NULL },
331    { "GetIpNetTable", NULL },
332    { NULL, NULL }
333};
334
335static sigar_advapi_t sigar_advapi = {
336    "advapi32.dll",
337    NULL,
338    { "ConvertStringSidToSidA", NULL },
339    { "QueryServiceStatusEx", NULL },
340    { NULL, NULL }
341};
342
343static sigar_ntdll_t sigar_ntdll = {
344    "ntdll.dll",
345    NULL,
346    { "NtQuerySystemInformation", NULL },
347    { "NtQueryInformationProcess", NULL },
348    { NULL, NULL }
349};
350
351static sigar_psapi_t sigar_psapi = {
352    "psapi.dll",
353    NULL,
354    { "EnumProcessModules", NULL },
355    { "EnumProcesses", NULL },
356    { "GetModuleFileNameExA", NULL },
357    { NULL, NULL }
358};
359
360static sigar_psapi_t sigar_winsta = {
361    "winsta.dll",
362    NULL,
363    { "WinStationQueryInformationW", NULL },
364    { NULL, NULL }
365};
366
367static sigar_psapi_t sigar_kernel = {
368    "kernel32.dll",
369    NULL,
370    { "GlobalMemoryStatusEx", NULL },
371    { NULL, NULL }
372};
373
374static sigar_mpr_t sigar_mpr = {
375    "mpr.dll",
376    NULL,
377    { "WNetGetConnectionA", NULL },
378    { NULL, NULL }
379};
380
381#define DLLMOD_COPY(name) \
382    memcpy(&(sigar->name), &sigar_##name, sizeof(sigar_##name))
383
384#define DLLMOD_INIT(name, all) \
385    sigar_dllmod_init(sigar, (sigar_dll_module_t *)&(sigar->name), all)
386
387#define DLLMOD_FREE(name) \
388    sigar_dllmod_free((sigar_dll_module_t *)&(sigar->name))
389
390static void sigar_dllmod_free(sigar_dll_module_t *module)
391{
392    if (module->handle) {
393        FreeLibrary(module->handle);
394        module->handle = NULL;
395    }
396}
397
398static int sigar_dllmod_init(sigar_t *sigar,
399                             sigar_dll_module_t *module,
400                             int all)
401{
402    sigar_dll_func_t *funcs = &module->funcs[0];
403    int is_debug = SIGAR_LOG_IS_DEBUG(sigar);
404    int rc, success;
405
406    if (module->handle == INVALID_HANDLE_VALUE) {
407        return ENOENT; /* XXX better rc */
408    }
409
410    if (module->handle) {
411        return SIGAR_OK;
412    }
413
414    module->handle = LoadLibrary(module->name);
415    if (!(success = (module->handle ? TRUE : FALSE))) {
416        rc = GetLastError();
417        /* dont try again */
418        module->handle = INVALID_HANDLE_VALUE;
419    }
420
421    if (is_debug) {
422        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
423                         "LoadLibrary(%s): %s",
424                         module->name,
425                         success ?
426                         "OK" :
427                         sigar_strerror(sigar, rc));
428    }
429
430    if (!success) {
431        return rc;
432    }
433
434    while (funcs->name) {
435        funcs->func = GetProcAddress(module->handle, funcs->name);
436
437        if (!(success = (funcs->func ? TRUE : FALSE))) {
438            rc = GetLastError();
439        }
440
441        if (is_debug) {
442            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
443                             "GetProcAddress(%s:%s): %s",
444                             module->name, funcs->name,
445                             success ?
446                             "OK" :
447                             sigar_strerror(sigar, rc));
448        }
449
450        if (all && !success) {
451            return rc;
452        }
453
454        funcs++;
455    }
456
457    return SIGAR_OK;
458}
459
460int sigar_wsa_init(sigar_t *sigar)
461{
462    if (sigar->ws_version == 0) {
463        WSADATA data;
464
465        if (WSAStartup(MAKEWORD(2, 0), &data)) {
466            sigar->ws_error = WSAGetLastError();
467            WSACleanup();
468            return sigar->ws_error;
469        }
470
471        sigar->ws_version = data.wVersion;
472    }
473
474    return SIGAR_OK;
475}
476
477static int sigar_enable_privilege(char *name)
478{
479    int status;
480    HANDLE handle;
481    TOKEN_PRIVILEGES tok;
482
483    SIGAR_ZERO(&tok);
484
485    if (!OpenProcessToken(GetCurrentProcess(),
486                          TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,
487                          &handle))
488    {
489        return GetLastError();
490    }
491
492    if (LookupPrivilegeValue(NULL, name,
493                             &tok.Privileges[0].Luid))
494    {
495        tok.PrivilegeCount = 1;
496        tok.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
497
498        if (AdjustTokenPrivileges(handle, FALSE, &tok, 0, NULL, 0)) {
499            status = SIGAR_OK;
500        }
501        else {
502            status = GetLastError();
503        }
504    }
505    else {
506        status = GetLastError();
507    }
508
509    CloseHandle(handle);
510
511    return status;
512}
513
514int sigar_os_open(sigar_t **sigar_ptr)
515{
516    LONG result;
517    OSVERSIONINFO version;
518    sigar_t *sigar;
519
520    *sigar_ptr = sigar = malloc(sizeof(*sigar));
521    sigar->machine = ""; /* local machine */
522    sigar->using_wide = 0; /*XXX*/
523
524    sigar->perfbuf = NULL;
525    sigar->perfbuf_size = 0;
526
527    version.dwOSVersionInfoSize = sizeof(version);
528    GetVersionEx(&version);
529
530    /*
531     * 4 == NT 4.0
532     * 5 == 2000, XP, 2003 Server
533     */
534    sigar->winnt = (version.dwMajorVersion == 4);
535
536    if (USING_WIDE_S(sigar)) {
537        WCHAR wmachine[MAX_PATH+1];
538
539        SIGAR_A2W(sigar->machine, wmachine, sizeof(wmachine));
540
541        result = RegConnectRegistryW(wmachine,
542                                     HKEY_PERFORMANCE_DATA,
543                                     &sigar->handle);
544    }
545    else {
546        result = RegConnectRegistryA(sigar->machine,
547                                     HKEY_PERFORMANCE_DATA,
548                                     &sigar->handle);
549    }
550
551    get_sysinfo(sigar);
552
553    DLLMOD_COPY(wtsapi);
554    DLLMOD_COPY(iphlpapi);
555    DLLMOD_COPY(advapi);
556    DLLMOD_COPY(ntdll);
557    DLLMOD_COPY(psapi);
558    DLLMOD_COPY(winsta);
559    DLLMOD_COPY(kernel);
560    DLLMOD_COPY(mpr);
561
562    sigar->log_level = -1; /* else below segfaults */
563    /* XXX init early for use by javasigar.c */
564    sigar_dllmod_init(sigar,
565                      (sigar_dll_module_t *)&sigar->advapi,
566                      FALSE);
567
568    sigar->netif_mib_rows = NULL;
569    sigar->netif_addr_rows = NULL;
570    sigar->netif_adapters = NULL;
571    sigar->netif_names = NULL;
572    sigar->pinfo.pid = -1;
573    sigar->ws_version = 0;
574    sigar->lcpu = -1;
575
576    /* increase process visibility */
577    sigar_enable_privilege(SE_DEBUG_NAME);
578
579    return result;
580}
581
582void dllmod_init_ntdll(sigar_t *sigar)
583{
584    DLLMOD_INIT(ntdll, FALSE);
585}
586
587int sigar_os_close(sigar_t *sigar)
588{
589    int retval;
590
591    DLLMOD_FREE(wtsapi);
592    DLLMOD_FREE(iphlpapi);
593    DLLMOD_FREE(advapi);
594    DLLMOD_FREE(ntdll);
595    DLLMOD_FREE(psapi);
596    DLLMOD_FREE(winsta);
597    DLLMOD_FREE(kernel);
598    DLLMOD_FREE(mpr);
599
600    if (sigar->perfbuf) {
601        free(sigar->perfbuf);
602    }
603
604    retval = RegCloseKey(sigar->handle);
605
606    if (sigar->ws_version != 0) {
607        WSACleanup();
608    }
609
610    if (sigar->netif_mib_rows) {
611        sigar_cache_destroy(sigar->netif_mib_rows);
612    }
613
614    if (sigar->netif_addr_rows) {
615        sigar_cache_destroy(sigar->netif_addr_rows);
616    }
617
618    if (sigar->netif_adapters) {
619        sigar_cache_destroy(sigar->netif_adapters);
620    }
621
622    if (sigar->netif_names) {
623        sigar_cache_destroy(sigar->netif_names);
624    }
625
626    free(sigar);
627
628    return retval;
629}
630
631char *sigar_os_error_string(sigar_t *sigar, int err)
632{
633    switch (err) {
634      case SIGAR_NO_SUCH_PROCESS:
635        return "No such process";
636        break;
637    }
638    return NULL;
639}
640
641#define sigar_GlobalMemoryStatusEx \
642    sigar->kernel.memory_status.func
643
644SIGAR_DECLARE(int) sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem)
645{
646    DLLMOD_INIT(kernel, TRUE);
647
648    if (sigar_GlobalMemoryStatusEx) {
649        MEMORYSTATUSEX memstat;
650
651        memstat.dwLength = sizeof(memstat);
652
653        if (!sigar_GlobalMemoryStatusEx(&memstat)) {
654            return GetLastError();
655        }
656
657        mem->total = memstat.ullTotalPhys;
658        mem->free  = memstat.ullAvailPhys;
659    }
660    else {
661        MEMORYSTATUS memstat;
662        GlobalMemoryStatus(&memstat);
663        mem->total = memstat.dwTotalPhys;
664        mem->free  = memstat.dwAvailPhys;
665    }
666
667    mem->used = mem->total - mem->free;
668
669    mem->actual_free = mem->free;
670    mem->actual_used = mem->used;
671    /* set actual_{free,used} */
672    get_mem_counters(sigar, NULL, mem);
673
674    sigar_mem_calc_ram(sigar, mem);
675
676    return SIGAR_OK;
677}
678
679SIGAR_DECLARE(int) sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap)
680{
681    DLLMOD_INIT(kernel, TRUE);
682
683    if (sigar_GlobalMemoryStatusEx) {
684        MEMORYSTATUSEX memstat;
685
686        memstat.dwLength = sizeof(memstat);
687
688        if (!sigar_GlobalMemoryStatusEx(&memstat)) {
689            return GetLastError();
690        }
691
692        swap->total = memstat.ullTotalPageFile;
693        swap->free  = memstat.ullAvailPageFile;
694    }
695    else {
696        MEMORYSTATUS memstat;
697        GlobalMemoryStatus(&memstat);
698        swap->total = memstat.dwTotalPageFile;
699        swap->free  = memstat.dwAvailPageFile;
700    }
701
702    swap->used = swap->total - swap->free;
703
704    if (get_mem_counters(sigar, swap, NULL) != SIGAR_OK) {
705        swap->page_in = SIGAR_FIELD_NOTIMPL;
706        swap->page_out = SIGAR_FIELD_NOTIMPL;
707    }
708
709    return SIGAR_OK;
710}
711
712static PERF_INSTANCE_DEFINITION *get_cpu_instance(sigar_t *sigar,
713                                                  DWORD *perf_offsets,
714                                                  DWORD *num, DWORD *err)
715{
716    PERF_OBJECT_TYPE *object = get_perf_object(sigar, PERF_TITLE_CPU_KEY, err);
717    PERF_COUNTER_DEFINITION *counter;
718    DWORD i;
719
720    if (!object) {
721        return NULL;
722    }
723
724    for (i=0, counter = PdhFirstCounter(object);
725         i<object->NumCounters;
726         i++, counter = PdhNextCounter(counter))
727    {
728        DWORD offset = counter->CounterOffset;
729
730        switch (counter->CounterNameTitleIndex) {
731          case PERF_TITLE_CPU_SYS:
732            perf_offsets[PERF_IX_CPU_SYS] = offset;
733            break;
734          case PERF_TITLE_CPU_USER:
735            perf_offsets[PERF_IX_CPU_USER] = offset;
736            break;
737          case PERF_TITLE_CPU_IDLE:
738            perf_offsets[PERF_IX_CPU_IDLE] = offset;
739            break;
740          case PERF_TITLE_CPU_IRQ:
741            perf_offsets[PERF_IX_CPU_IRQ] = offset;
742            break;
743        }
744    }
745
746    if (num) {
747        *num = object->NumInstances;
748    }
749
750    return PdhFirstInstance(object);
751}
752
753#define SPPI_MAX 128 /* XXX unhardcode; should move off this api anyhow */
754
755#define sigar_NtQuerySystemInformation \
756   sigar->ntdll.query_sys_info.func
757
758static int get_idle_cpu(sigar_t *sigar, sigar_cpu_t *cpu,
759                        DWORD idx,
760                        PERF_COUNTER_BLOCK *counter_block,
761                        DWORD *perf_offsets)
762{
763    cpu->idle = 0;
764
765    if (perf_offsets[PERF_IX_CPU_IDLE]) {
766        cpu->idle = PERF_VAL_CPU(PERF_IX_CPU_IDLE);
767    }
768    else {
769        /* windows NT and 2000 do not have an Idle counter */
770        DLLMOD_INIT(ntdll, FALSE);
771        if (sigar_NtQuerySystemInformation) {
772            DWORD retval, num;
773            SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[SPPI_MAX];
774            /* into the lungs of hell */
775            sigar_NtQuerySystemInformation(SystemProcessorPerformanceInformation,
776                                           &info, sizeof(info), &retval);
777
778            if (!retval) {
779                return GetLastError();
780            }
781            num = retval/sizeof(info[0]);
782
783            if (idx == -1) {
784                int i;
785                for (i=0; i<num; i++) {
786                    cpu->idle += NS100_2MSEC(info[i].IdleTime.QuadPart);
787                }
788            }
789            else if (idx < num) {
790                cpu->idle = NS100_2MSEC(info[idx].IdleTime.QuadPart);
791            }
792            else {
793                return ERROR_INVALID_DATA;
794            }
795        }
796        else {
797            return ERROR_INVALID_FUNCTION;
798        }
799    }
800
801    return SIGAR_OK;
802}
803
804static int sigar_cpu_perflib_get(sigar_t *sigar, sigar_cpu_t *cpu)
805{
806    int status;
807    PERF_INSTANCE_DEFINITION *inst;
808    PERF_COUNTER_BLOCK *counter_block;
809    DWORD perf_offsets[PERF_IX_CPU_MAX], err;
810
811    SIGAR_ZERO(cpu);
812    memset(&perf_offsets, 0, sizeof(perf_offsets));
813
814    inst = get_cpu_instance(sigar, (DWORD*)&perf_offsets, 0, &err);
815
816    if (!inst) {
817        return err;
818    }
819
820    /* first instance is total, rest are per-cpu */
821    counter_block = PdhGetCounterBlock(inst);
822
823    cpu->sys  = PERF_VAL_CPU(PERF_IX_CPU_SYS);
824    cpu->user = PERF_VAL_CPU(PERF_IX_CPU_USER);
825    status = get_idle_cpu(sigar, cpu, -1, counter_block, perf_offsets);
826    cpu->irq = PERF_VAL_CPU(PERF_IX_CPU_IRQ);
827    cpu->nice = 0; /* no nice here */
828    cpu->wait = 0; /*N/A?*/
829    cpu->total = cpu->sys + cpu->user + cpu->idle + cpu->wait + cpu->irq;
830
831    if (status != SIGAR_OK) {
832        sigar_log_printf(sigar, SIGAR_LOG_WARN,
833                         "unable to determine idle cpu time: %s",
834                         sigar_strerror(sigar, status));
835    }
836
837    return SIGAR_OK;
838}
839
840static int sigar_cpu_ntsys_get(sigar_t *sigar, sigar_cpu_t *cpu)
841{
842    DWORD retval, num;
843    int i;
844    SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION info[SPPI_MAX];
845    /* into the lungs of hell */
846    sigar_NtQuerySystemInformation(SystemProcessorPerformanceInformation,
847                                   &info, sizeof(info), &retval);
848
849    if (!retval) {
850        return GetLastError();
851    }
852    num = retval/sizeof(info[0]);
853    SIGAR_ZERO(cpu);
854
855    for (i=0; i<num; i++) {
856        cpu->idle += NS100_2MSEC(info[i].IdleTime.QuadPart);
857        cpu->user += NS100_2MSEC(info[i].UserTime.QuadPart);
858        cpu->sys  += NS100_2MSEC(info[i].KernelTime.QuadPart -
859                                 info[i].IdleTime.QuadPart);
860        cpu->irq  += NS100_2MSEC(info[i].InterruptTime.QuadPart);
861    }
862    cpu->total = cpu->idle + cpu->user + cpu->sys;
863
864    return SIGAR_OK;
865}
866
867SIGAR_DECLARE(int) sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu)
868{
869    DLLMOD_INIT(ntdll, FALSE);
870    if (sigar_NtQuerySystemInformation) {
871        return sigar_cpu_ntsys_get(sigar, cpu);
872    }
873    else {
874        return sigar_cpu_perflib_get(sigar, cpu);
875    }
876}
877
878
879#define PERF_TITLE_UPTIME_KEY 674 /* System Up Time */
880
881
882#define get_process_object(sigar, err) \
883    get_perf_object(sigar, PERF_TITLE_PROC_KEY, err)
884
885static int sigar_proc_list_get_perf(sigar_t *sigar,
886                                    sigar_proc_list_t *proclist)
887{
888
889    PERF_OBJECT_TYPE *object;
890    PERF_INSTANCE_DEFINITION *inst;
891    PERF_COUNTER_DEFINITION *counter;
892    DWORD i, err;
893    DWORD perf_offsets[PERF_IX_MAX];
894
895    perf_offsets[PERF_IX_PID] = 0;
896
897    object = get_process_object(sigar, &err);
898
899    if (!object) {
900        return err;
901    }
902
903    /*
904     * note we assume here:
905     *  block->NumObjectTypes == 1
906     *  object->ObjectNameTitleIndex == PERF_TITLE_PROC
907     *
908     * which should always be the case.
909     */
910
911    for (i=0, counter = PdhFirstCounter(object);
912         i<object->NumCounters;
913         i++, counter = PdhNextCounter(counter))
914    {
915        DWORD offset = counter->CounterOffset;
916
917        switch (counter->CounterNameTitleIndex) {
918          case PERF_TITLE_PID:
919            perf_offsets[PERF_IX_PID] = offset;
920            break;
921        }
922    }
923
924    for (i=0, inst = PdhFirstInstance(object);
925         i<object->NumInstances;
926         i++, inst = PdhNextInstance(inst))
927    {
928        PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst);
929        DWORD pid = PERF_VAL(PERF_IX_PID);
930
931        if (pid == 0) {
932            continue; /* dont include the system Idle process */
933        }
934
935        SIGAR_PROC_LIST_GROW(proclist);
936
937        proclist->data[proclist->number++] = pid;
938    }
939
940    return SIGAR_OK;
941}
942
943#define sigar_EnumProcesses \
944    sigar->psapi.enum_processes.func
945
946int sigar_os_proc_list_get(sigar_t *sigar,
947                           sigar_proc_list_t *proclist)
948{
949    DLLMOD_INIT(psapi, FALSE);
950
951    if (sigar_EnumProcesses) {
952        DWORD retval, *pids;
953        DWORD size = 0, i;
954
955        do {
956            /* re-use the perfbuf */
957            if (size == 0) {
958                size = perfbuf_init(sigar);
959            }
960            else {
961                size = perfbuf_grow(sigar);
962            }
963
964            if (!sigar_EnumProcesses((DWORD *)sigar->perfbuf,
965                                     sigar->perfbuf_size,
966                                     &retval))
967            {
968                return GetLastError();
969            }
970        } while (retval == sigar->perfbuf_size); //unlikely
971
972        pids = (DWORD *)sigar->perfbuf;
973
974        size = retval / sizeof(DWORD);
975
976        for (i=0; i<size; i++) {
977            DWORD pid = pids[i];
978            if (pid == 0) {
979                continue; /* dont include the system Idle process */
980            }
981            SIGAR_PROC_LIST_GROW(proclist);
982            proclist->data[proclist->number++] = pid;
983        }
984
985        return SIGAR_OK;
986    }
987    else {
988        return sigar_proc_list_get_perf(sigar, proclist);
989    }
990}
991
992#define PROCESS_DAC (PROCESS_QUERY_INFORMATION|PROCESS_VM_READ)
993
994static HANDLE open_process(sigar_pid_t pid)
995{
996    return OpenProcess(PROCESS_DAC, 0, (DWORD)pid);
997}
998
999/*
1000 * Pretty good explanation of counters:
1001 * http://www.semack.net/wiki/default.asp?db=SemackNetWiki&o=VirtualMemory
1002 */
1003SIGAR_DECLARE(int) sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
1004                                      sigar_proc_mem_t *procmem)
1005{
1006    int status = get_proc_info(sigar, pid);
1007    sigar_win32_pinfo_t *pinfo = &sigar->pinfo;
1008
1009    if (status != SIGAR_OK) {
1010        return status;
1011    }
1012
1013    procmem->size     = pinfo->size;     /* "Virtual Bytes" */
1014    procmem->resident = pinfo->resident; /* "Working Set" */
1015    procmem->share    = SIGAR_FIELD_NOTIMPL;
1016    procmem->page_faults  = pinfo->page_faults;
1017    procmem->minor_faults = SIGAR_FIELD_NOTIMPL;
1018    procmem->major_faults = SIGAR_FIELD_NOTIMPL;
1019
1020    return SIGAR_OK;
1021}
1022
1023#define TOKEN_DAC (STANDARD_RIGHTS_READ | READ_CONTROL | TOKEN_QUERY)
1024
1025#define FILETIME2MSEC(ft) \
1026        NS100_2MSEC((((long long)ft.dwHighDateTime << 32) | ft.dwLowDateTime))
1027
1028sigar_int64_t sigar_time_now_millis(void)
1029{
1030    SYSTEMTIME st;
1031    FILETIME time;
1032
1033    GetSystemTime(&st);
1034    SystemTimeToFileTime(&st, &time);
1035
1036    return sigar_FileTimeToTime(&time) / 1000;
1037}
1038
1039int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid,
1040                        sigar_proc_time_t *proctime)
1041{
1042    HANDLE proc = open_process(pid);
1043    FILETIME start_time, exit_time, system_time, user_time;
1044    int status = ERROR_SUCCESS;
1045
1046    if (!proc) {
1047        return GetLastError();
1048    }
1049
1050    if (!GetProcessTimes(proc,
1051                         &start_time, &exit_time,
1052                         &system_time, &user_time))
1053    {
1054        status = GetLastError();
1055    }
1056
1057    CloseHandle(proc);
1058
1059    if (status != ERROR_SUCCESS) {
1060        return status;
1061    }
1062
1063    if (start_time.dwHighDateTime) {
1064        proctime->start_time =
1065            sigar_FileTimeToTime(&start_time) / 1000;
1066    }
1067    else {
1068        proctime->start_time = 0;
1069    }
1070
1071    proctime->user = FILETIME2MSEC(user_time);
1072    proctime->sys  = FILETIME2MSEC(system_time);
1073    proctime->total = proctime->user + proctime->sys;
1074
1075    return SIGAR_OK;
1076}
1077
1078SIGAR_DECLARE(int) sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid,
1079                                        sigar_proc_state_t *procstate)
1080{
1081    int status = get_proc_info(sigar, pid);
1082    sigar_win32_pinfo_t *pinfo = &sigar->pinfo;
1083
1084    if (status != SIGAR_OK) {
1085        return status;
1086    }
1087
1088    memcpy(procstate->name, pinfo->name, sizeof(procstate->name));
1089    procstate->state = pinfo->state;
1090    procstate->ppid = pinfo->ppid;
1091    procstate->priority = pinfo->priority;
1092    procstate->nice = SIGAR_FIELD_NOTIMPL;
1093    procstate->tty =  SIGAR_FIELD_NOTIMPL;
1094    procstate->threads = pinfo->threads;
1095    procstate->processor = SIGAR_FIELD_NOTIMPL;
1096
1097    return SIGAR_OK;
1098}
1099
1100int get_proc_info(sigar_t *sigar, sigar_pid_t pid)
1101{
1102    PERF_OBJECT_TYPE *object;
1103    PERF_INSTANCE_DEFINITION *inst;
1104    PERF_COUNTER_DEFINITION *counter;
1105    DWORD i, err;
1106    DWORD perf_offsets[PERF_IX_MAX];
1107    sigar_win32_pinfo_t *pinfo = &sigar->pinfo;
1108    time_t timenow = time(NULL);
1109
1110    if (pinfo->pid == pid) {
1111        if ((timenow - pinfo->mtime) < SIGAR_LAST_PROC_EXPIRE) {
1112            return SIGAR_OK;
1113        }
1114    }
1115
1116    memset(&perf_offsets, 0, sizeof(perf_offsets));
1117
1118    object = get_process_object(sigar, &err);
1119
1120    if (object == NULL) {
1121        return err;
1122    }
1123
1124    pinfo->pid = pid;
1125    pinfo->mtime = timenow;
1126
1127    /*
1128     * note we assume here:
1129     *  block->NumObjectTypes == 1
1130     *  object->ObjectNameTitleIndex == PERF_TITLE_PROC
1131     *
1132     * which should always be the case.
1133     */
1134
1135    for (i=0, counter = PdhFirstCounter(object);
1136         i<object->NumCounters;
1137         i++, counter = PdhNextCounter(counter))
1138    {
1139        DWORD offset = counter->CounterOffset;
1140
1141        switch (counter->CounterNameTitleIndex) {
1142          case PERF_TITLE_CPUTIME:
1143            perf_offsets[PERF_IX_CPUTIME] = offset;
1144            break;
1145          case PERF_TITLE_PAGE_FAULTS:
1146            perf_offsets[PERF_IX_PAGE_FAULTS] = offset;
1147            break;
1148          case PERF_TITLE_MEM_VSIZE:
1149            assert(counter->CounterSize >= 8);
1150            perf_offsets[PERF_IX_MEM_VSIZE] = offset;
1151            break;
1152          case PERF_TITLE_MEM_SIZE:
1153            assert(counter->CounterSize >= 8);
1154            perf_offsets[PERF_IX_MEM_SIZE] = offset;
1155            break;
1156          case PERF_TITLE_THREAD_CNT:
1157            perf_offsets[PERF_IX_THREAD_CNT] = offset;
1158            break;
1159          case PERF_TITLE_HANDLE_CNT:
1160            perf_offsets[PERF_IX_HANDLE_CNT] = offset;
1161            break;
1162          case PERF_TITLE_PID:
1163            perf_offsets[PERF_IX_PID] = offset;
1164            break;
1165          case PERF_TITLE_PPID:
1166            perf_offsets[PERF_IX_PPID] = offset;
1167            break;
1168          case PERF_TITLE_PRIORITY:
1169            perf_offsets[PERF_IX_PRIORITY] = offset;
1170            break;
1171          case PERF_TITLE_START_TIME:
1172            perf_offsets[PERF_IX_START_TIME] = offset;
1173            break;
1174        }
1175    }
1176
1177    for (i=0, inst = PdhFirstInstance(object);
1178         i<object->NumInstances;
1179         i++, inst = PdhNextInstance(inst))
1180    {
1181        PERF_COUNTER_BLOCK *counter_block = PdhGetCounterBlock(inst);
1182        sigar_pid_t this_pid = PERF_VAL(PERF_IX_PID);
1183
1184        if (this_pid != pid) {
1185            continue;
1186        }
1187
1188        pinfo->state = 'R'; /* XXX? */
1189        SIGAR_W2A(PdhInstanceName(inst),
1190                  pinfo->name, sizeof(pinfo->name));
1191
1192        pinfo->size     = PERF_VAL64(PERF_IX_MEM_VSIZE);
1193        pinfo->resident = PERF_VAL64(PERF_IX_MEM_SIZE);
1194        pinfo->ppid     = PERF_VAL(PERF_IX_PPID);
1195        pinfo->priority = PERF_VAL(PERF_IX_PRIORITY);
1196        pinfo->handles  = PERF_VAL(PERF_IX_HANDLE_CNT);
1197        pinfo->threads  = PERF_VAL(PERF_IX_THREAD_CNT);
1198        pinfo->page_faults = PERF_VAL(PERF_IX_PAGE_FAULTS);
1199
1200        return SIGAR_OK;
1201    }
1202
1203    return SIGAR_NO_SUCH_PROCESS;
1204}
1205
1206int sigar_os_fs_type_get(sigar_file_system_t *fsp)
1207{
1208    return fsp->type;
1209}
1210
1211#ifndef FILE_READ_ONLY_VOLUME
1212#define FILE_READ_ONLY_VOLUME 0x00080000
1213#endif
1214#ifndef FILE_NAMED_STREAMS
1215#define FILE_NAMED_STREAMS 0x00040000
1216#endif
1217#ifndef FILE_SEQUENTIAL_WRITE_ONCE
1218#define FILE_SEQUENTIAL_WRITE_ONCE 0x00100000
1219#endif
1220#ifndef FILE_SUPPORTS_TRANSACTIONS
1221#define FILE_SUPPORTS_TRANSACTIONS 0x00200000
1222#endif
1223
1224static void get_fs_options(char *opts, int osize, long flags)
1225{
1226    *opts = '\0';
1227    if (flags & FILE_READ_ONLY_VOLUME)        strncat(opts, "ro", osize);
1228    else                                      strncat(opts, "rw", osize);
1229#if 0 /*XXX*/
1230    if (flags & FILE_CASE_PRESERVED_NAMES)    strncat(opts, ",casepn", osize);
1231    if (flags & FILE_CASE_SENSITIVE_SEARCH)   strncat(opts, ",casess", osize);
1232    if (flags & FILE_FILE_COMPRESSION)        strncat(opts, ",fcomp", osize);
1233    if (flags & FILE_NAMED_STREAMS)           strncat(opts, ",streams", osize);
1234    if (flags & FILE_PERSISTENT_ACLS)         strncat(opts, ",acls", osize);
1235    if (flags & FILE_SEQUENTIAL_WRITE_ONCE)   strncat(opts, ",wronce", osize);
1236    if (flags & FILE_SUPPORTS_ENCRYPTION)     strncat(opts, ",efs", osize);
1237    if (flags & FILE_SUPPORTS_OBJECT_IDS)     strncat(opts, ",oids", osize);
1238    if (flags & FILE_SUPPORTS_REPARSE_POINTS) strncat(opts, ",reparse", osize);
1239    if (flags & FILE_SUPPORTS_SPARSE_FILES)   strncat(opts, ",sparse", osize);
1240    if (flags & FILE_SUPPORTS_TRANSACTIONS)   strncat(opts, ",trans", osize);
1241    if (flags & FILE_UNICODE_ON_DISK)         strncat(opts, ",unicode", osize);
1242    if (flags & FILE_VOLUME_IS_COMPRESSED)    strncat(opts, ",vcomp", osize);
1243    if (flags & FILE_VOLUME_QUOTAS)           strncat(opts, ",quota", osize);
1244#endif
1245}
1246
1247#define sigar_WNetGetConnection \
1248    sigar->mpr.get_net_connection.func
1249
1250SIGAR_DECLARE(int) sigar_file_system_list_get(sigar_t *sigar,
1251                                              sigar_file_system_list_t *fslist)
1252{
1253    char name[256];
1254    char *ptr = name;
1255    /* XXX: hmm, Find{First,Next}Volume not available in my sdk */
1256    DWORD len = GetLogicalDriveStringsA(sizeof(name), name);
1257
1258    DLLMOD_INIT(mpr, TRUE);
1259
1260    if (len == 0) {
1261        return GetLastError();
1262    }
1263
1264    sigar_file_system_list_create(fslist);
1265
1266    while (*ptr) {
1267        sigar_file_system_t *fsp;
1268        DWORD flags, serialnum=0;
1269        char fsname[1024];
1270        UINT drive_type = GetDriveType(ptr);
1271        int type;
1272
1273        switch (drive_type) {
1274          case DRIVE_FIXED:
1275            type = SIGAR_FSTYPE_LOCAL_DISK;
1276            break;
1277          case DRIVE_REMOTE:
1278            type = SIGAR_FSTYPE_NETWORK;
1279            break;
1280          case DRIVE_CDROM:
1281            type = SIGAR_FSTYPE_CDROM;
1282            break;
1283          case DRIVE_RAMDISK:
1284            type = SIGAR_FSTYPE_RAM_DISK;
1285            break;
1286          case DRIVE_REMOVABLE:
1287            /* skip floppy, usb, etc. drives */
1288            ptr += strlen(ptr)+1;
1289            continue;
1290          default:
1291            type = SIGAR_FSTYPE_NONE;
1292            break;
1293        }
1294
1295        fsname[0] = '\0';
1296
1297        GetVolumeInformation(ptr, NULL, 0, &serialnum, NULL,
1298                             &flags, fsname, sizeof(fsname));
1299
1300        if (!serialnum && (drive_type == DRIVE_FIXED)) {
1301            ptr += strlen(ptr)+1;
1302            continue; /* ignore unformatted partitions */
1303        }
1304
1305        SIGAR_FILE_SYSTEM_LIST_GROW(fslist);
1306
1307        fsp = &fslist->data[fslist->number++];
1308
1309        fsp->type = type;
1310        SIGAR_SSTRCPY(fsp->dir_name, ptr);
1311        SIGAR_SSTRCPY(fsp->dev_name, ptr);
1312
1313        if ((drive_type == DRIVE_REMOTE) && sigar_WNetGetConnection) {
1314            DWORD len = sizeof(fsp->dev_name);
1315            char drive[3] = {'\0', ':', '\0'}; /* e.g. "X:" w/o trailing "\" */
1316            drive[0] = fsp->dir_name[0];
1317            sigar_WNetGetConnection(drive, fsp->dev_name, &len);
1318            /* ignoring failure, leaving dev_name as dir_name */
1319        }
1320
1321        /* we set fsp->type, just looking up sigar.c:fstype_names[type] */
1322        sigar_fs_type_get(fsp);
1323
1324        if (*fsname == '\0') {
1325            SIGAR_SSTRCPY(fsp->sys_type_name, fsp->type_name);
1326        }
1327        else {
1328            SIGAR_SSTRCPY(fsp->sys_type_name, fsname); /* CDFS, NTFS, etc */
1329        }
1330
1331        get_fs_options(fsp->options, sizeof(fsp->options)-1, flags);
1332
1333        ptr += strlen(ptr)+1;
1334    }
1335
1336    return SIGAR_OK;
1337}
1338
1339
1340
1341#define sigar_GetNetworkParams \
1342    sigar->iphlpapi.get_net_params.func
1343
1344#define sigar_GetAdaptersInfo \
1345    sigar->iphlpapi.get_adapters_info.func
1346
1347#define sigar_GetAdaptersAddresses \
1348    sigar->iphlpapi.get_adapters_addrs.func
1349
1350#define sigar_GetNumberOfInterfaces \
1351    sigar->iphlpapi.get_num_if.func
1352
1353static sigar_cache_t *sigar_netif_cache_new(sigar_t *sigar)
1354{
1355    DWORD num = 0;
1356
1357    DLLMOD_INIT(iphlpapi, FALSE);
1358
1359    if (sigar_GetNumberOfInterfaces) {
1360        DWORD rc = sigar_GetNumberOfInterfaces(&num);
1361
1362        if (rc == NO_ERROR) {
1363            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1364                             "GetNumberOfInterfaces=%d",
1365                             num);
1366        }
1367        else {
1368            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1369                             "GetNumberOfInterfaces failed: %s",
1370                             sigar_strerror(sigar, rc));
1371        }
1372    }
1373
1374    if (num == 0) {
1375        num = 10; /* reasonable default */
1376    }
1377
1378    return sigar_cache_new(num);
1379}
1380
1381static int sigar_get_adapters_info(sigar_t *sigar,
1382                                   PIP_ADAPTER_INFO *adapter)
1383{
1384    ULONG size = sigar->ifconf_len;
1385    DWORD rc;
1386
1387    DLLMOD_INIT(iphlpapi, FALSE);
1388
1389    if (!sigar_GetAdaptersInfo) {
1390        return SIGAR_ENOTIMPL;
1391    }
1392
1393    *adapter = (PIP_ADAPTER_INFO)sigar->ifconf_buf;
1394    rc = sigar_GetAdaptersInfo(*adapter, &size);
1395
1396    if (rc == ERROR_BUFFER_OVERFLOW) {
1397        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1398                         "GetAdaptersInfo "
1399                         "realloc ifconf_buf old=%d, new=%d",
1400                         sigar->ifconf_len, size);
1401        sigar->ifconf_len = size;
1402        sigar->ifconf_buf = realloc(sigar->ifconf_buf,
1403                                    sigar->ifconf_len);
1404
1405        *adapter = (PIP_ADAPTER_INFO)sigar->ifconf_buf;
1406        rc = sigar_GetAdaptersInfo(*adapter, &size);
1407    }
1408
1409    if (rc != NO_ERROR) {
1410        return rc;
1411    }
1412    else {
1413        return SIGAR_OK;
1414    }
1415}
1416
1417
1418static int sigar_get_adapters_addresses(sigar_t *sigar,
1419                                        ULONG family, ULONG flags,
1420                                        PIP_ADAPTER_ADDRESSES *addrs)
1421{
1422    ULONG size = sigar->ifconf_len;
1423    ULONG rc;
1424
1425    DLLMOD_INIT(iphlpapi, FALSE);
1426
1427    if (!sigar_GetAdaptersAddresses) {
1428        return SIGAR_ENOTIMPL;
1429    }
1430
1431    *addrs = (PIP_ADAPTER_ADDRESSES)sigar->ifconf_buf;
1432    rc = sigar_GetAdaptersAddresses(family,
1433                                    flags,
1434                                    NULL,
1435                                    *addrs,
1436                                    &size);
1437
1438    if (rc == ERROR_BUFFER_OVERFLOW) {
1439        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1440                         "GetAdaptersAddresses "
1441                         "realloc ifconf_buf old=%d, new=%d",
1442                         sigar->ifconf_len, size);
1443        sigar->ifconf_len = size;
1444        sigar->ifconf_buf = realloc(sigar->ifconf_buf,
1445                                    sigar->ifconf_len);
1446
1447        *addrs = (PIP_ADAPTER_ADDRESSES)sigar->ifconf_buf;
1448        rc = sigar_GetAdaptersAddresses(family,
1449                                        flags,
1450                                        NULL,
1451                                        *addrs,
1452                                        &size);
1453    }
1454
1455    if (rc != ERROR_SUCCESS) {
1456        return rc;
1457    }
1458    else {
1459        return SIGAR_OK;
1460    }
1461}
1462
1463#define sigar_GetIpAddrTable \
1464    sigar->iphlpapi.get_ipaddr_table.func
1465
1466static int sigar_get_ipaddr_table(sigar_t *sigar,
1467                                  PMIB_IPADDRTABLE *ipaddr)
1468{
1469    ULONG size = sigar->ifconf_len;
1470    DWORD rc;
1471
1472    DLLMOD_INIT(iphlpapi, FALSE);
1473
1474    if (!sigar_GetIpAddrTable) {
1475        return SIGAR_ENOTIMPL;
1476    }
1477
1478    *ipaddr = (PMIB_IPADDRTABLE)sigar->ifconf_buf;
1479    rc = sigar_GetIpAddrTable(*ipaddr, &size, FALSE);
1480
1481    if (rc == ERROR_INSUFFICIENT_BUFFER) {
1482        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1483                         "GetIpAddrTable "
1484                         "realloc ifconf_buf old=%d, new=%d",
1485                         sigar->ifconf_len, size);
1486        sigar->ifconf_len = size;
1487        sigar->ifconf_buf = realloc(sigar->ifconf_buf,
1488                                    sigar->ifconf_len);
1489
1490        *ipaddr = (PMIB_IPADDRTABLE)sigar->ifconf_buf;
1491        rc = sigar_GetIpAddrTable(*ipaddr, &size, FALSE);
1492    }
1493
1494    if (rc != NO_ERROR) {
1495        return rc;
1496    }
1497    else {
1498        return SIGAR_OK;
1499    }
1500}
1501
1502#ifndef MIB_IPADDR_PRIMARY
1503#define MIB_IPADDR_PRIMARY 0x0001
1504#endif
1505
1506static int sigar_get_netif_ipaddr(sigar_t *sigar,
1507                                  DWORD index,
1508                                  MIB_IPADDRROW **ipaddr)
1509{
1510    sigar_cache_entry_t *entry;
1511    *ipaddr = NULL;
1512
1513    if (sigar->netif_addr_rows) {
1514        entry = sigar_cache_get(sigar->netif_addr_rows, index);
1515        if (entry->value) {
1516            *ipaddr = (MIB_IPADDRROW *)entry->value;
1517        }
1518    }
1519    else {
1520        int status, i;
1521        MIB_IPADDRTABLE *mib;
1522
1523        sigar->netif_addr_rows =
1524            sigar_netif_cache_new(sigar);
1525
1526        status = sigar_get_ipaddr_table(sigar, &mib);
1527        if (status != SIGAR_OK) {
1528            return status;
1529        }
1530
1531        for (i=0; i<mib->dwNumEntries; i++) {
1532            MIB_IPADDRROW *row = &mib->table[i];
1533            short type;
1534
1535#ifdef SIGAR_USING_MSC6
1536            type = row->unused2;
1537#else
1538            type = row->wType;
1539#endif
1540            if (!(type & MIB_IPADDR_PRIMARY)) {
1541                continue;
1542            }
1543
1544            entry = sigar_cache_get(sigar->netif_addr_rows,
1545                                    row->dwIndex);
1546            if (!entry->value) {
1547                entry->value = malloc(sizeof(*row));
1548            }
1549            memcpy(entry->value, row, sizeof(*row));
1550
1551            if (row->dwIndex == index) {
1552                *ipaddr = row;
1553            }
1554        }
1555    }
1556
1557    if (*ipaddr) {
1558        return SIGAR_OK;
1559    }
1560    else {
1561        return ENOENT;
1562    }
1563}
1564
1565#define sigar_GetIpForwardTable \
1566    sigar->iphlpapi.get_ipforward_table.func
1567
1568
1569#define sigar_GetIfTable \
1570    sigar->iphlpapi.get_if_table.func
1571
1572#define sigar_GetIfEntry \
1573    sigar->iphlpapi.get_if_entry.func
1574
1575static int sigar_get_if_table(sigar_t *sigar, PMIB_IFTABLE *iftable)
1576{
1577    ULONG size = sigar->ifconf_len;
1578    DWORD rc;
1579
1580    DLLMOD_INIT(iphlpapi, FALSE);
1581
1582    if (!sigar_GetIfTable) {
1583        return SIGAR_ENOTIMPL;
1584    }
1585
1586    *iftable = (PMIB_IFTABLE)sigar->ifconf_buf;
1587    rc = sigar_GetIfTable(*iftable, &size, FALSE);
1588
1589    if (rc == ERROR_INSUFFICIENT_BUFFER) {
1590        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1591                         "GetIfTable "
1592                         "realloc ifconf_buf old=%d, new=%d",
1593                         sigar->ifconf_len, size);
1594        sigar->ifconf_len = size;
1595        sigar->ifconf_buf = realloc(sigar->ifconf_buf,
1596                                    sigar->ifconf_len);
1597
1598        *iftable = (PMIB_IFTABLE)sigar->ifconf_buf;
1599        rc = sigar_GetIfTable(*iftable, &size, FALSE);
1600    }
1601
1602    if (rc != NO_ERROR) {
1603        return rc;
1604    }
1605    else {
1606        return SIGAR_OK;
1607    }
1608}
1609
1610static int get_mib_ifrow(sigar_t *sigar,
1611                         const char *name,
1612                         MIB_IFROW **ifrp)
1613{
1614    int status, key, cached=0;
1615    sigar_cache_entry_t *entry;
1616
1617    if (sigar->netif_mib_rows) {
1618        cached = 1;
1619    }
1620    else {
1621        status = sigar_net_interface_list_get(sigar, NULL);
1622        if (status != SIGAR_OK) {
1623            return status;
1624        }
1625    }
1626    key = netif_hash(name);
1627    entry = sigar_cache_get(sigar->netif_mib_rows, key);
1628    if (!entry->value) {
1629        return ENOENT;
1630    }
1631
1632    *ifrp = (MIB_IFROW *)entry->value;
1633    if (cached) {
1634        /* refresh */
1635        if ((status = sigar_GetIfEntry(*ifrp)) != NO_ERROR) {
1636            return status;
1637        }
1638    }
1639
1640    return SIGAR_OK;
1641}
1642
1643int netif_hash(char *s)
1644{
1645    int hash = 0;
1646    while (*s) {
1647        hash = 31*hash + *s++;
1648    }
1649    return hash;
1650}
1651
1652/* Vista and later, wireless network cards are reported as IF_TYPE_IEEE80211 */
1653#ifndef IF_TYPE_IEEE80211
1654#define IF_TYPE_IEEE80211 71
1655#endif
1656
1657SIGAR_DECLARE(int)
1658sigar_net_interface_list_get(sigar_t *sigar,
1659                             sigar_net_interface_list_t *iflist)
1660{
1661    MIB_IFTABLE *ift;
1662    int i, status;
1663    int lo=0, eth=0, la=0;
1664
1665    if (!sigar->netif_mib_rows) {
1666        sigar->netif_mib_rows =
1667            sigar_netif_cache_new(sigar);
1668    }
1669
1670    if (!sigar->netif_names) {
1671        sigar->netif_names =
1672            sigar_netif_cache_new(sigar);
1673    }
1674
1675    if ((status = sigar_get_if_table(sigar, &ift)) != SIGAR_OK) {
1676        return status;
1677    }
1678
1679    if (iflist) {
1680        iflist->number = 0;
1681        iflist->size = ift->dwNumEntries;
1682        iflist->data =
1683            malloc(sizeof(*(iflist->data)) * iflist->size);
1684    }
1685
1686    for (i=0; i<ift->dwNumEntries; i++) {
1687        char name[16];
1688        int key;
1689        MIB_IFROW *ifr = ift->table + i;
1690        sigar_cache_entry_t *entry;
1691
1692        if (strEQ(ifr->bDescr, MS_LOOPBACK_ADAPTER)) {
1693            /* special-case */
1694            sprintf(name, NETIF_LA "%d", la++);
1695        }
1696        else if (ifr->dwType == MIB_IF_TYPE_LOOPBACK) {
1697            sprintf(name, "lo%d", lo++);
1698        }
1699        else if ((ifr->dwType == MIB_IF_TYPE_ETHERNET) ||
1700                 (ifr->dwType == IF_TYPE_IEEE80211))
1701        {
1702            sprintf(name, "eth%d", eth++);
1703        }
1704        else {
1705            continue; /*XXX*/
1706        }
1707
1708        if (iflist) {
1709            iflist->data[iflist->number++] = sigar_strdup(name);
1710        }
1711
1712        key = netif_hash(name);
1713        entry = sigar_cache_get(sigar->netif_mib_rows, key);
1714        if (!entry->value) {
1715            entry->value = malloc(sizeof(*ifr));
1716        }
1717        memcpy(entry->value, ifr, sizeof(*ifr));
1718
1719        /* save dwIndex -> name mapping for use by route_list */
1720        entry = sigar_cache_get(sigar->netif_names, ifr->dwIndex);
1721        if (!entry->value) {
1722            entry->value = sigar_strdup(name);
1723        }
1724    }
1725
1726    return SIGAR_OK;
1727}
1728
1729static int sigar_net_interface_ipv6_config_find(sigar_t *sigar, int index,
1730                                                sigar_net_interface_config_t *ifconfig)
1731{
1732#ifdef SIGAR_USING_MSC6
1733    return SIGAR_ENOTIMPL;
1734#else
1735    int status;
1736    PIP_ADAPTER_ADDRESSES aa, addrs;
1737    ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
1738
1739    status = sigar_get_adapters_addresses(sigar, AF_UNSPEC, flags, &aa);
1740
1741    if (status != SIGAR_OK) {
1742        return status;
1743    }
1744
1745    for (addrs = aa; addrs; addrs = addrs->Next) {
1746        PIP_ADAPTER_UNICAST_ADDRESS addr;
1747        if (addrs->IfIndex != index) {
1748            continue;
1749        }
1750        for (addr = addrs->FirstUnicastAddress; addr; addr = addr->Next) {
1751            struct sockaddr *sa = addr->Address.lpSockaddr;
1752
1753            if (sa->sa_family == AF_INET6) {
1754                struct in6_addr *inet6 = SIGAR_SIN6_ADDR(sa);
1755
1756                sigar_net_address6_set(ifconfig->address6, inet6);
1757                sigar_net_interface_scope6_set(ifconfig, inet6);
1758                if (addrs->FirstPrefix) {
1759                    ifconfig->prefix6_length = addrs->FirstPrefix->PrefixLength;
1760                }
1761                return SIGAR_OK;
1762            }
1763        }
1764    }
1765    return SIGAR_ENOENT;
1766#endif
1767}
1768
1769SIGAR_DECLARE(int)
1770sigar_net_interface_config_get(sigar_t *sigar,
1771                               const char *name,
1772                               sigar_net_interface_config_t *ifconfig)
1773{
1774    MIB_IFROW *ifr;
1775    MIB_IPADDRROW *ipaddr;
1776    int status;
1777
1778    if (!name) {
1779        return sigar_net_interface_config_primary_get(sigar, ifconfig);
1780    }
1781
1782    status = get_mib_ifrow(sigar, name, &ifr);
1783    if (status != SIGAR_OK) {
1784        return status;
1785    }
1786
1787    SIGAR_ZERO(ifconfig);
1788
1789    SIGAR_SSTRCPY(ifconfig->name, name);
1790
1791    ifconfig->mtu = ifr->dwMtu;
1792
1793    sigar_net_address_mac_set(ifconfig->hwaddr,
1794                              ifr->bPhysAddr,
1795                              SIGAR_IFHWADDRLEN);
1796
1797    SIGAR_SSTRCPY(ifconfig->description,
1798                  ifr->bDescr);
1799
1800    if (ifr->dwOperStatus & MIB_IF_OPER_STATUS_OPERATIONAL) {
1801        ifconfig->flags |= SIGAR_IFF_UP|SIGAR_IFF_RUNNING;
1802    }
1803
1804    status = sigar_get_netif_ipaddr(sigar,
1805                                    ifr->dwIndex,
1806                                    &ipaddr);
1807
1808    if (status == SIGAR_OK) {
1809        sigar_net_address_set(ifconfig->address,
1810                              ipaddr->dwAddr);
1811
1812        sigar_net_address_set(ifconfig->netmask,
1813                              ipaddr->dwMask);
1814
1815        if (ifr->dwType != MIB_IF_TYPE_LOOPBACK) {
1816            if (ipaddr->dwBCastAddr) {
1817                long bcast =
1818                    ipaddr->dwAddr & ipaddr->dwMask;
1819
1820                bcast |= ~ipaddr->dwMask;
1821                ifconfig->flags |= SIGAR_IFF_BROADCAST;
1822
1823                sigar_net_address_set(ifconfig->broadcast,
1824                                      bcast);
1825            }
1826        }
1827    }
1828
1829    /* hack for MS_LOOPBACK_ADAPTER */
1830    if (strnEQ(name, NETIF_LA, sizeof(NETIF_LA)-1)) {
1831        ifr->dwType = MIB_IF_TYPE_LOOPBACK;
1832    }
1833
1834    if (ifr->dwType == MIB_IF_TYPE_LOOPBACK) {
1835        ifconfig->flags |= SIGAR_IFF_LOOPBACK;
1836
1837        SIGAR_SSTRCPY(ifconfig->type,
1838                      SIGAR_NIC_LOOPBACK);
1839    }
1840    else {
1841        if (ipaddr) {
1842            ifconfig->flags |= SIGAR_IFF_MULTICAST;
1843        }
1844
1845        SIGAR_SSTRCPY(ifconfig->type,
1846                      SIGAR_NIC_ETHERNET);
1847    }
1848
1849    sigar_net_interface_ipv6_config_init(ifconfig);
1850    sigar_net_interface_ipv6_config_find(sigar, ifr->dwIndex, ifconfig);
1851
1852    return SIGAR_OK;
1853}
1854
1855#define IS_TCP_SERVER(state, flags) \
1856    ((flags & SIGAR_NETCONN_SERVER) && (state == MIB_TCP_STATE_LISTEN))
1857
1858#define IS_TCP_CLIENT(state, flags) \
1859    ((flags & SIGAR_NETCONN_CLIENT) && (state != MIB_TCP_STATE_LISTEN))
1860
1861#define sigar_GetTcpTable \
1862    sigar->iphlpapi.get_tcp_table.func
1863
1864static int net_conn_get_tcp(sigar_net_connection_walker_t *walker)
1865{
1866    sigar_t *sigar = walker->sigar;
1867    int flags = walker->flags;
1868    int i;
1869    DWORD rc, size=0;
1870    PMIB_TCPTABLE tcp;
1871
1872    DLLMOD_INIT(iphlpapi, FALSE);
1873
1874    if (!sigar_GetTcpTable) {
1875        return SIGAR_ENOTIMPL;
1876    }
1877
1878    rc = sigar_GetTcpTable(NULL, &size, FALSE);
1879    if (rc != ERROR_INSUFFICIENT_BUFFER) {
1880        return GetLastError();
1881    }
1882    tcp = malloc(size);
1883    rc = sigar_GetTcpTable(tcp, &size, FALSE);
1884    if (rc) {
1885        free(tcp);
1886        return GetLastError();
1887    }
1888
1889    /* go in reverse to get LISTEN states first */
1890    for (i = (tcp->dwNumEntries-1); i >= 0; i--) {
1891        sigar_net_connection_t conn;
1892        DWORD state = tcp->table[i].dwState;
1893
1894        if (!(IS_TCP_SERVER(state, flags) ||
1895              IS_TCP_CLIENT(state, flags)))
1896        {
1897            continue;
1898        }
1899
1900        conn.local_port  = htons((WORD)tcp->table[i].dwLocalPort);
1901        conn.remote_port = htons((WORD)tcp->table[i].dwRemotePort);
1902
1903        conn.type = SIGAR_NETCONN_TCP;
1904
1905        sigar_net_address_set(conn.local_address,
1906                              tcp->table[i].dwLocalAddr);
1907
1908        sigar_net_address_set(conn.remote_address,
1909                              tcp->table[i].dwRemoteAddr);
1910
1911        conn.send_queue = conn.receive_queue = SIGAR_FIELD_NOTIMPL;
1912
1913        switch (state) {
1914          case MIB_TCP_STATE_CLOSED:
1915            conn.state = SIGAR_TCP_CLOSE;
1916            break;
1917          case MIB_TCP_STATE_LISTEN:
1918            conn.state = SIGAR_TCP_LISTEN;
1919            break;
1920          case MIB_TCP_STATE_SYN_SENT:
1921            conn.state = SIGAR_TCP_SYN_SENT;
1922            break;
1923          case MIB_TCP_STATE_SYN_RCVD:
1924            conn.state = SIGAR_TCP_SYN_RECV;
1925            break;
1926          case MIB_TCP_STATE_ESTAB:
1927            conn.state = SIGAR_TCP_ESTABLISHED;
1928            break;
1929          case MIB_TCP_STATE_FIN_WAIT1:
1930            conn.state = SIGAR_TCP_FIN_WAIT1;
1931            break;
1932          case MIB_TCP_STATE_FIN_WAIT2:
1933            conn.state = SIGAR_TCP_FIN_WAIT2;
1934            break;
1935          case MIB_TCP_STATE_CLOSE_WAIT:
1936            conn.state = SIGAR_TCP_CLOSE_WAIT;
1937            break;
1938          case MIB_TCP_STATE_CLOSING:
1939            conn.state = SIGAR_TCP_CLOSING;
1940            break;
1941          case MIB_TCP_STATE_LAST_ACK:
1942            conn.state = SIGAR_TCP_LAST_ACK;
1943            break;
1944          case MIB_TCP_STATE_TIME_WAIT:
1945            conn.state = SIGAR_TCP_TIME_WAIT;
1946            break;
1947          case MIB_TCP_STATE_DELETE_TCB:
1948          default:
1949            conn.state = SIGAR_TCP_UNKNOWN;
1950            break;
1951        }
1952
1953        if (walker->add_connection(walker, &conn) != SIGAR_OK) {
1954            break;
1955        }
1956    }
1957
1958    free(tcp);
1959    return SIGAR_OK;
1960}
1961
1962static int net_conn_get_udp(sigar_net_connection_walker_t *walker)
1963{
1964    /* Disabled because it isn't properly implemented and
1965     * cause the windows runtime to abort due to use
1966     * of uninitialized variable in the for-loop below.
1967     * IS_UDP_SERVER(conn, flags) We don't use this
1968     * functionality anyway
1969     */
1970    return SIGAR_ENOTIMPL;
1971}
1972
1973SIGAR_DECLARE(int)
1974sigar_net_connection_walk(sigar_net_connection_walker_t *walker)
1975{
1976    int status;
1977
1978    if (walker->flags & SIGAR_NETCONN_TCP) {
1979        status = net_conn_get_tcp(walker);
1980
1981        if (status != SIGAR_OK) {
1982            return status;
1983        }
1984    }
1985
1986    if (walker->flags & SIGAR_NETCONN_UDP) {
1987        status = net_conn_get_udp(walker);
1988
1989        if (status != SIGAR_OK) {
1990            return status;
1991        }
1992    }
1993
1994    return SIGAR_OK;
1995}
1996