xref: /6.6.0/sigar/src/os/linux/linux_sigar.c (revision 42d0b984)
1/*
2 * Copyright (c) 2004-2009 Hyperic, Inc.
3 * Copyright (c) 2009 SpringSource, Inc.
4 * Copyright (c) 2009-2010 VMware, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 *     http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19#include <dirent.h>
20#include <stdio.h>
21#include <stdlib.h>
22#include <errno.h>
23#include <sys/param.h>
24#include <sys/stat.h>
25#include <sys/times.h>
26#include <sys/utsname.h>
27
28#include "sigar.h"
29#include "sigar_private.h"
30#include "sigar_util.h"
31#include "sigar_os.h"
32
33#define pageshift(x) ((x) << sigar->pagesize)
34
35#define PROC_MEMINFO PROC_FS_ROOT "meminfo"
36#define PROC_VMSTAT  PROC_FS_ROOT "vmstat"
37#define PROC_MTRR    PROC_FS_ROOT "mtrr"
38#define PROC_STAT    PROC_FS_ROOT "stat"
39#define PROC_UPTIME  PROC_FS_ROOT "uptime"
40#define PROC_LOADAVG PROC_FS_ROOT "loadavg"
41
42#define PROC_PSTAT   "/stat"
43#define PROC_PSTATUS "/status"
44
45#define SYS_BLOCK "/sys/block"
46#define PROC_PARTITIONS PROC_FS_ROOT "partitions"
47#define PROC_DISKSTATS  PROC_FS_ROOT "diskstats"
48
49/*
50 * /proc/self/stat fields:
51 * 1 - pid
52 * 2 - comm
53 * 3 - state
54 * 4 - ppid
55 * 5 - pgrp
56 * 6 - session
57 * 7 - tty_nr
58 * 8 - tpgid
59 * 9 - flags
60 * 10 - minflt
61 * 11 - cminflt
62 * 12 - majflt
63 * 13 - cmajflt
64 * 14 - utime
65 * 15 - stime
66 * 16 - cutime
67 * 17 - cstime
68 * 18 - priority
69 * 19 - nice
70 * 20 - 0 (removed field)
71 * 21 - itrealvalue
72 * 22 - starttime
73 * 23 - vsize
74 * 24 - rss
75 * 25 - rlim
76 * 26 - startcode
77 * 27 - endcode
78 * 28 - startstack
79 * 29 - kstkesp
80 * 30 - kstkeip
81 * 31 - signal
82 * 32 - blocked
83 * 33 - sigignore
84 * 34 - sigcache
85 * 35 - wchan
86 * 36 - nswap
87 * 37 - cnswap
88 * 38 - exit_signal <-- looking for this.
89 * 39 - processor
90 * ... more for newer RH
91 */
92
93#define PROC_SIGNAL_IX 38
94
95static int get_proc_signal_offset(void)
96{
97    char buffer[BUFSIZ], *ptr=buffer;
98    int fields = 0;
99    int status = sigar_file2str(PROCP_FS_ROOT "self/stat",
100                                buffer, sizeof(buffer));
101
102    if (status != SIGAR_OK) {
103        return 1;
104    }
105
106    while (*ptr) {
107        if (*ptr++ == ' ') {
108            fields++;
109        }
110    }
111
112    return (fields - PROC_SIGNAL_IX) + 1;
113}
114
115sigar_pid_t sigar_pid_get(sigar_t *sigar)
116{
117    /* XXX cannot safely cache getpid unless using nptl */
118    /* we can however, cache it for optimizations in the
119     * case of proc_env_get for example.
120     */
121    sigar->pid = getpid();
122    return sigar->pid;
123}
124
125static int sigar_boot_time_get(sigar_t *sigar)
126{
127    FILE *fp;
128    char buffer[BUFSIZ], *ptr;
129    int found = 0;
130
131    if (!(fp = fopen(PROC_STAT, "r"))) {
132        return errno;
133    }
134
135    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
136        if (strnEQ(ptr, "btime", 5)) {
137            if ((ptr = sigar_skip_token(ptr))) {
138                sigar->boot_time = sigar_strtoul(ptr);
139                found = 1;
140            }
141            break;
142        }
143    }
144
145    fclose(fp);
146
147    if (!found) {
148        /* should never happen */
149        sigar->boot_time = time(NULL);
150    }
151
152    return SIGAR_OK;
153}
154
155int sigar_os_open(sigar_t **sigar)
156{
157    int i, status;
158    int kernel_rev, has_nptl;
159    struct stat sb;
160    struct utsname name;
161
162    *sigar = malloc(sizeof(**sigar));
163
164    (*sigar)->pagesize = 0;
165    i = getpagesize();
166    while ((i >>= 1) > 0) {
167        (*sigar)->pagesize++;
168    }
169
170    status = sigar_boot_time_get(*sigar);
171    if (status != SIGAR_OK) {
172        return status;
173    }
174
175    (*sigar)->ticks = sysconf(_SC_CLK_TCK);
176
177    (*sigar)->ram = -1;
178
179    (*sigar)->proc_signal_offset = -1;
180
181    (*sigar)->last_proc_stat.pid = -1;
182
183    (*sigar)->lcpu = -1;
184
185    if (stat(PROC_DISKSTATS, &sb) == 0) {
186        (*sigar)->iostat = IOSTAT_DISKSTATS;
187    }
188    else if (stat(SYS_BLOCK, &sb) == 0) {
189        (*sigar)->iostat = IOSTAT_SYS;
190    }
191    else if (stat(PROC_PARTITIONS, &sb) == 0) {
192        /* XXX file exists does not mean is has the fields */
193        (*sigar)->iostat = IOSTAT_PARTITIONS;
194    }
195    else {
196        (*sigar)->iostat = IOSTAT_NONE;
197    }
198
199    /* hook for using mirrored /proc/net/tcp file */
200    (*sigar)->proc_net = getenv("SIGAR_PROC_NET");
201
202    uname(&name);
203    /* 2.X.y.z -> just need X (unless there is ever a kernel version 3!) */
204    kernel_rev = atoi(&name.release[2]);
205    if (kernel_rev >= 6) {
206        has_nptl = 1;
207    }
208    else {
209        has_nptl = getenv("SIGAR_HAS_NPTL") ? 1 : 0;
210    }
211    (*sigar)->has_nptl = has_nptl;
212
213    return SIGAR_OK;
214}
215
216int sigar_os_close(sigar_t *sigar)
217{
218    free(sigar);
219    return SIGAR_OK;
220}
221
222char *sigar_os_error_string(sigar_t *sigar, int err)
223{
224    return NULL;
225}
226
227static int sigar_cpu_total_count(sigar_t *sigar)
228{
229    sigar->ncpu = (int)sysconf(_SC_NPROCESSORS_CONF);
230    sigar_log_printf(sigar, SIGAR_LOG_DEBUG, "[cpu] ncpu=%d\n",
231                     sigar->ncpu);
232    return sigar->ncpu;
233}
234
235static int get_ram(sigar_t *sigar, sigar_mem_t *mem)
236{
237    char buffer[BUFSIZ], *ptr;
238    FILE *fp;
239    int total = 0;
240    sigar_uint64_t sys_total = (mem->total / (1024 * 1024));
241
242    if (sigar->ram > 0) {
243        /* return cached value */
244        mem->ram = sigar->ram;
245        return SIGAR_OK;
246    }
247
248    if (sigar->ram == 0) {
249        return ENOENT;
250    }
251
252    /*
253     * Memory Type Range Registers
254     * write-back registers add up to the total.
255     * Well, they are supposed to add up, but seen
256     * at least one configuration where that is not the
257     * case.
258     */
259    if (!(fp = fopen(PROC_MTRR, "r"))) {
260        return errno;
261    }
262
263    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
264        if (!(ptr = strstr(ptr, "size="))) {
265            continue;
266        }
267
268        if (!strstr(ptr, "write-back")) {
269            continue;
270        }
271
272        ptr += 5;
273        while (sigar_isspace(*ptr)) {
274            ++ptr;
275        }
276
277        total += atoi(ptr);
278    }
279
280    fclose(fp);
281
282    if ((total - sys_total) > 256) {
283        /* mtrr write-back registers are way off
284         * kernel should not be using more that 256MB of mem
285         */
286        total = 0; /* punt */
287    }
288
289    if (total == 0) {
290        return ENOENT;
291    }
292
293    mem->ram = sigar->ram = total;
294
295    return SIGAR_OK;
296}
297
298#define MEMINFO_PARAM(a) a ":", SSTRLEN(a ":")
299
300static  sigar_uint64_t sigar_meminfo(char *buffer,
301                                                 char *attr, int len)
302{
303    sigar_uint64_t val = 0;
304    char *ptr, *tok;
305
306    if ((ptr = strstr(buffer, attr))) {
307        ptr += len;
308        val = strtoull(ptr, &tok, 0);
309        while (*tok == ' ') {
310            ++tok;
311        }
312        if (*tok == 'k') {
313            val *= 1024;
314        }
315        else if (*tok == 'M') {
316            val *= (1024 * 1024);
317        }
318    }
319
320    return val;
321}
322
323int sigar_mem_get(sigar_t *sigar, sigar_mem_t *mem)
324{
325    sigar_uint64_t buffers, cached, kern;
326    char buffer[BUFSIZ];
327
328    int status = sigar_file2str(PROC_MEMINFO,
329                                buffer, sizeof(buffer));
330
331    if (status != SIGAR_OK) {
332        return status;
333    }
334
335    mem->total  = sigar_meminfo(buffer, MEMINFO_PARAM("MemTotal"));
336    mem->free   = sigar_meminfo(buffer, MEMINFO_PARAM("MemFree"));
337    mem->used   = mem->total - mem->free;
338
339    buffers = sigar_meminfo(buffer, MEMINFO_PARAM("Buffers"));
340    cached  = sigar_meminfo(buffer, MEMINFO_PARAM("Cached"));
341
342    kern = buffers + cached;
343    mem->actual_free = mem->free + kern;
344    mem->actual_used = mem->used - kern;
345
346    sigar_mem_calc_ram(sigar, mem);
347
348    if (get_ram(sigar, mem) != SIGAR_OK) {
349        /* XXX other options on failure? */
350    }
351
352    return SIGAR_OK;
353}
354
355int sigar_swap_get(sigar_t *sigar, sigar_swap_t *swap)
356{
357    char buffer[BUFSIZ], *ptr;
358
359    /* XXX: we open/parse the same file here as sigar_mem_get */
360    int status = sigar_file2str(PROC_MEMINFO,
361                                buffer, sizeof(buffer));
362
363    if (status != SIGAR_OK) {
364        return status;
365    }
366
367    swap->total  = sigar_meminfo(buffer, MEMINFO_PARAM("SwapTotal"));
368    swap->free   = sigar_meminfo(buffer, MEMINFO_PARAM("SwapFree"));
369    swap->used   = swap->total - swap->free;
370
371    swap->page_in = swap->page_out = -1;
372
373    status = sigar_file2str(PROC_VMSTAT,
374                            buffer, sizeof(buffer));
375
376    if (status == SIGAR_OK) {
377        /* 2.6+ kernel */
378        if ((ptr = strstr(buffer, "\npswpin"))) {
379            ptr = sigar_skip_token(ptr);
380            swap->page_in = sigar_strtoull(ptr);
381            ptr = sigar_skip_token(ptr);
382            swap->page_out = sigar_strtoull(ptr);
383        }
384    }
385    else {
386        /* 2.2, 2.4 kernels */
387        status = sigar_file2str(PROC_STAT,
388                                buffer, sizeof(buffer));
389        if (status != SIGAR_OK) {
390            return status;
391        }
392
393        if ((ptr = strstr(buffer, "\nswap"))) {
394            ptr = sigar_skip_token(ptr);
395            swap->page_in = sigar_strtoull(ptr);
396            swap->page_out = sigar_strtoull(ptr);
397        }
398    }
399
400    return SIGAR_OK;
401}
402
403static void get_cpu_metrics(sigar_t *sigar, sigar_cpu_t *cpu, char *line)
404{
405    char *ptr = sigar_skip_token(line); /* "cpu%d" */
406
407    cpu->user += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
408    cpu->nice += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
409    cpu->sys  += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
410    cpu->idle += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
411    if (*ptr == ' ') {
412        /* 2.6+ kernels only */
413        cpu->wait += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
414        cpu->irq += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
415        cpu->soft_irq += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
416    }
417    if (*ptr == ' ') {
418        /* 2.6.11+ kernels only */
419        cpu->stolen += SIGAR_TICK2MSEC(sigar_strtoull(ptr));
420    }
421    cpu->total =
422        cpu->user + cpu->nice + cpu->sys + cpu->idle +
423        cpu->wait + cpu->irq + cpu->soft_irq + cpu->stolen;
424}
425
426int sigar_cpu_get(sigar_t *sigar, sigar_cpu_t *cpu)
427{
428    char buffer[BUFSIZ];
429    int status = sigar_file2str(PROC_STAT, buffer, sizeof(buffer));
430
431    if (status != SIGAR_OK) {
432        return status;
433    }
434
435    SIGAR_ZERO(cpu);
436    get_cpu_metrics(sigar, cpu, buffer);
437
438    return SIGAR_OK;
439}
440
441int sigar_cpu_list_get(sigar_t *sigar, sigar_cpu_list_t *cpulist)
442{
443    FILE *fp;
444    char buffer[BUFSIZ], cpu_total[BUFSIZ], *ptr;
445    int core_rollup = sigar_cpu_core_rollup(sigar), i=0;
446    sigar_cpu_t *cpu;
447
448    if (!(fp = fopen(PROC_STAT, "r"))) {
449        return errno;
450    }
451
452    /* skip first line */
453    if (fgets(cpu_total, sizeof(cpu_total), fp) == NULL) {
454        fclose(fp);
455        return errno;
456    }
457
458    sigar_cpu_list_create(cpulist);
459
460    /* XXX: merge times of logical processors if hyperthreading */
461    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
462        if (!strnEQ(ptr, "cpu", 3)) {
463            break;
464        }
465
466        if (core_rollup && (i % sigar->lcpu)) {
467            /* merge times of logical processors */
468            cpu = &cpulist->data[cpulist->number-1];
469        }
470        else {
471            SIGAR_CPU_LIST_GROW(cpulist);
472            cpu = &cpulist->data[cpulist->number++];
473            SIGAR_ZERO(cpu);
474        }
475
476        get_cpu_metrics(sigar, cpu, ptr);
477
478        i++;
479    }
480
481    fclose(fp);
482
483    if (cpulist->number == 0) {
484        /* likely older kernel where cpu\d is not present */
485        cpu = &cpulist->data[cpulist->number++];
486        SIGAR_ZERO(cpu);
487        get_cpu_metrics(sigar, cpu, cpu_total);
488    }
489
490    return SIGAR_OK;
491}
492
493int sigar_uptime_get(sigar_t *sigar,
494                     sigar_uptime_t *uptime)
495{
496    char buffer[BUFSIZ], *ptr = buffer;
497    int status = sigar_file2str(PROC_UPTIME, buffer, sizeof(buffer));
498
499    if (status != SIGAR_OK) {
500        return status;
501    }
502
503    uptime->uptime   = strtod(buffer, &ptr);
504
505    return SIGAR_OK;
506}
507
508int sigar_loadavg_get(sigar_t *sigar,
509                      sigar_loadavg_t *loadavg)
510{
511    char buffer[BUFSIZ], *ptr = buffer;
512    int status = sigar_file2str(PROC_LOADAVG, buffer, sizeof(buffer));
513
514    if (status != SIGAR_OK) {
515        return status;
516    }
517
518    loadavg->loadavg[0] = strtod(buffer, &ptr);
519    loadavg->loadavg[1] = strtod(ptr, &ptr);
520    loadavg->loadavg[2] = strtod(ptr, &ptr);
521
522    return SIGAR_OK;
523}
524
525/*
526 * seems the easiest/fastest way to tell if a process listed in /proc
527 * is a thread is to check the "exit signal" flag in /proc/num/stat.
528 * any value other than SIGCHLD seems to be a thread.  this make hulk mad.
529 * redhat's procps patch (named "threadbadhack.pat") does not use
530 * this flag to filter out threads.  instead does much more expensive
531 * comparisions.  their patch also bubbles up thread cpu times to the main
532 * process.  functionality we currently lack.
533 * when nptl is in use, this is not the case and all threads spawned from
534 * a process have the same pid.  however, it seems both old-style linux
535 * threads and nptl threads can be run on the same machine.
536 * there is also the "Tgid" field in /proc/self/status which could be used
537 * to detect threads, but this is not available in older kernels.
538 */
539static  int proc_isthread(sigar_t *sigar, char *pidstr, int len)
540{
541    char buffer[BUFSIZ], *ptr=buffer;
542    int fd, n, offset=sigar->proc_signal_offset;
543
544    /* sprintf(buffer, "/proc/%s/stat", pidstr) */
545    memcpy(ptr, PROCP_FS_ROOT, SSTRLEN(PROCP_FS_ROOT));
546    ptr += SSTRLEN(PROCP_FS_ROOT);
547
548    memcpy(ptr, pidstr, len);
549    ptr += len;
550
551    memcpy(ptr, PROC_PSTAT, SSTRLEN(PROC_PSTAT));
552    ptr += SSTRLEN(PROC_PSTAT);
553
554    *ptr = '\0';
555
556    if ((fd = open(buffer, O_RDONLY)) < 0) {
557        /* unlikely if pid was from readdir proc */
558        return 0;
559    }
560
561    n = read(fd, buffer, sizeof(buffer));
562    close(fd);
563
564    if (n < 0) {
565        return 0; /* chances: slim..none */
566    }
567
568    buffer[n--] = '\0';
569
570    /* exit_signal is the second to last field so we look backwards.
571     * XXX if newer kernels drop more turds in this file we'll need
572     * to go the other way.  luckily linux has no real api for this shit.
573     */
574
575    /* skip trailing crap */
576    while ((n > 0) && !isdigit(buffer[n--])) ;
577
578    while (offset-- > 0) {
579        /* skip last field */
580        while ((n > 0) && isdigit(buffer[n--])) ;
581
582        /* skip whitespace */
583        while ((n > 0) && !isdigit(buffer[n--])) ;
584    }
585
586    if (n < 3) {
587        return 0; /* hulk smashed /proc? */
588    }
589
590    ptr = &buffer[n];
591    /*
592     * '17' == SIGCHLD == real process.
593     * '33' and '0' are threads
594     */
595    if ((*ptr++ == '1') &&
596        (*ptr++ == '7') &&
597        (*ptr++ == ' '))
598    {
599        return 0;
600    }
601
602    return 1;
603}
604
605int sigar_os_proc_list_get(sigar_t *sigar,
606                           sigar_proc_list_t *proclist)
607{
608    DIR *dirp = opendir(PROCP_FS_ROOT);
609    struct dirent *ent, dbuf;
610    register const int threadbadhack = !sigar->has_nptl;
611
612    if (!dirp) {
613        return errno;
614    }
615
616    if (threadbadhack && (sigar->proc_signal_offset == -1)) {
617        sigar->proc_signal_offset = get_proc_signal_offset();
618    }
619
620    while (readdir_r(dirp, &dbuf, &ent) == 0) {
621        if (!ent) {
622            break;
623        }
624
625        if (!sigar_isdigit(*ent->d_name)) {
626            continue;
627        }
628
629        if (threadbadhack &&
630            proc_isthread(sigar, ent->d_name, strlen(ent->d_name)))
631        {
632            continue;
633        }
634
635        /* XXX: more sanity checking */
636
637        SIGAR_PROC_LIST_GROW(proclist);
638
639        proclist->data[proclist->number++] =
640            strtoul(ent->d_name, NULL, 10);
641    }
642
643    closedir(dirp);
644
645    return SIGAR_OK;
646}
647
648static int proc_stat_read(sigar_t *sigar, sigar_pid_t pid)
649{
650    char buffer[BUFSIZ], *ptr=buffer, *tmp;
651    unsigned int len;
652    linux_proc_stat_t *pstat = &sigar->last_proc_stat;
653    int status;
654
655    time_t timenow = time(NULL);
656
657    /*
658     * short-lived cache read/parse of last /proc/pid/stat
659     * as this info is spread out across a few functions.
660     */
661    if (pstat->pid == pid) {
662        if ((timenow - pstat->mtime) < SIGAR_LAST_PROC_EXPIRE) {
663            return SIGAR_OK;
664        }
665    }
666
667    pstat->pid = pid;
668    pstat->mtime = timenow;
669
670    status = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTAT);
671
672    if (status != SIGAR_OK) {
673        return status;
674    }
675
676    if (!(ptr = strchr(ptr, '('))) {
677        return EINVAL;
678    }
679    if (!(tmp = strrchr(++ptr, ')'))) {
680        return EINVAL;
681    }
682    len = tmp-ptr;
683
684    if (len >= sizeof(pstat->name)) {
685        len = sizeof(pstat->name)-1;
686    }
687
688    /* (1,2) */
689    memcpy(pstat->name, ptr, len);
690    pstat->name[len] = '\0';
691    ptr = tmp+1;
692
693    SIGAR_SKIP_SPACE(ptr);
694    pstat->state = *ptr++; /* (3) */
695    SIGAR_SKIP_SPACE(ptr);
696
697    pstat->ppid = sigar_strtoul(ptr); /* (4) */
698    ptr = sigar_skip_token(ptr); /* (5) pgrp */
699    ptr = sigar_skip_token(ptr); /* (6) session */
700    pstat->tty = sigar_strtoul(ptr); /* (7) */
701    ptr = sigar_skip_token(ptr); /* (8) tty pgrp */
702
703    ptr = sigar_skip_token(ptr); /* (9) flags */
704    pstat->minor_faults = sigar_strtoull(ptr); /* (10) */
705    ptr = sigar_skip_token(ptr); /* (11) cmin flt */
706    pstat->major_faults = sigar_strtoull(ptr); /* (12) */
707    ptr = sigar_skip_token(ptr); /* (13) cmaj flt */
708
709    pstat->utime = SIGAR_TICK2MSEC(sigar_strtoull(ptr)); /* (14) */
710    pstat->stime = SIGAR_TICK2MSEC(sigar_strtoull(ptr)); /* (15) */
711
712    ptr = sigar_skip_token(ptr); /* (16) cutime */
713    ptr = sigar_skip_token(ptr); /* (17) cstime */
714
715    pstat->priority = sigar_strtoul(ptr); /* (18) */
716    pstat->nice     = sigar_strtoul(ptr); /* (19) */
717
718    ptr = sigar_skip_token(ptr); /* (20) timeout */
719    ptr = sigar_skip_token(ptr); /* (21) it_real_value */
720
721    pstat->start_time  = sigar_strtoul(ptr); /* (22) */
722    pstat->start_time /= sigar->ticks;
723    pstat->start_time += sigar->boot_time; /* seconds */
724    pstat->start_time *= 1000; /* milliseconds */
725
726    pstat->vsize = sigar_strtoull(ptr); /* (23) */
727    pstat->rss   = pageshift(sigar_strtoull(ptr)); /* (24) */
728
729    ptr = sigar_skip_token(ptr); /* (25) rlim */
730    ptr = sigar_skip_token(ptr); /* (26) startcode */
731    ptr = sigar_skip_token(ptr); /* (27) endcode */
732    ptr = sigar_skip_token(ptr); /* (28) startstack */
733    ptr = sigar_skip_token(ptr); /* (29) kstkesp */
734    ptr = sigar_skip_token(ptr); /* (30) kstkeip */
735    ptr = sigar_skip_token(ptr); /* (31) signal */
736    ptr = sigar_skip_token(ptr); /* (32) blocked */
737    ptr = sigar_skip_token(ptr); /* (33) sigignore */
738    ptr = sigar_skip_token(ptr); /* (34) sigcache */
739    ptr = sigar_skip_token(ptr); /* (35) wchan */
740    ptr = sigar_skip_token(ptr); /* (36) nswap */
741    ptr = sigar_skip_token(ptr); /* (37) cnswap */
742    ptr = sigar_skip_token(ptr); /* (38) exit_signal */
743
744    pstat->processor = sigar_strtoul(ptr); /* (39) */
745
746    return SIGAR_OK;
747}
748
749int sigar_proc_mem_get(sigar_t *sigar, sigar_pid_t pid,
750                       sigar_proc_mem_t *procmem)
751{
752    char buffer[BUFSIZ], *ptr=buffer;
753    int status = proc_stat_read(sigar, pid);
754    linux_proc_stat_t *pstat = &sigar->last_proc_stat;
755
756    procmem->minor_faults = pstat->minor_faults;
757    procmem->major_faults = pstat->major_faults;
758    procmem->page_faults =
759        procmem->minor_faults + procmem->major_faults;
760
761    status = SIGAR_PROC_FILE2STR(buffer, pid, "/statm");
762
763    if (status != SIGAR_OK) {
764        return status;
765    }
766
767    procmem->size     = pageshift(sigar_strtoull(ptr));
768    procmem->resident = pageshift(sigar_strtoull(ptr));
769    procmem->share    = pageshift(sigar_strtoull(ptr));
770
771    return SIGAR_OK;
772}
773
774#define NO_ID_MSG "[proc_cred] /proc/%lu" PROC_PSTATUS " missing "
775
776int sigar_proc_cred_get(sigar_t *sigar, sigar_pid_t pid,
777                        sigar_proc_cred_t *proccred)
778{
779    char buffer[BUFSIZ], *ptr;
780    int status = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTATUS);
781
782    if (status != SIGAR_OK) {
783        return status;
784    }
785
786    if ((ptr = strstr(buffer, "\nUid:"))) {
787        ptr = sigar_skip_token(ptr);
788
789        proccred->uid  = sigar_strtoul(ptr);
790        proccred->euid = sigar_strtoul(ptr);
791    }
792    else {
793        sigar_log_printf(sigar, SIGAR_LOG_WARN,
794                         NO_ID_MSG "Uid", pid);
795        return ENOENT;
796    }
797
798    if ((ptr = strstr(ptr, "\nGid:"))) {
799        ptr = sigar_skip_token(ptr);
800
801        proccred->gid  = sigar_strtoul(ptr);
802        proccred->egid = sigar_strtoul(ptr);
803    }
804    else {
805        sigar_log_printf(sigar, SIGAR_LOG_WARN,
806                         NO_ID_MSG "Gid", pid);
807        return ENOENT;
808    }
809
810    return SIGAR_OK;
811}
812
813int sigar_proc_time_get(sigar_t *sigar, sigar_pid_t pid,
814                        sigar_proc_time_t *proctime)
815{
816    int status = proc_stat_read(sigar, pid);
817    linux_proc_stat_t *pstat = &sigar->last_proc_stat;
818
819    if (status != SIGAR_OK) {
820        return status;
821    }
822
823    proctime->user = pstat->utime;
824    proctime->sys  = pstat->stime;
825    proctime->total = proctime->user + proctime->sys;
826    proctime->start_time = pstat->start_time;
827
828    return SIGAR_OK;
829}
830
831static int proc_status_get(sigar_t *sigar, sigar_pid_t pid,
832                           sigar_proc_state_t *procstate)
833{
834    char buffer[BUFSIZ], *ptr;
835    int status = SIGAR_PROC_FILE2STR(buffer, pid, PROC_PSTATUS);
836
837    if (status != SIGAR_OK) {
838        return status;
839    }
840
841    ptr = strstr(buffer, "\nThreads:");
842    if (ptr) {
843        /* 2.6+ kernel only */
844        ptr = sigar_skip_token(ptr);
845        procstate->threads = sigar_strtoul(ptr);
846    }
847    else {
848        procstate->threads = SIGAR_FIELD_NOTIMPL;
849    }
850
851    return SIGAR_OK;
852}
853
854int sigar_proc_state_get(sigar_t *sigar, sigar_pid_t pid,
855                         sigar_proc_state_t *procstate)
856{
857    int status = proc_stat_read(sigar, pid);
858    linux_proc_stat_t *pstat = &sigar->last_proc_stat;
859
860    if (status != SIGAR_OK) {
861        return status;
862    }
863
864    memcpy(procstate->name, pstat->name, sizeof(procstate->name));
865    procstate->state = pstat->state;
866
867    procstate->ppid     = pstat->ppid;
868    procstate->tty      = pstat->tty;
869    procstate->priority = pstat->priority;
870    procstate->nice     = pstat->nice;
871    procstate->processor = pstat->processor;
872
873    if (sigar_cpu_core_rollup(sigar)) {
874        procstate->processor /= sigar->lcpu;
875    }
876
877    proc_status_get(sigar, pid, procstate);
878
879    return SIGAR_OK;
880}
881
882int sigar_os_proc_args_get(sigar_t *sigar, sigar_pid_t pid,
883                           sigar_proc_args_t *procargs)
884{
885    return sigar_procfs_args_get(sigar, pid, procargs);
886}
887
888/* glibc 2.8 XXX use sysconf(_SC_ARG_MAX) */
889#ifndef ARG_MAX
890#define ARG_MAX 131072
891#endif
892
893int sigar_proc_env_get(sigar_t *sigar, sigar_pid_t pid,
894                       sigar_proc_env_t *procenv)
895{
896    int fd;
897    char buffer[ARG_MAX]; /* XXX: ARG_MAX == 130k */
898    char name[BUFSIZ];
899    size_t len;
900    char *ptr, *end;
901
902    /* optimize if pid == $$ and type == ENV_KEY */
903    SIGAR_PROC_ENV_KEY_LOOKUP();
904
905    (void)SIGAR_PROC_FILENAME(name, pid, "/environ");
906
907    if ((fd = open(name, O_RDONLY)) < 0) {
908        if (errno == ENOENT) {
909            return ESRCH;
910        }
911        return errno;
912    }
913
914    len = read(fd, buffer, sizeof(buffer));
915
916    close(fd);
917
918    buffer[len] = '\0';
919    ptr = buffer;
920
921    end = buffer + len;
922    while (ptr < end) {
923        char *val = strchr(ptr, '=');
924        int klen, vlen, status;
925        char key[128]; /* XXX is there a max key size? */
926
927        if (val == NULL) {
928            /* not key=val format */
929            break;
930        }
931
932        klen = val - ptr;
933        SIGAR_SSTRCPY(key, ptr);
934        key[klen] = '\0';
935        ++val;
936
937        vlen = strlen(val);
938        status = procenv->env_getter(procenv->data,
939                                     key, klen, val, vlen);
940
941        if (status != SIGAR_OK) {
942            /* not an error; just stop iterating */
943            break;
944        }
945
946        ptr += (klen + 1 + vlen + 1);
947    }
948
949    return SIGAR_OK;
950}
951
952int sigar_proc_fd_get(sigar_t *sigar, sigar_pid_t pid,
953                      sigar_proc_fd_t *procfd)
954{
955    int status =
956        sigar_proc_fd_count(sigar, pid, &procfd->total);
957
958    return status;
959}
960
961int sigar_proc_exe_get(sigar_t *sigar, sigar_pid_t pid,
962                       sigar_proc_exe_t *procexe)
963{
964    int len;
965    char name[BUFSIZ];
966
967    (void)SIGAR_PROC_FILENAME(name, pid, "/cwd");
968
969    if ((len = readlink(name, procexe->cwd,
970                        sizeof(procexe->cwd)-1)) < 0)
971    {
972        return errno;
973    }
974
975    procexe->cwd[len] = '\0';
976
977    (void)SIGAR_PROC_FILENAME(name, pid, "/exe");
978
979    if ((len = readlink(name, procexe->name,
980                        sizeof(procexe->name)-1)) < 0)
981    {
982        return errno;
983    }
984
985    procexe->name[len] = '\0';
986
987    (void)SIGAR_PROC_FILENAME(name, pid, "/root");
988
989    if ((len = readlink(name, procexe->root,
990                        sizeof(procexe->root)-1)) < 0)
991    {
992        return errno;
993    }
994
995    procexe->root[len] = '\0';
996
997    return SIGAR_OK;
998}
999
1000int sigar_proc_modules_get(sigar_t *sigar, sigar_pid_t pid,
1001                           sigar_proc_modules_t *procmods)
1002{
1003    FILE *fp;
1004    char buffer[BUFSIZ], *ptr;
1005    unsigned long inode, last_inode = 0;
1006
1007    (void)SIGAR_PROC_FILENAME(buffer, pid, "/maps");
1008
1009    if (!(fp = fopen(buffer, "r"))) {
1010        return errno;
1011    }
1012
1013    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
1014        int len, status;
1015        /* skip region, flags, offset, dev */
1016        ptr = sigar_skip_multiple_token(ptr, 4);
1017        inode = sigar_strtoul(ptr);
1018
1019        if ((inode == 0) || (inode == last_inode)) {
1020            last_inode = 0;
1021            continue;
1022        }
1023
1024        last_inode = inode;
1025        SIGAR_SKIP_SPACE(ptr);
1026        len = strlen(ptr);
1027        ptr[len-1] = '\0'; /* chop \n */
1028
1029        status =
1030            procmods->module_getter(procmods->data,
1031                                    ptr, len-1);
1032
1033        if (status != SIGAR_OK) {
1034            /* not an error; just stop iterating */
1035            break;
1036        }
1037    }
1038
1039    fclose(fp);
1040
1041    return SIGAR_OK;
1042}
1043
1044int sigar_thread_cpu_get(sigar_t *sigar,
1045                         sigar_uint64_t id,
1046                         sigar_thread_cpu_t *cpu)
1047{
1048    struct tms now;
1049
1050    if (id != 0) {
1051        return SIGAR_ENOTIMPL;
1052    }
1053
1054    times(&now);
1055
1056    cpu->user  = SIGAR_TICK2NSEC(now.tms_utime);
1057    cpu->sys   = SIGAR_TICK2NSEC(now.tms_stime);
1058    cpu->total = SIGAR_TICK2NSEC(now.tms_utime + now.tms_stime);
1059
1060    return SIGAR_OK;
1061}
1062
1063#include <mntent.h>
1064
1065int sigar_os_fs_type_get(sigar_file_system_t *fsp)
1066{
1067    char *type = fsp->sys_type_name;
1068
1069    switch (*type) {
1070      case 'e':
1071        if (strnEQ(type, "ext", 3)) {
1072            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1073        }
1074        break;
1075      case 'g':
1076        if (strEQ(type, "gfs")) {
1077            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1078        }
1079        break;
1080      case 'h':
1081        if (strEQ(type, "hpfs")) {
1082            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1083        }
1084        break;
1085      case 'j':
1086        if (strnEQ(type, "jfs", 3)) {
1087            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1088        }
1089        break;
1090      case 'o':
1091        if (strnEQ(type, "ocfs", 4)) {
1092            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1093        }
1094        break;
1095      case 'p':
1096        if (strnEQ(type, "psfs", 4)) {
1097            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1098        }
1099        break;
1100      case 'r':
1101        if (strEQ(type, "reiserfs")) {
1102            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1103        }
1104        break;
1105      case 'v':
1106        if (strEQ(type, "vzfs")) {
1107            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1108        }
1109        break;
1110      case 'x':
1111        if (strEQ(type, "xfs") || strEQ(type, "xiafs")) {
1112            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
1113        }
1114        break;
1115    }
1116
1117    return fsp->type;
1118}
1119
1120int sigar_file_system_list_get(sigar_t *sigar,
1121                               sigar_file_system_list_t *fslist)
1122{
1123    struct mntent ent;
1124    char buf[1025]; /* buffer for strings within ent */
1125    FILE *fp;
1126    sigar_file_system_t *fsp;
1127
1128    if (!(fp = setmntent(MOUNTED, "r"))) {
1129        return errno;
1130    }
1131
1132    sigar_file_system_list_create(fslist);
1133
1134    while (getmntent_r(fp, &ent, buf, sizeof(buf))) {
1135        SIGAR_FILE_SYSTEM_LIST_GROW(fslist);
1136
1137        fsp = &fslist->data[fslist->number++];
1138
1139        fsp->type = SIGAR_FSTYPE_UNKNOWN; /* unknown, will be set later */
1140        SIGAR_SSTRCPY(fsp->dir_name, ent.mnt_dir);
1141        SIGAR_SSTRCPY(fsp->dev_name, ent.mnt_fsname);
1142        SIGAR_SSTRCPY(fsp->sys_type_name, ent.mnt_type);
1143        SIGAR_SSTRCPY(fsp->options, ent.mnt_opts);
1144        sigar_fs_type_get(fsp);
1145    }
1146
1147    endmntent(fp);
1148
1149    return SIGAR_OK;
1150}
1151
1152#define ST_MAJOR(sb) major((sb).st_rdev)
1153#define ST_MINOR(sb) minor((sb).st_rdev)
1154
1155static int get_iostat_sys(sigar_t *sigar,
1156                          const char *dirname,
1157                          sigar_disk_usage_t *disk,
1158                          sigar_iodev_t **iodev)
1159{
1160    char stat[1025], dev[1025];
1161    char *name, *ptr, *fsdev;
1162    int partition, status;
1163
1164    if (!(*iodev = sigar_iodev_get(sigar, dirname))) {
1165        return ENXIO;
1166    }
1167
1168    name = fsdev = (*iodev)->name;
1169
1170    if (SIGAR_NAME_IS_DEV(name)) {
1171        name += SSTRLEN(SIGAR_DEV_PREFIX); /* strip "/dev/" */
1172    }
1173
1174    while (!sigar_isdigit(*fsdev)) {
1175        fsdev++;
1176    }
1177
1178    partition = strtoul(fsdev, NULL, 0);
1179    *fsdev = '\0';
1180
1181    snprintf(stat, sizeof(stat),
1182             SYS_BLOCK "/%s/%s%d/stat", name, name, partition);
1183
1184    status = sigar_file2str(stat, dev, sizeof(dev));
1185    if (status != SIGAR_OK) {
1186        return status;
1187    }
1188
1189    ptr = dev;
1190    ptr = sigar_skip_token(ptr);
1191    disk->reads = sigar_strtoull(ptr);
1192    ptr = sigar_skip_token(ptr);
1193    disk->writes = sigar_strtoull(ptr);
1194
1195    disk->read_bytes  = SIGAR_FIELD_NOTIMPL;
1196    disk->write_bytes = SIGAR_FIELD_NOTIMPL;
1197    disk->queue       = SIGAR_FIELD_NOTIMPL;
1198
1199    return SIGAR_OK;
1200}
1201
1202static int get_iostat_proc_dstat(sigar_t *sigar,
1203                                 const char *dirname,
1204                                 sigar_disk_usage_t *disk,
1205                                 sigar_iodev_t **iodev,
1206                                 sigar_disk_usage_t *device_usage)
1207{
1208    FILE *fp;
1209    char buffer[1025];
1210    char *ptr;
1211    struct stat sb;
1212    int status=ENOENT;
1213
1214    SIGAR_DISK_STATS_INIT(device_usage);
1215
1216    if (!(*iodev = sigar_iodev_get(sigar, dirname))) {
1217        return ENXIO;
1218    }
1219
1220    if (stat((*iodev)->name, &sb) < 0) {
1221        return errno;
1222    }
1223
1224    if (SIGAR_LOG_IS_DEBUG(sigar)) {
1225        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1226                         PROC_DISKSTATS " %s -> %s [%d,%d]",
1227                         dirname, (*iodev)->name,
1228                         ST_MAJOR(sb), ST_MINOR(sb));
1229    }
1230
1231    if (!(fp = fopen(PROC_DISKSTATS, "r"))) {
1232        return errno;
1233    }
1234
1235    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
1236        unsigned long major, minor;
1237
1238        major = sigar_strtoul(ptr);
1239        minor = sigar_strtoul(ptr);
1240
1241        if ((major == ST_MAJOR(sb)) &&
1242            ((minor == ST_MINOR(sb)) || (minor == 0)))
1243        {
1244            int num;
1245            unsigned long
1246                rio, rmerge, rsect, ruse,
1247                wio, wmerge, wsect, wuse,
1248                running, use, aveq;
1249
1250            ptr = sigar_skip_token(ptr); /* name */
1251
1252            num = sscanf(ptr,
1253                         "%lu %lu %lu %lu "
1254                         "%lu %lu %lu %lu "
1255                         "%lu %lu %lu",
1256                         &rio,     /* 1  # reads issued */
1257                         &rmerge,  /* 2  # reads merged */
1258                         &rsect,   /* 3  # sectors read */
1259                         &ruse,    /* 4  # millis spent reading */
1260                         &wio,     /* 5  # writes completed */
1261                         &wmerge,  /* 6  # writes merged */
1262                         &wsect,   /* 7  # sectors written */
1263                         &wuse,    /* 8  # millis spent writing */
1264                         &running, /* 9  # I/Os currently in progress */
1265                         &use,     /* 10 # millis spent doing I/Os */
1266                         &aveq);   /* 11 # of millis spent doing I/Os (weighted) */
1267
1268            if (num == 11) {
1269                disk->rtime = ruse;
1270                disk->wtime = wuse;
1271                disk->time = use;
1272                disk->qtime = aveq;
1273            }
1274            else if (num == 4) {
1275                wio = rsect;
1276                rsect = rmerge;
1277                wsect = ruse;
1278                disk->time = disk->qtime = SIGAR_FIELD_NOTIMPL;
1279            }
1280            else {
1281                status = ENOENT;
1282            }
1283
1284            disk->reads = rio;
1285            disk->writes = wio;
1286            disk->read_bytes  = rsect;
1287            disk->write_bytes = wsect;
1288
1289            /* convert sectors to bytes (512 is fixed size in 2.6 kernels) */
1290            disk->read_bytes  *= 512;
1291            disk->write_bytes *= 512;
1292
1293            if (minor == ST_MINOR(sb)) {
1294                status = SIGAR_OK;
1295                break;
1296            }
1297            else if (minor == 0) {
1298                memcpy(device_usage, disk, sizeof(*device_usage));
1299            }
1300        }
1301    }
1302
1303    fclose(fp);
1304
1305    return status;
1306}
1307
1308static int get_iostat_procp(sigar_t *sigar,
1309                            const char *dirname,
1310                            sigar_disk_usage_t *disk,
1311                            sigar_iodev_t **iodev)
1312{
1313    FILE *fp;
1314    char buffer[1025];
1315    char *ptr;
1316    struct stat sb;
1317
1318    if (!(*iodev = sigar_iodev_get(sigar, dirname))) {
1319        return ENXIO;
1320    }
1321
1322    if (stat((*iodev)->name, &sb) < 0) {
1323        return errno;
1324    }
1325
1326    if (SIGAR_LOG_IS_DEBUG(sigar)) {
1327        sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1328                         PROC_PARTITIONS " %s -> %s [%d,%d]",
1329                         dirname, (*iodev)->name,
1330                         ST_MAJOR(sb), ST_MINOR(sb));
1331    }
1332
1333    if (!(fp = fopen(PROC_PARTITIONS, "r"))) {
1334        return errno;
1335    }
1336
1337    if (fgets(buffer, sizeof(buffer), fp) == NULL) { /* skip header */
1338        fclose(fp);
1339        return errno;
1340    }
1341
1342    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
1343        unsigned long major, minor;
1344
1345        major = sigar_strtoul(ptr);
1346        minor = sigar_strtoul(ptr);
1347
1348        if ((major == ST_MAJOR(sb)) && (minor == ST_MINOR(sb))) {
1349            ptr = sigar_skip_token(ptr); /* blocks */
1350            ptr = sigar_skip_token(ptr); /* name */
1351            disk->reads = sigar_strtoull(ptr); /* rio */
1352            ptr = sigar_skip_token(ptr);  /* rmerge */
1353            disk->read_bytes  = sigar_strtoull(ptr); /* rsect */
1354            disk->rtime = sigar_strtoull(ptr); /* ruse */
1355            disk->writes = sigar_strtoull(ptr); /* wio */
1356            ptr = sigar_skip_token(ptr);  /* wmerge */
1357            disk->write_bytes = sigar_strtoull(ptr); /* wsect */
1358            disk->wtime = sigar_strtoull(ptr); /* wuse */
1359            ptr = sigar_skip_token(ptr); /* running */
1360            disk->time = sigar_strtoull(ptr); /* use */
1361            disk->qtime  = sigar_strtoull(ptr); /* aveq */
1362
1363            /* convert sectors to bytes (512 is fixed size in 2.6 kernels) */
1364            disk->read_bytes  *= 512;
1365            disk->write_bytes *= 512;
1366
1367            fclose(fp);
1368            return SIGAR_OK;
1369        }
1370    }
1371
1372    fclose(fp);
1373
1374    return ENOENT;
1375}
1376
1377int sigar_disk_usage_get(sigar_t *sigar, const char *name,
1378                         sigar_disk_usage_t *disk)
1379{
1380    int status;
1381    sigar_iodev_t *iodev = NULL;
1382    sigar_disk_usage_t device_usage;
1383    SIGAR_DISK_STATS_INIT(disk);
1384
1385    /*
1386     * 2.2 has metrics /proc/stat, but wtf is the device mapping?
1387     * 2.4 has /proc/partitions w/ the metrics.
1388     * 2.6 has /proc/partitions w/o the metrics.
1389     *     instead the metrics are within the /proc-like /sys filesystem.
1390     *     also has /proc/diskstats
1391     */
1392    switch (sigar->iostat) {
1393      case IOSTAT_SYS:
1394        status = get_iostat_sys(sigar, name, disk, &iodev);
1395        break;
1396      case IOSTAT_DISKSTATS:
1397        status = get_iostat_proc_dstat(sigar, name, disk, &iodev, &device_usage);
1398        break;
1399      case IOSTAT_PARTITIONS:
1400        status = get_iostat_procp(sigar, name, disk, &iodev);
1401        break;
1402      /*
1403       * case IOSTAT_SOME_OTHER_WIERD_THING:
1404       * break;
1405       */
1406      case IOSTAT_NONE:
1407      default:
1408        status = ENOENT;
1409        break;
1410    }
1411
1412    if ((status == SIGAR_OK) && iodev) {
1413        sigar_uptime_t uptime;
1414        sigar_uint64_t interval, ios;
1415        double tput, util;
1416        sigar_disk_usage_t *partition_usage=NULL;
1417
1418        sigar_uptime_get(sigar, &uptime);
1419
1420        if (iodev->is_partition &&
1421            (sigar->iostat == IOSTAT_DISKSTATS))
1422        {
1423            /* 2.6 kernels do not have per-partition times */
1424            partition_usage = disk;
1425            disk = &device_usage;
1426        }
1427
1428        disk->snaptime = uptime.uptime;
1429
1430        if (iodev->disk.snaptime) {
1431            interval = disk->snaptime - iodev->disk.snaptime;
1432        }
1433        else {
1434            interval = disk->snaptime;
1435        }
1436
1437        ios =
1438            (disk->reads - iodev->disk.reads) +
1439            (disk->writes - iodev->disk.writes);
1440
1441        if (disk->time == SIGAR_FIELD_NOTIMPL) {
1442            disk->service_time = SIGAR_FIELD_NOTIMPL;
1443        }
1444        else {
1445            tput = ((double)ios) * HZ / interval;
1446            util = ((double)(disk->time - iodev->disk.time)) / interval * HZ;
1447            disk->service_time = tput ? util / tput : 0.0;
1448        }
1449        if (disk->qtime == SIGAR_FIELD_NOTIMPL) {
1450            disk->queue = SIGAR_FIELD_NOTIMPL;
1451        }
1452        else {
1453            util = ((double)(disk->qtime - iodev->disk.qtime)) / interval;
1454            disk->queue = util / 1000.0;
1455        }
1456
1457        memcpy(&iodev->disk, disk, sizeof(iodev->disk));
1458        if (partition_usage) {
1459            partition_usage->service_time = disk->service_time;
1460            partition_usage->queue = disk->queue;
1461        }
1462    }
1463
1464    return status;
1465}
1466
1467int sigar_file_system_usage_get(sigar_t *sigar,
1468                                const char *dirname,
1469                                sigar_file_system_usage_t *fsusage)
1470{
1471    int status = sigar_statvfs(sigar, dirname, fsusage);
1472
1473    if (status != SIGAR_OK) {
1474        return status;
1475    }
1476
1477    fsusage->use_percent = sigar_file_system_usage_calc_used(sigar, fsusage);
1478
1479    (void)sigar_disk_usage_get(sigar, dirname, &fsusage->disk);
1480
1481    return SIGAR_OK;
1482}
1483
1484static  char *cpu_info_strval(char *ptr)
1485{
1486    if ((ptr = strchr(ptr, ':'))) {
1487        ptr++;
1488        while (isspace (*ptr)) ptr++;
1489        return ptr;
1490    }
1491    return NULL;
1492}
1493
1494static  void cpu_info_strcpy(char *ptr, char *buf, int len)
1495{
1496    int slen;
1497    ptr = cpu_info_strval(ptr);
1498    if (!ptr) {
1499        return;
1500    }
1501    slen = strlen(ptr);
1502    strncpy(buf, ptr, len);
1503    buf[len] = '\0';
1504    if (slen < len) {
1505        buf[slen-1] = '\0'; /* rid \n */
1506    }
1507}
1508
1509static int get_cpu_info(sigar_t *sigar, sigar_cpu_info_t *info,
1510                        FILE *fp)
1511{
1512    char buffer[BUFSIZ], *ptr;
1513
1514    int found = 0;
1515
1516    /* UML vm wont have "cpu MHz" or "cache size" fields */
1517    info->mhz        = 0;
1518    info->cache_size = 0;
1519
1520#ifdef __powerpc64__
1521    SIGAR_SSTRCPY(info->vendor, "IBM");
1522#endif
1523
1524    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
1525        switch (*ptr) {
1526          case 'p': /* processor        : 0 */
1527            if (strnEQ(ptr, "processor", 9)) {
1528                found = 1;
1529            }
1530            break;
1531          case 'v':
1532            /* "vendor_id" or "vendor" */
1533            if (strnEQ(ptr, "vendor", 6)) {
1534                cpu_info_strcpy(ptr, info->vendor, sizeof(info->vendor));
1535                if (strEQ(info->vendor, "GenuineIntel")) {
1536                    SIGAR_SSTRCPY(info->vendor, "Intel");
1537                }
1538                else if (strEQ(info->vendor, "AuthenticAMD")) {
1539                    SIGAR_SSTRCPY(info->vendor, "AMD");
1540                }
1541            }
1542            break;
1543          case 'f':
1544            if (strnEQ(ptr, "family", 6)) {
1545                /* IA64 version of "model name" */
1546                cpu_info_strcpy(ptr, info->model, sizeof(info->model));
1547                sigar_cpu_model_adjust(sigar, info);
1548            }
1549            break;
1550          case 'm':
1551            if (strnEQ(ptr, "model name", 10)) {
1552                cpu_info_strcpy(ptr, info->model, sizeof(info->model));
1553                sigar_cpu_model_adjust(sigar, info);
1554            }
1555            break;
1556          case 'c':
1557            if (strnEQ(ptr, "cpu MHz", 7)) {
1558                ptr = cpu_info_strval(ptr);
1559                info->mhz = atoi(ptr);
1560            }
1561            else if (strnEQ(ptr, "cache size", 10)) {
1562                ptr = cpu_info_strval(ptr);
1563                info->cache_size = sigar_strtoul(ptr);
1564            }
1565#ifdef __powerpc64__
1566            /* each /proc/cpuinfo entry looks like so:
1567             *   processor       : 0
1568             *   cpu             : POWER5 (gr)
1569             *   clock           : 1656.392000MHz
1570             *   revision        : 2.2
1571             */
1572            else if (strnEQ(ptr, "clock", 5)) {
1573                ptr = cpu_info_strval(ptr);
1574                info->mhz = atoi(ptr);
1575            }
1576            else if (strnEQ(ptr, "cpu", 3)) {
1577                cpu_info_strcpy(ptr, info->model, sizeof(info->model));
1578
1579                if ((ptr = strchr(info->model, ' '))) {
1580                    /* "POWER5 (gr)" -> "POWER5" */
1581                    *ptr = '\0';
1582                }
1583            }
1584#endif
1585            break;
1586           /* lone \n means end of info for this processor */
1587          case '\n':
1588            return found;
1589        }
1590    }
1591
1592    return found;
1593}
1594
1595/* /proc/cpuinfo MHz will change w/ AMD + PowerNow */
1596static void get_cpuinfo_max_freq(sigar_cpu_info_t *cpu_info, int num)
1597{
1598    int status;
1599    char max_freq[PATH_MAX];
1600    snprintf(max_freq, sizeof(max_freq),
1601             "/sys/devices/system/cpu/cpu%d"
1602             "/cpufreq/cpuinfo_max_freq", num);
1603
1604    status =
1605        sigar_file2str(max_freq, max_freq, sizeof(max_freq)-1);
1606
1607    if (status == SIGAR_OK) {
1608        cpu_info->mhz_max = atoi(max_freq) / 1000;
1609    }
1610}
1611
1612static void get_cpuinfo_min_freq(sigar_cpu_info_t *cpu_info, int num)
1613{
1614    int status;
1615    char min_freq[PATH_MAX];
1616    snprintf(min_freq, sizeof(min_freq),
1617             "/sys/devices/system/cpu/cpu%d"
1618             "/cpufreq/cpuinfo_min_freq", num);
1619
1620    status =
1621        sigar_file2str(min_freq, min_freq, sizeof(min_freq)-1);
1622
1623    if (status == SIGAR_OK) {
1624        cpu_info->mhz_min = atoi(min_freq) / 1000;
1625    }
1626}
1627
1628int sigar_cpu_info_list_get(sigar_t *sigar,
1629                            sigar_cpu_info_list_t *cpu_infos)
1630{
1631    FILE *fp;
1632    int core_rollup = sigar_cpu_core_rollup(sigar), i=0;
1633
1634    if (!(fp = fopen(PROC_FS_ROOT "cpuinfo", "r"))) {
1635        return errno;
1636    }
1637
1638    (void)sigar_cpu_total_count(sigar);
1639    sigar_cpu_info_list_create(cpu_infos);
1640
1641    while (get_cpu_info(sigar, &cpu_infos->data[cpu_infos->number], fp)) {
1642        sigar_cpu_info_t *info;
1643
1644        if (core_rollup && (i++ % sigar->lcpu)) {
1645            continue; /* fold logical processors */
1646        }
1647
1648        info = &cpu_infos->data[cpu_infos->number];
1649        get_cpuinfo_max_freq(info, cpu_infos->number);
1650        get_cpuinfo_min_freq(info, cpu_infos->number);
1651
1652        info->total_cores = sigar->ncpu;
1653        info->cores_per_socket = sigar->lcpu;
1654        info->total_sockets = sigar_cpu_socket_count(sigar);
1655
1656        ++cpu_infos->number;
1657        SIGAR_CPU_INFO_LIST_GROW(cpu_infos);
1658    }
1659
1660    fclose(fp);
1661
1662    return SIGAR_OK;
1663}
1664
1665static  unsigned int hex2int(const char *x, int len)
1666{
1667    int i;
1668    unsigned int j;
1669
1670    for (i=0, j=0; i<len; i++) {
1671        register int ch = x[i];
1672        j <<= 4;
1673        if (isdigit(ch)) {
1674            j |= ch - '0';
1675        }
1676        else if (isupper(ch)) {
1677            j |= ch - ('A' - 10);
1678        }
1679        else {
1680            j |= ch - ('a' - 10);
1681        }
1682    }
1683
1684    return j;
1685}
1686
1687#define HEX_ENT_LEN 8
1688
1689#define ROUTE_FMT "%16s %128s %128s %X %"PRIu64" %"PRIu64"%"PRIu64" %128s %"PRIu64" %"PRIu64" %"PRIu64"\n"
1690#define RTF_UP 0x0001
1691
1692int sigar_net_route_list_get(sigar_t *sigar,
1693                             sigar_net_route_list_t *routelist)
1694{
1695    FILE *fp;
1696    char buffer[1024];
1697    char net_addr[128], gate_addr[128], mask_addr[128];
1698    unsigned int flags;
1699    sigar_net_route_t *route;
1700
1701    routelist->size = routelist->number = 0;
1702
1703    if (!(fp = fopen(PROC_FS_ROOT "net/route", "r"))) {
1704        return errno;
1705    }
1706
1707    sigar_net_route_list_create(routelist);
1708
1709    if (fgets(buffer, sizeof(buffer), fp) == NULL) { /* skip header */
1710        fclose(fp);
1711        return errno;
1712    }
1713
1714    while (fgets(buffer, sizeof(buffer), fp)) {
1715        int num;
1716
1717        SIGAR_NET_ROUTE_LIST_GROW(routelist);
1718        route = &routelist->data[routelist->number++];
1719
1720        /* XXX rid sscanf */
1721        num = sscanf(buffer, ROUTE_FMT,
1722                     route->ifname, net_addr, gate_addr,
1723                     &flags, &route->refcnt, &route->use,
1724                     &route->metric, mask_addr,
1725                     &route->mtu, &route->window, &route->irtt);
1726
1727        if ((num < 10) || !(flags & RTF_UP)) {
1728            --routelist->number;
1729            continue;
1730        }
1731
1732        route->flags = flags;
1733
1734        sigar_net_address_set(route->destination, hex2int(net_addr, HEX_ENT_LEN));
1735        sigar_net_address_set(route->gateway, hex2int(gate_addr, HEX_ENT_LEN));
1736        sigar_net_address_set(route->mask, hex2int(mask_addr, HEX_ENT_LEN));
1737    }
1738
1739    fclose(fp);
1740
1741    return SIGAR_OK;
1742}
1743
1744int sigar_net_interface_stat_get(sigar_t *sigar, const char *name,
1745                                 sigar_net_interface_stat_t *ifstat)
1746{
1747    int found = 0;
1748    char buffer[BUFSIZ];
1749    FILE *fp = fopen(PROC_FS_ROOT "net/dev", "r");
1750
1751    if (!fp) {
1752        return errno;
1753    }
1754
1755    /* skip header */
1756    if (fgets(buffer, sizeof(buffer), fp) == NULL ||
1757        fgets(buffer, sizeof(buffer), fp) == NULL) {
1758        fclose(fp);
1759        return errno;
1760    }
1761
1762    while (fgets(buffer, sizeof(buffer), fp)) {
1763        char *ptr, *dev;
1764
1765        dev = buffer;
1766        while (isspace(*dev)) {
1767            dev++;
1768        }
1769
1770        if (!(ptr = strchr(dev, ':'))) {
1771            continue;
1772        }
1773
1774        *ptr++ = 0;
1775
1776        if (!strEQ(dev, name)) {
1777            continue;
1778        }
1779
1780        found = 1;
1781        ifstat->rx_bytes    = sigar_strtoull(ptr);
1782        ifstat->rx_packets  = sigar_strtoull(ptr);
1783        ifstat->rx_errors   = sigar_strtoull(ptr);
1784        ifstat->rx_dropped  = sigar_strtoull(ptr);
1785        ifstat->rx_overruns = sigar_strtoull(ptr);
1786        ifstat->rx_frame    = sigar_strtoull(ptr);
1787
1788        /* skip: compressed multicast */
1789        ptr = sigar_skip_multiple_token(ptr, 2);
1790
1791        ifstat->tx_bytes      = sigar_strtoull(ptr);
1792        ifstat->tx_packets    = sigar_strtoull(ptr);
1793        ifstat->tx_errors     = sigar_strtoull(ptr);
1794        ifstat->tx_dropped    = sigar_strtoull(ptr);
1795        ifstat->tx_overruns   = sigar_strtoull(ptr);
1796        ifstat->tx_collisions = sigar_strtoull(ptr);
1797        ifstat->tx_carrier    = sigar_strtoull(ptr);
1798
1799        ifstat->speed         = SIGAR_FIELD_NOTIMPL;
1800
1801        break;
1802    }
1803
1804    fclose(fp);
1805
1806    return found ? SIGAR_OK : ENXIO;
1807}
1808
1809static  void convert_hex_address(sigar_net_address_t *address,
1810                                             char *ptr, int len)
1811{
1812    if (len > HEX_ENT_LEN) {
1813        int i;
1814        for (i=0; i<=3; i++, ptr+=HEX_ENT_LEN) {
1815            address->addr.in6[i] = hex2int(ptr, HEX_ENT_LEN);
1816        }
1817
1818        address->family = SIGAR_AF_INET6;
1819    }
1820    else {
1821        address->addr.in =
1822            (len == HEX_ENT_LEN) ? hex2int(ptr, HEX_ENT_LEN) : 0;
1823
1824        address->family = SIGAR_AF_INET;
1825    }
1826}
1827
1828typedef struct {
1829    sigar_net_connection_list_t *connlist;
1830    sigar_net_connection_t *conn;
1831    unsigned long port;
1832} net_conn_getter_t;
1833
1834static int proc_net_walker(sigar_net_connection_walker_t *walker,
1835                           sigar_net_connection_t *conn)
1836{
1837    net_conn_getter_t *getter =
1838        (net_conn_getter_t *)walker->data;
1839
1840    if (getter->connlist) {
1841        SIGAR_NET_CONNLIST_GROW(getter->connlist);
1842        memcpy(&getter->connlist->data[getter->connlist->number++],
1843               conn, sizeof(*conn));
1844    }
1845    else {
1846        if ((getter->port == conn->local_port) &&
1847            (conn->remote_port == 0))
1848        {
1849            memcpy(getter->conn, conn, sizeof(*conn));
1850            return !SIGAR_OK; /* break loop */
1851        }
1852    }
1853
1854    return SIGAR_OK; /* continue loop */
1855}
1856
1857#define SKIP_WHILE(p, c) while (*p == c) p++
1858#define SKIP_PAST(p, c) \
1859   while(*p && (*p != c)) p++; \
1860   SKIP_WHILE(p, c)
1861
1862typedef struct {
1863    FILE *fp;
1864    int (*close)(FILE *);
1865} xproc_t;
1866
1867static FILE *xproc_open(const char *command, xproc_t *xproc)
1868{
1869    struct stat sb;
1870    if (stat(command, &sb) == 0) {
1871        if (sb.st_mode & S_IXUSR) {
1872            /* executable script for testing large
1873             * conn table where we can sleep() to better
1874             * simulate /proc/net/tcp behavior
1875             */
1876            xproc->fp = popen(command, "r");
1877            xproc->close = pclose;
1878        }
1879        else {
1880            xproc->fp = fopen(command, "r");
1881            xproc->close = fclose;
1882        }
1883        return xproc->fp;
1884    }
1885    else {
1886        return NULL;
1887    }
1888}
1889
1890static int proc_net_read(sigar_net_connection_walker_t *walker,
1891                         const char *fname,
1892                         int type)
1893{
1894    FILE *fp = NULL;
1895    char buffer[8192];
1896    sigar_t *sigar = walker->sigar;
1897    char *ptr = sigar->proc_net;
1898    int flags = walker->flags;
1899    xproc_t xproc = { NULL, fclose };
1900
1901    if (ptr) {
1902        snprintf(buffer, sizeof(buffer),
1903                 "%s/%s", ptr,
1904                 fname + sizeof(PROC_FS_ROOT)-1);
1905
1906        if ((fp = xproc_open(buffer, &xproc))) {
1907            if (SIGAR_LOG_IS_DEBUG(sigar)) {
1908                sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1909                                 "[proc_net] using %s",
1910                                 buffer);
1911            }
1912        }
1913        else if (SIGAR_LOG_IS_DEBUG(sigar)) {
1914            sigar_log_printf(sigar, SIGAR_LOG_DEBUG,
1915                             "[proc_net] cannot open %s",
1916                             buffer);
1917        }
1918    }
1919
1920    if (!(fp || (fp = fopen(fname, "r")))) {
1921        return errno;
1922    }
1923
1924    if (fgets(buffer, sizeof(buffer), fp) == NULL) { /* skip header */
1925        fclose(fp);
1926        return errno;
1927    }
1928
1929    while ((ptr = fgets(buffer, sizeof(buffer), fp))) {
1930        sigar_net_connection_t conn;
1931        char *laddr, *raddr;
1932        int laddr_len=0, raddr_len=0;
1933        int more;
1934
1935        /* skip leading space */
1936        SKIP_WHILE(ptr, ' ');
1937
1938        /* skip "%d: " */
1939        SKIP_PAST(ptr, ' ');
1940
1941        laddr = ptr;
1942        while (*ptr && (*ptr != ':')) {
1943            laddr_len++;
1944            ptr++;
1945        }
1946        SKIP_WHILE(ptr, ':');
1947
1948        conn.local_port = (strtoul(ptr, &ptr, 16) & 0xffff);
1949
1950        SKIP_WHILE(ptr, ' ');
1951
1952        raddr = ptr;
1953        while (*ptr && (*ptr != ':')) {
1954            raddr_len++;
1955            ptr++;
1956        }
1957        SKIP_WHILE(ptr, ':');
1958
1959        conn.remote_port = (strtoul(ptr, &ptr, 16) & 0xffff);
1960
1961        SKIP_WHILE(ptr, ' ');
1962
1963        if (!((conn.remote_port && (flags & SIGAR_NETCONN_CLIENT)) ||
1964              (!conn.remote_port && (flags & SIGAR_NETCONN_SERVER))))
1965        {
1966            continue;
1967        }
1968
1969        conn.type = type;
1970
1971        convert_hex_address(&conn.local_address,
1972                            laddr, laddr_len);
1973
1974        convert_hex_address(&conn.remote_address,
1975                            raddr, raddr_len);
1976
1977        /* SIGAR_TCP_* currently matches TCP_* in linux/tcp.h */
1978        conn.state = hex2int(ptr, 2);
1979        ptr += 2;
1980        SKIP_WHILE(ptr, ' ');
1981
1982        conn.send_queue = hex2int(ptr, HEX_ENT_LEN);
1983        ptr += HEX_ENT_LEN+1; /* tx + ':' */;
1984
1985        conn.receive_queue = hex2int(ptr, HEX_ENT_LEN);
1986        ptr += HEX_ENT_LEN;
1987        SKIP_WHILE(ptr, ' ');
1988
1989        SKIP_PAST(ptr, ' '); /* tr:tm->whem */
1990        SKIP_PAST(ptr, ' '); /* retrnsmt */
1991
1992        conn.uid = sigar_strtoul(ptr);
1993
1994        SKIP_WHILE(ptr, ' ');
1995        SKIP_PAST(ptr, ' '); /* timeout */
1996
1997        conn.inode = sigar_strtoul(ptr);
1998
1999        more = walker->add_connection(walker, &conn);
2000        if (more != SIGAR_OK) {
2001            xproc.close(fp);
2002            return SIGAR_OK;
2003        }
2004    }
2005
2006    xproc.close(fp);
2007
2008    return SIGAR_OK;
2009}
2010
2011int sigar_net_connection_walk(sigar_net_connection_walker_t *walker)
2012{
2013    int flags = walker->flags;
2014    int status;
2015
2016    if (flags & SIGAR_NETCONN_TCP) {
2017        status = proc_net_read(walker,
2018                               PROC_FS_ROOT "net/tcp",
2019                               SIGAR_NETCONN_TCP);
2020
2021        if (status != SIGAR_OK) {
2022            return status;
2023        }
2024
2025        status = proc_net_read(walker,
2026                               PROC_FS_ROOT "net/tcp6",
2027                               SIGAR_NETCONN_TCP);
2028
2029        if (!((status == SIGAR_OK) || (status == ENOENT))) {
2030            return status;
2031        }
2032    }
2033
2034    if (flags & SIGAR_NETCONN_UDP) {
2035        status = proc_net_read(walker,
2036                               PROC_FS_ROOT "net/udp",
2037                               SIGAR_NETCONN_UDP);
2038
2039        if (status != SIGAR_OK) {
2040            return status;
2041        }
2042
2043        status = proc_net_read(walker,
2044                               PROC_FS_ROOT "net/udp6",
2045                               SIGAR_NETCONN_UDP);
2046
2047        if (!((status == SIGAR_OK) || (status == ENOENT))) {
2048            return status;
2049        }
2050    }
2051
2052    if (flags & SIGAR_NETCONN_RAW) {
2053        status = proc_net_read(walker,
2054                               PROC_FS_ROOT "net/raw",
2055                               SIGAR_NETCONN_RAW);
2056
2057        if (status != SIGAR_OK) {
2058            return status;
2059        }
2060
2061        status = proc_net_read(walker,
2062                               PROC_FS_ROOT "net/raw6",
2063                               SIGAR_NETCONN_RAW);
2064
2065        if (!((status == SIGAR_OK) || (status == ENOENT))) {
2066            return status;
2067        }
2068    }
2069
2070    /* XXX /proc/net/unix */
2071
2072    return SIGAR_OK;
2073}
2074
2075int sigar_net_connection_list_get(sigar_t *sigar,
2076                                  sigar_net_connection_list_t *connlist,
2077                                  int flags)
2078{
2079    int status;
2080    sigar_net_connection_walker_t walker;
2081    net_conn_getter_t getter;
2082
2083    sigar_net_connection_list_create(connlist);
2084
2085    getter.conn = NULL;
2086    getter.connlist = connlist;
2087
2088    walker.sigar = sigar;
2089    walker.flags = flags;
2090    walker.data = &getter;
2091    walker.add_connection = proc_net_walker;
2092
2093    status = sigar_net_connection_walk(&walker);
2094
2095    if (status != SIGAR_OK) {
2096        sigar_net_connection_list_destroy(sigar, connlist);
2097    }
2098
2099    return status;
2100}
2101
2102static int sigar_net_connection_get(sigar_t *sigar,
2103                                    sigar_net_connection_t *netconn,
2104                                    unsigned long port,
2105                                    int flags)
2106{
2107    int status;
2108    sigar_net_connection_walker_t walker;
2109    net_conn_getter_t getter;
2110
2111    getter.conn = netconn;
2112    getter.connlist = NULL;
2113    getter.port = port;
2114
2115    walker.sigar = sigar;
2116    walker.flags = flags;
2117    walker.data = &getter;
2118    walker.add_connection = proc_net_walker;
2119
2120    status = sigar_net_connection_walk(&walker);
2121
2122    return status;
2123}
2124
2125int sigar_net_interface_ipv6_config_get(sigar_t *sigar, const char *name,
2126                                        sigar_net_interface_config_t *ifconfig)
2127{
2128    FILE *fp;
2129    char addr[32+1], ifname[8+1];
2130    int status = SIGAR_ENOENT;
2131    unsigned int idx, prefix, scope, flags;
2132
2133    if (!(fp = fopen(PROC_FS_ROOT "net/if_inet6", "r"))) {
2134        return errno;
2135    }
2136
2137    while (fscanf(fp, "%32s %02x %02x %02x %02x %8s\n",
2138                  addr, &idx, &prefix, &scope, &flags, ifname) != EOF)
2139    {
2140        if (strEQ(name, ifname)) {
2141            status = SIGAR_OK;
2142            break;
2143        }
2144    }
2145
2146    fclose(fp);
2147
2148    if (status == SIGAR_OK) {
2149        int i=0;
2150        unsigned char *addr6 = (unsigned char *)&(ifconfig->address6.addr.in6);
2151        char *ptr = addr;
2152
2153        for (i=0; i<16; i++, ptr+=2) {
2154            addr6[i] = (unsigned char)hex2int(ptr, 2);
2155        }
2156
2157        ifconfig->prefix6_length = prefix;
2158        ifconfig->scope6 = scope;
2159    }
2160
2161    return status;
2162}
2163
2164#define SNMP_TCP_PREFIX "Tcp: "
2165
2166SIGAR_DECLARE(int)
2167sigar_tcp_get(sigar_t *sigar,
2168              sigar_tcp_t *tcp)
2169{
2170    FILE *fp;
2171    char buffer[1024], *ptr=buffer;
2172    int status = SIGAR_ENOENT;
2173
2174    if (!(fp = fopen(PROC_FS_ROOT "net/snmp", "r"))) {
2175        return errno;
2176    }
2177
2178    while (fgets(buffer, sizeof(buffer), fp)) {
2179        if (strnEQ(buffer, SNMP_TCP_PREFIX, sizeof(SNMP_TCP_PREFIX)-1)) {
2180            if (fgets(buffer, sizeof(buffer), fp)) {
2181                status = SIGAR_OK;
2182                break;
2183            }
2184        }
2185    }
2186
2187    fclose(fp);
2188
2189    if (status == SIGAR_OK) {
2190        /* assuming field order, same in 2.2, 2.4 and 2.6 kernels */
2191        /* Tcp: RtoAlgorithm RtoMin RtoMax MaxConn */
2192        ptr = sigar_skip_multiple_token(ptr, 5);
2193        tcp->active_opens = sigar_strtoull(ptr);
2194        tcp->passive_opens = sigar_strtoull(ptr);
2195        tcp->attempt_fails = sigar_strtoull(ptr);
2196        tcp->estab_resets = sigar_strtoull(ptr);
2197        tcp->curr_estab = sigar_strtoull(ptr);
2198        tcp->in_segs = sigar_strtoull(ptr);
2199        tcp->out_segs = sigar_strtoull(ptr);
2200        tcp->retrans_segs = sigar_strtoull(ptr);
2201        tcp->in_errs = sigar_strtoull(ptr);
2202        tcp->out_rsts = sigar_strtoull(ptr);
2203    }
2204
2205    return status;
2206}
2207
2208static int sigar_proc_nfs_gets(char *file, char *tok,
2209                               char *buffer, size_t size)
2210{
2211    int status = ENOENT;
2212    int len = strlen(tok);
2213    FILE *fp = fopen(file, "r");
2214
2215    if (!fp) {
2216        return SIGAR_ENOTIMPL;
2217    }
2218
2219    while (fgets(buffer, size, fp)) {
2220        if (strnEQ(buffer, tok, len)) {
2221            status = SIGAR_OK;
2222            break;
2223        }
2224    }
2225
2226    fclose(fp);
2227
2228    return status;
2229}
2230
2231static int sigar_nfs_v2_get(char *file, sigar_nfs_v2_t *nfs)
2232{
2233    char buffer[BUFSIZ], *ptr=buffer;
2234    int status =
2235        sigar_proc_nfs_gets(file,
2236                            "proc2", buffer, sizeof(buffer));
2237
2238    if (status != SIGAR_OK) {
2239        return status;
2240    }
2241
2242    ptr = sigar_skip_multiple_token(ptr, 2);
2243
2244    nfs->null = sigar_strtoull(ptr);
2245    nfs->getattr = sigar_strtoull(ptr);
2246    nfs->setattr = sigar_strtoull(ptr);
2247    nfs->root = sigar_strtoull(ptr);
2248    nfs->lookup = sigar_strtoull(ptr);
2249    nfs->readlink = sigar_strtoull(ptr);
2250    nfs->read = sigar_strtoull(ptr);
2251    nfs->writecache = sigar_strtoull(ptr);
2252    nfs->write = sigar_strtoull(ptr);
2253    nfs->create = sigar_strtoull(ptr);
2254    nfs->remove = sigar_strtoull(ptr);
2255    nfs->rename = sigar_strtoull(ptr);
2256    nfs->link = sigar_strtoull(ptr);
2257    nfs->symlink = sigar_strtoull(ptr);
2258    nfs->mkdir = sigar_strtoull(ptr);
2259    nfs->rmdir = sigar_strtoull(ptr);
2260    nfs->readdir = sigar_strtoull(ptr);
2261    nfs->fsstat = sigar_strtoull(ptr);
2262
2263    return SIGAR_OK;
2264}
2265
2266int sigar_nfs_client_v2_get(sigar_t *sigar,
2267                            sigar_nfs_client_v2_t *nfs)
2268{
2269    return sigar_nfs_v2_get(PROC_FS_ROOT "net/rpc/nfs",
2270                            (sigar_nfs_v2_t *)nfs);
2271}
2272
2273int sigar_nfs_server_v2_get(sigar_t *sigar,
2274                            sigar_nfs_server_v2_t *nfs)
2275{
2276    return sigar_nfs_v2_get(PROC_FS_ROOT "net/rpc/nfsd",
2277                            (sigar_nfs_v2_t *)nfs);
2278}
2279
2280static int sigar_nfs_v3_get(char *file, sigar_nfs_v3_t *nfs)
2281{
2282    char buffer[BUFSIZ], *ptr=buffer;
2283    int status =
2284        sigar_proc_nfs_gets(file,
2285                            "proc3", buffer, sizeof(buffer));
2286
2287    if (status != SIGAR_OK) {
2288        return status;
2289    }
2290
2291    ptr = sigar_skip_multiple_token(ptr, 2);
2292
2293    nfs->null = sigar_strtoull(ptr);
2294    nfs->getattr = sigar_strtoull(ptr);
2295    nfs->setattr = sigar_strtoull(ptr);
2296    nfs->lookup = sigar_strtoull(ptr);
2297    nfs->access = sigar_strtoull(ptr);
2298    nfs->readlink = sigar_strtoull(ptr);
2299    nfs->read = sigar_strtoull(ptr);
2300    nfs->write = sigar_strtoull(ptr);
2301    nfs->create = sigar_strtoull(ptr);
2302    nfs->mkdir = sigar_strtoull(ptr);
2303    nfs->symlink = sigar_strtoull(ptr);
2304    nfs->mknod = sigar_strtoull(ptr);
2305    nfs->remove = sigar_strtoull(ptr);
2306    nfs->rmdir = sigar_strtoull(ptr);
2307    nfs->rename = sigar_strtoull(ptr);
2308    nfs->link = sigar_strtoull(ptr);
2309    nfs->readdir = sigar_strtoull(ptr);
2310    nfs->readdirplus = sigar_strtoull(ptr);
2311    nfs->fsstat = sigar_strtoull(ptr);
2312    nfs->fsinfo = sigar_strtoull(ptr);
2313    nfs->pathconf = sigar_strtoull(ptr);
2314    nfs->commit = sigar_strtoull(ptr);
2315
2316    return SIGAR_OK;
2317}
2318
2319int sigar_nfs_client_v3_get(sigar_t *sigar,
2320                            sigar_nfs_client_v3_t *nfs)
2321{
2322    return sigar_nfs_v3_get(PROC_FS_ROOT "net/rpc/nfs",
2323                            (sigar_nfs_v3_t *)nfs);
2324}
2325
2326int sigar_nfs_server_v3_get(sigar_t *sigar,
2327                            sigar_nfs_server_v3_t *nfs)
2328{
2329    return sigar_nfs_v3_get(PROC_FS_ROOT "net/rpc/nfsd",
2330                            (sigar_nfs_v3_t *)nfs);
2331}
2332
2333#include <net/if_arp.h>
2334
2335static char *get_hw_type(int type)
2336{
2337    switch (type) {
2338    case ARPHRD_AX25:
2339        return "ax25";
2340    case ARPHRD_ECONET:
2341        return "ec";
2342    case ARPHRD_ETHER:
2343        return "ether";
2344    case ARPHRD_FDDI:
2345        return "fddi";
2346    case ARPHRD_DLCI:
2347        return "dlci";
2348    case ARPHRD_FRAD:
2349        return "frad";
2350    case ARPHRD_HDLC:
2351        return "hdlc";
2352    case ARPHRD_LAPB:
2353        return "lapb";
2354    case ARPHRD_HIPPI:
2355        return "hippi";
2356    case ARPHRD_IRDA:
2357        return "irda";
2358    case ARPHRD_LOOPBACK:
2359        return "loop";
2360    case ARPHRD_NETROM:
2361        return "netrom";
2362    case ARPHRD_PPP:
2363        return "ppp";
2364    case ARPHRD_ROSE:
2365        return "rose";
2366    case ARPHRD_SIT:
2367        return "sit";
2368    case ARPHRD_SLIP:
2369        return "slip";
2370    case ARPHRD_CSLIP:
2371        return "cslip";
2372    case ARPHRD_SLIP6:
2373        return "slip6";
2374    case ARPHRD_CSLIP6:
2375        return "cslip6";
2376    case ARPHRD_ADAPT:
2377        return "adaptive";
2378    case ARPHRD_IEEE802:
2379        return "tr";
2380    case ARPHRD_IEEE802_TR:
2381        return "tr";
2382    case ARPHRD_TUNNEL:
2383        return "tunnel";
2384    case ARPHRD_X25:
2385        return "x25";
2386    default:
2387        return "unknown";
2388    }
2389}
2390
2391int sigar_arp_list_get(sigar_t *sigar,
2392                       sigar_arp_list_t *arplist)
2393{
2394    FILE *fp;
2395    char buffer[1024];
2396    char net_addr[128], hwaddr[128], mask_addr[128];
2397    int status;
2398    unsigned int flags, type;
2399    sigar_arp_t *arp;
2400
2401    arplist->size = arplist->number = 0;
2402
2403    if (!(fp = fopen(PROC_FS_ROOT "net/arp", "r"))) {
2404        return errno;
2405    }
2406
2407    sigar_arp_list_create(arplist);
2408
2409    if (fgets(buffer, sizeof(buffer), fp) == NULL) { /* skip header */
2410        fclose(fp);
2411        return errno;
2412    }
2413    while (fgets(buffer, sizeof(buffer), fp)) {
2414        int num;
2415
2416        SIGAR_ARP_LIST_GROW(arplist);
2417        arp = &arplist->data[arplist->number++];
2418
2419        /* XXX rid sscanf */
2420        num = sscanf(buffer, "%128s 0x%x 0x%x %128s %128s %16s",
2421                     net_addr, &type, &flags,
2422                     hwaddr, mask_addr, arp->ifname);
2423
2424        if (num < 6) {
2425            --arplist->number;
2426            continue;
2427        }
2428
2429        arp->flags = flags;
2430        status = inet_pton(AF_INET, net_addr, &arp->address.addr);
2431        if (status > 0) {
2432            arp->address.family = SIGAR_AF_INET;
2433        }
2434        else if ((status = inet_pton(AF_INET6, net_addr, &arp->address.addr)) > 0) {
2435            arp->address.family = SIGAR_AF_INET6;
2436        }
2437        else {
2438            sigar_log_printf(sigar, SIGAR_LOG_WARN,
2439                             "[arp] failed to parse address='%s' (%s)\n", net_addr,
2440                             ((status == 0) ? "Invalid format" : sigar_strerror(sigar, errno)));
2441            --arplist->number;
2442            continue;
2443        }
2444
2445        num = sscanf(hwaddr, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
2446                     &arp->hwaddr.addr.mac[0],
2447                     &arp->hwaddr.addr.mac[1],
2448                     &arp->hwaddr.addr.mac[2],
2449                     &arp->hwaddr.addr.mac[3],
2450                     &arp->hwaddr.addr.mac[4],
2451                     &arp->hwaddr.addr.mac[5]);
2452        if (num < 6) {
2453            sigar_log_printf(sigar, SIGAR_LOG_WARN,
2454                             "[arp] failed to parse hwaddr='%s' (%s)\n", hwaddr);
2455            --arplist->number;
2456            continue;
2457        }
2458        arp->hwaddr.family = SIGAR_AF_LINK;
2459
2460        SIGAR_SSTRCPY(arp->type, get_hw_type(type));
2461    }
2462
2463    fclose(fp);
2464
2465    return SIGAR_OK;
2466}
2467
2468int sigar_proc_port_get(sigar_t *sigar, int protocol,
2469                        unsigned long port, sigar_pid_t *pid)
2470{
2471    int status;
2472    sigar_net_connection_t netconn;
2473    DIR *dirp;
2474    struct dirent *ent, dbuf;
2475
2476    SIGAR_ZERO(&netconn);
2477    *pid = 0;
2478
2479    status = sigar_net_connection_get(sigar, &netconn, port,
2480                                      SIGAR_NETCONN_SERVER|protocol);
2481
2482    if (status != SIGAR_OK) {
2483        return status;
2484    }
2485
2486    if (netconn.local_port != port) {
2487        return SIGAR_OK; /* XXX or ENOENT? */
2488    }
2489
2490    if (!(dirp = opendir(PROCP_FS_ROOT))) {
2491        return errno;
2492    }
2493
2494    while (readdir_r(dirp, &dbuf, &ent) == 0) {
2495        DIR *fd_dirp;
2496        struct dirent *fd_ent, fd_dbuf;
2497        struct stat sb;
2498        char fd_name[BUFSIZ], pid_name[BUFSIZ];
2499        int len, slen;
2500
2501        if (ent == NULL) {
2502            break;
2503        }
2504
2505        if (!sigar_isdigit(*ent->d_name)) {
2506            continue;
2507        }
2508
2509        /* sprintf(pid_name, "/proc/%s", ent->d_name) */
2510        memcpy(&pid_name[0], PROCP_FS_ROOT, SSTRLEN(PROCP_FS_ROOT));
2511        len = SSTRLEN(PROCP_FS_ROOT);
2512        pid_name[len++] = '/';
2513
2514        slen = strlen(ent->d_name);
2515        memcpy(&pid_name[len], ent->d_name, slen);
2516        len += slen;
2517        pid_name[len] = '\0';
2518
2519        if (stat(pid_name, &sb) < 0) {
2520            continue;
2521        }
2522        if (sb.st_uid != netconn.uid) {
2523            continue;
2524        }
2525
2526        /* sprintf(fd_name, "%s/fd", pid_name) */
2527        memcpy(&fd_name[0], pid_name, len);
2528        memcpy(&fd_name[len], "/fd", 3);
2529        fd_name[len+=3] = '\0';
2530
2531        if (!(fd_dirp = opendir(fd_name))) {
2532            continue;
2533        }
2534
2535        while (readdir_r(fd_dirp, &fd_dbuf, &fd_ent) == 0) {
2536            char fd_ent_name[BUFSIZ];
2537
2538            if (fd_ent == NULL) {
2539                break;
2540            }
2541
2542            if (!sigar_isdigit(*fd_ent->d_name)) {
2543                continue;
2544            }
2545
2546            /* sprintf(fd_ent_name, "%s/%s", fd_name, fd_ent->d_name) */
2547            slen = strlen(fd_ent->d_name);
2548            memcpy(&fd_ent_name[0], fd_name, len);
2549            fd_ent_name[len] = '/';
2550            memcpy(&fd_ent_name[len+1], fd_ent->d_name, slen);
2551            fd_ent_name[len+1+slen] = '\0';
2552
2553            if (stat(fd_ent_name, &sb) < 0) {
2554                continue;
2555            }
2556
2557            if (sb.st_ino == netconn.inode) {
2558                closedir(fd_dirp);
2559                closedir(dirp);
2560                *pid = strtoul(ent->d_name, NULL, 10);
2561                return SIGAR_OK;
2562            }
2563
2564        }
2565
2566        closedir(fd_dirp);
2567    }
2568
2569    closedir(dirp);
2570
2571    return SIGAR_OK;
2572}
2573
2574static void generic_vendor_parse(char *line, sigar_sys_info_t *info)
2575{
2576    char *ptr;
2577    int len = 0;
2578
2579    while (*line) {
2580        SIGAR_SKIP_SPACE(line);
2581        if (!isdigit(*line)) {
2582            ++line;
2583            continue;
2584        }
2585
2586        ptr = line;
2587        while ((isdigit(*ptr) || (*ptr == '.'))) {
2588            ++ptr;
2589            ++len;
2590        }
2591
2592        if (len) {
2593            /* sanity check */
2594            if (len > sizeof(info->vendor_version)) {
2595                continue;
2596            }
2597            memcpy(info->vendor_version, line, len);/*XXX*/
2598            info->vendor_version[len] = '\0';
2599            return;
2600        }
2601    }
2602}
2603
2604static void redhat_vendor_parse(char *line, sigar_sys_info_t *info)
2605{
2606    char *start, *end;
2607
2608    generic_vendor_parse(line, info); /* super.parse */
2609
2610    if ((start = strchr(line, '('))) {
2611        ++start;
2612        if ((end = strchr(start, ')'))) {
2613            int len = end-start;
2614            memcpy(info->vendor_code_name, start, len);/*XXX*/
2615            info->vendor_code_name[len] = '\0';
2616        }
2617    }
2618
2619#define RHEL_PREFIX "Red Hat Enterprise Linux "
2620#define CENTOS_VENDOR "CentOS"
2621#define SL_VENDOR "Scientific Linux"
2622
2623    if (strnEQ(line, RHEL_PREFIX, sizeof(RHEL_PREFIX)-1)) {
2624        snprintf(info->vendor_version,
2625                 sizeof(info->vendor_version),
2626                 "Enterprise Linux %c",
2627                 info->vendor_version[0]);
2628    }
2629    else if (strnEQ(line, CENTOS_VENDOR, sizeof(CENTOS_VENDOR)-1)) {
2630        SIGAR_SSTRCPY(info->vendor, CENTOS_VENDOR);
2631    }
2632    else if (strnEQ(line, SL_VENDOR, sizeof(SL_VENDOR)-1)) {
2633        SIGAR_SSTRCPY(info->vendor, SL_VENDOR);
2634    }
2635}
2636
2637#define is_quote(c) ((c == '\'') || (c == '"'))
2638
2639static void kv_parse(char *data, sigar_sys_info_t *info,
2640                     void (*func)(sigar_sys_info_t *, char *, char *))
2641{
2642    char *ptr = data;
2643    int len = strlen(data);
2644    char *end = data+len;
2645
2646    while (ptr < end) {
2647        char *val = strchr(ptr, '=');
2648        int klen, vlen;
2649        char key[256], *ix;
2650
2651        if (!val) {
2652            continue;
2653        }
2654        klen = val - ptr;
2655        SIGAR_SSTRCPY(key, ptr);
2656        key[klen] = '\0';
2657        ++val;
2658
2659        if ((ix = strchr(val, '\n'))) {
2660            *ix = '\0';
2661        }
2662        vlen = strlen(val);
2663        if (is_quote(*val)) {
2664            if (is_quote(val[vlen-1])) {
2665                val[vlen-1] =  '\0';
2666            }
2667            ++val;
2668        }
2669
2670        func(info, key, val);
2671
2672        ptr += (klen + 1 + vlen + 1);
2673    }
2674}
2675
2676static void lsb_parse(sigar_sys_info_t *info,
2677                      char *key, char *val)
2678{
2679    if (strEQ(key, "DISTRIB_ID")) {
2680        SIGAR_SSTRCPY(info->vendor, val);
2681    }
2682    else if (strEQ(key, "DISTRIB_RELEASE")) {
2683        SIGAR_SSTRCPY(info->vendor_version, val);
2684    }
2685    else if (strEQ(key, "DISTRIB_CODENAME")) {
2686        SIGAR_SSTRCPY(info->vendor_code_name, val);
2687    }
2688}
2689
2690static void lsb_vendor_parse(char *data, sigar_sys_info_t *info)
2691{
2692    kv_parse(data, info, lsb_parse);
2693}
2694
2695static void xen_parse(sigar_sys_info_t *info,
2696                      char *key, char *val)
2697{
2698    if (strEQ(key, "PRODUCT_VERSION")) {
2699        SIGAR_SSTRCPY(info->vendor_version, val);
2700    }
2701    else if (strEQ(key, "KERNEL_VERSION")) {
2702        SIGAR_SSTRCPY(info->version, val);
2703    }
2704}
2705
2706static void xen_vendor_parse(char *data, sigar_sys_info_t *info)
2707{
2708    kv_parse(data, info, xen_parse);
2709
2710    snprintf(info->description,
2711             sizeof(info->description),
2712             "XenServer %s",
2713             info->vendor_version);
2714}
2715
2716typedef struct {
2717    const char *name;
2718    const char *file;
2719    void (*parse)(char *, sigar_sys_info_t *);
2720} linux_vendor_info_t;
2721
2722static linux_vendor_info_t linux_vendors[] = {
2723    { "Fedora",    "/etc/fedora-release", NULL },
2724    { "SuSE",      "/etc/SuSE-release", NULL },
2725    { "Gentoo",    "/etc/gentoo-release", NULL },
2726    { "Slackware", "/etc/slackware-version", NULL },
2727    { "Mandrake",  "/etc/mandrake-release", NULL },
2728    { "VMware",    "/proc/vmware/version", NULL },
2729    { "XenSource", "/etc/xensource-inventory", xen_vendor_parse },
2730    { "Red Hat",   "/etc/redhat-release", redhat_vendor_parse },
2731    { "lsb",       "/etc/lsb-release", lsb_vendor_parse },
2732    { "Debian",    "/etc/debian_version", NULL },
2733    { NULL }
2734};
2735
2736static int get_linux_vendor_info(sigar_sys_info_t *info)
2737{
2738    int i, status = ENOENT;
2739    /* env vars for testing */
2740    const char *release_file = getenv("SIGAR_OS_RELEASE_FILE");
2741    const char *vendor_name  = getenv("SIGAR_OS_VENDOR_NAME");
2742    char buffer[8192], *data;
2743    linux_vendor_info_t *vendor = NULL;
2744
2745    for (i=0; linux_vendors[i].name; i++) {
2746        struct stat sb;
2747        vendor = &linux_vendors[i];
2748
2749        if (release_file && vendor_name) {
2750            if (!strEQ(vendor->name, vendor_name)) {
2751                continue;
2752            }
2753        }
2754        else {
2755            if (stat(vendor->file, &sb) < 0) {
2756                continue;
2757            }
2758            release_file = vendor->file;
2759        }
2760
2761        status =
2762            sigar_file2str(release_file, buffer, sizeof(buffer)-1);
2763
2764        break;
2765    }
2766
2767    if (status != SIGAR_OK) {
2768        return status;
2769    }
2770
2771    data = buffer;
2772
2773    SIGAR_SSTRCPY(info->vendor, vendor->name);
2774
2775    if (vendor->parse) {
2776        vendor->parse(data, info);
2777    }
2778    else {
2779        generic_vendor_parse(data, info);
2780    }
2781
2782    if (info->description[0] == '\0') {
2783        snprintf(info->description,
2784                 sizeof(info->description),
2785                 "%s %s",
2786                 info->vendor, info->vendor_version);
2787    }
2788
2789    return SIGAR_OK;
2790}
2791
2792int sigar_os_sys_info_get(sigar_t *sigar,
2793                          sigar_sys_info_t *sysinfo)
2794{
2795
2796    get_linux_vendor_info(sysinfo);
2797
2798    return SIGAR_OK;
2799}
2800