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