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