xref: /6.6.0/sigar/src/sigar.c (revision b3ad9dcc)
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 <errno.h>
20#include <stdio.h>
21
22#ifndef WIN32
23#include <sys/types.h>
24#include <sys/socket.h>
25#include <sys/time.h>
26#endif
27#if defined(__OpenBSD__) || defined(__FreeBSD__)
28#include <netinet/in.h>
29#endif
30#ifndef WIN32
31#include <arpa/inet.h>
32#endif
33
34#include "sigar.h"
35#include "sigar_private.h"
36#include "sigar_util.h"
37#include "sigar_os.h"
38#include "sigar_format.h"
39
40SIGAR_DECLARE(int) sigar_open(sigar_t **sigar)
41{
42    int status = sigar_os_open(sigar);
43
44    if (status == SIGAR_OK) {
45        /* use env to revert to old behavior */
46        (*sigar)->cpu_list_cores = getenv("SIGAR_CPU_LIST_SOCKETS") ? 0 : 1;
47        (*sigar)->pid = 0;
48        (*sigar)->ifconf_buf = NULL;
49        (*sigar)->ifconf_len = 0;
50        (*sigar)->log_level = -1; /* log nothing by default */
51        (*sigar)->log_impl = NULL;
52        (*sigar)->log_data = NULL;
53        (*sigar)->ptql_re_impl = NULL;
54        (*sigar)->ptql_re_data = NULL;
55        (*sigar)->self_path = NULL;
56        (*sigar)->fsdev = NULL;
57        (*sigar)->pids = NULL;
58        (*sigar)->proc_cpu = NULL;
59        (*sigar)->net_listen = NULL;
60        (*sigar)->net_services_tcp = NULL;
61        (*sigar)->net_services_udp = NULL;
62    }
63
64    return status;
65}
66
67SIGAR_DECLARE(int) sigar_close(sigar_t *sigar)
68{
69    if (sigar->ifconf_buf) {
70        free(sigar->ifconf_buf);
71    }
72    if (sigar->self_path) {
73        free(sigar->self_path);
74    }
75    if (sigar->pids) {
76        sigar_proc_list_destroy(sigar, sigar->pids);
77        free(sigar->pids);
78    }
79    if (sigar->fsdev) {
80        sigar_cache_destroy(sigar->fsdev);
81    }
82    if (sigar->proc_cpu) {
83        sigar_cache_destroy(sigar->proc_cpu);
84    }
85    if (sigar->net_listen) {
86        sigar_cache_destroy(sigar->net_listen);
87    }
88    if (sigar->net_services_tcp) {
89        sigar_cache_destroy(sigar->net_services_tcp);
90    }
91    if (sigar->net_services_udp) {
92        sigar_cache_destroy(sigar->net_services_udp);
93    }
94
95    return sigar_os_close(sigar);
96}
97
98#ifdef WIN32
99#include <process.h>
100#endif
101
102#ifndef __linux__ /* linux has a special case */
103SIGAR_DECLARE(sigar_pid_t) sigar_pid_get(sigar_t *sigar)
104{
105    if (!sigar->pid) {
106        sigar->pid = getpid();
107    }
108
109    return sigar->pid;
110}
111#endif
112
113/* XXX: add clear() function */
114/* XXX: check for stale-ness using start_time */
115SIGAR_DECLARE(int) sigar_proc_cpu_get(sigar_t *sigar, sigar_pid_t pid,
116                                      sigar_proc_cpu_t *proccpu)
117{
118    sigar_cache_entry_t *entry;
119    sigar_proc_cpu_t *prev;
120    sigar_uint64_t otime, time_now = sigar_time_now_millis();
121    sigar_uint64_t time_diff, total_diff;
122    int status;
123
124    if (!sigar->proc_cpu) {
125        sigar->proc_cpu = sigar_cache_new(128);
126    }
127
128    entry = sigar_cache_get(sigar->proc_cpu, pid);
129    if (entry->value) {
130        prev = (sigar_proc_cpu_t *)entry->value;
131    }
132    else {
133        prev = entry->value = malloc(sizeof(*prev));
134        SIGAR_ZERO(prev);
135    }
136
137    time_diff = time_now - prev->last_time;
138    proccpu->last_time = time_now;
139
140    if (time_diff < 1000) {
141        /* we were just called within < 1 second ago. */
142        memcpy(proccpu, prev, sizeof(*proccpu));
143        return SIGAR_OK;
144    }
145
146    otime = prev->total;
147
148    status =
149        sigar_proc_time_get(sigar, pid,
150                            (sigar_proc_time_t *)proccpu);
151
152    if (status != SIGAR_OK) {
153        return status;
154    }
155
156    if (proccpu->total < otime) {
157        /* XXX this should not happen */
158        otime = 0;
159    }
160
161    if (otime == 0) {
162        /* first time called */
163        proccpu->percent = 0.0;
164    } else {
165        total_diff = proccpu->total - otime;
166        proccpu->percent = total_diff / (double)time_diff;
167    }
168
169    memcpy(prev, proccpu, sizeof(*prev));
170
171    return SIGAR_OK;
172}
173
174int sigar_proc_list_create(sigar_proc_list_t *proclist)
175{
176    proclist->number = 0;
177    proclist->size = SIGAR_PROC_LIST_MAX;
178    proclist->data = malloc(sizeof(*(proclist->data)) *
179                            proclist->size);
180    return SIGAR_OK;
181}
182
183int sigar_proc_list_grow(sigar_proc_list_t *proclist)
184{
185    proclist->data = realloc(proclist->data,
186                             sizeof(*(proclist->data)) *
187                             (proclist->size + SIGAR_PROC_LIST_MAX));
188    proclist->size += SIGAR_PROC_LIST_MAX;
189
190    return SIGAR_OK;
191}
192
193SIGAR_DECLARE(int) sigar_proc_list_destroy(sigar_t *sigar,
194                                           sigar_proc_list_t *proclist)
195{
196    if (proclist->size) {
197        free(proclist->data);
198        proclist->number = proclist->size = 0;
199    }
200
201    return SIGAR_OK;
202}
203
204SIGAR_DECLARE(int) sigar_proc_list_get(sigar_t *sigar,
205                                       sigar_proc_list_t *proclist)
206{
207    if (proclist == NULL) {
208        /* internal re-use */
209        if (sigar->pids == NULL) {
210            sigar->pids = malloc(sizeof(*sigar->pids));
211            sigar_proc_list_create(sigar->pids);
212        }
213        else {
214            sigar->pids->number = 0;
215        }
216        proclist = sigar->pids;
217    }
218    else {
219        sigar_proc_list_create(proclist);
220    }
221
222    return sigar_os_proc_list_get(sigar, proclist);
223}
224
225SIGAR_DECLARE(int)
226sigar_proc_list_get_children(sigar_t* sigar,
227                             sigar_pid_t ppid,
228                             sigar_proc_list_t* proclist) {
229    if (proclist == NULL) {
230        /* internal re-use */
231        if (sigar->pids == NULL) {
232            sigar->pids = malloc(sizeof(*sigar->pids));
233            sigar_proc_list_create(sigar->pids);
234        } else {
235            sigar->pids->number = 0;
236        }
237        proclist = sigar->pids;
238    } else {
239        sigar_proc_list_create(proclist);
240    }
241
242    return sigar_os_proc_list_get_children(sigar, ppid, proclist);
243}
244
245int sigar_proc_args_create(sigar_proc_args_t *procargs)
246{
247    procargs->number = 0;
248    procargs->size = SIGAR_PROC_ARGS_MAX;
249    procargs->data = malloc(sizeof(*(procargs->data)) *
250                            procargs->size);
251    return SIGAR_OK;
252}
253
254int sigar_proc_args_grow(sigar_proc_args_t *procargs)
255{
256    procargs->data = realloc(procargs->data,
257                             sizeof(*(procargs->data)) *
258                             (procargs->size + SIGAR_PROC_ARGS_MAX));
259    procargs->size += SIGAR_PROC_ARGS_MAX;
260
261    return SIGAR_OK;
262}
263
264
265int sigar_file_system_list_create(sigar_file_system_list_t *fslist)
266{
267    fslist->number = 0;
268    fslist->size = SIGAR_FS_MAX;
269    fslist->data = malloc(sizeof(*(fslist->data)) *
270                          fslist->size);
271    return SIGAR_OK;
272}
273
274int sigar_file_system_list_grow(sigar_file_system_list_t *fslist)
275{
276    fslist->data = realloc(fslist->data,
277                           sizeof(*(fslist->data)) *
278                           (fslist->size + SIGAR_FS_MAX));
279    fslist->size += SIGAR_FS_MAX;
280
281    return SIGAR_OK;
282}
283
284/* indexed with sigar_file_system_type_e */
285static const char *fstype_names[] = {
286    "unknown", "none", "local", "remote", "ram", "cdrom", "swap"
287};
288
289static int sigar_common_fs_type_get(sigar_file_system_t *fsp)
290{
291    char *type = fsp->sys_type_name;
292
293    switch (*type) {
294      case 'n':
295        if (strnEQ(type, "nfs", 3)) {
296            fsp->type = SIGAR_FSTYPE_NETWORK;
297        }
298        break;
299      case 's':
300        if (strEQ(type, "smbfs")) { /* samba */
301            fsp->type = SIGAR_FSTYPE_NETWORK;
302        }
303        else if (strEQ(type, "swap")) {
304            fsp->type = SIGAR_FSTYPE_SWAP;
305        }
306        break;
307      case 'a':
308        if (strEQ(type, "afs")) {
309            fsp->type = SIGAR_FSTYPE_NETWORK;
310        }
311        break;
312      case 'i':
313        if (strEQ(type, "iso9660")) {
314            fsp->type = SIGAR_FSTYPE_CDROM;
315        }
316        break;
317      case 'c':
318        if (strEQ(type, "cvfs")) {
319            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
320        }
321        else if (strEQ(type, "cifs")) {
322            fsp->type = SIGAR_FSTYPE_NETWORK;
323        }
324        break;
325      case 'm':
326        if (strEQ(type, "msdos") || strEQ(type, "minix")) {
327            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
328        }
329        break;
330      case 'h':
331        if (strEQ(type, "hpfs")) {
332            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
333        }
334        break;
335      case 'v':
336        if (strEQ(type, "vxfs")) {
337            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
338        }
339        else if (strEQ(type, "vfat")) {
340            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
341        }
342        break;
343      case 'z':
344        if (strEQ(type, "zfs")) {
345            fsp->type = SIGAR_FSTYPE_LOCAL_DISK;
346        }
347        break;
348    }
349
350    return fsp->type;
351}
352
353void sigar_fs_type_get(sigar_file_system_t *fsp)
354{
355    if (!(fsp->type ||                    /* already set */
356          sigar_os_fs_type_get(fsp) ||    /* try os specifics first */
357          sigar_common_fs_type_get(fsp))) /* try common ones last */
358    {
359        fsp->type = SIGAR_FSTYPE_NONE;
360    }
361
362    if (fsp->type >= SIGAR_FSTYPE_MAX) {
363        fsp->type = SIGAR_FSTYPE_NONE;
364    }
365
366    strcpy(fsp->type_name, fstype_names[fsp->type]);
367}
368
369
370SIGAR_DECLARE(int)
371sigar_file_system_list_destroy(sigar_t *sigar,
372                               sigar_file_system_list_t *fslist)
373{
374    if (fslist->size) {
375        free(fslist->data);
376        fslist->number = fslist->size = 0;
377    }
378
379    return SIGAR_OK;
380}
381
382int sigar_net_interface_list_create(sigar_net_interface_list_t *iflist)
383{
384    iflist->number = 0;
385    iflist->size = SIGAR_NET_IFLIST_MAX;
386    iflist->data = malloc(sizeof(*(iflist->data)) *
387                          iflist->size);
388    return SIGAR_OK;
389}
390
391int sigar_net_interface_list_grow(sigar_net_interface_list_t *iflist)
392{
393    iflist->data = realloc(iflist->data,
394                           sizeof(*(iflist->data)) *
395                           (iflist->size + SIGAR_NET_IFLIST_MAX));
396    iflist->size += SIGAR_NET_IFLIST_MAX;
397
398    return SIGAR_OK;
399}
400
401SIGAR_DECLARE(int)
402sigar_net_interface_list_destroy(sigar_t *sigar,
403                                 sigar_net_interface_list_t *iflist)
404{
405    unsigned int i;
406
407    if (iflist->size) {
408        for (i=0; i<iflist->number; i++) {
409            free(iflist->data[i]);
410        }
411        free(iflist->data);
412        iflist->number = iflist->size = 0;
413    }
414
415    return SIGAR_OK;
416}
417
418int sigar_net_connection_list_create(sigar_net_connection_list_t *connlist)
419{
420    connlist->number = 0;
421    connlist->size = SIGAR_NET_CONNLIST_MAX;
422    connlist->data = malloc(sizeof(*(connlist->data)) *
423                            connlist->size);
424    return SIGAR_OK;
425}
426
427int sigar_net_connection_list_grow(sigar_net_connection_list_t *connlist)
428{
429    connlist->data =
430        realloc(connlist->data,
431                sizeof(*(connlist->data)) *
432                (connlist->size + SIGAR_NET_CONNLIST_MAX));
433    connlist->size += SIGAR_NET_CONNLIST_MAX;
434
435    return SIGAR_OK;
436}
437
438SIGAR_DECLARE(int)
439sigar_net_connection_list_destroy(sigar_t *sigar,
440                                  sigar_net_connection_list_t *connlist)
441{
442    if (connlist->size) {
443        free(connlist->data);
444        connlist->number = connlist->size = 0;
445    }
446
447    return SIGAR_OK;
448}
449
450#if !defined(__linux__)
451/*
452 * implement sigar_net_connection_list_get using sigar_net_connection_walk
453 * linux has its own list_get impl.
454 */
455static int net_connection_list_walker(sigar_net_connection_walker_t *walker,
456                                      sigar_net_connection_t *conn)
457{
458    sigar_net_connection_list_t *connlist =
459        (sigar_net_connection_list_t *)walker->data;
460
461    SIGAR_NET_CONNLIST_GROW(connlist);
462    memcpy(&connlist->data[connlist->number++],
463           conn, sizeof(*conn));
464
465    return SIGAR_OK; /* continue loop */
466}
467
468SIGAR_DECLARE(int)
469sigar_net_connection_list_get(sigar_t *sigar,
470                              sigar_net_connection_list_t *connlist,
471                              int flags)
472{
473    int status;
474    sigar_net_connection_walker_t walker;
475
476    sigar_net_connection_list_create(connlist);
477
478    walker.sigar = sigar;
479    walker.flags = flags;
480    walker.data = connlist;
481    walker.add_connection = net_connection_list_walker;
482
483    status = sigar_net_connection_walk(&walker);
484
485    if (status != SIGAR_OK) {
486        sigar_net_connection_list_destroy(sigar, connlist);
487    }
488
489    return status;
490}
491#endif
492
493#ifdef DARWIN
494#include <AvailabilityMacros.h>
495#endif
496#ifdef MAC_OS_X_VERSION_10_5
497#  if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5
498#    define SIGAR_NO_UTMP
499#  endif
500/* else 10.4 and earlier or compiled with -mmacosx-version-min=10.3 */
501#endif
502
503#ifndef WIN32
504#include <sys/resource.h>
505#endif
506
507#ifdef HAVE_LIBDLPI_H
508#include <libdlpi.h>
509
510static void hwaddr_libdlpi_lookup(sigar_t *sigar, sigar_net_interface_config_t *ifconfig)
511{
512    dlpi_handle_t handle;
513    dlpi_info_t linkinfo;
514    uchar_t addr[DLPI_PHYSADDR_MAX];
515    uint_t alen = sizeof(addr);
516
517    if (dlpi_open(ifconfig->name, &handle, 0) != DLPI_SUCCESS) {
518        return;
519    }
520
521    if (dlpi_get_physaddr(handle, DL_CURR_PHYS_ADDR, addr, &alen) == DLPI_SUCCESS &&
522        dlpi_info(handle, &linkinfo, 0) == DLPI_SUCCESS) {
523        if (alen < sizeof(ifconfig->hwaddr.addr.mac)) {
524            sigar_net_address_mac_set(ifconfig->hwaddr, addr, alen);
525            SIGAR_SSTRCPY(ifconfig->type, dlpi_mactype(linkinfo.di_mactype));
526        }
527    }
528
529    dlpi_close(handle);
530}
531#endif
532
533
534#if !defined(WIN32) && !defined(NETWARE) && !defined(DARWIN) && \
535    !defined(__FreeBSD__) && !defined(__OpenBSD__) && !defined(__NetBSD__)
536
537/* XXX: prolly will be moving these stuffs into os_net.c */
538#include <sys/ioctl.h>
539#include <net/if.h>
540
541#ifndef SIOCGIFCONF
542#include <sys/sockio.h>
543#endif
544
545#if defined(_AIX) || defined(__osf__) /* good buddies */
546
547#include <net/if_dl.h>
548
549static void hwaddr_aix_lookup(sigar_t *sigar, sigar_net_interface_config_t *ifconfig)
550{
551    char *ent, *end;
552    struct ifreq *ifr;
553
554    /* XXX: assumes sigar_net_interface_list_get has been called */
555    end = sigar->ifconf_buf + sigar->ifconf_len;
556
557    for (ent = sigar->ifconf_buf;
558         ent < end;
559         ent += sizeof(*ifr))
560    {
561        ifr = (struct ifreq *)ent;
562
563        if (ifr->ifr_addr.sa_family != AF_LINK) {
564            continue;
565        }
566
567        if (strEQ(ifr->ifr_name, ifconfig->name)) {
568            struct sockaddr_dl *sdl = (struct sockaddr_dl *)&ifr->ifr_addr;
569
570            sigar_net_address_mac_set(ifconfig->hwaddr,
571                                      LLADDR(sdl),
572                                      sdl->sdl_alen);
573            return;
574        }
575    }
576
577    sigar_hwaddr_set_null(ifconfig);
578}
579
580#elif !defined(SIOCGIFHWADDR)
581
582#include <net/if_arp.h>
583
584static void hwaddr_arp_lookup(sigar_net_interface_config_t *ifconfig, int sock)
585{
586    struct arpreq areq;
587    struct sockaddr_in *sa;
588
589    memset(&areq, 0, sizeof(areq));
590    sa = (struct sockaddr_in *)&areq.arp_pa;
591    sa->sin_family = AF_INET;
592    sa->sin_addr.s_addr = ifconfig->address.addr.in;
593
594    if (ioctl(sock, SIOCGARP, &areq) < 0) {
595        /* ho-hum */
596        sigar_hwaddr_set_null(ifconfig);
597    }
598    else {
599        sigar_net_address_mac_set(ifconfig->hwaddr,
600                                  areq.arp_ha.sa_data,
601                                  SIGAR_IFHWADDRLEN);
602    }
603}
604
605#endif
606
607#ifdef __linux__
608
609#include <net/if_arp.h>
610
611#ifndef ARPHRD_CISCO /* not in 2.2 kernel headers */
612#define ARPHRD_CISCO 513 /* Cisco HDLC. */
613#endif
614
615static void get_interface_type(sigar_net_interface_config_t *ifconfig,
616                               int family)
617{
618    char *type;
619
620    switch (family) {
621      case ARPHRD_SLIP:
622        type = SIGAR_NIC_SLIP;
623        break;
624      case ARPHRD_CSLIP:
625        type = SIGAR_NIC_CSLIP;
626        break;
627      case ARPHRD_SLIP6:
628        type = SIGAR_NIC_SLIP6;
629        break;
630      case ARPHRD_CSLIP6:
631        type = SIGAR_NIC_CSLIP6;
632        break;
633      case ARPHRD_ADAPT:
634        type = SIGAR_NIC_ADAPTIVE;
635        break;
636      case ARPHRD_ETHER:
637        type = SIGAR_NIC_ETHERNET;
638        break;
639      case ARPHRD_ASH:
640        type = SIGAR_NIC_ASH;
641        break;
642      case ARPHRD_FDDI:
643        type = SIGAR_NIC_FDDI;
644        break;
645      case ARPHRD_HIPPI:
646        type = SIGAR_NIC_HIPPI;
647        break;
648      case ARPHRD_AX25:
649        type = SIGAR_NIC_AX25;
650        break;
651      case ARPHRD_ROSE:
652        type = SIGAR_NIC_ROSE;
653        break;
654      case ARPHRD_NETROM:
655        type = SIGAR_NIC_NETROM;
656        break;
657      case ARPHRD_X25:
658        type = SIGAR_NIC_X25;
659        break;
660      case ARPHRD_TUNNEL:
661        type = SIGAR_NIC_TUNNEL;
662        break;
663      case ARPHRD_PPP:
664        type = SIGAR_NIC_PPP;
665        break;
666      case ARPHRD_CISCO:
667        type = SIGAR_NIC_HDLC;
668        break;
669      case ARPHRD_LAPB:
670        type = SIGAR_NIC_LAPB;
671        break;
672      case ARPHRD_ARCNET:
673        type = SIGAR_NIC_ARCNET;
674        break;
675      case ARPHRD_DLCI:
676        type = SIGAR_NIC_DLCI;
677        break;
678      case ARPHRD_FRAD:
679        type = SIGAR_NIC_FRAD;
680        break;
681      case ARPHRD_SIT:
682        type = SIGAR_NIC_SIT;
683        break;
684      case ARPHRD_IRDA:
685        type = SIGAR_NIC_IRDA;
686        break;
687      case ARPHRD_ECONET:
688        type = SIGAR_NIC_EC;
689        break;
690      default:
691        type = SIGAR_NIC_UNSPEC;
692        break;
693    }
694
695    SIGAR_SSTRCPY(ifconfig->type, type);
696}
697
698#endif
699
700int sigar_net_interface_config_get(sigar_t *sigar, const char *name,
701                                   sigar_net_interface_config_t *ifconfig)
702{
703    int sock;
704    struct ifreq ifr;
705
706    if (!name) {
707        return sigar_net_interface_config_primary_get(sigar, ifconfig);
708    }
709
710    SIGAR_ZERO(ifconfig);
711
712    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
713        return errno;
714    }
715
716    SIGAR_SSTRCPY(ifconfig->name, name);
717    SIGAR_SSTRCPY(ifr.ifr_name, name);
718
719#define ifr_s_addr(ifr) \
720    ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr
721
722    if (!ioctl(sock, SIOCGIFADDR, &ifr)) {
723        sigar_net_address_set(ifconfig->address,
724                              ifr_s_addr(ifr));
725    }
726
727    if (!ioctl(sock, SIOCGIFNETMASK, &ifr)) {
728        sigar_net_address_set(ifconfig->netmask,
729                              ifr_s_addr(ifr));
730    }
731
732    if (!ioctl(sock, SIOCGIFFLAGS, &ifr)) {
733        sigar_uint64_t flags = ifr.ifr_flags;
734#ifdef __linux__
735# ifndef IFF_DYNAMIC
736#  define IFF_DYNAMIC 0x8000 /* not in 2.2 kernel */
737# endif /* IFF_DYNAMIC */
738        int is_mcast = flags & IFF_MULTICAST;
739        int is_slave = flags & IFF_SLAVE;
740        int is_master = flags & IFF_MASTER;
741        int is_dynamic = flags & IFF_DYNAMIC;
742        /*
743         * XXX: should just define SIGAR_IFF_*
744         * and test IFF_* bits on given platform.
745         * this is the only diff between solaris/hpux/linux
746         * for the flags we care about.
747         *
748         */
749        flags &= ~(IFF_MULTICAST|IFF_SLAVE|IFF_MASTER);
750        if (is_mcast) {
751            flags |= SIGAR_IFF_MULTICAST;
752        }
753        if (is_slave) {
754            flags |= SIGAR_IFF_SLAVE;
755        }
756        if (is_master) {
757            flags |= SIGAR_IFF_MASTER;
758        }
759        if (is_dynamic) {
760            flags |= SIGAR_IFF_DYNAMIC;
761        }
762#endif
763        ifconfig->flags = flags;
764    }
765    else {
766        /* should always be able to get flags for existing device */
767        /* other ioctls may fail if device is not enabled: ok */
768        close(sock);
769        return errno;
770    }
771
772    if (ifconfig->flags & IFF_LOOPBACK) {
773        sigar_net_address_set(ifconfig->destination,
774                              ifconfig->address.addr.in);
775        sigar_net_address_set(ifconfig->broadcast, 0);
776        sigar_hwaddr_set_null(ifconfig);
777        SIGAR_SSTRCPY(ifconfig->type,
778                      SIGAR_NIC_LOOPBACK);
779    }
780    else {
781        if (!ioctl(sock, SIOCGIFDSTADDR, &ifr)) {
782            sigar_net_address_set(ifconfig->destination,
783                                  ifr_s_addr(ifr));
784        }
785
786        if (!ioctl(sock, SIOCGIFBRDADDR, &ifr)) {
787            sigar_net_address_set(ifconfig->broadcast,
788                                  ifr_s_addr(ifr));
789        }
790
791#if defined(HAVE_LIBDLPI_H)
792        hwaddr_libdlpi_lookup(sigar, ifconfig);
793#elif defined(SIOCGIFHWADDR)
794        if (!ioctl(sock, SIOCGIFHWADDR, &ifr)) {
795            get_interface_type(ifconfig,
796                               ifr.ifr_hwaddr.sa_family);
797            sigar_net_address_mac_set(ifconfig->hwaddr,
798                                      ifr.ifr_hwaddr.sa_data,
799                                      IFHWADDRLEN);
800        }
801#elif defined(_AIX) || defined(__osf__)
802        hwaddr_aix_lookup(sigar, ifconfig);
803        SIGAR_SSTRCPY(ifconfig->type,
804                      SIGAR_NIC_ETHERNET);
805#else
806        hwaddr_arp_lookup(ifconfig, sock);
807        SIGAR_SSTRCPY(ifconfig->type,
808                      SIGAR_NIC_ETHERNET);
809#endif
810    }
811
812#if defined(SIOCGLIFMTU) && !defined(__hpux)
813    {
814        struct lifreq lifr;
815        SIGAR_SSTRCPY(lifr.lifr_name, name);
816        if(!ioctl(sock, SIOCGLIFMTU, &lifr)) {
817            ifconfig->mtu = lifr.lifr_mtu;
818        }
819    }
820#elif defined(SIOCGIFMTU)
821    if (!ioctl(sock, SIOCGIFMTU, &ifr)) {
822#  if defined(__hpux)
823        ifconfig->mtu = ifr.ifr_metric;
824#  else
825        ifconfig->mtu = ifr.ifr_mtu;
826#endif
827    }
828#else
829    ifconfig->mtu = 0; /*XXX*/
830#endif
831
832    if (!ioctl(sock, SIOCGIFMETRIC, &ifr)) {
833        ifconfig->metric = ifr.ifr_metric ? ifr.ifr_metric : 1;
834    }
835
836#if defined(SIOCGIFTXQLEN)
837    if (!ioctl(sock, SIOCGIFTXQLEN, &ifr)) {
838        ifconfig->tx_queue_len = ifr.ifr_qlen;
839    }
840    else {
841        ifconfig->tx_queue_len = -1; /* net-tools behaviour */
842    }
843#else
844    ifconfig->tx_queue_len = -1;
845#endif
846
847    close(sock);
848
849    /* XXX can we get a better description like win32? */
850    SIGAR_SSTRCPY(ifconfig->description,
851                  ifconfig->name);
852
853    sigar_net_interface_ipv6_config_init(ifconfig);
854    sigar_net_interface_ipv6_config_get(sigar, name, ifconfig);
855
856    return SIGAR_OK;
857}
858
859#ifdef _AIX
860#  define MY_SIOCGIFCONF CSIOCGIFCONF
861#else
862#  define MY_SIOCGIFCONF SIOCGIFCONF
863#endif
864
865#ifdef __osf__
866static int sigar_netif_configured(sigar_t *sigar, char *name)
867{
868    int status;
869    sigar_net_interface_config_t ifconfig;
870
871    status = sigar_net_interface_config_get(sigar, name, &ifconfig);
872
873    return status == SIGAR_OK;
874}
875#endif
876
877#ifdef __linux__
878static  int has_interface(sigar_net_interface_list_t *iflist,
879                                      char *name)
880{
881    register int i;
882    register int num = iflist->number;
883    register char **data = iflist->data;
884    for (i=0; i<num; i++) {
885        if (strEQ(name, data[i])) {
886            return 1;
887        }
888    }
889    return 0;
890}
891
892static int proc_net_interface_list_get(sigar_t *sigar,
893                                       sigar_net_interface_list_t *iflist)
894{
895    /* certain interfaces such as VMware vmnic
896     * are not returned by ioctl(SIOCGIFCONF).
897     * check /proc/net/dev for any ioctl missed.
898     */
899    char buffer[BUFSIZ];
900    FILE *fp = fopen("/proc/net/dev", "r");
901
902    if (!fp) {
903        return errno;
904    }
905
906    /* skip header */
907    if (fgets(buffer, sizeof(buffer), fp) == NULL ||
908        fgets(buffer, sizeof(buffer), fp) == NULL) {
909        fclose(fp);
910        return errno;
911    }
912
913    while (fgets(buffer, sizeof(buffer), fp)) {
914        char *ptr, *dev;
915
916        dev = buffer;
917        while (isspace(*dev)) {
918            dev++;
919        }
920
921        if (!(ptr = strchr(dev, ':'))) {
922            continue;
923        }
924
925        *ptr++ = 0;
926
927        if (has_interface(iflist, dev)) {
928            continue;
929        }
930
931        SIGAR_NET_IFLIST_GROW(iflist);
932
933        iflist->data[iflist->number++] =
934            sigar_strdup(dev);
935    }
936
937    fclose(fp);
938
939    return SIGAR_OK;
940}
941#endif
942
943int sigar_net_interface_list_get(sigar_t *sigar,
944                                 sigar_net_interface_list_t *iflist)
945{
946    int n, lastlen=0;
947    struct ifreq *ifr;
948    struct ifconf ifc;
949    int sock = socket(AF_INET, SOCK_DGRAM, 0);
950
951    if (sock < 0) {
952        return errno;
953    }
954
955    for (;;) {
956        if (!sigar->ifconf_buf || lastlen) {
957            sigar->ifconf_len += sizeof(struct ifreq) * SIGAR_NET_IFLIST_MAX;
958            sigar->ifconf_buf = realloc(sigar->ifconf_buf, sigar->ifconf_len);
959        }
960
961        ifc.ifc_len = sigar->ifconf_len;
962        ifc.ifc_buf = sigar->ifconf_buf;
963
964        if (ioctl(sock, MY_SIOCGIFCONF, &ifc) < 0) {
965            /* EINVAL should mean num_interfaces > ifc.ifc_len */
966            if ((errno != EINVAL) ||
967                (lastlen == ifc.ifc_len))
968            {
969                free(ifc.ifc_buf);
970                return errno;
971            }
972        }
973
974        if (ifc.ifc_len < sigar->ifconf_len) {
975            break; /* got em all */
976        }
977
978        if (ifc.ifc_len != lastlen) {
979            /* might be more */
980            lastlen = ifc.ifc_len;
981            continue;
982        }
983
984        break;
985    }
986
987    close(sock);
988
989    iflist->number = 0;
990    iflist->size = ifc.ifc_len;
991    iflist->data = malloc(sizeof(*(iflist->data)) *
992                          iflist->size);
993
994    ifr = ifc.ifc_req;
995    for (n = 0; n < ifc.ifc_len; n += sizeof(struct ifreq), ifr++) {
996#if defined(_AIX) || defined(__osf__) /* pass the bourbon */
997        if (ifr->ifr_addr.sa_family != AF_LINK) {
998            /* XXX: dunno if this is right.
999             * otherwise end up with two 'en0' and three 'lo0'
1000             * with the same ip address.
1001             */
1002            continue;
1003        }
1004#   ifdef __osf__
1005        /* weed out "sl0", "tun0" and the like */
1006        /* XXX must be a better way to check this */
1007        if (!sigar_netif_configured(sigar, ifr->ifr_name)) {
1008            continue;
1009        }
1010#   endif
1011#endif
1012        iflist->data[iflist->number++] =
1013            sigar_strdup(ifr->ifr_name);
1014    }
1015
1016#ifdef __linux__
1017    proc_net_interface_list_get(sigar, iflist);
1018#endif
1019
1020    return SIGAR_OK;
1021}
1022
1023#endif /* WIN32 */
1024
1025SIGAR_DECLARE(int)
1026sigar_net_interface_config_primary_get(sigar_t *sigar,
1027                                       sigar_net_interface_config_t *ifconfig)
1028{
1029    int i, status, found=0;
1030    sigar_net_interface_list_t iflist;
1031    sigar_net_interface_config_t possible_config;
1032
1033    possible_config.flags = 0;
1034
1035    if ((status = sigar_net_interface_list_get(sigar, &iflist)) != SIGAR_OK) {
1036        return status;
1037    }
1038
1039    for (i=0; i<iflist.number; i++) {
1040        status = sigar_net_interface_config_get(sigar,
1041                                                iflist.data[i], ifconfig);
1042
1043        if ((status != SIGAR_OK) ||
1044            (ifconfig->flags & SIGAR_IFF_LOOPBACK) ||
1045            !ifconfig->hwaddr.addr.in)   /* no mac address */
1046        {
1047            continue;
1048        }
1049
1050        if (!possible_config.flags) {
1051            /* save for later for use if we're not connected to the net
1052             * or all interfaces are aliases (e.g. solaris zone)
1053             */
1054            memcpy(&possible_config, ifconfig, sizeof(*ifconfig));
1055        }
1056        if (!ifconfig->address.addr.in) {
1057            continue; /* no ip address */
1058        }
1059        if (strchr(iflist.data[i], ':')) {
1060            continue; /* alias */
1061        }
1062
1063        found = 1;
1064        break;
1065    }
1066
1067    sigar_net_interface_list_destroy(sigar, &iflist);
1068
1069    if (found) {
1070        return SIGAR_OK;
1071    }
1072    else if (possible_config.flags) {
1073        memcpy(ifconfig, &possible_config, sizeof(*ifconfig));
1074        return SIGAR_OK;
1075    }
1076    else {
1077        return SIGAR_ENXIO;
1078    }
1079}
1080
1081struct hostent *sigar_gethostbyname(const char *name,
1082                                    sigar_hostent_t *data)
1083{
1084    struct hostent *hp = NULL;
1085
1086#if defined(__linux__)
1087    gethostbyname_r(name, &data->hs,
1088                    data->buffer, sizeof(data->buffer),
1089                    &hp, &data->error);
1090#elif defined(__sun)
1091    hp = gethostbyname_r(name, &data->hs,
1092                         data->buffer, sizeof(data->buffer),
1093                         &data->error);
1094#elif defined(SIGAR_HAS_HOSTENT_DATA)
1095    if (gethostbyname_r(name, &data->hs, &data->hd) == 0) {
1096        hp = &data->hs;
1097    }
1098    else {
1099        data->error = h_errno;
1100    }
1101#else
1102    hp = gethostbyname(name);
1103#endif
1104
1105    return hp;
1106}
1107