]> git.sur5r.net Git - i3/i3status/blob - src/print_ip_addr.c
Fix shown IP address belonging to wrong interface
[i3/i3status] / src / print_ip_addr.c
1 // vim:ts=4:sw=4:expandtab
2 #include <sys/types.h>
3 #include <sys/socket.h>
4 #include <netinet/in.h>
5
6 #include <stdlib.h>
7 #include <stdio.h>
8 #include <string.h>
9 #include <netdb.h>
10 #include <ifaddrs.h>
11 #include <net/if.h>
12
13 #include "i3status.h"
14
15 /*
16  * Return a copy of the .ifa_name field passed as argument where the optional
17  * IP label, if present, is removed.
18  *
19  * example:
20  * - strip_optional_label("eth0") => "eth0"
21  * - strip_optional_label("eth0:label") => "eth0"
22  *
23  * The memory for the returned string is obtained with malloc(3), and can be
24  * freed with free(3).
25  *
26  *
27  */
28 static char *strip_optional_label(const char *ifa_name) {
29     char *copy = sstrdup(ifa_name);
30
31     char *ptr = strchr(copy, ':');
32
33     if (ptr) {
34         *ptr = '\0';
35     }
36
37     return copy;
38 }
39
40 /*
41  * Return the IP address for the given interface or "no IP" if the
42  * interface is up and running but hasn't got an IP address yet
43  *
44  */
45 const char *get_ip_addr(const char *interface, int family) {
46     static char part[512];
47     socklen_t len = 0;
48     if (family == AF_INET)
49         len = sizeof(struct sockaddr_in);
50     else if (family == AF_INET6)
51         len = sizeof(struct sockaddr_in6);
52
53     memset(part, 0, sizeof(part));
54
55     struct ifaddrs *ifaddr, *addrp;
56     bool found = false;
57
58     getifaddrs(&ifaddr);
59
60     if (ifaddr == NULL)
61         return NULL;
62
63     /* Skip until we are at the input family address of interface */
64     for (addrp = ifaddr; addrp != NULL; addrp = addrp->ifa_next) {
65         /* Strip the label if present in the .ifa_name field. */
66         char *stripped_ifa_name = strip_optional_label(addrp->ifa_name);
67
68         bool name_matches = strcmp(stripped_ifa_name, interface) != 0;
69         free(stripped_ifa_name);
70         if (name_matches) {
71             /* The interface does not have the right name, skip it. */
72             continue;
73         }
74
75         if (addrp->ifa_addr != NULL && addrp->ifa_addr->sa_family == family) {
76             /* We found the right interface with the right address. */
77             break;
78         }
79
80         /* Check if the interface is down. If it is, no need to look any
81          * further. */
82         if ((addrp->ifa_flags & IFF_RUNNING) == 0) {
83             freeifaddrs(ifaddr);
84             return NULL;
85         }
86
87         found = true;
88     }
89
90     if (addrp == NULL) {
91         freeifaddrs(ifaddr);
92         return (found ? "no IP" : NULL);
93     }
94
95     int ret;
96     if ((ret = getnameinfo(addrp->ifa_addr, len, part, sizeof(part), NULL, 0, NI_NUMERICHOST)) != 0) {
97         fprintf(stderr, "i3status: getnameinfo(): %s\n", gai_strerror(ret));
98         freeifaddrs(ifaddr);
99         return "no IP";
100     }
101
102     freeifaddrs(ifaddr);
103     return part;
104 }