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 
sigar_open(sigar_t **sigar)40 SIGAR_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 
sigar_close(sigar_t *sigar)67 SIGAR_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 */
SIGAR_DECLAREnull103 SIGAR_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 */
sigar_proc_cpu_get(sigar_t *sigar, sigar_pid_t pid, sigar_proc_cpu_t *proccpu)115 SIGAR_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 
sigar_proc_list_create(sigar_proc_list_t *proclist)174 int 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 
sigar_proc_list_grow(sigar_proc_list_t *proclist)183 int 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 
sigar_proc_list_destroy(sigar_t *sigar, sigar_proc_list_t *proclist)193 SIGAR_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 
sigar_proc_list_get(sigar_t *sigar, sigar_proc_list_t *proclist)204 SIGAR_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 
225 SIGAR_DECLARE(int)
sigar_proc_list_get_children(sigar_t* sigar, sigar_pid_t ppid, sigar_proc_list_t* proclist)226 sigar_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 
sigar_proc_args_create(sigar_proc_args_t *procargs)245 int 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 
sigar_proc_args_grow(sigar_proc_args_t *procargs)254 int 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 
sigar_file_system_list_create(sigar_file_system_list_t *fslist)265 int 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 
sigar_file_system_list_grow(sigar_file_system_list_t *fslist)274 int 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 */
285 static const char *fstype_names[] = {
286     "unknown", "none", "local", "remote", "ram", "cdrom", "swap"
287 };
288 
sigar_common_fs_type_get(sigar_file_system_t *fsp)289 static 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 
sigar_fs_type_get(sigar_file_system_t *fsp)353 void 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 
370 SIGAR_DECLARE(int)
sigar_file_system_list_destroy(sigar_t *sigar, sigar_file_system_list_t *fslist)371 sigar_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 
sigar_net_interface_list_create(sigar_net_interface_list_t *iflist)382 int 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 
sigar_net_interface_list_grow(sigar_net_interface_list_t *iflist)391 int 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 
401 SIGAR_DECLARE(int)
sigar_net_interface_list_destroy(sigar_t *sigar, sigar_net_interface_list_t *iflist)402 sigar_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 
sigar_net_connection_list_create(sigar_net_connection_list_t *connlist)418 int 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 
sigar_net_connection_list_grow(sigar_net_connection_list_t *connlist)427 int 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 
438 SIGAR_DECLARE(int)
sigar_net_connection_list_destroy(sigar_t *sigar, sigar_net_connection_list_t *connlist)439 sigar_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  */
net_connection_list_walker(sigar_net_connection_walker_t *walker, sigar_net_connection_t *conn)455 static 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 
468 SIGAR_DECLARE(int)
sigar_net_connection_list_get(sigar_t *sigar, sigar_net_connection_list_t *connlist, int flags)469 sigar_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 
hwaddr_libdlpi_lookup(sigar_t *sigar, sigar_net_interface_config_t *ifconfig)510 static 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 
hwaddr_aix_lookup(sigar_t *sigar, sigar_net_interface_config_t *ifconfig)549 static 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 
hwaddr_arp_lookup(sigar_net_interface_config_t *ifconfig, int sock)584 static 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 
get_interface_type(sigar_net_interface_config_t *ifconfig, int family)615 static 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 
sigar_net_interface_config_get(sigar_t *sigar, const char *name, sigar_net_interface_config_t *ifconfig)700 int 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__
sigar_netif_configured(sigar_t *sigar, char *name)866 static 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__
has_interface(sigar_net_interface_list_t *iflist, char *name)878 static  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 
proc_net_interface_list_get(sigar_t *sigar, sigar_net_interface_list_t *iflist)892 static 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 
sigar_net_interface_list_get(sigar_t *sigar, sigar_net_interface_list_t *iflist)943 int 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 
1025 SIGAR_DECLARE(int)
sigar_net_interface_config_primary_get(sigar_t *sigar, sigar_net_interface_config_t *ifconfig)1026 sigar_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 
sigar_gethostbyname(const char *name, sigar_hostent_t *data)1081 struct 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