xref: /6.6.0/sigar/src/os/solaris/solaris_sigar.c (revision 42d0b984)
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
136int sigar_os_close(sigar_t *sigar)
137{
138    kstat_close(sigar->kc);
139    if (sigar->mib2.sd != -1) {
140        close_mib2(&sigar->mib2);
141    }
142
143    if (sigar->ks.lcpu) {
144        free(sigar->ks.cpu);
145        free(sigar->ks.cpu_info);
146        free(sigar->ks.cpuid);
147    }
148    if (sigar->pinfo) {
149        free(sigar->pinfo);
150    }
151    if (sigar->cpulist.size != 0) {
152        sigar_cpu_list_destroy(sigar, &sigar->cpulist);
153    }
154    if (sigar->plib) {
155        dlclose(sigar->plib);
156    }
157    if (sigar->pargs) {
158        sigar_cache_destroy(sigar->pargs);
159    }
160    free(sigar);
161    return SIGAR_OK;
162}
163
164char *sigar_os_error_string(sigar_t *sigar, int err)
165{
166    switch (err) {
167      case SIGAR_EMIB2:
168        return sigar->mib2.errmsg;
169      default:
170        return NULL;
171    }
172}
173
174int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem)
175{
176    kstat_ctl_t *kc = sigar->kc;
177    kstat_t *ksp;
178    sigar_uint64_t kern = 0;
179
180    SIGAR_ZERO(mem);
181
182    /* XXX: is mem hot swappable or can we just do this during open ? */
183    mem->total = sysconf(_SC_PHYS_PAGES);
184    mem->total <<= sigar->pagesize;
185
186    if (sigar_kstat_update(sigar) == -1) {
187        return errno;
188    }
189
190    if ((ksp = sigar->ks.syspages) && kstat_read(kc, ksp, NULL) >= 0) {
191        sigar_koffsets_init_syspages(sigar, ksp);
192
193        mem->free = kSYSPAGES(KSTAT_SYSPAGES_FREE);
194        mem->free <<= sigar->pagesize;
195
196        mem->used = mem->total - mem->free;
197    }
198
199    if ((ksp = sigar->ks.mempages) && kstat_read(kc, ksp, NULL) >= 0) {
200        sigar_koffsets_init_mempages(sigar, ksp);
201    }
202
203    /* XXX mdb ::memstat cachelist/freelist not available to kstat, see: */
204    /* http://bugs.opensolaris.org/bugdatabase/view_bug.do?bug_id=6821980 */
205
206    /* ZFS ARC cache. see: http://opensolaris.org/jive/thread.jspa?messageID=393695 */
207    if ((ksp = kstat_lookup(sigar->kc, "zfs", 0, "arcstats")) &&
208        (kstat_read(sigar->kc, ksp, NULL) != -1))
209    {
210        kstat_named_t *kn;
211
212        if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "size"))) {
213            kern = kn->value.i64;
214        }
215        if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "c_min"))) {
216            /* c_min cannot be reclaimed they say */
217            if (kern > kn->value.i64) {
218                kern -= kn->value.i64;
219            }
220        }
221    }
222
223    mem->actual_free = mem->free + kern;
224    mem->actual_used = mem->used - kern;
225
226    sigar_mem_calc_ram(sigar, mem);
227
228    return SIGAR_OK;
229}
230
231int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap)
232{
233    kstat_t *ksp;
234    kstat_named_t *kn;
235    swaptbl_t *stab;
236    int num, i;
237    char path[PATH_MAX+1]; /* {un,re}used */
238
239    /* see: man swapctl(2) */
240    if ((num = swapctl(SC_GETNSWP, NULL)) == -1) {
241        return errno;
242    }
243
244    stab = malloc(num * sizeof(stab->swt_ent[0]) + sizeof(*stab));
245
246    stab->swt_n = num;
247    for (i=0; i<num; i++) {
248        stab->swt_ent[i].ste_path = path;
249    }
250
251    if ((num = swapctl(SC_LIST, stab)) == -1) {
252        free(stab);
253        return errno;
254    }
255
256    num = num < stab->swt_n ? num : stab->swt_n;
257    swap->total = swap->free = 0;
258    for (i=0; i<num; i++) {
259        if (stab->swt_ent[i].ste_flags & ST_INDEL) {
260            continue; /* swap file is being deleted */
261        }
262        swap->total += stab->swt_ent[i].ste_pages;
263        swap->free  += stab->swt_ent[i].ste_free;
264    }
265    free(stab);
266
267    swap->total <<= sigar->pagesize;
268    swap->free  <<= sigar->pagesize;
269    swap->used  = swap->total - swap->free;
270
271    if (sigar_kstat_update(sigar) == -1) {
272        return errno;
273    }
274    if (!(ksp = kstat_lookup(sigar->kc, "cpu", -1, "vm"))) {
275        swap->page_in = swap->page_out = SIGAR_FIELD_NOTIMPL;
276        return SIGAR_OK;
277    }
278
279    swap->page_in = swap->page_out = 0;
280
281    /* XXX: these stats do not exist in this form on solaris 8 or 9.
282     * they are in the raw cpu_stat struct, but thats not
283     * binary compatible
284     */
285    do {
286        if (kstat_read(sigar->kc, ksp, NULL) < 0) {
287            break;
288        }
289
290        if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "pgin"))) {
291            swap->page_in += kn->value.i64;  /* vmstat -s | grep "page ins" */
292        }
293        if ((kn = (kstat_named_t *)kstat_data_lookup(ksp, "pgout"))) {
294            swap->page_out += kn->value.i64; /* vmstat -s | grep "page outs" */
295        }
296    } while ((ksp = kstat_next(ksp, "cpu", -1, "vm")));
297
298    return SIGAR_OK;
299}
300
301#ifndef KSTAT_NAMED_STR_PTR
302/* same offset as KSTAT_NAMED_STR_PTR(brand) */
303#define KSTAT_NAMED_STR_PTR(n) (char *)((n)->value.i32)
304#endif
305
306static int get_chip_brand(sigar_t *sigar, int processor,
307                          sigar_cpu_info_t *info)
308{
309    kstat_t *ksp = sigar->ks.cpu_info[processor];
310    kstat_named_t *brand;
311
312    if (sigar->solaris_version < 10) {
313        /* don't bother; doesn't exist. */
314        return 0;
315    }
316
317    if (ksp &&
318        (kstat_read(sigar->kc, ksp, NULL) != -1) &&
319        (brand = (kstat_named_t *)kstat_data_lookup(ksp, "brand")))
320    {
321        char *name = KSTAT_NAMED_STR_PTR(brand);
322
323        char *vendor = "Sun";
324        char *vendors[] = {
325            "Intel", "AMD", NULL
326        };
327        int i;
328
329        if (!name) {
330            return 0;
331        }
332
333        for (i=0; vendors[i]; i++) {
334            if (strstr(name, vendors[i])) {
335                vendor = vendors[i];
336                break;
337            }
338        }
339
340        SIGAR_SSTRCPY(info->vendor, vendor);
341#if 0
342        SIGAR_SSTRCPY(info->model, name);
343        sigar_cpu_model_adjust(sigar, info);
344#endif
345        return 1;
346    }
347    else {
348        return 0;
349    }
350}
351
352static void free_chip_id(void *ptr)
353{
354    /*noop*/
355}
356
357static int get_chip_id(sigar_t *sigar, int processor)
358{
359    kstat_t *ksp = sigar->ks.cpu_info[processor];
360    kstat_named_t *chipid;
361
362    if (ksp &&
363        (kstat_read(sigar->kc, ksp, NULL) != -1) &&
364        (chipid = (kstat_named_t *)kstat_data_lookup(ksp, "chip_id")))
365    {
366        return chipid->value.i32;
367    }
368    else {
369        return -1;
370    }
371}
372
373int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu)
374{
375    int status, i;
376
377    status = sigar_cpu_list_get(sigar, &sigar->cpulist);
378
379    if (status != SIGAR_OK) {
380        return status;
381    }
382
383    SIGAR_ZERO(cpu);
384
385    for (i=0; i<sigar->cpulist.number; i++) {
386        sigar_cpu_t *xcpu = &sigar->cpulist.data[i];
387
388        cpu->user  += xcpu->user;
389        cpu->sys   += xcpu->sys;
390        cpu->idle  += xcpu->idle;
391        cpu->nice  += xcpu->nice;
392        cpu->wait  += xcpu->wait;
393        cpu->total += xcpu->total;
394    }
395
396    return SIGAR_OK;
397}
398
399int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist)
400{
401    kstat_ctl_t *kc = sigar->kc;
402    kstat_t *ksp;
403    uint_t cpuinfo[CPU_STATES];
404    unsigned int i;
405    int is_debug = SIGAR_LOG_IS_DEBUG(sigar);
406    sigar_cache_t *chips;
407
408    if (sigar_kstat_update(sigar) == -1) {
409        return errno;
410    }
411
412    if (cpulist == &sigar->cpulist) {
413        if (sigar->cpulist.size == 0) {
414            /* create once */
415            sigar_cpu_list_create(cpulist);
416        }
417        else {
418            /* reset, re-using cpulist.data */
419            sigar->cpulist.number = 0;
420        }
421    }
422    else {
423        sigar_cpu_list_create(cpulist);
424    }
425
426    if (is_debug) {
427        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
428                         "[cpu_list] OS reports %d CPUs",
429                         sigar->ncpu);
430    }
431
432    chips = sigar_cache_new(16);
433    chips->free_value = free_chip_id;
434
435    for (i=0; i<sigar->ncpu; i++) {
436        sigar_cpu_t *cpu;
437        char *buf;
438        int chip_id;
439        sigar_cache_entry_t *ent;
440
441        if (!CPU_ONLINE(sigar->ks.cpuid[i])) {
442            sigar_log_printf(sigar, SIGAR_LOG_INFO,
443                             "cpu %d (id=%d) is offline",
444                             i, sigar->ks.cpuid[i]);
445            continue;
446        }
447
448        if (!(ksp = sigar->ks.cpu[i])) {
449            sigar_log_printf(sigar, SIGAR_LOG_ERROR,
450                             "NULL ksp for cpu %d (id=%d)",
451                             i, sigar->ks.cpuid[i]);
452            continue; /* shouldnot happen */
453        }
454
455        if (kstat_read(kc, ksp, NULL) < 0) {
456            sigar_log_printf(sigar, SIGAR_LOG_ERROR,
457                             "kstat_read failed for cpu %d (id=%d): %s",
458                             i, sigar->ks.cpuid[i],
459                             sigar_strerror(sigar, errno));
460            continue; /* shouldnot happen */
461        }
462
463        /*
464         * cpu_stat_t is not binary compatible between solaris versions.
465         * since cpu_stat is a 'raw' kstat and not 'named' we cannot
466         * use name based lookups as we do for others.
467         * the start of the cpu_stat_t structure is binary compatible,
468         * which looks like so:
469         * typedef struct cpu_stat {
470         *    kmutex_t        cpu_stat_lock;
471         *    cpu_sysinfo_t   cpu_sysinfo;
472         *    ...
473         *    typedef struct cpu_sysinfo {
474         *       ulong cpu[CPU_STATES];
475         *       ...
476         * we just copy the piece we need below:
477         */
478        buf = ksp->ks_data;
479        buf += sizeof(kmutex_t);
480        memcpy(&cpuinfo[0], buf, sizeof(cpuinfo));
481        chip_id = sigar->cpu_list_cores ? -1 : get_chip_id(sigar, i);
482
483        if (chip_id == -1) {
484            SIGAR_CPU_LIST_GROW(cpulist);
485            cpu = &cpulist->data[cpulist->number++];
486            SIGAR_ZERO(cpu);
487        }
488        else {
489            /* merge times of logical processors */
490            ent = sigar_cache_get(chips, chip_id);
491            if (ent->value) {
492                cpu = &cpulist->data[(long)ent->value-1];
493            }
494            else {
495                SIGAR_CPU_LIST_GROW(cpulist);
496                cpu = &cpulist->data[cpulist->number++];
497                ent->value = (void *)(long)cpulist->number;
498                SIGAR_ZERO(cpu);
499
500                if (is_debug) {
501                    sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
502                                     "[cpu_list] Merging times of"
503                                     " logical processors for chip_id=%d",
504                                     chip_id);
505                }
506            }
507        }
508
509        cpu->user += SIGAR_TICK2MSEC(cpuinfo[CPU_USER]);
510        cpu->sys  += SIGAR_TICK2MSEC(cpuinfo[CPU_KERNEL]);
511        cpu->idle += SIGAR_TICK2MSEC(cpuinfo[CPU_IDLE]);
512        cpu->wait += SIGAR_TICK2MSEC(cpuinfo[CPU_WAIT]);
513        cpu->nice += 0; /* no cpu->nice */
514        cpu->total = cpu->user + cpu->sys + cpu->idle + cpu->wait;
515    }
516
517    sigar_cache_destroy(chips);
518
519    return SIGAR_OK;
520}
521
522int sigar_uptime_get(sigar_t *sigar,
523                     sigar_uptime_t *uptime)
524{
525    if (sigar->boot_time) {
526        uptime->uptime = time(NULL) - sigar->boot_time;
527    }
528    else {
529        uptime->uptime = 0; /* XXX: shouldn't happen */
530    }
531
532    return SIGAR_OK;
533}
534
535static int loadavg_keys[] = {
536    KSTAT_SYSTEM_LOADAVG_1,
537    KSTAT_SYSTEM_LOADAVG_2,
538    KSTAT_SYSTEM_LOADAVG_3
539};
540
541int sigar_loadavg_get(sigar_t *sigar,
542                      sigar_loadavg_t *loadavg)
543{
544    kstat_t *ksp;
545    int i;
546
547    if (sigar_kstat_update(sigar) == -1) {
548        return errno;
549    }
550
551    if (!(ksp = sigar->ks.system)) {
552        return -1;
553    }
554
555    if (kstat_read(sigar->kc, ksp, NULL) < 0) {
556        return -1;
557    }
558
559    sigar_koffsets_init_system(sigar, ksp);
560
561    for (i=0; i<3; i++) {
562        loadavg->loadavg[i] = (double)kSYSTEM(loadavg_keys[i]) / FSCALE;
563    }
564
565    return SIGAR_OK;
566}
567
568#define LIBPROC "/usr/lib/libproc.so"
569
570#define CHECK_PSYM(s) \
571    if (!sigar->s) { \
572        sigar_log_printf(sigar, SIGAR_LOG_WARN, \
573                         "[%s] Symbol not found: %s", \
574                         SIGAR_FUNC, #s); \
575        dlclose(sigar->plib); \
576        sigar->plib = NULL; \
577        return SIGAR_ENOTIMPL; \
578    }
579
580static char *proc_readlink(const char *name, char *buffer, size_t size)
581{
582    int len;
583
584    if ((len = readlink(name, buffer, size-1)) < 0) {
585        return NULL;
586    }
587
588    buffer[len] = '\0';
589    return buffer;
590}
591
592static int sigar_init_libproc(sigar_t *sigar)
593{
594    if (sigar->plib) {
595        return SIGAR_OK;
596    }
597
598    /* libproc.so ships with 5.8+ */
599    /* interface is undocumented, see libproc.h in the sun jdk sources */
600    sigar->plib = dlopen(LIBPROC, RTLD_LAZY);
601
602    if (!sigar->plib) {
603        sigar_log_printf(sigar, SIGAR_LOG_WARN,
604                         "[%s] dlopen(%s) = %s",
605                         SIGAR_FUNC, LIBPROC, dlerror());
606        return SIGAR_ENOTIMPL;
607    }
608
609    sigar->pgrab    = (proc_grab_func_t)dlsym(sigar->plib, "Pgrab");
610    sigar->pfree    = (proc_free_func_t)dlsym(sigar->plib, "Pfree");
611    sigar->pcreate_agent = (proc_create_agent_func_t)dlsym(sigar->plib, "Pcreate_agent");
612    sigar->pdestroy_agent = (proc_destroy_agent_func_t)dlsym(sigar->plib, "Pdestroy_agent");
613    sigar->pobjname = (proc_objname_func_t)dlsym(sigar->plib, "Pobjname");
614    sigar->pexename = (proc_exename_func_t)dlsym(sigar->plib, "Pexecname");
615    sigar->pdirname = (proc_dirname_func_t)dlsym(sigar->plib, "proc_dirname");
616    sigar->pfstat64 = (proc_fstat64_func_t)dlsym(sigar->plib, "pr_fstat64");
617    sigar->pgetsockopt = (proc_getsockopt_func_t)dlsym(sigar->plib, "pr_getsockopt");
618    sigar->pgetsockname = (proc_getsockname_func_t)dlsym(sigar->plib, "pr_getsockname");
619
620    CHECK_PSYM(pgrab);
621    CHECK_PSYM(pfree);
622    CHECK_PSYM(pobjname);
623
624    return SIGAR_OK;
625}
626
627/* from libproc.h, not included w/ solaris distro */
628/* Error codes from Pgrab(), Pfgrab_core(), and Pgrab_core() */
629#define G_STRANGE       -1      /* Unanticipated error, errno is meaningful */
630#define G_NOPROC        1       /* No such process */
631#define G_NOCORE        2       /* No such core file */
632#define G_NOPROCORCORE  3       /* No such proc or core (for proc_arg_grab) */
633#define G_NOEXEC        4       /* Cannot locate executable file */
634#define G_ZOMB          5       /* Zombie process */
635#define G_PERM          6       /* No permission */
636#define G_BUSY          7       /* Another process has control */
637#define G_SYS           8       /* System process */
638#define G_SELF          9       /* Process is self */
639#define G_INTR          10      /* Interrupt received while grabbing */
640#define G_LP64          11      /* Process is _LP64, self is ILP32 */
641#define G_FORMAT        12      /* File is not an ELF format core file */
642#define G_ELF           13      /* Libelf error, elf_errno() is meaningful */
643#define G_NOTE          14      /* Required PT_NOTE Phdr not present in core */
644
645static int sigar_pgrab(sigar_t *sigar, sigar_pid_t pid,
646                       const char *func,
647                       struct ps_prochandle **phandle)
648{
649    int pstatus;
650
651    if (!(*phandle = sigar->pgrab(pid, 0x01, &pstatus))) {
652        switch (pstatus) {
653          case G_NOPROC:
654            return ESRCH;
655          case G_PERM:
656            return EACCES;
657          default:
658            sigar_log_printf(sigar, SIGAR_LOG_ERROR,
659                             "[%s] Pgrab error=%d",
660                             func, pstatus);
661            return ENOTSUP; /*XXX*/
662        }
663    }
664
665    return SIGAR_OK;
666}
667
668int sigar_os_proc_list_get(sigar_t *sigar,
669                           sigar_proc_list_t *proclist)
670{
671    return sigar_proc_list_procfs_get(sigar, proclist);
672}
673
674int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
675                       sigar_proc_mem_t *procmem)
676{
677    int status = sigar_proc_psinfo_get(sigar, pid);
678    psinfo_t *pinfo = sigar->pinfo;
679    prusage_t usage;
680
681    if (status != SIGAR_OK) {
682        return status;
683    }
684
685    procmem->size     = pinfo->pr_size << 10;
686    procmem->resident = pinfo->pr_rssize << 10;
687    procmem->share    = SIGAR_FIELD_NOTIMPL;
688
689    if (sigar_proc_usage_get(sigar, &usage, pid) == SIGAR_OK) {
690        procmem->minor_faults = usage.pr_minf;
691        procmem->major_faults = usage.pr_majf;
692        procmem->page_faults =
693            procmem->minor_faults +
694            procmem->major_faults;
695    }
696    else {
697        procmem->minor_faults = SIGAR_FIELD_NOTIMPL;
698        procmem->major_faults = SIGAR_FIELD_NOTIMPL;
699        procmem->page_faults = SIGAR_FIELD_NOTIMPL;
700    }
701
702    return SIGAR_OK;
703}
704
705int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid,
706                        sigar_proc_cred_t *proccred)
707{
708    int status = sigar_proc_psinfo_get(sigar, pid);
709    psinfo_t *pinfo = sigar->pinfo;
710
711    if (status != SIGAR_OK) {
712        return status;
713    }
714
715    proccred->uid  = pinfo->pr_uid;
716    proccred->gid  = pinfo->pr_gid;
717    proccred->euid = pinfo->pr_euid;
718    proccred->egid = pinfo->pr_egid;
719
720    return SIGAR_OK;
721}
722
723#define TIMESTRUCT_2MSEC(t) \
724    ((t.tv_sec * MILLISEC) + (t.tv_nsec / (NANOSEC/MILLISEC)))
725
726int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid,
727                        sigar_proc_time_t *proctime)
728{
729    prusage_t usage;
730    int status;
731
732    if ((status = sigar_proc_usage_get(sigar, &usage, pid)) != SIGAR_OK) {
733        return status;
734    }
735
736    proctime->start_time = usage.pr_create.tv_sec + sigar->boot_time;
737    proctime->start_time *= MILLISEC;
738
739    if (usage.pr_utime.tv_sec < 0) {
740        /* XXX wtf?  seen on solaris 10, only for the self process */
741        pstatus_t pstatus;
742
743        status = sigar_proc_status_get(sigar, &pstatus, pid);
744        if (status != SIGAR_OK) {
745            return status;
746        }
747
748        usage.pr_utime.tv_sec  = pstatus.pr_utime.tv_sec;
749        usage.pr_utime.tv_nsec = pstatus.pr_utime.tv_nsec;
750        usage.pr_stime.tv_sec  = pstatus.pr_stime.tv_sec;
751        usage.pr_stime.tv_nsec = pstatus.pr_stime.tv_nsec;
752    }
753
754    proctime->user = TIMESTRUCT_2MSEC(usage.pr_utime);
755    proctime->sys  = TIMESTRUCT_2MSEC(usage.pr_stime);
756    proctime->total = proctime->user + proctime->sys;
757
758    return SIGAR_OK;
759}
760
761int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid,
762                         sigar_proc_state_t *procstate)
763{
764    int status = sigar_proc_psinfo_get(sigar, pid);
765    psinfo_t *pinfo = sigar->pinfo;
766
767    if (status != SIGAR_OK) {
768        return status;
769    }
770
771    SIGAR_SSTRCPY(procstate->name, pinfo->pr_fname);
772    procstate->ppid = pinfo->pr_ppid;
773    procstate->tty  = pinfo->pr_ttydev;
774    procstate->priority = pinfo->pr_lwp.pr_pri;
775    procstate->nice     = pinfo->pr_lwp.pr_nice - NZERO;
776    procstate->threads  = pinfo->pr_nlwp;
777    procstate->processor = pinfo->pr_lwp.pr_onpro;
778
779    switch (pinfo->pr_lwp.pr_state) {
780      case SONPROC:
781      case SRUN:
782        procstate->state = 'R';
783        break;
784      case SZOMB:
785        procstate->state = 'Z';
786        break;
787      case SSLEEP:
788        procstate->state = 'S';
789        break;
790      case SSTOP:
791        procstate->state = 'T';
792        break;
793      case SIDL:
794        procstate->state = 'D';
795        break;
796    }
797
798    return SIGAR_OK;
799}
800
801typedef struct {
802    int timestamp;
803    char *args;
804} pargs_t;
805
806static void pargs_free(void *value)
807{
808    pargs_t *pargs = (pargs_t *)value;
809    if (pargs->args != NULL) {
810        free(pargs->args);
811    }
812    free(pargs);
813}
814
815static int ucb_ps_args_get(sigar_t *sigar, sigar_pid_t pid,
816                           sigar_proc_args_t *procargs,
817                           int timestamp)
818{
819    char buffer[9086], *args=NULL, *arg;
820    sigar_cache_entry_t *ent;
821    FILE *fp;
822    pargs_t *pargs;
823
824    if (!sigar->pargs) {
825        sigar->pargs = sigar_cache_new(15);
826        sigar->pargs->free_value = pargs_free;
827    }
828
829    ent = sigar_cache_get(sigar->pargs, pid);
830    if (ent->value) {
831        pargs = (pargs_t *)ent->value;
832        if (pargs->timestamp != timestamp) {
833            if (pargs->args) {
834                free(pargs->args);
835                pargs->args = NULL;
836            }
837        }
838    }
839    else {
840        pargs = malloc(sizeof(*pargs));
841        pargs->args = NULL;
842        ent->value = pargs;
843    }
844
845    pargs->timestamp = timestamp;
846
847    if (pargs->args) {
848        args = pargs->args;
849    }
850    else {
851        snprintf(buffer, sizeof(buffer),
852                 SIGAR_USR_UCB_PS " -ww %ld", (long)pid);
853
854        if (!(fp = popen(buffer, "r"))) {
855            return errno;
856        }
857        /* skip header */
858        (void)fgets(buffer, sizeof(buffer), fp);
859        if ((args = fgets(buffer, sizeof(buffer), fp))) {
860            int len;
861
862            /* skip PID,TT,S,TIME */
863            args = sigar_skip_multiple_token(args, 4);
864            SIGAR_SKIP_SPACE(args);
865            len = strlen(args);
866            if (len > 0) {
867                args[len-1] = '\0'; /* chop \n */
868            }
869
870            pargs->args = malloc(len+1);
871            memcpy(pargs->args, args, len);
872        }
873
874        pclose(fp);
875
876        if (!args) {
877            return ESRCH;
878        }
879    }
880
881    while (*args && (arg = sigar_getword(&args, ' '))) {
882        SIGAR_PROC_ARGS_GROW(procargs);
883        procargs->data[procargs->number++] = arg;
884    }
885
886    return SIGAR_OK;
887}
888
889int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid,
890                           sigar_proc_args_t *procargs)
891{
892    psinfo_t *pinfo;
893    int fd, status;
894    char buffer[9086];
895    char *argvb[56];
896    char **argvp = argvb;
897
898    int n;
899    size_t nread = 0;
900    unsigned int argv_size;
901
902    if ((status = sigar_proc_psinfo_get(sigar, pid)) != SIGAR_OK) {
903        return status;
904    }
905    pinfo = sigar->pinfo;
906
907    if (pinfo->pr_argc == 0) {
908        procargs->number = 0;
909        return SIGAR_OK;
910    }
911    else if (pinfo->pr_dmodel != PR_MODEL_NATIVE) {
912        /* we are compiled in 32bit mode
913         * punt any 64bit native process,
914         * sizeof our structures can't handle.
915         */
916        if (sigar->use_ucb_ps) {
917            return ucb_ps_args_get(sigar, pid, procargs,
918                                   pinfo->pr_start.tv_sec);
919        }
920        else {
921            return ENOTSUP;
922        }
923    }
924
925    argv_size = sizeof(*argvp) * pinfo->pr_argc;
926
927    (void)SIGAR_PROC_FILENAME(buffer, pid, "/as");
928
929    if ((fd = open(buffer, O_RDONLY)) < 0) {
930        if ((errno == EACCES) && sigar->use_ucb_ps) {
931            return ucb_ps_args_get(sigar, pid, procargs,
932                                   pinfo->pr_start.tv_sec);
933        }
934        else {
935            return PROC_ERRNO;
936        }
937    }
938
939    if (argv_size > sizeof(argvb)) {
940        argvp = malloc(argv_size);
941    }
942
943    if ((nread = pread(fd, argvp, argv_size, pinfo->pr_argv)) <= 0) {
944        close(fd);
945        if (argvp != argvb) {
946            free(argvp);
947        }
948        return errno;
949    }
950
951    for (n = 0; n < pinfo->pr_argc; n++) {
952        int alen;
953        char *arg;
954
955        if ((nread = pread(fd, buffer, sizeof(buffer)-1, (off_t)argvp[n])) <= 0) {
956            close(fd);
957            if (argvp != argvb) {
958                free(argvp);
959            }
960            return errno;
961        }
962
963        buffer[nread] = '\0';
964        alen = strlen(buffer)+1;
965        arg = malloc(alen);
966        memcpy(arg, buffer, alen);
967
968        SIGAR_PROC_ARGS_GROW(procargs);
969        procargs->data[procargs->number++] = arg;
970    }
971
972    if (argvp != argvb) {
973        free(argvp);
974    }
975
976    close(fd);
977
978    return SIGAR_OK;
979}
980
981int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid,
982                       sigar_proc_env_t *procenv)
983{
984    psinfo_t *pinfo;
985    int fd, status;
986    char buffer[BUFSIZ], *offsets[512];
987    size_t nread;
988    int n=0, max=sizeof(offsets)/sizeof(char *);
989
990    if ((status = sigar_proc_psinfo_get(sigar, pid)) != SIGAR_OK) {
991        return status;
992    }
993    pinfo = sigar->pinfo;
994
995    (void)SIGAR_PROC_FILENAME(buffer, pid, "/as");
996
997    if ((fd = open(buffer, O_RDONLY)) < 0) {
998        return PROC_ERRNO;
999    }
1000
1001    if ((nread = pread(fd, offsets, sizeof(offsets),
1002                       pinfo->pr_envp)) <= 0)
1003    {
1004        close(fd);
1005        return errno;
1006    }
1007
1008    while ((n < max) && offsets[n]) {
1009        char *val;
1010        int klen, vlen, status;
1011        char key[128]; /* XXX is there a max key size? */
1012
1013        if ((nread = pread(fd, buffer, sizeof(buffer),
1014                           (off_t)offsets[n++])) <= 0)
1015        {
1016            close(fd);
1017            return errno;
1018        }
1019
1020        val = strchr(buffer, '=');
1021
1022        if (val == NULL) {
1023            break; /*XXX*/
1024        }
1025
1026        klen = val - buffer;
1027        SIGAR_SSTRCPY(key, buffer);
1028        key[klen] = '\0';
1029        ++val;
1030
1031        vlen = strlen(val);
1032
1033        status = procenv->env_getter(procenv->data,
1034                                     key, klen, val, vlen);
1035
1036        if (status != SIGAR_OK) {
1037            /* not an error; just stop iterating */
1038            break;
1039        }
1040    }
1041
1042    close(fd);
1043
1044    return SIGAR_OK;
1045}
1046
1047int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid,
1048                      sigar_proc_fd_t *procfd)
1049{
1050    int status =
1051        sigar_proc_fd_count(sigar, pid, &procfd->total);
1052
1053    return status;
1054}
1055
1056static int sigar_proc_path_exe_get(sigar_t *sigar, sigar_pid_t pid,
1057                                   sigar_proc_exe_t *procexe)
1058{
1059    /* solaris 10+ */
1060    char buffer[BUFSIZ];
1061
1062    (void)SIGAR_PROC_FILENAME(buffer, pid, "/path/a.out");
1063    if (!proc_readlink(buffer, procexe->name, sizeof(procexe->name))) {
1064        procexe->name[0] = '\0';
1065    }
1066
1067    (void)SIGAR_PROC_FILENAME(buffer, pid, "/path/cwd");
1068    if (!proc_readlink(buffer, procexe->cwd, sizeof(procexe->cwd))) {
1069        procexe->cwd[0] = '\0';
1070    }
1071
1072    (void)SIGAR_PROC_FILENAME(buffer, pid, "/path/root");
1073    if (!proc_readlink(buffer, procexe->root, sizeof(procexe->root))) {
1074        procexe->root[0] = '\0';
1075    }
1076
1077    return SIGAR_OK;
1078}
1079
1080static int proc_module_get_exe(void *data, char *name, int len)
1081{
1082    sigar_proc_exe_t *procexe = (sigar_proc_exe_t *)data;
1083    SIGAR_STRNCPY(procexe->name, name, sizeof(procexe->name));
1084    return !SIGAR_OK; /* break loop */
1085}
1086
1087static int sigar_which_exe_get(sigar_t *sigar, sigar_proc_exe_t *procexe)
1088{
1089    char *path = getenv("PATH");
1090    char exe[PATH_MAX];
1091    if (path == NULL) {
1092        return EINVAL;
1093    }
1094
1095    while (path) {
1096        char *ptr = strchr(path, ':');
1097        if (!ptr) {
1098            break;
1099        }
1100        exe[0] = '\0';
1101        strncat(exe, path, ptr-path);
1102        strncat(exe, "/", 1);
1103        strcat(exe, procexe->name);
1104        if (access(exe, X_OK) == 0) {
1105            SIGAR_STRNCPY(procexe->name, exe, sizeof(procexe->name));
1106            break;
1107        }
1108        path = ptr+1;
1109    }
1110
1111    return ENOENT;
1112}
1113
1114int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid,
1115                       sigar_proc_exe_t *procexe)
1116{
1117    int status;
1118    char buffer[BUFSIZ];
1119    struct ps_prochandle *phandle;
1120
1121    if (sigar->solaris_version >= 10) {
1122        return sigar_proc_path_exe_get(sigar, pid, procexe);
1123    }
1124
1125    if ((status = sigar_init_libproc(sigar)) != SIGAR_OK) {
1126        return status;
1127    }
1128
1129    procexe->name[0] = '\0';
1130
1131    /* Pgrab would return G_SELF error */
1132    if (pid == sigar_pid_get(sigar)) {
1133        sigar_proc_modules_t procmods;
1134        procmods.module_getter = proc_module_get_exe;
1135        procmods.data = procexe;
1136
1137        status =
1138            sigar_dlinfo_modules(sigar, &procmods);
1139        if (status == SIGAR_OK) {
1140            if (procexe->name[0] != '/') {
1141                sigar_which_exe_get(sigar, procexe);
1142            }
1143        }
1144    }
1145    else {
1146        status = sigar_pgrab(sigar, pid, SIGAR_FUNC, &phandle);
1147
1148        if (status == SIGAR_OK) {
1149            sigar->pexename(phandle, procexe->name, sizeof(procexe->name));
1150            sigar->pfree(phandle);
1151        }
1152    }
1153
1154    if (procexe->name[0] == '\0') {
1155        /*XXX*/
1156    }
1157
1158    (void)SIGAR_PROC_FILENAME(buffer, pid, "/cwd");
1159
1160    if (!sigar->pdirname(buffer, procexe->cwd, sizeof(procexe->cwd))) {
1161        procexe->cwd[0] = '\0';
1162    }
1163
1164    (void)SIGAR_PROC_FILENAME(buffer, pid, "/root");
1165
1166    if (!(sigar->pdirname(buffer, procexe->root, sizeof(procexe->root)))) {
1167        procexe->root[0] = '\0';
1168    }
1169
1170    return SIGAR_OK;
1171}
1172
1173static int sigar_read_xmaps(sigar_t *sigar,
1174                            prxmap_t *xmaps, int total,
1175                            unsigned long *last_inode,
1176                            struct ps_prochandle *phandle,
1177                            sigar_proc_modules_t *procmods)
1178{
1179    int status, i;
1180    unsigned long inode;
1181    char buffer[BUFSIZ];
1182
1183    for (i=0; i<total; i++) {
1184        if (xmaps[i].pr_mflags & MA_ANON) {
1185            continue; /* heap, stack, etc */
1186        }
1187
1188        inode = xmaps[i].pr_ino;
1189
1190        if ((inode == 0) || (inode == *last_inode)) {
1191            *last_inode = 0;
1192            continue;
1193        }
1194
1195        *last_inode = inode;
1196
1197        sigar->pobjname(phandle, xmaps[i].pr_vaddr, buffer, sizeof(buffer));
1198
1199        status =
1200            procmods->module_getter(procmods->data, buffer, strlen(buffer));
1201
1202        if (status != SIGAR_OK) {
1203            /* not an error; just stop iterating */
1204            return status;
1205        }
1206    }
1207
1208    return SIGAR_OK;
1209}
1210
1211static int sigar_pgrab_modules(sigar_t *sigar, sigar_pid_t pid,
1212                               sigar_proc_modules_t *procmods)
1213{
1214    int fd, pstatus;
1215    off_t map_size, nread;
1216    unsigned long last_inode = 0;
1217    prxmap_t xmaps[15]; /* ~2K */
1218    struct ps_prochandle *phandle;
1219    struct stat statbuf;
1220    char buffer[BUFSIZ];
1221
1222    (void)SIGAR_PROC_FILENAME(buffer, pid, "/xmap");
1223
1224    if ((fd = open(buffer, O_RDONLY)) < 0) {
1225        return errno;
1226    }
1227
1228    if (fstat(fd, &statbuf) < 0) {
1229        close(fd);
1230        return errno;
1231    }
1232
1233    map_size = statbuf.st_size;
1234
1235    if (SIGAR_LOG_IS_DEBUG(sigar)) {
1236        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1237                         "[%s] pid=%d, size=%d",
1238                         SIGAR_FUNC, pid, map_size);
1239    }
1240
1241    if ((pstatus = sigar_init_libproc(sigar)) != SIGAR_OK) {
1242        close(fd);
1243        return pstatus;
1244    }
1245
1246    pstatus = sigar_pgrab(sigar, pid, SIGAR_FUNC, &phandle);
1247
1248    if (pstatus != SIGAR_OK) {
1249        close(fd);
1250        return pstatus;
1251    }
1252
1253    for (nread=0; nread<statbuf.st_size; ) {
1254        off_t wanted = map_size > sizeof(xmaps) ? sizeof(xmaps) : map_size;
1255        int total = wanted / sizeof(prxmap_t);
1256
1257        if (pread(fd, xmaps, wanted, nread) != wanted) {
1258            close(fd);
1259            sigar->pfree(phandle);
1260            return errno;
1261        }
1262
1263        if (SIGAR_LOG_IS_DEBUG(sigar)) {
1264            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1265                             "[%s] nread=%d, map_size=%d, wanted=%d, total=%d",
1266                             SIGAR_FUNC,
1267                             nread, map_size, wanted, total);
1268        }
1269
1270        if (sigar_read_xmaps(sigar, xmaps, total,
1271                             &last_inode,
1272                             phandle, procmods) != SIGAR_OK)
1273        {
1274            break;
1275        }
1276
1277        nread += wanted;
1278        map_size -= wanted;
1279    }
1280
1281    close(fd);
1282
1283    sigar->pfree(phandle);
1284
1285    return SIGAR_OK;
1286}
1287
1288int sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid,
1289                           sigar_proc_modules_t *procmods)
1290{
1291    if (pid == sigar_pid_get(sigar)) {
1292        /* Pgrab would return G_SELF, this is faster anyhow */
1293        /* XXX one difference to Pgrab, first entry is not the exe name */
1294        return sigar_dlinfo_modules(sigar, procmods);
1295    }
1296    else {
1297        return sigar_pgrab_modules(sigar, pid, procmods);
1298    }
1299}
1300
1301#define TIME_NSEC(t) \
1302    (SIGAR_SEC2NANO((t).tv_sec) + (sigar_uint64_t)(t).tv_nsec)
1303
1304int sigar_thread_cpu_get(sigar_t *sigar,
1305                         sigar_uint64_t id,
1306                         sigar_thread_cpu_t *cpu)
1307{
1308    struct lwpinfo info;
1309
1310    if (id != 0) {
1311        return SIGAR_ENOTIMPL;
1312    }
1313
1314    _lwp_info(&info);
1315
1316    cpu->user  = TIME_NSEC(info.lwp_utime);
1317    cpu->sys   = TIME_NSEC(info.lwp_stime);
1318    cpu->total = TIME_NSEC(info.lwp_utime) + TIME_NSEC(info.lwp_stime);
1319
1320    return SIGAR_OK;
1321}
1322
1323#include <sys/mnttab.h>
1324
1325int sigar_os_fs_type_get(sigar_file_system_t *fsp)
1326{
1327    char *type = fsp->sys_type_name;
1328
1329    switch (*type) {
1330      case 'u':
1331        if (strEQ(type, "ufs")) {
1332            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1333        }
1334        break;
1335        /* XXX */
1336    }
1337
1338    return fsp->type;
1339}
1340
1341int sigar_file_system_list_get(sigar_t *sigar,
1342                               sigar_file_system_list_t *fslist)
1343{
1344    struct mnttab ent;
1345    sigar_file_system_t *fsp;
1346    FILE *fp = fopen(MNTTAB, "r");
1347
1348    if (!fp) {
1349        return errno;
1350    }
1351
1352    sigar_file_system_list_create(fslist);
1353
1354    while (getmntent(fp, &ent) == 0) {
1355        if (strstr(ent.mnt_mntopts, "ignore")) {
1356            continue; /* e.g. vold */
1357        }
1358
1359        SIGAR_FILE_SYSTEM_LIST_GROW(fslist);
1360
1361        fsp = &fslist->data[fslist->number++];
1362
1363        SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_mountp);
1364        SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_special);
1365        SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_fstype);
1366        SIGAR_SSTRCPY(fsp->options, ent.mnt_mntopts);
1367        sigar_fs_type_init(fsp);
1368    }
1369
1370    fclose(fp);
1371
1372    return SIGAR_OK;
1373}
1374
1375typedef struct {
1376    char device[PATH_MAX];
1377    char name[8];
1378    int instance;
1379} fsdev_path_t;
1380
1381typedef struct {
1382    char name[256];
1383    int is_partition;
1384    sigar_disk_usage_t disk;
1385} iodev_t;
1386
1387static fsdev_path_t *get_fsdev_paths(sigar_t *sigar,
1388                                     sigar_file_system_list_t *fslist)
1389{
1390    int i, ndisk, size;
1391    char buffer[BUFSIZ], *ptr;
1392    char *dev, *inst, *drv;
1393    fsdev_path_t *paths, *mapping;
1394    FILE *fp = fopen("/etc/path_to_inst", "r");
1395
1396    if (!fp) {
1397        return NULL;
1398    }
1399
1400    for (i=0, ndisk=0; i<fslist->number; i++) {
1401        sigar_file_system_t *fsp = &fslist->data[i];
1402        if (fsp->type == SIGAR_FSTYPE_LOCAL_DISK) {
1403            ndisk++;
1404        }
1405    }
1406
1407    size = sizeof(*paths) * (ndisk+1);
1408    mapping = paths = malloc(size);
1409    memset(mapping, '\0', size);
1410
1411    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
1412        /* eat dust java */
1413        char *q;
1414
1415        SIGAR_SKIP_SPACE(ptr);
1416        if (*ptr == '#') {
1417            continue;
1418        }
1419        if (*ptr == '"') {
1420            ptr++;
1421        }
1422        dev = ptr;
1423        if (!(q = strchr(ptr, '"'))) {
1424            continue;
1425        }
1426        ptr = q+1;
1427        *q = '\0';
1428        SIGAR_SKIP_SPACE(ptr);
1429        inst = ptr;
1430        while (sigar_isdigit(*ptr)) {
1431            ptr++;
1432        }
1433        *ptr = '\0';
1434        ptr++;
1435        SIGAR_SKIP_SPACE(ptr);
1436        if (*ptr == '"') {
1437            ptr++;
1438        }
1439        drv = ptr;
1440        if (!(q = strchr(ptr, '"'))) {
1441            continue;
1442        }
1443        *q = '\0';
1444
1445        if (!(strEQ(drv, "sd") ||
1446              strEQ(drv, "ssd") ||
1447              strEQ(drv, "st") ||
1448              strEQ(drv, "dad") ||
1449              strEQ(drv, "cmdk")))
1450        {
1451            continue;
1452        }
1453
1454        paths->instance = atoi(inst);
1455        if (!kstat_lookup(sigar->kc, drv, paths->instance, NULL)) {
1456            continue;
1457        }
1458
1459        SIGAR_SSTRCPY(paths->device, dev);
1460        SIGAR_SSTRCPY(paths->name, drv);
1461
1462        if (--ndisk < 0) {
1463            /* XXX prevent overflow */
1464            break;
1465        }
1466        paths++;
1467    }
1468    fclose(fp);
1469
1470    return mapping;
1471}
1472
1473static int create_fsdev_cache(sigar_t *sigar)
1474{
1475    fsdev_path_t *paths, *mapping;
1476    sigar_file_system_list_t fslist;
1477    int i, j;
1478    int status;
1479    int debug = SIGAR_LOG_IS_DEBUG(sigar);
1480
1481    sigar->fsdev = sigar_cache_new(15);
1482
1483    status = sigar_file_system_list_get(sigar, &fslist);
1484
1485    if (status != SIGAR_OK) {
1486        return status;
1487    }
1488
1489    if (!(mapping = get_fsdev_paths(sigar, &fslist))) {
1490        sigar_file_system_list_destroy(sigar, &fslist);
1491        return ENOENT;
1492    }
1493
1494    for (i=0; i<fslist.number; i++) {
1495        sigar_file_system_t *fsp = &fslist.data[i];
1496
1497        if (fsp->type == SIGAR_FSTYPE_LOCAL_DISK) {
1498            char device[PATH_MAX+1], *ptr=device;
1499            int len = readlink(fsp->dev_name, device, sizeof(device)-1);
1500            char *s;
1501            char partition;
1502
1503            if (len < 0) {
1504                continue;
1505            }
1506            device[len] = '\0';
1507
1508            if (debug) {
1509                sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[fsdev] name=%s, dev=%s",
1510                                 fsp->dev_name, device);
1511            }
1512
1513            while (strnEQ(ptr, "../", 3)) {
1514                ptr += 3;
1515            }
1516            if (strnEQ(ptr, "devices", 7)) {
1517                ptr += 7;
1518            }
1519            if ((s = strchr(ptr, ':'))) {
1520                partition = *(s+1);
1521            }
1522            else {
1523                continue;
1524            }
1525
1526            for (j=0, paths=mapping; paths->name[0]; j++) {
1527                if (strnEQ(paths->device, ptr, strlen(paths->device))) {
1528                    sigar_cache_entry_t *ent;
1529                    struct stat sb;
1530                    int retval = stat(fsp->dir_name, &sb);
1531                    iodev_t *iodev;
1532
1533                    if (retval == 0) {
1534                        iodev = malloc(sizeof(*iodev));
1535
1536                        SIGAR_DISK_STATS_INIT(&iodev->disk);
1537                        /* e.g. sd9,g
1538                         * module    == sd
1539                         * instance  == 9
1540                         * partition == 8
1541                         */
1542                        snprintf(iodev->name, sizeof(iodev->name), "%s%d,%c",
1543                                 paths->name, paths->instance, partition);
1544
1545                        ent = sigar_cache_get(sigar->fsdev, SIGAR_FSDEV_ID(sb));
1546                        ent->value = iodev;
1547
1548                        if (debug) {
1549                            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1550                                             "[fsdev] map %s -> %s",
1551                                             fsp->dir_name, iodev->name);
1552                        }
1553                    }
1554                    break;
1555                }
1556                paths++;
1557            }
1558        }
1559    }
1560
1561    free(mapping);
1562    sigar_file_system_list_destroy(sigar, &fslist);
1563
1564    return SIGAR_OK;
1565}
1566
1567static int io_kstat_read(sigar_t *sigar,
1568                         sigar_disk_usage_t *disk,
1569                         kstat_t *ksp)
1570{
1571    kstat_io_t *io;
1572
1573    kstat_read(sigar->kc, ksp, NULL);
1574
1575    io = (kstat_io_t *)ksp->ks_data;
1576
1577    disk->reads       = io->reads;
1578    disk->writes      = io->writes;
1579    disk->read_bytes  = io->nread;
1580    disk->write_bytes = io->nwritten;
1581    disk->qtime       = io->wlentime;
1582    disk->rtime       = io->rlentime;
1583    disk->wtime       = io->wlentime;
1584    disk->time        = disk->rtime + disk->wtime;
1585    disk->snaptime    = ksp->ks_snaptime;
1586
1587    return SIGAR_OK;
1588}
1589
1590
1591static int sigar_kstat_disk_usage_get(sigar_t *sigar, const char *name,
1592                                      sigar_disk_usage_t *disk,
1593                                      kstat_t **kio)
1594{
1595    kstat_t *ksp;
1596
1597    if (sigar_kstat_update(sigar) == -1) {
1598        return errno;
1599    }
1600
1601    for (ksp = sigar->kc->kc_chain;
1602         ksp;
1603         ksp = ksp->ks_next)
1604    {
1605        if (ksp->ks_type != KSTAT_TYPE_IO) {
1606            continue;
1607        }
1608        if (strEQ(ksp->ks_name, name)) {
1609            int status = io_kstat_read(sigar, disk, ksp);
1610            *kio = ksp;
1611            return status;
1612        }
1613    }
1614
1615    return ENXIO;
1616}
1617
1618static int simple_hash(const char *s)
1619{
1620    int hash = 0;
1621    while (*s) {
1622        hash = 31*hash + *s++;
1623    }
1624    return hash;
1625}
1626
1627int sigar_disk_usage_get(sigar_t *sigar, const char *name,
1628                         sigar_disk_usage_t *disk)
1629{
1630    kstat_t *ksp;
1631    int status;
1632    iodev_t *iodev = NULL;
1633    sigar_cache_entry_t *ent;
1634    sigar_uint64_t id;
1635
1636    SIGAR_DISK_STATS_INIT(disk);
1637
1638    if (!sigar->fsdev) {
1639        if (create_fsdev_cache(sigar) != SIGAR_OK) {
1640            return SIGAR_OK;
1641        }
1642    }
1643
1644    if (*name == '/') {
1645        struct stat sb;
1646
1647        if (stat(name, &sb) < 0) {
1648            return errno;
1649        }
1650
1651        id = SIGAR_FSDEV_ID(sb);
1652        ent = sigar_cache_get(sigar->fsdev, id);
1653        if (ent->value == NULL) {
1654            return ENXIO;
1655        }
1656        iodev = (iodev_t *)ent->value;
1657
1658        status = sigar_kstat_disk_usage_get(sigar, iodev->name, disk, &ksp);
1659    }
1660    else {
1661        status = sigar_kstat_disk_usage_get(sigar, name, disk, &ksp);
1662        if (status != SIGAR_OK) {
1663            return status;
1664        }
1665        id = simple_hash(name); /*XXX*/
1666        ent = sigar_cache_get(sigar->fsdev, id);
1667        if (ent->value) {
1668            iodev = (iodev_t *)ent->value;
1669        }
1670        else {
1671            ent->value = iodev = malloc(sizeof(*iodev));
1672            SIGAR_SSTRCPY(iodev->name, name);
1673            SIGAR_DISK_STATS_INIT(&iodev->disk);
1674        }
1675    }
1676
1677    /* service_time formula derived from opensolaris.org:iostat.c */
1678    if ((status == SIGAR_OK) && iodev) {
1679        sigar_uint64_t delta;
1680        double avw, avr, tps, mtps;
1681        double etime, hr_etime;
1682
1683        if (iodev->disk.snaptime) {
1684            delta = disk->snaptime - iodev->disk.snaptime;
1685        }
1686        else {
1687            delta = ksp->ks_crtime - ksp->ks_snaptime;
1688        }
1689
1690        hr_etime = (double)delta;
1691        if (hr_etime == 0.0) {
1692            hr_etime = (double)NANOSEC;
1693        }
1694        etime = hr_etime / (double)NANOSEC;
1695
1696        tps =
1697            (((double)(disk->reads - iodev->disk.reads)) / etime) +
1698            (((double)(disk->writes - iodev->disk.writes)) / etime);
1699
1700        delta = disk->wtime - iodev->disk.wtime;
1701        if (delta) {
1702            avw = (double)delta;
1703            avw /= hr_etime;
1704        }
1705        else {
1706            avw = 0.0;
1707        }
1708
1709        delta = disk->rtime - iodev->disk.rtime;
1710        if (delta) {
1711            avr = (double)delta;
1712            avr /= hr_etime;
1713        }
1714        else {
1715            avr = 0.0;
1716        }
1717
1718        disk->queue = avw;
1719        disk->service_time = 0.0;
1720
1721        if (tps && (avw != 0.0 || avr != 0.0)) {
1722            mtps = 1000.0 / tps;
1723            if (avw != 0.0) {
1724                disk->service_time += avw * mtps;
1725            }
1726            if (avr != 0.0) {
1727                disk->service_time += avr * mtps;
1728            }
1729        }
1730
1731        memcpy(&iodev->disk, disk, sizeof(iodev->disk));
1732    }
1733
1734    return status;
1735}
1736
1737int sigar_file_system_usage_get(sigar_t *sigar,
1738                                const char *dirname,
1739                                sigar_file_system_usage_t *fsusage)
1740{
1741    int status = sigar_statvfs(sigar, dirname, fsusage);
1742
1743    if (status != SIGAR_OK) {
1744        return status;
1745    }
1746
1747    fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage);
1748
1749    sigar_disk_usage_get(sigar, dirname, &fsusage->disk);
1750
1751    return SIGAR_OK;
1752}
1753
1754int sigar_cpu_info_list_get(sigar_t *sigar,
1755                            sigar_cpu_info_list_t *cpu_infos)
1756{
1757    processor_info_t stats;
1758    unsigned int i;
1759    int status = SIGAR_OK;
1760    int brand = -1;
1761    sigar_cache_t *chips;
1762    int is_debug = SIGAR_LOG_IS_DEBUG(sigar);
1763    int nsockets = 0;
1764
1765    if (sigar_kstat_update(sigar) == -1) { /* for sigar->ncpu */
1766        return errno;
1767    }
1768
1769    /*
1770     * stats we care about will be the same for each
1771     * online processor, so just grab the first.
1772     */
1773    for (i=0; i<sigar->ncpu; i++) {
1774        processorid_t id = sigar->ks.cpuid[i];
1775
1776        if ((status = processor_info(id, &stats)) < 0) {
1777            continue;
1778        }
1779        else {
1780            status = SIGAR_OK;
1781            break;
1782        }
1783    }
1784
1785    if (status != SIGAR_OK) {
1786        /* should never happen */
1787        return ENOENT;
1788    }
1789
1790    sigar_cpu_info_list_create(cpu_infos);
1791    chips = sigar_cache_new(16);
1792    chips->free_value = free_chip_id;
1793
1794    for (i=0; i<sigar->ncpu; i++) {
1795        sigar_cpu_info_t *info;
1796        int chip_id = get_chip_id(sigar, i);
1797
1798        if (chip_id != -1) {
1799            sigar_cache_entry_t *ent =
1800                sigar_cache_get(chips, chip_id);
1801
1802            if (ent->value) {
1803                if (!sigar->cpu_list_cores) {
1804                    continue;
1805                }
1806            }
1807            else {
1808                ++nsockets;
1809                ent->value = chips; /*anything non-NULL*/
1810                if (is_debug) {
1811                    sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1812                                     "[cpu_list] Merging info of"
1813                                     " logical processors for chip_id=%d",
1814                                     chip_id);
1815                }
1816            }
1817        }
1818        else {
1819            ++nsockets;
1820        }
1821
1822        SIGAR_CPU_INFO_LIST_GROW(cpu_infos);
1823
1824        info = &cpu_infos->data[cpu_infos->number++];
1825
1826        SIGAR_SSTRCPY(info->model, stats.pi_processor_type);
1827
1828        if (brand == -1) {
1829            brand = get_chip_brand(sigar, i, info);
1830        }
1831
1832        if (strEQ(info->model, "i386")) {
1833            if (!brand) {
1834                /* assume Intel on x86 */
1835                SIGAR_SSTRCPY(info->vendor, "Intel");
1836            }
1837            SIGAR_SSTRCPY(info->model, "x86");
1838        }
1839        else {
1840            if (!brand) {
1841                /* assume Sun */
1842                SIGAR_SSTRCPY(info->vendor, "Sun");
1843            }
1844            /* s/sparc/Sparc/ */
1845            info->model[0] = toupper(info->model[0]);
1846        }
1847
1848        if (brand) {
1849            SIGAR_SSTRCPY(info->vendor, cpu_infos->data[0].vendor);
1850        }
1851
1852        info->mhz = stats.pi_clock;
1853        info->cache_size = SIGAR_FIELD_NOTIMPL; /*XXX*/
1854    }
1855
1856    sigar_cache_destroy(chips);
1857
1858    for (i=0; i<cpu_infos->number; i++) {
1859        sigar_cpu_info_t *info = &cpu_infos->data[i];
1860        info->total_sockets = nsockets;
1861        info->total_cores = sigar->ncpu;
1862        info->cores_per_socket = sigar->ncpu / nsockets;
1863    }
1864
1865    return SIGAR_OK;
1866}
1867
1868int sigar_net_route_list_get(sigar_t *sigar,
1869                             sigar_net_route_list_t *routelist)
1870
1871{
1872    char *data;
1873    int len, rc;
1874    struct opthdr *op;
1875    size_t nread=0, size=0;
1876    const char *size_from;
1877
1878    sigar_net_route_list_create(routelist);
1879
1880    while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) {
1881        mib2_ipRouteEntry_t *entry;
1882        char *end;
1883
1884        if (op->level != MIB2_IP) {
1885            continue;
1886        }
1887
1888        if (op->name == 0) {
1889            /* we want to use this size for bincompat */
1890            size = ((mib2_ip_t *)data)->ipRouteEntrySize;
1891            continue;
1892        }
1893        else if (op->name != MIB2_IP_21) {
1894            continue;
1895        }
1896
1897        if (size == 0) {
1898            size_from = "sizeof";
1899            size = sizeof(*entry);
1900        }
1901        else {
1902            size_from = "mib2_ip";
1903        }
1904
1905        if (SIGAR_LOG_IS_DEBUG(sigar)) {
1906            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1907                             "[route_list] ipRouteEntrySize=%d (from %s)",
1908                             size, size_from);
1909        }
1910
1911        for (entry = (mib2_ipRouteEntry_t *)data, end = data + len;
1912             (char *)entry < end;
1913             nread+=size, entry = (mib2_ipRouteEntry_t *)((char *)data+nread))
1914        {
1915            sigar_net_route_t *route;
1916            int type = entry->ipRouteInfo.re_ire_type;
1917
1918            /* filter same as netstat -r */
1919            if ((type == IRE_CACHE) ||
1920                (type == IRE_BROADCAST) ||
1921                (type == IRE_LOCAL))
1922            {
1923                continue;
1924            }
1925
1926            SIGAR_NET_ROUTE_LIST_GROW(routelist);
1927            route = &routelist->data[routelist->number++];
1928
1929            sigar_net_address_set(route->destination,
1930                                  entry->ipRouteDest);
1931
1932            sigar_net_address_set(route->gateway,
1933                                  entry->ipRouteNextHop);
1934
1935            sigar_net_address_set(route->mask,
1936                                  entry->ipRouteMask);
1937
1938            route->refcnt      = entry->ipRouteInfo.re_ref;
1939            route->irtt        = entry->ipRouteInfo.re_rtt;
1940            route->metric      = entry->ipRouteMetric1;
1941
1942            SIGAR_SSTRCPY(route->ifname, entry->ipRouteIfIndex.o_bytes);
1943
1944            route->flags = RTF_UP;
1945            if ((route->destination.addr.in == 0) &&
1946                (route->mask.addr.in == 0))
1947            {
1948                route->flags |= RTF_GATEWAY;
1949            }
1950
1951            route->use = route->window = route->mtu =
1952                SIGAR_FIELD_NOTIMPL; /*XXX*/
1953        }
1954    }
1955
1956    if (rc != GET_MIB2_EOD) {
1957        close_mib2(&sigar->mib2);
1958        return SIGAR_EMIB2;
1959    }
1960
1961    return SIGAR_OK;
1962}
1963
1964static void ifstat_kstat_common(sigar_net_interface_stat_t *ifstat,
1965                                kstat_named_t *data, int ndata)
1966{
1967    int i;
1968
1969    for (i=0; i<ndata; i++) {
1970        sigar_uint64_t value = data[i].value.KSTAT_UINT;
1971
1972        char *ptr = data[i].name;
1973
1974        switch (*ptr) {
1975          case 'c':
1976            if (strEQ(ptr, "collisions")) {
1977                ifstat->tx_collisions = value;
1978            }
1979            break;
1980          case 'd':
1981            if (strEQ(ptr, "drop")) {
1982                ifstat->rx_dropped = value;
1983                ifstat->tx_dropped = value;
1984            }
1985            break;
1986          case 'i':
1987            if (strEQ(ptr, "ipackets")) {
1988                if (ifstat->rx_packets == 0) {
1989                    ifstat->rx_packets = value;
1990                }
1991            }
1992            else if (strEQ(ptr, "ipackets64")) {
1993                ifstat->rx_packets = data[i].value.ui64;
1994            }
1995            else if (strEQ(ptr, "ierrors")) {
1996                ifstat->rx_errors = value;
1997            }
1998            else if (strEQ(ptr, "ifspeed")) {
1999                ifstat->speed = value;
2000            }
2001            break;
2002          case 'f':
2003            if (strEQ(ptr, "framing")) {
2004                ifstat->rx_frame = value;
2005            }
2006            break;
2007          case 'm':
2008            if (strEQ(ptr, "missed")) {
2009                ifstat->rx_dropped = value;
2010                ifstat->tx_dropped = value;
2011            }
2012            break;
2013          case 'n':
2014            if (strEQ(ptr, "nocarrier")) {
2015                ifstat->tx_carrier = value;
2016            }
2017            break;
2018          case 'o':
2019            if (strEQ(ptr, "obytes")) {
2020                if (ifstat->tx_bytes == 0) {
2021                    ifstat->tx_bytes = value;
2022                }
2023            }
2024            else if (strEQ(ptr, "obytes64")) {
2025                ifstat->tx_bytes = data[i].value.ui64;
2026            }
2027            else if (strEQ(ptr, "oerrors")) {
2028                ifstat->tx_errors = value;
2029            }
2030            else if (strEQ(ptr, "oflo")) {
2031                ifstat->tx_overruns = value;
2032            }
2033            else if (strEQ(ptr, "opackets")) {
2034                if (ifstat->tx_packets == 0) {
2035                    ifstat->tx_packets = value;
2036                }
2037            }
2038            else if (strEQ(ptr, "opackets64")) {
2039                ifstat->tx_packets = data[i].value.ui64;
2040            }
2041            else if (strEQ(ptr, "toolong_errors")) {
2042                ifstat->tx_overruns = value;
2043            }
2044            break;
2045          case 'r':
2046            if (strEQ(ptr, "rbytes")) {
2047                if (ifstat->rx_bytes == 0) {
2048                    ifstat->rx_bytes = value;
2049                }
2050            }
2051            else if (strEQ(ptr, "rbytes64")) {
2052                ifstat->rx_bytes = data[i].value.ui64;
2053            }
2054            else if (strEQ(ptr, "rx_overflow")) {
2055                ifstat->rx_overruns = value;
2056            }
2057            break;
2058          default:
2059            break;
2060        }
2061    }
2062}
2063
2064static int sigar_net_ifstat_get_any(sigar_t *sigar, const char *name,
2065                                    sigar_net_interface_stat_t *ifstat)
2066{
2067    kstat_ctl_t *kc = sigar->kc;
2068    kstat_t *ksp;
2069    kstat_named_t *data;
2070
2071    if (sigar_kstat_update(sigar) == -1) {
2072        return errno;
2073    }
2074
2075    if (!(ksp = kstat_lookup(kc, NULL, -1, (char *)name))) {
2076        return ENXIO;
2077    }
2078
2079    if (kstat_read(kc, ksp, NULL) < 0) {
2080        return ENOENT;
2081    }
2082
2083    data = (kstat_named_t *)ksp->ks_data;
2084
2085    ifstat_kstat_common(ifstat, data, ksp->ks_ndata);
2086
2087    return SIGAR_OK;
2088}
2089
2090/* loopback interface only has rx/tx packets */
2091static int sigar_net_ifstat_get_lo(sigar_t *sigar, const char *name,
2092                                   sigar_net_interface_stat_t *ifstat)
2093{
2094    ifstat->rx_packets    = 0;
2095    ifstat->rx_bytes      = SIGAR_FIELD_NOTIMPL;
2096    ifstat->rx_errors     = SIGAR_FIELD_NOTIMPL;
2097    ifstat->rx_dropped    = SIGAR_FIELD_NOTIMPL;
2098    ifstat->rx_overruns   = SIGAR_FIELD_NOTIMPL;
2099    ifstat->rx_frame      = SIGAR_FIELD_NOTIMPL;
2100
2101    ifstat->tx_packets    = 0;
2102    ifstat->tx_bytes      = SIGAR_FIELD_NOTIMPL;
2103    ifstat->tx_errors     = SIGAR_FIELD_NOTIMPL;
2104    ifstat->tx_dropped    = SIGAR_FIELD_NOTIMPL;
2105    ifstat->tx_overruns   = SIGAR_FIELD_NOTIMPL;
2106    ifstat->tx_collisions = SIGAR_FIELD_NOTIMPL;
2107    ifstat->tx_carrier    = SIGAR_FIELD_NOTIMPL;
2108
2109    ifstat->speed         = SIGAR_FIELD_NOTIMPL;
2110
2111    return sigar_net_ifstat_get_any(sigar, name, ifstat);
2112}
2113
2114int sigar_net_interface_stat_get(sigar_t *sigar, const char *name,
2115                                 sigar_net_interface_stat_t *ifstat)
2116{
2117    ifstat->speed = SIGAR_FIELD_NOTIMPL;
2118
2119    if (strnEQ(name, "lo", 2)) {
2120        return sigar_net_ifstat_get_lo(sigar, name, ifstat);
2121    }
2122    else {
2123        SIGAR_ZERO(ifstat);
2124        return sigar_net_ifstat_get_any(sigar, name, ifstat);
2125    }
2126}
2127
2128int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name,
2129                                        sigar_net_interface_config_t *ifconfig)
2130{
2131    int sock;
2132    struct lifreq lifr;
2133
2134    if ((sock = socket(AF_INET6, SOCK_DGRAM, 0)) < 0) {
2135        return errno;
2136    }
2137
2138    SIGAR_SSTRCPY(lifr.lifr_name, name);
2139
2140    if (ioctl(sock, SIOCGLIFADDR, &lifr) == 0) {
2141        struct in6_addr *addr = SIGAR_SIN6_ADDR(&lifr.lifr_addr);
2142
2143        sigar_net_address6_set(ifconfig->address6, addr);
2144        sigar_net_interface_scope6_set(ifconfig, addr);
2145        ifconfig->prefix6_length = lifr.lifr_addrlen;
2146    }
2147
2148    close(sock);
2149    return SIGAR_OK;
2150}
2151
2152#define TCPQ_SIZE(s) ((s) >= 0 ? (s) : 0)
2153
2154static int tcp_connection_get(sigar_net_connection_walker_t *walker,
2155                              struct mib2_tcpConnEntry *entry,
2156                              int len)
2157{
2158    int flags = walker->flags;
2159    int status;
2160    char *end = (char *)entry + len;
2161
2162    while ((char *)entry < end) {
2163        int state = entry->tcpConnEntryInfo.ce_state;
2164
2165        if (((flags & SIGAR_NETCONN_SERVER) && (state == TCPS_LISTEN)) ||
2166            ((flags & SIGAR_NETCONN_CLIENT) && (state != TCPS_LISTEN)))
2167        {
2168            sigar_net_connection_t conn;
2169
2170            SIGAR_ZERO(&conn);
2171
2172            sigar_net_address_set(conn.local_address, entry->tcpConnLocalAddress);
2173            sigar_net_address_set(conn.remote_address, entry->tcpConnRemAddress);
2174
2175            conn.local_port = entry->tcpConnLocalPort;
2176            conn.remote_port = entry->tcpConnRemPort;
2177            conn.type = SIGAR_NETCONN_TCP;
2178            conn.send_queue =
2179                TCPQ_SIZE(entry->tcpConnEntryInfo.ce_snxt -
2180                          entry->tcpConnEntryInfo.ce_suna - 1);
2181            conn.receive_queue =
2182                TCPQ_SIZE(entry->tcpConnEntryInfo.ce_rnxt -
2183                          entry->tcpConnEntryInfo.ce_rack);
2184
2185            switch (state) {
2186              case TCPS_CLOSED:
2187                conn.state = SIGAR_TCP_CLOSE;
2188                break;
2189              case TCPS_IDLE:
2190                conn.state = SIGAR_TCP_IDLE;
2191                break;
2192              case TCPS_BOUND:
2193                conn.state = SIGAR_TCP_BOUND;
2194                break;
2195              case TCPS_LISTEN:
2196                conn.state = SIGAR_TCP_LISTEN;
2197                break;
2198              case TCPS_SYN_SENT:
2199                conn.state = SIGAR_TCP_SYN_SENT;
2200                break;
2201              case TCPS_SYN_RCVD:
2202                conn.state = SIGAR_TCP_SYN_RECV;
2203                break;
2204              case TCPS_ESTABLISHED:
2205                conn.state = SIGAR_TCP_ESTABLISHED;
2206                break;
2207              case TCPS_CLOSE_WAIT:
2208                conn.state = SIGAR_TCP_CLOSE_WAIT;
2209                break;
2210              case TCPS_FIN_WAIT_1:
2211                conn.state = SIGAR_TCP_FIN_WAIT1;
2212                break;
2213              case TCPS_CLOSING:
2214                conn.state = SIGAR_TCP_CLOSING;
2215                break;
2216              case TCPS_LAST_ACK:
2217                conn.state = SIGAR_TCP_LAST_ACK;
2218                break;
2219              case TCPS_FIN_WAIT_2:
2220                conn.state = SIGAR_TCP_FIN_WAIT2;
2221                break;
2222              case TCPS_TIME_WAIT:
2223                conn.state = SIGAR_TCP_TIME_WAIT;
2224                break;
2225              default:
2226                conn.state = SIGAR_TCP_UNKNOWN;
2227                break;
2228            }
2229
2230            status = walker->add_connection(walker, &conn);
2231            if (status != SIGAR_OK) {
2232                return status;
2233            }
2234        }
2235
2236        entry++;
2237    }
2238
2239    return SIGAR_OK;
2240}
2241
2242static int udp_connection_get(sigar_net_connection_walker_t *walker,
2243                              struct mib2_udpEntry *entry,
2244                              int len)
2245{
2246    int flags = walker->flags;
2247    int status;
2248    char *end = (char *)entry + len;
2249
2250    while ((char *)entry < end) {
2251        int state = entry->udpEntryInfo.ue_state;
2252
2253        /* XXX dunno if this state check is right */
2254        if (((flags & SIGAR_NETCONN_SERVER) && (state == MIB2_UDP_idle)) ||
2255            ((flags & SIGAR_NETCONN_CLIENT) && (state != MIB2_UDP_idle)))
2256        {
2257            sigar_net_connection_t conn;
2258
2259            SIGAR_ZERO(&conn);
2260
2261            sigar_net_address_set(conn.local_address, entry->udpLocalAddress);
2262            sigar_net_address_set(conn.remote_address, 0);
2263
2264            conn.local_port = entry->udpLocalPort;
2265            conn.remote_port = 0;
2266            conn.type = SIGAR_NETCONN_UDP;
2267
2268            status = walker->add_connection(walker, &conn);
2269            if (status != SIGAR_OK) {
2270                return status;
2271            }
2272        }
2273
2274        entry++;
2275    }
2276
2277    return SIGAR_OK;
2278}
2279
2280int sigar_net_connection_walk(sigar_net_connection_walker_t *walker)
2281{
2282    sigar_t *sigar = walker->sigar;
2283    int flags = walker->flags;
2284    int status;
2285    int want_tcp = flags & SIGAR_NETCONN_TCP;
2286    int want_udp = flags & SIGAR_NETCONN_UDP;
2287    char *data;
2288    int len;
2289    int rc;
2290    struct opthdr *op;
2291
2292    while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) {
2293        if ((op->level == MIB2_TCP) &&
2294            (op->name == MIB2_TCP_13) &&
2295            want_tcp)
2296        {
2297            status =
2298                tcp_connection_get(walker,
2299                                   (struct mib2_tcpConnEntry *)data,
2300                                   len);
2301        }
2302        else if ((op->level == MIB2_UDP) &&
2303                 (op->name == MIB2_UDP_5) &&
2304                 want_udp)
2305        {
2306            status =
2307                udp_connection_get(walker,
2308                                   (struct mib2_udpEntry *)data,
2309                                   len);
2310        }
2311        else {
2312            status = SIGAR_OK;
2313        }
2314
2315        if (status != SIGAR_OK) {
2316            break;
2317        }
2318    }
2319
2320    if (rc != GET_MIB2_EOD) {
2321        close_mib2(&sigar->mib2);
2322        return SIGAR_EMIB2;
2323    }
2324
2325    return SIGAR_OK;
2326}
2327
2328SIGAR_DECLARE(int)
2329sigar_tcp_get(sigar_t *sigar,
2330              sigar_tcp_t *tcp)
2331{
2332    char *data;
2333    int len;
2334    int rc;
2335    struct opthdr *op;
2336    mib2_tcp_t *mib = NULL;
2337
2338    while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) {
2339        if ((op->level == MIB2_TCP) && (op->name == 0)) {
2340            mib = (mib2_tcp_t *)data;
2341            break;
2342        }
2343    }
2344
2345    if (mib) {
2346        tcp->active_opens = mib->tcpActiveOpens;
2347        tcp->passive_opens = mib->tcpPassiveOpens;
2348        tcp->attempt_fails = mib->tcpAttemptFails;
2349        tcp->estab_resets = mib->tcpEstabResets;
2350        tcp->curr_estab = mib->tcpCurrEstab;
2351        tcp->in_segs = mib->tcpInSegs;
2352        tcp->out_segs = mib->tcpOutSegs;
2353        tcp->retrans_segs = mib->tcpRetransSegs;
2354        tcp->in_errs = SIGAR_FIELD_NOTIMPL; /* XXX mib2_ip_t.tcpInErrs */
2355        tcp->out_rsts = mib->tcpOutRsts;
2356        return SIGAR_OK;
2357    }
2358    else {
2359        return SIGAR_ENOTIMPL;
2360    }
2361}
2362
2363static int sigar_nfs_get(sigar_t *sigar,
2364                         char *type,
2365                         char **names,
2366                         char *nfs)
2367{
2368    size_t offset;
2369    kstat_t *ksp;
2370    int i;
2371
2372    if (sigar_kstat_update(sigar) == -1) {
2373        return errno;
2374    }
2375
2376    if (!(ksp = kstat_lookup(sigar->kc, "nfs", 0, type))) {
2377        return SIGAR_ENOTIMPL;
2378    }
2379
2380    if (kstat_read(sigar->kc, ksp, NULL) < 0) {
2381        return errno;
2382    }
2383
2384    for (i=0, offset=0;
2385         names[i];
2386         i++, offset+=sizeof(sigar_uint64_t))
2387    {
2388        sigar_uint64_t val;
2389        kstat_named_t *kv =
2390            kstat_data_lookup(ksp, names[i]);
2391
2392        if (kv) {
2393            val = kv->value.ui64;
2394        }
2395        else {
2396            val = -1;
2397        }
2398
2399        *(sigar_uint64_t *)((char *)nfs + offset) = val;
2400    }
2401
2402    return SIGAR_OK;
2403}
2404
2405static char *nfs_v2_names[] = {
2406    "null",
2407    "getattr",
2408    "setattr",
2409    "root",
2410    "lookup",
2411    "readlink",
2412    "read",
2413    "wrcache",
2414    "write",
2415    "create",
2416    "remove",
2417    "rename",
2418    "link",
2419    "symlink",
2420    "mkdir",
2421    "rmdir",
2422    "readdir",
2423    "statfs",
2424    NULL
2425};
2426
2427int sigar_nfs_client_v2_get(sigar_t *sigar,
2428                            sigar_nfs_client_v2_t *nfs)
2429{
2430    return sigar_nfs_get(sigar, "rfsreqcnt_v2", nfs_v2_names, (char *)nfs);
2431}
2432
2433int sigar_nfs_server_v2_get(sigar_t *sigar,
2434                            sigar_nfs_server_v2_t *nfs)
2435{
2436    return sigar_nfs_get(sigar, "rfsproccnt_v2", nfs_v2_names, (char *)nfs);
2437}
2438
2439static char *nfs_v3_names[] = {
2440    "null",
2441    "getattr",
2442    "setattr",
2443    "lookup",
2444    "access",
2445    "readlink",
2446    "read",
2447    "write",
2448    "create",
2449    "mkdir",
2450    "symlink",
2451    "mknod",
2452    "remove",
2453    "rmdir",
2454    "rename",
2455    "link",
2456    "readdir",
2457    "readdirplus",
2458    "fsstat",
2459    "fsinfo",
2460    "pathconf",
2461    "commit",
2462    NULL
2463};
2464
2465int sigar_nfs_client_v3_get(sigar_t *sigar,
2466                            sigar_nfs_client_v3_t *nfs)
2467{
2468    return sigar_nfs_get(sigar, "rfsreqcnt_v3", nfs_v3_names, (char *)nfs);
2469}
2470
2471int sigar_nfs_server_v3_get(sigar_t *sigar,
2472                            sigar_nfs_server_v3_t *nfs)
2473{
2474    return sigar_nfs_get(sigar, "rfsproccnt_v3", nfs_v3_names, (char *)nfs);
2475}
2476
2477int sigar_arp_list_get(sigar_t *sigar,
2478                       sigar_arp_list_t *arplist)
2479{
2480    char *data;
2481    int len, rc;
2482    struct opthdr *op;
2483    size_t nread=0, size=0;
2484    const char *size_from;
2485
2486    sigar_arp_list_create(arplist);
2487
2488    while ((rc = get_mib2(&sigar->mib2, &op, &data, &len)) == GET_MIB2_OK) {
2489        mib2_ipNetToMediaEntry_t *entry;
2490        char *end;
2491
2492        if (op->level != MIB2_IP) {
2493            continue;
2494        }
2495
2496        if (op->name == 0) {
2497            /* we want to use this size for bincompat */
2498            size = ((mib2_ip_t *)data)->ipNetToMediaEntrySize;
2499            continue;
2500        }
2501        else if (op->name != MIB2_IP_MEDIA) {
2502            continue;
2503        }
2504
2505        if (size == 0) {
2506            size_from = "sizeof";
2507            size = sizeof(*entry);
2508        }
2509        else {
2510            size_from = "mib2_ip";
2511        }
2512
2513        if (SIGAR_LOG_IS_DEBUG(sigar)) {
2514            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
2515                             "[arp_list] ipNetToMediaEntrySize=%d (from %s)",
2516                             size, size_from);
2517        }
2518
2519        for (entry = (mib2_ipNetToMediaEntry_t *)data, end = data + len;
2520             (char *)entry < end;
2521             nread+=size, entry = (mib2_ipNetToMediaEntry_t *)((char *)data+nread))
2522        {
2523            sigar_arp_t *arp;
2524
2525            SIGAR_ARP_LIST_GROW(arplist);
2526            arp = &arplist->data[arplist->number++];
2527
2528            sigar_net_address_set(arp->address,
2529                                  entry->ipNetToMediaNetAddress);
2530
2531            sigar_net_address_mac_set(arp->hwaddr,
2532                                      entry->ipNetToMediaPhysAddress.o_bytes,
2533                                      entry->ipNetToMediaPhysAddress.o_length);
2534
2535            SIGAR_SSTRCPY(arp->ifname, entry->ipNetToMediaIfIndex.o_bytes);
2536
2537            arp->flags = entry->ipNetToMediaInfo.ntm_flags;
2538            SIGAR_SSTRCPY(arp->type, "ether"); /*XXX*/
2539        }
2540    }
2541
2542    if (rc != GET_MIB2_EOD) {
2543        close_mib2(&sigar->mib2);
2544        return SIGAR_EMIB2;
2545    }
2546
2547    return SIGAR_OK;
2548}
2549
2550static int find_port(sigar_t *sigar, struct ps_prochandle *phandle,
2551                     sigar_pid_t pid, unsigned long port)
2552{
2553    DIR *dirp;
2554    struct dirent *ent;
2555    char pname[PATH_MAX];
2556    struct stat64 statb;
2557    int found=0;
2558
2559    sprintf(pname, "/proc/%d/fd", (int)pid);
2560
2561    if (!(dirp = opendir(pname))) {
2562        return 0;
2563    }
2564
2565    while ((ent = readdir(dirp))) {
2566        int fd;
2567
2568        if (!sigar_isdigit(ent->d_name[0])) {
2569            continue;
2570        }
2571        fd = atoi(ent->d_name);
2572
2573        if (sigar->pfstat64(phandle, fd, &statb) == -1) {
2574            continue;
2575        }
2576
2577        if ((statb.st_mode & S_IFMT) == S_IFSOCK) {
2578            struct sockaddr_in sin;
2579            struct sockaddr *sa = (struct sockaddr *)&sin;
2580            socklen_t len = sizeof(sin);
2581            int opt, optsz, rc;
2582
2583            optsz = sizeof(opt);
2584            rc = sigar->pgetsockopt(phandle, fd, SOL_SOCKET, SO_TYPE, &opt, &optsz);
2585            if (rc != 0) {
2586                continue;
2587            }
2588            if (opt != SOCK_STREAM) {
2589                continue;
2590            }
2591            optsz = sizeof(opt);
2592            rc = sigar->pgetsockopt(phandle, fd, SOL_SOCKET, SO_ACCEPTCONN, &opt, &optsz);
2593            if (rc != 0) {
2594                continue;
2595            }
2596            if (opt != SO_ACCEPTCONN) {
2597                continue;
2598            }
2599
2600            rc = sigar->pgetsockname(phandle, fd, sa, &len);
2601            if (rc != 0) {
2602                continue;
2603            }
2604
2605            if ((sa->sa_family == AF_INET) ||
2606                (sa->sa_family == AF_INET6))
2607            {
2608                if (ntohs(sin.sin_port) == port) {
2609                    found = 1;
2610                    break;
2611                }
2612            }
2613        }
2614    }
2615
2616    closedir(dirp);
2617
2618    return found;
2619}
2620
2621/* derived from /usr/bin/pfiles.c */
2622int sigar_proc_port_get(sigar_t *sigar, int protocol,
2623                        unsigned long port, sigar_pid_t *pid)
2624{
2625    sigar_proc_list_t pids;
2626    int i, status, found=0;
2627
2628    if (sigar->solaris_version < 10) {
2629        return SIGAR_ENOTIMPL;
2630    }
2631
2632    if ((status = sigar_init_libproc(sigar)) != SIGAR_OK) {
2633        return SIGAR_ENOTIMPL;
2634    }
2635    status = sigar_proc_list_get(sigar, &pids);
2636    if (status != SIGAR_OK) {
2637        return status;
2638    }
2639
2640    for (i=0; i<pids.number; i++) {
2641        sigar_pid_t ps_id = pids.data[i];
2642        struct ps_prochandle *phandle;
2643
2644        if (ps_id == sigar_pid_get(sigar)) {
2645            continue; /* XXX */
2646        }
2647        status = sigar_pgrab(sigar, ps_id, SIGAR_FUNC, &phandle);
2648
2649        if (status != SIGAR_OK) {
2650            continue;
2651        }
2652
2653        if (sigar->pcreate_agent(phandle) == 0) {
2654            found = find_port(sigar, phandle, ps_id, port);
2655            sigar->pdestroy_agent(phandle);
2656        }
2657
2658        sigar->pfree(phandle);
2659        if (found) {
2660            *pid = ps_id;
2661            break;
2662        }
2663    }
2664
2665    sigar_proc_list_destroy(sigar, &pids);
2666
2667    return found ? SIGAR_OK : ENOENT;
2668}
2669
2670int sigar_os_sys_info_get(sigar_t *sigar,
2671                          sigar_sys_info_t *sys_info)
2672{
2673    char *vendor_version;
2674
2675    sysinfo(SI_ARCHITECTURE, sys_info->arch, sizeof(sys_info->arch));
2676
2677    SIGAR_SSTRCPY(sys_info->name, "Solaris");
2678    SIGAR_SSTRCPY(sys_info->vendor, "Sun Microsystems");
2679
2680    if (strEQ(sys_info->version, "5.6")) {
2681        vendor_version = "2.6";
2682    }
2683    else {
2684        if ((vendor_version = strchr(sys_info->version, '.'))) {
2685            ++vendor_version;
2686        }
2687        else {
2688            vendor_version = sys_info->version;
2689        }
2690    }
2691
2692    SIGAR_SSTRCPY(sys_info->vendor_version, vendor_version);
2693
2694    snprintf(sys_info->description,
2695             sizeof(sys_info->description),
2696             "%s %s",
2697             sys_info->name, sys_info->vendor_version);
2698
2699    return SIGAR_OK;
2700}
2701