xref: /6.6.0/sigar/src/os/solaris/solaris_sigar.c (revision e1b339d4)
1/*
2 * Copyright (c) 2004-2008 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_util.h"
22#include "sigar_os.h"
23
24#include <inet/ip.h>
25#include <inet/tcp.h>
26#include <net/if.h>
27#include <net/route.h>
28#include <sys/lwp.h>
29#include <sys/proc.h>
30#include <sys/sockio.h>
31#include <sys/swap.h>
32#include <sys/stat.h>
33#include <sys/systeminfo.h>
34#include <sys/utsname.h>
35#include <dlfcn.h>
36#include <dirent.h>
37
38#define PROC_ERRNO ((errno == ENOENT) ? ESRCH : errno)
39#define SIGAR_USR_UCB_PS "/usr/ucb/ps"
40
41
42/* like kstat_lookup but start w/ ksp->ks_next instead of kc->kc_chain */
43static kstat_t *
44kstat_next(kstat_t *ksp, char *ks_module, int ks_instance, char *ks_name)
45{
46    if (ksp) {
47        ksp = ksp->ks_next;
48    }
49    for (; ksp; ksp = ksp->ks_next) {
50        if ((ks_module == NULL ||
51             strcmp(ksp->ks_module, ks_module) == 0) &&
52            (ks_instance == -1 || ksp->ks_instance == ks_instance) &&
53            (ks_name == NULL || strcmp(ksp->ks_name, ks_name) == 0))
54            return ksp;
55    }
56
57    errno = ENOENT;
58    return NULL;
59}
60
61int sigar_os_open(sigar_t **sig)
62{
63    kstat_ctl_t *kc;
64    kstat_t *ksp;
65    sigar_t *sigar;
66    int i, status;
67    struct utsname name;
68    char *ptr;
69
70    if ((kc = kstat_open()) == NULL) {
71       *sig = NULL;
72       return errno;
73    }
74
75    /*
76     * Use calloc instead of malloc to set everything to 0
77     * to avoid having to set each individual member to 0/NULL
78     * later.
79     */
80    if ((*sig = sigar = calloc(1, sizeof(*sigar))) == NULL) {
81       return ENOMEM;
82    }
83
84    uname(&name);
85    if ((ptr = strchr(name.release, '.'))) {
86        sigar->solaris_version = atoi(ptr + 1);
87    }
88    else {
89        sigar->solaris_version = 6;
90    }
91
92    if ((ptr = getenv("SIGAR_USE_UCB_PS"))) {
93        sigar->use_ucb_ps = strEQ(ptr, "true");
94    }
95
96    if (sigar->use_ucb_ps) {
97        if (access(SIGAR_USR_UCB_PS, X_OK) == -1) {
98            sigar->use_ucb_ps = 0;
99        }
100        else {
101            sigar->use_ucb_ps = 1;
102        }
103    }
104
105    sigar->pagesize = 0;
106    i = sysconf(_SC_PAGESIZE);
107    while ((i >>= 1) > 0) {
108        sigar->pagesize++;
109    }
110
111    sigar->ticks = sysconf(_SC_CLK_TCK);
112    sigar->kc = kc;
113
114    sigar->koffsets.system[0] = -1;
115    sigar->koffsets.mempages[0] = -1;
116    sigar->koffsets.syspages[0] = -1;
117
118    if ((status = sigar_get_kstats(sigar)) != SIGAR_OK) {
119        fprintf(stderr, "status=%d\n", status);
120    }
121
122    if ((ksp = sigar->ks.system) &&
123        (kstat_read(kc, ksp, NULL) >= 0))
124    {
125        sigar_koffsets_init_system(sigar, ksp);
126
127        sigar->boot_time = kSYSTEM(KSTAT_SYSTEM_BOOT_TIME);
128    }
129
130    sigar->last_pid = -1;
131    sigar->mib2.sd = -1;
132
133    return SIGAR_OK;
134}
135
136static int sigar_cpu_list_destroy(sigar_t *sigar, sigar_cpu_list_t *cpulist)
137{
138    if (cpulist->size) {
139        free(cpulist->data);
140        cpulist->number = cpulist->size = 0;
141    }
142
143    return SIGAR_OK;
144}
145
146int sigar_os_close(sigar_t *sigar)
147{
148    kstat_close(sigar->kc);
149    if (sigar->mib2.sd != -1) {
150        close_mib2(&sigar->mib2);
151    }
152
153    if (sigar->ks.lcpu) {
154        free(sigar->ks.cpu);
155        free(sigar->ks.cpu_info);
156        free(sigar->ks.cpuid);
157    }
158    if (sigar->pinfo) {
159        free(sigar->pinfo);
160    }
161    if (sigar->cpulist.size != 0) {
162        sigar_cpu_list_destroy(sigar, &sigar->cpulist);
163    }
164    if (sigar->plib) {
165        dlclose(sigar->plib);
166    }
167    if (sigar->pargs) {
168        sigar_cache_destroy(sigar->pargs);
169    }
170    free(sigar);
171    return SIGAR_OK;
172}
173
174char *sigar_os_error_string(sigar_t *sigar, int err)
175{
176    switch (err) {
177      case SIGAR_EMIB2:
178        return sigar->mib2.errmsg;
179      default:
180        return NULL;
181    }
182}
183
184int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem)
185{
186    kstat_ctl_t *kc = sigar->kc;
187    kstat_t *ksp;
188    sigar_uint64_t kern = 0;
189
190    SIGAR_ZERO(mem);
191
192    /* XXX: is mem hot swappable or can we just do this during open ? */
193    mem->total = sysconf(_SC_PHYS_PAGES);
194    mem->total <<= sigar->pagesize;
195
196    if (sigar_kstat_update(sigar) == -1) {
197        return errno;
198    }
199
200    if ((ksp = sigar->ks.syspages) && kstat_read(kc, ksp, NULL) >= 0) {
201        sigar_koffsets_init_syspages(sigar, ksp);
202
203        mem->free = kSYSPAGES(KSTAT_SYSPAGES_FREE);
204        mem->free <<= sigar->pagesize;
205
206        mem->used = mem->total - mem->free;
207    }
208
209    if ((ksp = sigar->ks.mempages) && kstat_read(kc, ksp, NULL) >= 0) {
210        sigar_koffsets_init_mempages(sigar, ksp);
211    }
212
213    /* XXX mdb ::memstat cachelist/freelist not available to kstat, see: */
214    /* http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6821980 */
215
216    /* ZFS ARC cache. see: http://opensolaris.org/jive/thread.jspa?messageID=393695 */
217    if ((ksp = kstat_lookup(sigar->kc, "zfs", 0, "arcstats")) &&
218        (kstat_read(sigar->kc, ksp, NULL) != -1))
219    {
220        kstat_named_t *kn;
221
222        if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "size"))) {
223            kern = kn->value.i64;
224        }
225        if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "c_min"))) {
226            /* c_min cannot be reclaimed they say */
227            if (kern > kn->value.i64) {
228                kern -= kn->value.i64;
229            }
230        }
231    }
232
233    mem->actual_free = mem->free + kern;
234    mem->actual_used = mem->used - kern;
235
236    sigar_mem_calc_ram(sigar, mem);
237
238    return SIGAR_OK;
239}
240
241int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap)
242{
243    kstat_t *ksp;
244    kstat_named_t *kn;
245    swaptbl_t *stab;
246    int num, i;
247    char path[PATH_MAX+1]; /* {un,re}used */
248
249    /* see: man swapctl(2) */
250    if ((num = swapctl(SC_GETNSWP, NULL)) == -1) {
251        return errno;
252    }
253
254    stab = malloc(num * sizeof(stab->swt_ent[0]) + sizeof(*stab));
255
256    stab->swt_n = num;
257    for (i=0; i<num; i++) {
258        stab->swt_ent[i].ste_path = path;
259    }
260
261    if ((num = swapctl(SC_LIST, stab)) == -1) {
262        free(stab);
263        return errno;
264    }
265
266    num = num < stab->swt_n ? num : stab->swt_n;
267    swap->total = swap->free = 0;
268    for (i=0; i<num; i++) {
269        if (stab->swt_ent[i].ste_flags & ST_INDEL) {
270            continue; /* swap file is being deleted */
271        }
272        swap->total += stab->swt_ent[i].ste_pages;
273        swap->free  += stab->swt_ent[i].ste_free;
274    }
275    free(stab);
276
277    swap->total <<= sigar->pagesize;
278    swap->free  <<= sigar->pagesize;
279    swap->used  = swap->total - swap->free;
280
281    swap->allocstall = -1;
282    swap->allocstall_dma = -1;
283    swap->allocstall_dma32 = -1;
284    swap->allocstall_normal = -1;
285    swap->allocstall_movable = -1;
286
287    if (sigar_kstat_update(sigar) == -1) {
288        return errno;
289    }
290    if (!(ksp = kstat_lookup(sigar->kc, "cpu", -1, "vm"))) {
291        swap->page_in = swap->page_out = SIGAR_FIELD_NOTIMPL;
292        return SIGAR_OK;
293    }
294
295    swap->page_in = swap->page_out = 0;
296
297    /* XXX: these stats do not exist in this form on solaris 8 or 9.
298     * they are in the raw cpu_stat struct, but thats not
299     * binary compatible
300     */
301    do {
302        if (kstat_read(sigar->kc, ksp, NULL) < 0) {
303            break;
304        }
305
306        if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "pgin"))) {
307            swap->page_in += kn->value.i64;  /* vmstat -s | grep "page ins" */
308        }
309        if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "pgout"))) {
310            swap->page_out += kn->value.i64; /* vmstat -s | grep "page outs" */
311        }
312    } while ((ksp = kstat_next(ksp, "cpu", -1, "vm")));
313
314    return SIGAR_OK;
315}
316
317#ifndef KSTAT_NAMED_STR_PTR
318/* same offset as KSTAT_NAMED_STR_PTR(brand) */
319#define KSTAT_NAMED_STR_PTR(n) (char *)((n)->value.i32)
320#endif
321
322
323static void free_chip_id(void *ptr)
324{
325    /*noop*/
326}
327
328static int get_chip_id(sigar_t *sigar, int processor)
329{
330    kstat_t *ksp = sigar->ks.cpu_info[processor];
331    kstat_named_t *chipid;
332
333    if (ksp &&
334        (kstat_read(sigar->kc, ksp, NULL) != -1) &&
335        (chipid = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id")))
336    {
337        return chipid->value.i32;
338    }
339    else {
340        return -1;
341    }
342}
343
344static int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist);
345
346int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu)
347{
348    int status, i;
349
350    status = sigar_cpu_list_get(sigar, &sigar->cpulist);
351
352    if (status != SIGAR_OK) {
353        return status;
354    }
355
356    SIGAR_ZERO(cpu);
357
358    for (i=0; i<sigar->cpulist.number; i++) {
359        sigar_cpu_t *xcpu = &sigar->cpulist.data[i];
360
361        cpu->user  += xcpu->user;
362        cpu->sys   += xcpu->sys;
363        cpu->idle  += xcpu->idle;
364        cpu->nice  += xcpu->nice;
365        cpu->wait  += xcpu->wait;
366        cpu->total += xcpu->total;
367    }
368
369    return SIGAR_OK;
370}
371
372
373
374static int sigar_cpu_list_create(sigar_cpu_list_t *cpulist);
375
376static int sigar_cpu_list_grow(sigar_cpu_list_t *cpulist);
377
378#define SIGAR_CPU_LIST_GROW(cpulist) \
379    if (cpulist->number >= cpulist->size) { \
380        sigar_cpu_list_grow(cpulist); \
381    }
382
383static int sigar_cpu_list_create(sigar_cpu_list_t *cpulist)
384{
385    cpulist->number = 0;
386    cpulist->size = SIGAR_CPU_INFO_MAX;
387    cpulist->data = malloc(sizeof(*(cpulist->data)) *
388                           cpulist->size);
389    return SIGAR_OK;
390}
391
392static int sigar_cpu_list_grow(sigar_cpu_list_t *cpulist)
393{
394    cpulist->data = realloc(cpulist->data,
395                            sizeof(*(cpulist->data)) *
396                            (cpulist->size + SIGAR_CPU_INFO_MAX));
397    cpulist->size += SIGAR_CPU_INFO_MAX;
398
399    return SIGAR_OK;
400}
401
402
403
404
405static int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist)
406{
407    kstat_ctl_t *kc = sigar->kc;
408    kstat_t *ksp;
409    uint_t cpuinfo[CPU_STATES];
410    unsigned int i;
411    int is_debug = SIGAR_LOG_IS_DEBUG(sigar);
412    sigar_cache_t *chips;
413
414    if (sigar_kstat_update(sigar) == -1) {
415        return errno;
416    }
417
418    if (cpulist == &sigar->cpulist) {
419        if (sigar->cpulist.size == 0) {
420            /* create once */
421            sigar_cpu_list_create(cpulist);
422        }
423        else {
424            /* reset, re-using cpulist.data */
425            sigar->cpulist.number = 0;
426        }
427    }
428    else {
429        sigar_cpu_list_create(cpulist);
430    }
431
432    if (is_debug) {
433        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
434                         "[cpu_list] OS reports %d CPUs",
435                         sigar->ncpu);
436    }
437
438    chips = sigar_cache_new(16);
439    chips->free_value = free_chip_id;
440
441    for (i=0; i<sigar->ncpu; i++) {
442        sigar_cpu_t *cpu;
443        char *buf;
444        int chip_id;
445        sigar_cache_entry_t *ent;
446
447        if (!CPU_ONLINE(sigar->ks.cpuid[i])) {
448            sigar_log_printf(sigar, SIGAR_LOG_INFO,
449                             "cpu %d (id=%d) is offline",
450                             i, sigar->ks.cpuid[i]);
451            continue;
452        }
453
454        if (!(ksp = sigar->ks.cpu[i])) {
455            sigar_log_printf(sigar, SIGAR_LOG_ERROR,
456                             "NULL ksp for cpu %d (id=%d)",
457                             i, sigar->ks.cpuid[i]);
458            continue; /* shouldnot happen */
459        }
460
461        if (kstat_read(kc, ksp, NULL) < 0) {
462            sigar_log_printf(sigar, SIGAR_LOG_ERROR,
463                             "kstat_read failed for cpu %d (id=%d): %s",
464                             i, sigar->ks.cpuid[i],
465                             sigar_strerror(sigar, errno));
466            continue; /* shouldnot happen */
467        }
468
469        /*
470         * cpu_stat_t is not binary compatible between solaris versions.
471         * since cpu_stat is a 'raw' kstat and not 'named' we cannot
472         * use name based lookups as we do for others.
473         * the start of the cpu_stat_t structure is binary compatible,
474         * which looks like so:
475         * typedef struct cpu_stat {
476         *    kmutex_t        cpu_stat_lock;
477         *    cpu_sysinfo_t   cpu_sysinfo;
478         *    ...
479         *    typedef struct cpu_sysinfo {
480         *       ulong cpu[CPU_STATES];
481         *       ...
482         * we just copy the piece we need below:
483         */
484        buf = ksp->ks_data;
485        buf += sizeof(kmutex_t);
486        memcpy(&cpuinfo[0], buf, sizeof(cpuinfo));
487        chip_id = sigar->cpu_list_cores ? -1 : get_chip_id(sigar, i);
488
489        if (chip_id == -1) {
490            SIGAR_CPU_LIST_GROW(cpulist);
491            cpu = &cpulist->data[cpulist->number++];
492            SIGAR_ZERO(cpu);
493        }
494        else {
495            /* merge times of logical processors */
496            ent = sigar_cache_get(chips, chip_id);
497            if (ent->value) {
498                cpu = &cpulist->data[(long)ent->value-1];
499            }
500            else {
501                SIGAR_CPU_LIST_GROW(cpulist);
502                cpu = &cpulist->data[cpulist->number++];
503                ent->value = (void *)(long)cpulist->number;
504                SIGAR_ZERO(cpu);
505
506                if (is_debug) {
507                    sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
508                                     "[cpu_list] Merging times of"
509                                     " logical processors for chip_id=%d",
510                                     chip_id);
511                }
512            }
513        }
514
515        cpu->user += SIGAR_TICK2MSEC(cpuinfo[CPU_USER]);
516        cpu->sys  += SIGAR_TICK2MSEC(cpuinfo[CPU_KERNEL]);
517        cpu->idle += SIGAR_TICK2MSEC(cpuinfo[CPU_IDLE]);
518        cpu->wait += SIGAR_TICK2MSEC(cpuinfo[CPU_WAIT]);
519        cpu->nice += 0; /* no cpu->nice */
520        cpu->total = cpu->user + cpu->sys + cpu->idle + cpu->wait;
521    }
522
523    sigar_cache_destroy(chips);
524
525    return SIGAR_OK;
526}
527
528#define LIBPROC "/usr/lib/libproc.so"
529
530#define CHECK_PSYM(s) \
531    if (!sigar->s) { \
532        sigar_log_printf(sigar, SIGAR_LOG_WARN, \
533                         "[%s] Symbol not found: %s", \
534                         SIGAR_FUNC, #s); \
535        dlclose(sigar->plib); \
536        sigar->plib = NULL; \
537        return SIGAR_ENOTIMPL; \
538    }
539
540
541/* from libproc.h, not included w/ solaris distro */
542/* Error codes from Pgrab(), Pfgrab_core(), and Pgrab_core() */
543#define G_STRANGE       -1      /* Unanticipated error, errno is meaningful */
544#define G_NOPROC        1       /* No such process */
545#define G_NOCORE        2       /* No such core file */
546#define G_NOPROCORCORE  3       /* No such proc or core (for proc_arg_grab) */
547#define G_NOEXEC        4       /* Cannot locate executable file */
548#define G_ZOMB          5       /* Zombie process */
549#define G_PERM          6       /* No permission */
550#define G_BUSY          7       /* Another process has control */
551#define G_SYS           8       /* System process */
552#define G_SELF          9       /* Process is self */
553#define G_INTR          10      /* Interrupt received while grabbing */
554#define G_LP64          11      /* Process is _LP64, self is ILP32 */
555#define G_FORMAT        12      /* File is not an ELF format core file */
556#define G_ELF           13      /* Libelf error, elf_errno() is meaningful */
557#define G_NOTE          14      /* Required PT_NOTE Phdr not present in core */
558
559
560int sigar_os_proc_list_get(sigar_t *sigar,
561                           sigar_proc_list_t *proclist)
562{
563    return sigar_proc_list_procfs_get(sigar, proclist);
564}
565
566int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
567                       sigar_proc_mem_t *procmem)
568{
569    int status = sigar_proc_psinfo_get(sigar, pid);
570    psinfo_t *pinfo = sigar->pinfo;
571    prusage_t usage;
572
573    if (status != SIGAR_OK) {
574        return status;
575    }
576
577    procmem->size     = pinfo->pr_size << 10;
578    procmem->resident = pinfo->pr_rssize << 10;
579    procmem->share    = SIGAR_FIELD_NOTIMPL;
580
581    if (sigar_proc_usage_get(sigar, &usage, pid) == SIGAR_OK) {
582        procmem->minor_faults = usage.pr_minf;
583        procmem->major_faults = usage.pr_majf;
584        procmem->page_faults =
585            procmem->minor_faults +
586            procmem->major_faults;
587    }
588    else {
589        procmem->minor_faults = SIGAR_FIELD_NOTIMPL;
590        procmem->major_faults = SIGAR_FIELD_NOTIMPL;
591        procmem->page_faults = SIGAR_FIELD_NOTIMPL;
592    }
593
594    return SIGAR_OK;
595}
596
597#define TIMESTRUCT_2MSEC(t) \
598    ((t.tv_sec * MILLISEC) + (t.tv_nsec / (NANOSEC/MILLISEC)))
599
600int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid,
601                        sigar_proc_time_t *proctime)
602{
603    prusage_t usage;
604    int status;
605
606    if ((status = sigar_proc_usage_get(sigar, &usage, pid)) != SIGAR_OK) {
607        return status;
608    }
609
610    proctime->start_time = usage.pr_create.tv_sec + sigar->boot_time;
611    proctime->start_time *= MILLISEC;
612
613    if (usage.pr_utime.tv_sec < 0) {
614        /* XXX wtf?  seen on solaris 10, only for the self process */
615        pstatus_t pstatus;
616
617        status = sigar_proc_status_get(sigar, &pstatus, pid);
618        if (status != SIGAR_OK) {
619            return status;
620        }
621
622        usage.pr_utime.tv_sec  = pstatus.pr_utime.tv_sec;
623        usage.pr_utime.tv_nsec = pstatus.pr_utime.tv_nsec;
624        usage.pr_stime.tv_sec  = pstatus.pr_stime.tv_sec;
625        usage.pr_stime.tv_nsec = pstatus.pr_stime.tv_nsec;
626    }
627
628    proctime->user = TIMESTRUCT_2MSEC(usage.pr_utime);
629    proctime->sys  = TIMESTRUCT_2MSEC(usage.pr_stime);
630    proctime->total = proctime->user + proctime->sys;
631
632    return SIGAR_OK;
633}
634
635int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid,
636                         sigar_proc_state_t *procstate)
637{
638    int status = sigar_proc_psinfo_get(sigar, pid);
639    psinfo_t *pinfo = sigar->pinfo;
640
641    if (status != SIGAR_OK) {
642        return status;
643    }
644
645    SIGAR_SSTRCPY(procstate->name, pinfo->pr_fname);
646    procstate->ppid = pinfo->pr_ppid;
647    procstate->tty  = pinfo->pr_ttydev;
648    procstate->priority = pinfo->pr_lwp.pr_pri;
649    procstate->nice     = pinfo->pr_lwp.pr_nice - NZERO;
650    procstate->threads  = pinfo->pr_nlwp;
651    procstate->processor = pinfo->pr_lwp.pr_onpro;
652
653    switch (pinfo->pr_lwp.pr_state) {
654      case SONPROC:
655      case SRUN:
656        procstate->state = 'R';
657        break;
658      case SZOMB:
659        procstate->state = 'Z';
660        break;
661      case SSLEEP:
662        procstate->state = 'S';
663        break;
664      case SSTOP:
665        procstate->state = 'T';
666        break;
667      case SIDL:
668        procstate->state = 'D';
669        break;
670    }
671
672    return SIGAR_OK;
673}
674
675#include <sys/mnttab.h>
676
677int sigar_os_fs_type_get(sigar_file_system_t *fsp)
678{
679    char *type = fsp->sys_type_name;
680
681    switch (*type) {
682      case 'u':
683        if (strEQ(type, "ufs")) {
684            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
685        }
686        break;
687        /* XXX */
688    }
689
690    return fsp->type;
691}
692
693int sigar_file_system_list_get(sigar_t *sigar,
694                               sigar_file_system_list_t *fslist)
695{
696    struct mnttab ent;
697    sigar_file_system_t *fsp;
698    FILE *fp = fopen(MNTTAB, "r");
699
700    if (!fp) {
701        return errno;
702    }
703
704    sigar_file_system_list_create(fslist);
705
706    while (getmntent(fp, &ent) == 0) {
707        if (strstr(ent.mnt_mntopts, "ignore")) {
708            continue; /* e.g. vold */
709        }
710
711        SIGAR_FILE_SYSTEM_LIST_GROW(fslist);
712
713        fsp = &fslist->data[fslist->number++];
714
715        SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_mountp);
716        SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_special);
717        SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_fstype);
718        SIGAR_SSTRCPY(fsp->options, ent.mnt_mntopts);
719        sigar_fs_type_init(fsp);
720    }
721
722    fclose(fp);
723
724    return SIGAR_OK;
725}
726
727int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name,
728                                        sigar_net_interface_config_t *ifconfig)
729{
730    int sock;
731    struct lifreq lifr;
732
733    if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
734        return errno;
735    }
736
737    SIGAR_SSTRCPY(lifr.lifr_name, name);
738
739    if (ioctl(sock, SIOCGLIFADDR, &lifr) == 0) {
740        struct in6_addr *addr = SIGAR_SIN6_ADDR(&lifr.lifr_addr);
741
742        sigar_net_address6_set(ifconfig->address6, addr);
743        sigar_net_interface_scope6_set(ifconfig, addr);
744        ifconfig->prefix6_length = lifr.lifr_addrlen;
745    }
746
747    close(sock);
748    return SIGAR_OK;
749}
750
751#define TCPQ_SIZE(s) ((s) >= 0 ? (s) : 0)
752
753static int tcp_connection_get(sigar_net_connection_walker_t *walker,
754                              struct mib2_tcpConnEntry *entry,
755                              int len)
756{
757    int flags = walker->flags;
758    int status;
759    char *end = (char *)entry + len;
760
761    while ((char *)entry < end) {
762        int state = entry->tcpConnEntryInfo.ce_state;
763
764        if (((flags & SIGAR_NETCONN_SERVER) && (state == TCPS_LISTEN)) ||
765            ((flags & SIGAR_NETCONN_CLIENT) && (state != TCPS_LISTEN)))
766        {
767            sigar_net_connection_t conn;
768
769            SIGAR_ZERO(&conn);
770
771            sigar_net_address_set(conn.local_address, entry->tcpConnLocalAddress);
772            sigar_net_address_set(conn.remote_address, entry->tcpConnRemAddress);
773
774            conn.local_port = entry->tcpConnLocalPort;
775            conn.remote_port = entry->tcpConnRemPort;
776            conn.type = SIGAR_NETCONN_TCP;
777            conn.send_queue =
778                TCPQ_SIZE(entry->tcpConnEntryInfo.ce_snxt -
779                          entry->tcpConnEntryInfo.ce_suna - 1);
780            conn.receive_queue =
781                TCPQ_SIZE(entry->tcpConnEntryInfo.ce_rnxt -
782                          entry->tcpConnEntryInfo.ce_rack);
783
784            switch (state) {
785              case TCPS_CLOSED:
786                conn.state = SIGAR_TCP_CLOSE;
787                break;
788              case TCPS_IDLE:
789                conn.state = SIGAR_TCP_IDLE;
790                break;
791              case TCPS_BOUND:
792                conn.state = SIGAR_TCP_BOUND;
793                break;
794              case TCPS_LISTEN:
795                conn.state = SIGAR_TCP_LISTEN;
796                break;
797              case TCPS_SYN_SENT:
798                conn.state = SIGAR_TCP_SYN_SENT;
799                break;
800              case TCPS_SYN_RCVD:
801                conn.state = SIGAR_TCP_SYN_RECV;
802                break;
803              case TCPS_ESTABLISHED:
804                conn.state = SIGAR_TCP_ESTABLISHED;
805                break;
806              case TCPS_CLOSE_WAIT:
807                conn.state = SIGAR_TCP_CLOSE_WAIT;
808                break;
809              case TCPS_FIN_WAIT_1:
810                conn.state = SIGAR_TCP_FIN_WAIT1;
811                break;
812              case TCPS_CLOSING:
813                conn.state = SIGAR_TCP_CLOSING;
814                break;
815              case TCPS_LAST_ACK:
816                conn.state = SIGAR_TCP_LAST_ACK;
817                break;
818              case TCPS_FIN_WAIT_2:
819                conn.state = SIGAR_TCP_FIN_WAIT2;
820                break;
821              case TCPS_TIME_WAIT:
822                conn.state = SIGAR_TCP_TIME_WAIT;
823                break;
824              default:
825                conn.state = SIGAR_TCP_UNKNOWN;
826                break;
827            }
828
829            status = walker->add_connection(walker, &conn);
830            if (status != SIGAR_OK) {
831                return status;
832            }
833        }
834
835        entry++;
836    }
837
838    return SIGAR_OK;
839}
840
841static int udp_connection_get(sigar_net_connection_walker_t *walker,
842                              struct mib2_udpEntry *entry,
843                              int len)
844{
845    int flags = walker->flags;
846    int status;
847    char *end = (char *)entry + len;
848
849    while ((char *)entry < end) {
850        int state = entry->udpEntryInfo.ue_state;
851
852        /* XXX dunno if this state check is right */
853        if (((flags & SIGAR_NETCONN_SERVER) && (state == MIB2_UDP_idle)) ||
854            ((flags & SIGAR_NETCONN_CLIENT) && (state != MIB2_UDP_idle)))
855        {
856            sigar_net_connection_t conn;
857
858            SIGAR_ZERO(&conn);
859
860            sigar_net_address_set(conn.local_address, entry->udpLocalAddress);
861            sigar_net_address_set(conn.remote_address, 0);
862
863            conn.local_port = entry->udpLocalPort;
864            conn.remote_port = 0;
865            conn.type = SIGAR_NETCONN_UDP;
866
867            status = walker->add_connection(walker, &conn);
868            if (status != SIGAR_OK) {
869                return status;
870            }
871        }
872
873        entry++;
874    }
875
876    return SIGAR_OK;
877}
878
879int sigar_net_connection_walk(sigar_net_connection_walker_t *walker)
880{
881    sigar_t *sigar = walker->sigar;
882    int flags = walker->flags;
883    int status;
884    int want_tcp = flags & SIGAR_NETCONN_TCP;
885    int want_udp = flags & SIGAR_NETCONN_UDP;
886    char *data;
887    int len;
888    int rc;
889    struct opthdr *op;
890
891    while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) {
892        if ((op->level == MIB2_TCP) &&
893            (op->name == MIB2_TCP_13) &&
894            want_tcp)
895        {
896            status =
897                tcp_connection_get(walker,
898                                   (struct mib2_tcpConnEntry *)data,
899                                   len);
900        }
901        else if ((op->level == MIB2_UDP) &&
902                 (op->name == MIB2_UDP_5) &&
903                 want_udp)
904        {
905            status =
906                udp_connection_get(walker,
907                                   (struct mib2_udpEntry *)data,
908                                   len);
909        }
910        else {
911            status = SIGAR_OK;
912        }
913
914        if (status != SIGAR_OK) {
915            break;
916        }
917    }
918
919    if (rc != GET_MIB2_EOD) {
920        close_mib2(&sigar->mib2);
921        return SIGAR_EMIB2;
922    }
923
924    return SIGAR_OK;
925}
926