]> git.sur5r.net Git - i3/i3status/blob - src/print_ipv6_addr.c
able to print percentage
[i3/i3status] / src / print_ipv6_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 #include <arpa/inet.h>
6
7 #include <stdio.h>
8 #include <stdbool.h>
9 #include <unistd.h>
10 #include <stdlib.h>
11 #include <netdb.h>
12 #include <string.h>
13 #include <yajl/yajl_gen.h>
14 #include <yajl/yajl_version.h>
15
16 #include "i3status.h"
17
18 static char *get_sockname(struct addrinfo *addr) {
19     static char buf[INET6_ADDRSTRLEN + 1];
20     struct sockaddr_storage local;
21     int ret;
22     int fd;
23
24     if ((fd = socket(addr->ai_family, SOCK_DGRAM, 0)) == -1) {
25         perror("socket()");
26         return NULL;
27     }
28
29     /* Since the socket was created with SOCK_DGRAM, this is
30      * actually not establishing a connection or generating
31      * any other network traffic. Instead, as a side-effect,
32      * it saves the local address with which packets would
33      * be sent to the destination. */
34     if (connect(fd, addr->ai_addr, addr->ai_addrlen) == -1) {
35         /* We don’t display the error here because most
36          * likely, there just is no IPv6 connectivity.
37          * Thus, don’t spam the user’s console but just
38          * try the next address. */
39         (void)close(fd);
40         return NULL;
41     }
42
43     socklen_t local_len = sizeof(struct sockaddr_storage);
44     if (getsockname(fd, (struct sockaddr *)&local, &local_len) == -1) {
45         perror("getsockname()");
46         (void)close(fd);
47         return NULL;
48     }
49
50     memset(buf, 0, INET6_ADDRSTRLEN + 1);
51     if ((ret = getnameinfo((struct sockaddr *)&local, local_len,
52                            buf, sizeof(buf), NULL, 0,
53                            NI_NUMERICHOST)) != 0) {
54         fprintf(stderr, "i3status: getnameinfo(): %s\n", gai_strerror(ret));
55         (void)close(fd);
56         return NULL;
57     }
58
59     (void)close(fd);
60     return buf;
61 }
62
63 /*
64  * Returns the IPv6 address with which you have connectivity at the moment.
65  * The char * is statically allocated and mustn't be freed
66  */
67 static char *get_ipv6_addr(void) {
68     struct addrinfo hints;
69     struct addrinfo *result, *resp;
70     static struct addrinfo *cached = NULL;
71
72     /* To save dns lookups (if they are not cached locally) and creating
73      * sockets, we save the fd and keep it open. */
74     if (cached != NULL)
75         return get_sockname(cached);
76
77     memset(&hints, 0, sizeof(struct addrinfo));
78     hints.ai_family = AF_INET6;
79     hints.ai_socktype = SOCK_DGRAM;
80
81     /* We use the public IPv6 of the K root server here. It doesn’t matter
82      * which IPv6 address we use (we don’t even send any packets), as long
83      * as it’s considered global by the kernel.
84      * NB: We don’t use a hostname since that would trigger a DNS lookup.
85      * By using an IPv6 address, getaddrinfo() will *not* do a DNS lookup,
86      * but return the address in the appropriate struct. */
87     if (getaddrinfo("2001:7fd::1", "domain", &hints, &result) != 0) {
88         /* We don’t display the error here because most
89          * likely, there just is no connectivity.
90          * Thus, don’t spam the user’s console. */
91         return NULL;
92     }
93
94     for (resp = result; resp != NULL; resp = resp->ai_next) {
95         char *addr_string = get_sockname(resp);
96         /* If we could not get our own address and there is more than
97          * one result for resolving k.root-servers.net, we cannot
98          * cache. Otherwise, no matter if we got IPv6 connectivity or
99          * not, we will cache the (single) result and are done. */
100         if (!addr_string && result->ai_next != NULL)
101             continue;
102
103         if ((cached = malloc(sizeof(struct addrinfo))) == NULL)
104             return NULL;
105         memcpy(cached, resp, sizeof(struct addrinfo));
106         if ((cached->ai_addr = malloc(resp->ai_addrlen)) == NULL) {
107             cached = NULL;
108             return NULL;
109         }
110         memcpy(cached->ai_addr, resp->ai_addr, resp->ai_addrlen);
111         freeaddrinfo(result);
112         return addr_string;
113     }
114
115     freeaddrinfo(result);
116     return NULL;
117 }
118
119 void print_ipv6_info(yajl_gen json_gen, char *buffer, const char *format_up, const char *format_down) {
120     const char *walk;
121     char *addr_string = get_ipv6_addr();
122     char *outwalk = buffer;
123
124     if (addr_string == NULL) {
125         START_COLOR("color_bad");
126         outwalk += sprintf(outwalk, "%s", format_down);
127         END_COLOR;
128         OUTPUT_FULL_TEXT(buffer);
129         return;
130     }
131
132     START_COLOR("color_good");
133     for (walk = format_up; *walk != '\0'; walk++) {
134         if (*walk != '%') {
135             *(outwalk++) = *walk;
136
137         } else if (BEGINS_WITH(walk + 1, "ip")) {
138             outwalk += sprintf(outwalk, "%s", addr_string);
139             walk += strlen("ip");
140
141         } else {
142             *(outwalk++) = '%';
143         }
144     }
145     END_COLOR;
146     OUTPUT_FULL_TEXT(buffer);
147 }