| 1 | #include <stdbool.h> |
| 2 | #include "plugin_proc.h" |
| 3 | |
| 4 | #define PLUGIN_PROC_MODULE_NETWIRELESS_NAME "/proc/net/wireless" |
| 5 | |
| 6 | #define CONFIG_SECTION_PLUGIN_PROC_NETWIRELESS "plugin:" PLUGIN_PROC_CONFIG_NAME ":" PLUGIN_PROC_MODULE_NETWIRELESS_NAME |
| 7 | |
| 8 | |
| 9 | static struct netwireless { |
| 10 | char *name; |
| 11 | uint32_t hash; |
| 12 | |
| 13 | //flags |
| 14 | bool configured; |
| 15 | struct timeval updated; |
| 16 | |
| 17 | int do_status; |
| 18 | int do_quality; |
| 19 | int do_discarded_packets; |
| 20 | int do_missed_beacon; |
| 21 | |
| 22 | // Data collected |
| 23 | // status |
| 24 | kernel_uint_t status; |
| 25 | |
| 26 | // Quality |
| 27 | NETDATA_DOUBLE link; |
| 28 | NETDATA_DOUBLE level; |
| 29 | NETDATA_DOUBLE noise; |
| 30 | |
| 31 | // Discarded packets |
| 32 | kernel_uint_t nwid; |
| 33 | kernel_uint_t crypt; |
| 34 | kernel_uint_t frag; |
| 35 | kernel_uint_t retry; |
| 36 | kernel_uint_t misc; |
| 37 | |
| 38 | // missed beacon |
| 39 | kernel_uint_t missed_beacon; |
| 40 | |
| 41 | const char *chart_id_net_status; |
| 42 | const char *chart_id_net_link; |
| 43 | const char *chart_id_net_level; |
| 44 | const char *chart_id_net_noise; |
| 45 | const char *chart_id_net_discarded_packets; |
| 46 | const char *chart_id_net_missed_beacon; |
| 47 | |
| 48 | const char *chart_family; |
| 49 | |
| 50 | // charts |
| 51 | // status |
| 52 | RRDSET *st_status; |
| 53 | |
| 54 | // Quality |
| 55 | RRDSET *st_link; |
| 56 | RRDSET *st_level; |
| 57 | RRDSET *st_noise; |
| 58 | |
| 59 | // Discarded Packets |
| 60 | RRDSET *st_discarded_packets; |
| 61 | // Missed beacon |
| 62 | RRDSET *st_missed_beacon; |
| 63 | |
| 64 | // Dimensions |
| 65 | // status |
| 66 | RRDDIM *rd_status; |
| 67 | |
| 68 | // Quality |
| 69 | RRDDIM *rd_link; |
| 70 | RRDDIM *rd_level; |
| 71 | RRDDIM *rd_noise; |
| 72 | |
| 73 | // Discarded packets |
| 74 | RRDDIM *rd_nwid; |
| 75 | RRDDIM *rd_crypt; |
| 76 | RRDDIM *rd_frag; |
| 77 | RRDDIM *rd_retry; |
| 78 | RRDDIM *rd_misc; |
| 79 | |
| 80 | // missed beacon |
| 81 | RRDDIM *rd_missed_beacon; |
| 82 | |
| 83 | struct netwireless *next; |
| 84 | } *netwireless_root = NULL; |
| 85 | |
| 86 | static void netwireless_free_st(struct netwireless *wireless_dev) |
| 87 | { |
| 88 | if (wireless_dev->st_status) rrdset_is_obsolete___safe_from_collector_thread(wireless_dev->st_status); |
| 89 | if (wireless_dev->st_link) rrdset_is_obsolete___safe_from_collector_thread(wireless_dev->st_link); |
| 90 | if (wireless_dev->st_level) rrdset_is_obsolete___safe_from_collector_thread(wireless_dev->st_level); |
| 91 | if (wireless_dev->st_noise) rrdset_is_obsolete___safe_from_collector_thread(wireless_dev->st_noise); |
| 92 | if (wireless_dev->st_discarded_packets) |
| 93 | rrdset_is_obsolete___safe_from_collector_thread(wireless_dev->st_discarded_packets); |
| 94 | if (wireless_dev->st_missed_beacon) rrdset_is_obsolete___safe_from_collector_thread(wireless_dev->st_missed_beacon); |
| 95 | |
| 96 | wireless_dev->st_status = NULL; |
| 97 | wireless_dev->st_link = NULL; |
| 98 | wireless_dev->st_level = NULL; |
| 99 | wireless_dev->st_noise = NULL; |
| 100 | wireless_dev->st_discarded_packets = NULL; |
| 101 | wireless_dev->st_missed_beacon = NULL; |
| 102 | } |
| 103 | |
| 104 | static void netwireless_free(struct netwireless *wireless_dev) |
| 105 | { |
| 106 | wireless_dev->next = NULL; |
| 107 | freez((void *)wireless_dev->name); |
| 108 | netwireless_free_st(wireless_dev); |
| 109 | freez((void *)wireless_dev->chart_id_net_status); |
| 110 | freez((void *)wireless_dev->chart_id_net_link); |
| 111 | freez((void *)wireless_dev->chart_id_net_level); |
| 112 | freez((void *)wireless_dev->chart_id_net_noise); |
| 113 | freez((void *)wireless_dev->chart_id_net_discarded_packets); |
| 114 | freez((void *)wireless_dev->chart_id_net_missed_beacon); |
| 115 | |
| 116 | freez((void *)wireless_dev); |
| 117 | } |
| 118 | |
| 119 | static void netwireless_cleanup(struct timeval *timestamp) |
| 120 | { |
| 121 | struct netwireless *previous = NULL; |
| 122 | struct netwireless *current; |
| 123 | // search it, from beginning to the end |
| 124 | for (current = netwireless_root; current;) { |
| 125 | |
| 126 | if (timercmp(¤t->updated, timestamp, <)) { |
| 127 | struct netwireless *to_free = current; |
| 128 | current = current->next; |
| 129 | netwireless_free(to_free); |
| 130 | |
| 131 | if (previous) { |
| 132 | previous->next = current; |
| 133 | } else { |
| 134 | netwireless_root = current; |
| 135 | } |
| 136 | } else { |
| 137 | previous = current; |
| 138 | current = current->next; |
| 139 | } |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | // finds an existing interface or creates a new entry |
| 144 | static struct netwireless *find_or_create_wireless(const char *name) |
| 145 | { |
| 146 | struct netwireless *wireless; |
| 147 | uint32_t hash = simple_hash(name); |
| 148 | |
| 149 | // search it, from beginning to the end |
| 150 | for (wireless = netwireless_root ; wireless ; wireless = wireless->next) { |
| 151 | if (unlikely(hash == wireless->hash && !strcmp(name, wireless->name))) { |
| 152 | return wireless; |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | // create a new one |
| 157 | wireless = callocz(1, sizeof(struct netwireless)); |
| 158 | wireless->name = strdupz(name); |
| 159 | wireless->hash = hash; |
| 160 | |
| 161 | // link it to the end |
| 162 | if (netwireless_root) { |
| 163 | struct netwireless *last_node; |
| 164 | for (last_node = netwireless_root; last_node->next ; last_node = last_node->next); |
| 165 | |
| 166 | last_node->next = wireless; |
| 167 | } else |
| 168 | netwireless_root = wireless; |
| 169 | |
| 170 | return wireless; |
| 171 | } |
| 172 | |
| 173 | static void configure_device(int do_status, int do_quality, int do_discarded_packets, int do_missed, |
| 174 | struct netwireless *wireless_dev) { |
| 175 | wireless_dev->do_status = do_status; |
| 176 | wireless_dev->do_quality = do_quality; |
| 177 | wireless_dev->do_discarded_packets = do_discarded_packets; |
| 178 | wireless_dev->do_missed_beacon = do_missed; |
| 179 | wireless_dev->configured = true; |
| 180 | |
| 181 | char buffer[RRD_ID_LENGTH_MAX + 1]; |
| 182 | |
| 183 | snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s_status", wireless_dev->name); |
| 184 | wireless_dev->chart_id_net_status = strdupz(buffer); |
| 185 | |
| 186 | snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s_link_quality", wireless_dev->name); |
| 187 | wireless_dev->chart_id_net_link = strdupz(buffer); |
| 188 | |
| 189 | snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s_signal_level", wireless_dev->name); |
| 190 | wireless_dev->chart_id_net_level = strdupz(buffer); |
| 191 | |
| 192 | snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s_noise_level", wireless_dev->name); |
| 193 | wireless_dev->chart_id_net_noise = strdupz(buffer); |
| 194 | |
| 195 | snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s_discarded_packets", wireless_dev->name); |
| 196 | wireless_dev->chart_id_net_discarded_packets = strdupz(buffer); |
| 197 | |
| 198 | snprintfz(buffer, RRD_ID_LENGTH_MAX, "%s_missed_beacon", wireless_dev->name); |
| 199 | wireless_dev->chart_id_net_missed_beacon = strdupz(buffer); |
| 200 | } |
| 201 | |
| 202 | static void add_labels_to_wireless(struct netwireless *w, RRDSET *st) { |
| 203 | rrdlabels_add(st->rrdlabels, "device", w->name, RRDLABEL_SRC_AUTO); |
| 204 | } |
| 205 | |
| 206 | int do_proc_net_wireless(int update_every, usec_t dt) |
| 207 | { |
| 208 | UNUSED(dt); |
| 209 | static procfile *ff = NULL; |
| 210 | static int do_status, do_quality = -1, do_discarded_packets, do_beacon; |
| 211 | static const char *proc_net_wireless_filename = NULL; |
| 212 | |
| 213 | if (unlikely(do_quality == -1)) { |
| 214 | char filename[FILENAME_MAX + 1]; |
| 215 | snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/net/wireless"); |
| 216 | |
| 217 | proc_net_wireless_filename = inicfg_get(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_NETWIRELESS,"filename to monitor", filename); |
| 218 | do_status = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_NETWIRELESS, "status for all interfaces", CONFIG_BOOLEAN_AUTO); |
| 219 | do_quality = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_NETWIRELESS, "quality for all interfaces", CONFIG_BOOLEAN_AUTO); |
| 220 | do_discarded_packets = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_NETWIRELESS, "discarded packets for all interfaces", CONFIG_BOOLEAN_AUTO); |
| 221 | do_beacon = inicfg_get_boolean_ondemand(&netdata_config, CONFIG_SECTION_PLUGIN_PROC_NETWIRELESS, "missed beacon for all interface", CONFIG_BOOLEAN_AUTO); |
| 222 | } |
| 223 | |
| 224 | if (unlikely(!ff)) { |
| 225 | ff = procfile_open(proc_net_wireless_filename, " \t,|", PROCFILE_FLAG_DEFAULT); |
| 226 | if (unlikely(!ff)) return 1; |
| 227 | } |
| 228 | |
| 229 | ff = procfile_readall(ff); |
| 230 | if (unlikely(!ff)) return 1; |
| 231 | |
| 232 | size_t lines = procfile_lines(ff); |
| 233 | struct timeval timestamp; |
| 234 | size_t l; |
| 235 | gettimeofday(×tamp, NULL); |
| 236 | for (l = 2; l < lines; l++) { |
| 237 | if (unlikely(procfile_linewords(ff, l) < 11)) continue; |
| 238 | |
| 239 | char *name = procfile_lineword(ff, l, 0); |
| 240 | size_t len = strlen(name); |
| 241 | if (name[len - 1] == ':') name[len - 1] = '\0'; |
| 242 | |
| 243 | struct netwireless *wireless_dev = find_or_create_wireless(name); |
| 244 | |
| 245 | if (unlikely(!wireless_dev->configured)) { |
| 246 | configure_device(do_status, do_quality, do_discarded_packets, do_beacon, wireless_dev); |
| 247 | } |
| 248 | |
| 249 | if (likely(do_status != CONFIG_BOOLEAN_NO)) { |
| 250 | wireless_dev->status = str2kernel_uint_t(procfile_lineword(ff, l, 1)); |
| 251 | |
| 252 | if (unlikely(!wireless_dev->st_status)) { |
| 253 | wireless_dev->st_status = rrdset_create_localhost( |
| 254 | "wireless", |
| 255 | wireless_dev->chart_id_net_status, |
| 256 | NULL, |
| 257 | wireless_dev->name, |
| 258 | "wireless.status", |
| 259 | "Internal status reported by interface.", |
| 260 | "status", |
| 261 | PLUGIN_PROC_NAME, |
| 262 | PLUGIN_PROC_MODULE_NETWIRELESS_NAME, |
| 263 | NETDATA_CHART_PRIO_WIRELESS_IFACE, |
| 264 | update_every, |
| 265 | RRDSET_TYPE_LINE); |
| 266 | |
| 267 | wireless_dev->rd_status = rrddim_add(wireless_dev->st_status, "status", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 268 | |
| 269 | add_labels_to_wireless(wireless_dev, wireless_dev->st_status); |
| 270 | } |
| 271 | |
| 272 | rrddim_set_by_pointer(wireless_dev->st_status, wireless_dev->rd_status, |
| 273 | (collected_number)wireless_dev->status); |
| 274 | rrdset_done(wireless_dev->st_status); |
| 275 | } |
| 276 | |
| 277 | if (likely(do_quality != CONFIG_BOOLEAN_NO)) { |
| 278 | wireless_dev->link = str2ndd(procfile_lineword(ff, l, 2), NULL); |
| 279 | wireless_dev->level = str2ndd(procfile_lineword(ff, l, 3), NULL); |
| 280 | wireless_dev->noise = str2ndd(procfile_lineword(ff, l, 4), NULL); |
| 281 | |
| 282 | if (unlikely(!wireless_dev->st_link)) { |
| 283 | wireless_dev->st_link = rrdset_create_localhost( |
| 284 | "wireless", |
| 285 | wireless_dev->chart_id_net_link, |
| 286 | NULL, |
| 287 | wireless_dev->name, |
| 288 | "wireless.link_quality", |
| 289 | "Overall quality of the link. This is an aggregate value, and depends on the driver and hardware.", |
| 290 | "value", |
| 291 | PLUGIN_PROC_NAME, |
| 292 | PLUGIN_PROC_MODULE_NETWIRELESS_NAME, |
| 293 | NETDATA_CHART_PRIO_WIRELESS_IFACE + 1, |
| 294 | update_every, |
| 295 | RRDSET_TYPE_LINE); |
| 296 | |
| 297 | wireless_dev->rd_link = rrddim_add(wireless_dev->st_link, "link_quality", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 298 | |
| 299 | add_labels_to_wireless(wireless_dev, wireless_dev->st_link); |
| 300 | } |
| 301 | |
| 302 | if (unlikely(!wireless_dev->st_level)) { |
| 303 | wireless_dev->st_level = rrdset_create_localhost( |
| 304 | "wireless", |
| 305 | wireless_dev->chart_id_net_level, |
| 306 | NULL, |
| 307 | wireless_dev->name, |
| 308 | "wireless.signal_level", |
| 309 | "The signal level is the wireless signal power level received by the wireless client. The closer the value is to 0, the stronger the signal.", |
| 310 | "dBm", |
| 311 | PLUGIN_PROC_NAME, |
| 312 | PLUGIN_PROC_MODULE_NETWIRELESS_NAME, |
| 313 | NETDATA_CHART_PRIO_WIRELESS_IFACE + 2, |
| 314 | update_every, |
| 315 | RRDSET_TYPE_LINE); |
| 316 | |
| 317 | wireless_dev->rd_level = rrddim_add(wireless_dev->st_level, "signal_level", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 318 | |
| 319 | add_labels_to_wireless(wireless_dev, wireless_dev->st_level); |
| 320 | } |
| 321 | |
| 322 | if (unlikely(!wireless_dev->st_noise)) { |
| 323 | wireless_dev->st_noise = rrdset_create_localhost( |
| 324 | "wireless", |
| 325 | wireless_dev->chart_id_net_noise, |
| 326 | NULL, |
| 327 | wireless_dev->name, |
| 328 | "wireless.noise_level", |
| 329 | "The noise level indicates the amount of background noise in your environment. The closer the value to 0, the greater the noise level.", |
| 330 | "dBm", |
| 331 | PLUGIN_PROC_NAME, |
| 332 | PLUGIN_PROC_MODULE_NETWIRELESS_NAME, |
| 333 | NETDATA_CHART_PRIO_WIRELESS_IFACE + 3, |
| 334 | update_every, |
| 335 | RRDSET_TYPE_LINE); |
| 336 | |
| 337 | wireless_dev->rd_noise = rrddim_add(wireless_dev->st_noise, "noise_level", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 338 | |
| 339 | add_labels_to_wireless(wireless_dev, wireless_dev->st_noise); |
| 340 | } |
| 341 | |
| 342 | rrddim_set_by_pointer(wireless_dev->st_link, wireless_dev->rd_link, (collected_number)wireless_dev->link); |
| 343 | rrdset_done(wireless_dev->st_link); |
| 344 | |
| 345 | rrddim_set_by_pointer(wireless_dev->st_level, wireless_dev->rd_level, (collected_number)wireless_dev->level); |
| 346 | rrdset_done(wireless_dev->st_level); |
| 347 | |
| 348 | rrddim_set_by_pointer(wireless_dev->st_noise, wireless_dev->rd_noise, (collected_number)wireless_dev->noise); |
| 349 | rrdset_done(wireless_dev->st_noise); |
| 350 | } |
| 351 | |
| 352 | if (likely(do_discarded_packets)) { |
| 353 | wireless_dev->nwid = str2kernel_uint_t(procfile_lineword(ff, l, 5)); |
| 354 | wireless_dev->crypt = str2kernel_uint_t(procfile_lineword(ff, l, 6)); |
| 355 | wireless_dev->frag = str2kernel_uint_t(procfile_lineword(ff, l, 7)); |
| 356 | wireless_dev->retry = str2kernel_uint_t(procfile_lineword(ff, l, 8)); |
| 357 | wireless_dev->misc = str2kernel_uint_t(procfile_lineword(ff, l, 9)); |
| 358 | |
| 359 | if (unlikely(!wireless_dev->st_discarded_packets)) { |
| 360 | wireless_dev->st_discarded_packets = rrdset_create_localhost( |
| 361 | "wireless", |
| 362 | wireless_dev->chart_id_net_discarded_packets, |
| 363 | NULL, |
| 364 | wireless_dev->name, |
| 365 | "wireless.discarded_packets", |
| 366 | "Packet discarded in the wireless adapter due to \"wireless\" specific problems.", |
| 367 | "packets/s", |
| 368 | PLUGIN_PROC_NAME, |
| 369 | PLUGIN_PROC_MODULE_NETWIRELESS_NAME, |
| 370 | NETDATA_CHART_PRIO_WIRELESS_IFACE + 4, |
| 371 | update_every, |
| 372 | RRDSET_TYPE_LINE); |
| 373 | |
| 374 | wireless_dev->rd_nwid = rrddim_add(wireless_dev->st_discarded_packets, "nwid", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 375 | wireless_dev->rd_crypt = rrddim_add(wireless_dev->st_discarded_packets, "crypt", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 376 | wireless_dev->rd_frag = rrddim_add(wireless_dev->st_discarded_packets, "frag", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 377 | wireless_dev->rd_retry = rrddim_add(wireless_dev->st_discarded_packets, "retry", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 378 | wireless_dev->rd_misc = rrddim_add(wireless_dev->st_discarded_packets, "misc", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 379 | |
| 380 | add_labels_to_wireless(wireless_dev, wireless_dev->st_discarded_packets); |
| 381 | } |
| 382 | |
| 383 | rrddim_set_by_pointer(wireless_dev->st_discarded_packets, wireless_dev->rd_nwid, (collected_number)wireless_dev->nwid); |
| 384 | rrddim_set_by_pointer(wireless_dev->st_discarded_packets, wireless_dev->rd_crypt, (collected_number)wireless_dev->crypt); |
| 385 | rrddim_set_by_pointer(wireless_dev->st_discarded_packets, wireless_dev->rd_frag, (collected_number)wireless_dev->frag); |
| 386 | rrddim_set_by_pointer(wireless_dev->st_discarded_packets, wireless_dev->rd_retry, (collected_number)wireless_dev->retry); |
| 387 | rrddim_set_by_pointer(wireless_dev->st_discarded_packets, wireless_dev->rd_misc, (collected_number)wireless_dev->misc); |
| 388 | |
| 389 | rrdset_done(wireless_dev->st_discarded_packets); |
| 390 | } |
| 391 | |
| 392 | if (likely(do_beacon)) { |
| 393 | wireless_dev->missed_beacon = str2kernel_uint_t(procfile_lineword(ff, l, 10)); |
| 394 | |
| 395 | if (unlikely(!wireless_dev->st_missed_beacon)) { |
| 396 | wireless_dev->st_missed_beacon = rrdset_create_localhost( |
| 397 | "wireless", |
| 398 | wireless_dev->chart_id_net_missed_beacon, |
| 399 | NULL, |
| 400 | wireless_dev->name, |
| 401 | "wireless.missed_beacons", |
| 402 | "Number of missed beacons.", |
| 403 | "frames/s", |
| 404 | PLUGIN_PROC_NAME, |
| 405 | PLUGIN_PROC_MODULE_NETWIRELESS_NAME, |
| 406 | NETDATA_CHART_PRIO_WIRELESS_IFACE + 5, |
| 407 | update_every, |
| 408 | RRDSET_TYPE_LINE); |
| 409 | |
| 410 | wireless_dev->rd_missed_beacon = rrddim_add(wireless_dev->st_missed_beacon, "missed_beacons", NULL, 1, 1, RRD_ALGORITHM_INCREMENTAL); |
| 411 | |
| 412 | add_labels_to_wireless(wireless_dev, wireless_dev->st_missed_beacon); |
| 413 | } |
| 414 | |
| 415 | rrddim_set_by_pointer(wireless_dev->st_missed_beacon, wireless_dev->rd_missed_beacon, (collected_number)wireless_dev->missed_beacon); |
| 416 | rrdset_done(wireless_dev->st_missed_beacon); |
| 417 | } |
| 418 | |
| 419 | wireless_dev->updated = timestamp; |
| 420 | } |
| 421 | |
| 422 | netwireless_cleanup(×tamp); |
| 423 | return 0; |
| 424 | } |