]> git.sur5r.net Git - i3/i3status/blob - src/print_wireless_info.c
530d9cf8eb3c2533e107458cc1d1de5229c63ad1
[i3/i3status] / src / print_wireless_info.c
1 // vim:ts=8:expandtab
2 #include <stdio.h>
3 #include <string.h>
4
5 #ifdef LINUX
6 #include <iwlib.h>
7 #else
8 #ifndef __FreeBSD__
9 #define IW_ESSID_MAX_SIZE 32
10 #endif
11 #endif
12
13 #ifdef __FreeBSD__
14 #include <sys/param.h>
15 #include <sys/ioctl.h>
16 #include <sys/socket.h>
17 #include <ifaddrs.h>
18 #include <net/if.h>
19 #include <net/if_media.h>
20 #include <net80211/ieee80211.h>
21 #include <net80211/ieee80211_ioctl.h>
22 #include <unistd.h>
23 #define IW_ESSID_MAX_SIZE IEEE80211_NWID_LEN
24 #endif
25
26 #include "i3status.h"
27
28 #define WIRELESS_INFO_FLAG_HAS_ESSID                    (1 << 0)
29 #define WIRELESS_INFO_FLAG_HAS_QUALITY                  (1 << 1)
30 #define WIRELESS_INFO_FLAG_HAS_SIGNAL                   (1 << 2)
31 #define WIRELESS_INFO_FLAG_HAS_NOISE                    (1 << 3)
32
33 #define PERCENT_VALUE(value, total) ((int)(value * 100 / (float)total + 0.5f))
34
35 typedef struct {
36         int flags;
37         char essid[IW_ESSID_MAX_SIZE + 1];
38         int quality;
39         int quality_max;
40         int quality_average;
41         int signal_level;
42         int signal_level_max;
43         int noise_level;
44         int noise_level_max;
45         int bitrate;
46 } wireless_info_t;
47
48 static int get_wireless_info(const char *interface, wireless_info_t *info) {
49         memset(info, 0, sizeof(wireless_info_t));
50
51 #ifdef LINUX
52         int skfd = iw_sockets_open();
53         if (skfd < 0) {
54                 perror("iw_sockets_open");
55                 return 0;
56         }
57
58         wireless_config wcfg;
59         if (iw_get_basic_config(skfd, interface, &wcfg) < 0) {
60             close(skfd);
61             return 0;
62         }
63
64         if (wcfg.has_essid && wcfg.essid_on) {
65                 info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID;
66                 strncpy(&info->essid[0], wcfg.essid, IW_ESSID_MAX_SIZE);
67                 info->essid[IW_ESSID_MAX_SIZE] = '\0';
68         }
69
70         /* Wireless quality is a relative value in a driver-specific range.
71            Signal and noise level can be either relative or absolute values
72            in dBm. Furthermore, noise and quality can be expressed directly
73            in dBm or in RCPI (802.11k), which we convert to dBm. When those
74            values are expressed directly in dBm, they range from -192 to 63,
75            and since the values are packed into 8 bits, we need to perform
76            8-bit arithmetic on them. Assume absolute values if everything
77            else fails (driver bug). */
78
79         iwrange range;
80         if (iw_get_range_info(skfd, interface, &range) < 0) {
81                 close(skfd);
82                 return 0;
83         }
84
85         iwstats stats;
86         if (iw_get_stats(skfd, interface, &stats, &range, 1) < 0) {
87                 close(skfd);
88                 return 0;
89         }
90
91         if (stats.qual.level != 0 || (stats.qual.updated & (IW_QUAL_DBM | IW_QUAL_RCPI))) {
92                 if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
93                         info->quality = stats.qual.qual;
94                         info->quality_max = range.max_qual.qual;
95                         info->quality_average = range.avg_qual.qual;
96                         info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
97                 }
98
99                 if (stats.qual.updated & IW_QUAL_RCPI) {
100                         if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
101                                 info->signal_level = stats.qual.level / 2.0 - 110 + 0.5;
102                                 info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
103                         }
104                         if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
105                                 info->noise_level = stats.qual.noise / 2.0 - 110 + 0.5;
106                                 info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
107                         }
108                 }
109                 else {
110                         if ((stats.qual.updated & IW_QUAL_DBM) || stats.qual.level > range.max_qual.level) {
111                                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
112                                         info->signal_level = stats.qual.level;
113                                         if (info->signal_level > 63)
114                                                 info->signal_level -= 256;
115                                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
116                                 }
117                                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
118                                         info->noise_level = stats.qual.noise;
119                                         if (info->noise_level > 63)
120                                                 info->noise_level -= 256;
121                                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
122                                 }
123                         }
124                         else {
125                                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
126                                         info->signal_level = stats.qual.level;
127                                         info->signal_level_max = range.max_qual.level;
128                                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
129                                 }
130                                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
131                                         info->noise_level = stats.qual.noise;
132                                         info->noise_level_max = range.max_qual.noise;
133                                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
134                                 }
135                         }
136                 }
137         }
138         else {
139                 if (!(stats.qual.updated & IW_QUAL_QUAL_INVALID)) {
140                         info->quality = stats.qual.qual;
141                         info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
142                 }
143                 if (!(stats.qual.updated & IW_QUAL_LEVEL_INVALID)) {
144                         info->quality = stats.qual.level;
145                         info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
146                 }
147                 if (!(stats.qual.updated & IW_QUAL_NOISE_INVALID)) {
148                         info->quality = stats.qual.noise;
149                         info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
150                 }
151         }
152
153         struct iwreq wrq;
154         if (iw_get_ext(skfd, interface, SIOCGIWRATE, &wrq) >= 0)
155                 info->bitrate = wrq.u.bitrate.value;
156
157         close(skfd);
158         return 1;
159 #endif
160 #ifdef __FreeBSD__
161         int s, len, inwid;
162         uint8_t buf[24 * 1024], *cp;
163         struct ieee80211req na;
164         char network_id[IEEE80211_NWID_LEN + 1];
165
166         if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
167                 return (0);
168
169         memset(&na, 0, sizeof(na));
170         strlcpy(na.i_name, interface, sizeof(na.i_name));
171         na.i_type = IEEE80211_IOC_SSID;
172         na.i_data = &info->essid[0];
173         na.i_len = IEEE80211_NWID_LEN + 1;
174         if ((inwid = ioctl(s, SIOCG80211, (caddr_t)&na)) == -1) {
175                 close(s);
176                 return (0);
177         }
178         if (inwid == 0) {
179                 if (na.i_len <= IEEE80211_NWID_LEN)
180                         len = na.i_len + 1;
181                 else
182                         len = IEEE80211_NWID_LEN + 1;
183                 info->essid[len -1] = '\0';
184         } else {
185                 close(s);
186                 return (0);
187         }
188         info->flags |= WIRELESS_INFO_FLAG_HAS_ESSID;
189
190         memset(&na, 0, sizeof(na));
191         strlcpy(na.i_name, interface, sizeof(na.i_name));
192         na.i_type = IEEE80211_IOC_SCAN_RESULTS;
193         na.i_data = buf;
194         na.i_len = sizeof(buf);
195
196         if (ioctl(s, SIOCG80211, (caddr_t)&na) == -1) {
197                 printf("fail\n");
198                 close(s);
199                 return (0);
200         }
201
202         close(s);
203         len = na.i_len;
204         cp = buf;
205         struct ieee80211req_scan_result *sr;
206         uint8_t *vp;
207         sr = (struct ieee80211req_scan_result *)cp;
208         vp = (u_int8_t *)(sr + 1);
209         strlcpy(network_id, (const char *)vp, sr->isr_ssid_len + 1);
210         if (!strcmp(network_id, &info->essid[0])) {
211                 info->signal_level = sr->isr_rssi;
212                 info->flags |= WIRELESS_INFO_FLAG_HAS_SIGNAL;
213                 info->noise_level = sr->isr_noise;
214                 info->flags |= WIRELESS_INFO_FLAG_HAS_NOISE;
215                 info->quality = sr->isr_intval;
216                 info->flags |= WIRELESS_INFO_FLAG_HAS_QUALITY;
217         }
218
219         return 1;
220 #endif
221         return 0;
222 }
223
224 void print_wireless_info(const char *interface, const char *format_up, const char *format_down) {
225         const char *walk;
226         wireless_info_t info;
227         if (output_format == O_I3BAR)
228                 printf("{\"name\":\"wireless\", \"instance\": \"%s\", ", interface);
229
230         if (get_wireless_info(interface, &info)) {
231                 walk = format_up;
232                 if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY)
233                         printf("%s", info.quality < info.quality_average ? color("color_degraded") : color("color_good"));
234         }
235         else {
236                 walk = format_down;
237                 printf("%s", color("color_bad"));
238         }
239
240         if (output_format == O_I3BAR)
241                 printf("\"full_text\":\"");
242
243         for (; *walk != '\0'; walk++) {
244                 if (*walk != '%') {
245                         putchar(*walk);
246                         continue;
247                 }
248
249                 if (BEGINS_WITH(walk+1, "quality")) {
250                         if (info.flags & WIRELESS_INFO_FLAG_HAS_QUALITY) {
251                                 if (info.quality_max)
252                                         printf("%03d%%", PERCENT_VALUE(info.quality, info.quality_max));
253                                 else
254                                         printf("%d", info.quality);
255                         }
256                         else {
257                                 printf("no value");
258                         }
259                         walk += strlen("quality");
260                 }
261
262                 if (BEGINS_WITH(walk+1, "signal")) {
263                         if (info.flags & WIRELESS_INFO_FLAG_HAS_SIGNAL) {
264                                 if (info.signal_level_max)
265                                         printf("%03d%%", PERCENT_VALUE(info.signal_level, info.signal_level_max));
266                                 else
267                                         printf("%d dBm", info.signal_level);
268                         }
269                         else {
270                                 printf("no value");
271                         }
272                         walk += strlen("signal");
273                 }
274
275                 if (BEGINS_WITH(walk+1, "noise")) {
276                         if (info.flags & WIRELESS_INFO_FLAG_HAS_NOISE) {
277                                 if (info.noise_level_max)
278                                         printf("%03d%%", PERCENT_VALUE(info.noise_level, info.noise_level_max));
279                                 else
280                                         printf("%d dBm", info.noise_level);
281                         }
282                         else {
283                                 printf("no value");
284                         }
285                         walk += strlen("noise");
286                 }
287
288                 if (BEGINS_WITH(walk+1, "essid")) {
289                         if (info.flags & WIRELESS_INFO_FLAG_HAS_ESSID)
290                                 printf("%s", info.essid);
291                         else
292                                 printf("no value");
293                         walk += strlen("essid");
294                 }
295
296                 if (BEGINS_WITH(walk+1, "ip")) {
297                         const char *ip_address = get_ip_addr(interface);
298                         if (ip_address != NULL)
299                                 (void)printf("%s", get_ip_addr(interface));
300                         else (void)printf("no IP");
301                         walk += strlen("ip");
302                 }
303
304 #ifdef LINUX
305                 if (BEGINS_WITH(walk+1, "bitrate")) {
306                         char buffer[128];
307
308                         iw_print_bitrate(buffer, sizeof(buffer), info.bitrate);
309
310                         printf("%s", buffer);
311                         walk += strlen("bitrate");
312                 }
313 #endif
314         }
315
316         (void)printf("%s", endcolor());
317
318         if (output_format == O_I3BAR)
319                 printf("\"}");
320 }