| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "windows_plugin.h" |
| 4 | #include "windows-internals.h" |
| 5 | #include "libnetdata/os/windows-wmi/windows-wmi.h" |
| 6 | |
| 7 | #include <windows.h> |
| 8 | #include <wchar.h> |
| 9 | |
| 10 | #include <sensorsapi.h> |
| 11 | #include <sensors.h> |
| 12 | #include <propidl.h> |
| 13 | |
| 14 | static ISensorManager *pSensorManager = NULL; |
| 15 | static ND_THREAD *sensors_thread_update = NULL; |
| 16 | static netdata_mutex_t sensors_mutex; |
| 17 | |
| 18 | #define NETDATA_WIN_SENSOR_STATES (6) |
| 19 | #define NETDATA_WIN_VECTOR_POS (3) |
| 20 | |
| 21 | enum netdata_win_sensor_monitored { |
| 22 | NETDATA_WIN_SENSOR_CELSIUS, |
| 23 | NETDATA_WIN_SENSOR_POWER_WATTS, |
| 24 | NETDATA_WIN_SENSOR_CURRENT_AMPS, |
| 25 | NETDATA_WIN_SENSOR_RELATIVITY_HUMIDITY, |
| 26 | NETDATA_WIN_SENSOR_LIGHT_LEVEL, |
| 27 | NETDATA_WIN_SENSOR_DATA_TYPE_LIGHT_TEMPERATURE, |
| 28 | NETDATA_WIN_SENSOR_VOLTAGE, |
| 29 | NETDATA_WIN_SENSOR_RESISTENCE, |
| 30 | NETDATA_WIN_SENSOR_CAPACITANCE_FARAD, |
| 31 | NETDATA_WIN_SENSOR_INDUCTANCE_HENRY, |
| 32 | NETDATA_WIN_SENSOR_ATMOSPHERE_PRESSURE, |
| 33 | NETDATA_WIN_SENSOR_LATITUDE_DEGREES, |
| 34 | NETDATA_WIN_SENSOR_LONGITUDE_DEGREES, |
| 35 | NETDATA_WIN_SENSOR_FORCE_NEWTONS, |
| 36 | NETDATA_WIN_SENSOR_GAUGE_PRESSURE, |
| 37 | NETDATA_WIN_SENSOR_HUMAN_PRESENCE, |
| 38 | NETDATA_WIN_SENSOR_HUMAN_PROXIMITY_METERS, |
| 39 | |
| 40 | // Add only one vector axis here |
| 41 | NETDATA_WIN_SENSOR_TYPE_DISTANCE_X, |
| 42 | NETDATA_WIN_SENSOR_ACCELERATION_X_G, |
| 43 | |
| 44 | NETDATA_WIN_SENSOR_LAST_WELL_DEFINED, |
| 45 | |
| 46 | // Remaining axis should be added here |
| 47 | NETDATA_WIN_SENSOR_TYPE_DISTANCE_Y, |
| 48 | NETDATA_WIN_SENSOR_TYPE_DISTANCE_Z, |
| 49 | NETDATA_WIN_SENSOR_ACCELERATION_Y_G, |
| 50 | NETDATA_WIN_SENSOR_ACCELERATION_Z_G, |
| 51 | |
| 52 | NETDATA_WIN_SENSOR_NEVER_USE_ME, |
| 53 | |
| 54 | // Custom sensors |
| 55 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_USAGE, |
| 56 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_BOOLEAN_ARRAY, |
| 57 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE1, |
| 58 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE2, |
| 59 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE3, |
| 60 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE4, |
| 61 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE5, |
| 62 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE6, |
| 63 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE7, |
| 64 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE8, |
| 65 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE9, |
| 66 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE10, |
| 67 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE11, |
| 68 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE12, |
| 69 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE13, |
| 70 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE14, |
| 71 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE15, |
| 72 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE16, |
| 73 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE17, |
| 74 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE18, |
| 75 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE19, |
| 76 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE20, |
| 77 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE21, |
| 78 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE22, |
| 79 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE23, |
| 80 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE24, |
| 81 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE25, |
| 82 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE26, |
| 83 | NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE27 |
| 84 | }; |
| 85 | |
| 86 | REFPROPERTYKEY sensor_keys[] = { |
| 87 | &SENSOR_DATA_TYPE_TEMPERATURE_CELSIUS, |
| 88 | &SENSOR_DATA_TYPE_ELECTRICAL_POWER_WATTS, |
| 89 | &SENSOR_DATA_TYPE_CURRENT_AMPS, |
| 90 | &SENSOR_DATA_TYPE_RELATIVE_HUMIDITY_PERCENT, |
| 91 | &SENSOR_DATA_TYPE_LIGHT_LEVEL_LUX, |
| 92 | &SENSOR_DATA_TYPE_LIGHT_TEMPERATURE_KELVIN, |
| 93 | &SENSOR_DATA_TYPE_VOLTAGE_VOLTS, |
| 94 | &SENSOR_DATA_TYPE_RESISTANCE_OHMS, |
| 95 | &SENSOR_DATA_TYPE_CAPACITANCE_FARAD, |
| 96 | &SENSOR_DATA_TYPE_INDUCTANCE_HENRY, |
| 97 | &SENSOR_DATA_TYPE_ATMOSPHERIC_PRESSURE_BAR, |
| 98 | &SENSOR_DATA_TYPE_LATITUDE_DEGREES, |
| 99 | &SENSOR_DATA_TYPE_LONGITUDE_DEGREES, |
| 100 | &SENSOR_DATA_TYPE_FORCE_NEWTONS, |
| 101 | &SENSOR_DATA_TYPE_GAUGE_PRESSURE_PASCAL, |
| 102 | &SENSOR_DATA_TYPE_HUMAN_PRESENCE, |
| 103 | &SENSOR_DATA_TYPE_HUMAN_PROXIMITY_METERS, |
| 104 | |
| 105 | // Add only one vector axis here |
| 106 | &SENSOR_DATA_TYPE_DISTANCE_X_METERS, |
| 107 | &SENSOR_DATA_TYPE_ACCELERATION_X_G, |
| 108 | |
| 109 | // Main loop stop |
| 110 | NULL, |
| 111 | |
| 112 | // Remaining axis should be added here |
| 113 | &SENSOR_DATA_TYPE_DISTANCE_Y_METERS, |
| 114 | &SENSOR_DATA_TYPE_DISTANCE_Z_METERS, |
| 115 | &SENSOR_DATA_TYPE_ACCELERATION_Y_G, |
| 116 | &SENSOR_DATA_TYPE_ACCELERATION_Z_G, |
| 117 | |
| 118 | // Stop additional |
| 119 | NULL, |
| 120 | |
| 121 | &SENSOR_DATA_TYPE_CUSTOM_USAGE, |
| 122 | &SENSOR_DATA_TYPE_CUSTOM_BOOLEAN_ARRAY, |
| 123 | &SENSOR_DATA_TYPE_CUSTOM_VALUE1, |
| 124 | &SENSOR_DATA_TYPE_CUSTOM_VALUE2, |
| 125 | &SENSOR_DATA_TYPE_CUSTOM_VALUE3, |
| 126 | &SENSOR_DATA_TYPE_CUSTOM_VALUE4, |
| 127 | &SENSOR_DATA_TYPE_CUSTOM_VALUE5, |
| 128 | &SENSOR_DATA_TYPE_CUSTOM_VALUE6, |
| 129 | &SENSOR_DATA_TYPE_CUSTOM_VALUE7, |
| 130 | &SENSOR_DATA_TYPE_CUSTOM_VALUE8, |
| 131 | &SENSOR_DATA_TYPE_CUSTOM_VALUE9, |
| 132 | &SENSOR_DATA_TYPE_CUSTOM_VALUE10, |
| 133 | &SENSOR_DATA_TYPE_CUSTOM_VALUE11, |
| 134 | &SENSOR_DATA_TYPE_CUSTOM_VALUE12, |
| 135 | &SENSOR_DATA_TYPE_CUSTOM_VALUE13, |
| 136 | &SENSOR_DATA_TYPE_CUSTOM_VALUE14, |
| 137 | &SENSOR_DATA_TYPE_CUSTOM_VALUE15, |
| 138 | &SENSOR_DATA_TYPE_CUSTOM_VALUE16, |
| 139 | &SENSOR_DATA_TYPE_CUSTOM_VALUE17, |
| 140 | &SENSOR_DATA_TYPE_CUSTOM_VALUE18, |
| 141 | &SENSOR_DATA_TYPE_CUSTOM_VALUE19, |
| 142 | &SENSOR_DATA_TYPE_CUSTOM_VALUE20, |
| 143 | &SENSOR_DATA_TYPE_CUSTOM_VALUE21, |
| 144 | &SENSOR_DATA_TYPE_CUSTOM_VALUE22, |
| 145 | &SENSOR_DATA_TYPE_CUSTOM_VALUE23, |
| 146 | &SENSOR_DATA_TYPE_CUSTOM_VALUE24, |
| 147 | &SENSOR_DATA_TYPE_CUSTOM_VALUE25, |
| 148 | &SENSOR_DATA_TYPE_CUSTOM_VALUE26, |
| 149 | &SENSOR_DATA_TYPE_CUSTOM_VALUE27}; |
| 150 | |
| 151 | static struct win_sensor_config { |
| 152 | const char *title; |
| 153 | const char *units; |
| 154 | const char *context; |
| 155 | const char *family; |
| 156 | |
| 157 | int priority; |
| 158 | } configs[] = { |
| 159 | { |
| 160 | .title = "Sensor Temperature", |
| 161 | .units = "Cel", |
| 162 | .context = "system.hw.sensor.temperature.input", |
| 163 | .family = "Temperature", |
| 164 | .priority = NETDATA_CHART_PRIO_SENSORS, |
| 165 | }, |
| 166 | { |
| 167 | .title = "Sensor Power", |
| 168 | .units = "W", |
| 169 | .context = "system.hw.sensor.power.input", |
| 170 | .family = "Power", |
| 171 | .priority = NETDATA_CHART_PRIO_SENSOR_POWER, |
| 172 | }, |
| 173 | { |
| 174 | .title = "Sensor Current", |
| 175 | .units = "A", |
| 176 | .context = "system.hw.sensor.current.input", |
| 177 | .family = "Current", |
| 178 | .priority = NETDATA_CHART_PRIO_SENSOR_CURRENT, |
| 179 | }, |
| 180 | { |
| 181 | .title = "Sensor Humidity", |
| 182 | .units = "%", |
| 183 | .context = "system.hw.sensor.humidity.input", |
| 184 | .family = "Humidity", |
| 185 | .priority = NETDATA_CHART_PRIO_SENSOR_HUMIDITY, |
| 186 | }, |
| 187 | { |
| 188 | .title = "Ambient light level", |
| 189 | .units = "lx", |
| 190 | .context = "system.hw.sensor.lux.input", |
| 191 | .family = "illuminance", |
| 192 | .priority = NETDATA_CHART_PRIO_SENSOR_LUX, |
| 193 | }, |
| 194 | { |
| 195 | .title = "Color temperature of light", |
| 196 | .units = "Cel", |
| 197 | .context = "system.hw.sensor.color.input", |
| 198 | .family = "Temperature", |
| 199 | .priority = NETDATA_CHART_PRIO_SENSOR_TEMPERATURE, |
| 200 | }, |
| 201 | { |
| 202 | .title = "Electrical potential", |
| 203 | .units = "V", |
| 204 | .context = "system.hw.sensor.voltage.input", |
| 205 | .family = "Potential", |
| 206 | .priority = NETDATA_CHART_PRIO_SENSOR_VOLTAGE, |
| 207 | }, |
| 208 | { |
| 209 | .title = "Electrical resistence", |
| 210 | .units = "Ohms", |
| 211 | .context = "system.hw.sensor.resistance.input", |
| 212 | .family = "Resistance", |
| 213 | .priority = NETDATA_CHART_PRIO_SENSOR_RESISTANCE, |
| 214 | }, |
| 215 | { |
| 216 | .title = "Electrical capacitance", |
| 217 | .units = "F", |
| 218 | .context = "system.hw.sensor.capacitance.input", |
| 219 | .family = "Capacitance", |
| 220 | .priority = NETDATA_CHART_PRIO_SENSOR_CAPACITANCE, |
| 221 | }, |
| 222 | { |
| 223 | .title = "Electrical inductance", |
| 224 | .units = "H", |
| 225 | .context = "system.hw.sensor.inductance.input", |
| 226 | .family = "Inductance", |
| 227 | .priority = NETDATA_CHART_PRIO_SENSOR_INDUCTANCE, |
| 228 | }, |
| 229 | { |
| 230 | .title = "Ambient atmospheric pressure", |
| 231 | .units = "Pa", |
| 232 | .context = "system.hw.sensor.pressure.input", |
| 233 | .family = "Pressure", |
| 234 | .priority = NETDATA_CHART_PRIO_SENSOR_AMBIENT_PRESSURE, |
| 235 | }, |
| 236 | { |
| 237 | .title = "Geographic latitude", |
| 238 | .units = "Degrees", |
| 239 | .context = "system.hw.sensor.latitude.input", |
| 240 | .family = "Location", |
| 241 | .priority = NETDATA_CHART_PRIO_SENSOR_LATITUDE, |
| 242 | }, |
| 243 | { |
| 244 | .title = "Geographic longitude", |
| 245 | .units = "Degrees", |
| 246 | .context = "system.hw.sensor.longitude.input", |
| 247 | .family = "Location", |
| 248 | .priority = NETDATA_CHART_PRIO_SENSOR_LONGITUDE, |
| 249 | }, |
| 250 | { |
| 251 | .title = "Force", |
| 252 | .units = "N", |
| 253 | .context = "system.hw.sensor.force.input", |
| 254 | .family = "Force", |
| 255 | .priority = NETDATA_CHART_PRIO_SENSOR_FORCE, |
| 256 | }, |
| 257 | { |
| 258 | .title = "Gauge Pressure", |
| 259 | .units = "Pa", |
| 260 | .context = "system.hw.sensor.gauge_pressure.input", |
| 261 | .family = "Pressure", |
| 262 | .priority = NETDATA_CHART_PRIO_SENSOR_GAUGE_PRESSURE, |
| 263 | }, |
| 264 | { |
| 265 | .title = "Human presence", |
| 266 | .units = "presence", |
| 267 | .context = "system.hw.sensor.human_presence.input", |
| 268 | .family = "Presence", |
| 269 | .priority = NETDATA_CHART_PRIO_SENSOR_HUMAN, |
| 270 | }, |
| 271 | { |
| 272 | .title = "Human proximity", |
| 273 | .units = "m", |
| 274 | .context = "system.hw.sensor.human_proximity.input", |
| 275 | .family = "Proximity", |
| 276 | .priority = NETDATA_CHART_PRIO_SENSOR_PROXIMITY, |
| 277 | }, |
| 278 | { |
| 279 | .title = "Distance", |
| 280 | .units = "m", |
| 281 | .context = "system.hw.sensor.distance.input", |
| 282 | .family = "Distance", |
| 283 | .priority = NETDATA_CHART_PRIO_SENSOR_DISTANCE, |
| 284 | }, |
| 285 | { |
| 286 | .title = "Acceleration.", |
| 287 | .units = "g", |
| 288 | .context = "system.hw.sensor.acceleration.input", |
| 289 | .family = "Acceleration", |
| 290 | .priority = NETDATA_CHART_PRIO_SENSOR_ACCELERATION, |
| 291 | }, |
| 292 | { |
| 293 | .title = "Custom Chart", |
| 294 | .units = "nd", |
| 295 | .context = "system.hw.sensor.custom", |
| 296 | .family = "sensors", |
| 297 | .priority = NETDATA_CHART_PRIO_SENSOR_MIN_CUSTOM, |
| 298 | }}; |
| 299 | |
| 300 | struct netdata_sensors_extra_values { |
| 301 | collected_number value; |
| 302 | enum netdata_win_sensor_monitored sensor_data_type; |
| 303 | RRDDIM *rd_value; |
| 304 | |
| 305 | struct netdata_sensors_extra_values *next; |
| 306 | }; |
| 307 | |
| 308 | struct netdata_sensors_extra_config { |
| 309 | const char *units; |
| 310 | const char *title; |
| 311 | |
| 312 | int multiplier; |
| 313 | }; |
| 314 | |
| 315 | struct sensor_data { |
| 316 | bool initialized; |
| 317 | bool first_time; |
| 318 | bool enabled; |
| 319 | enum netdata_win_sensor_monitored sensor_data_type; |
| 320 | struct win_sensor_config *config; |
| 321 | struct netdata_sensors_extra_values *values; |
| 322 | |
| 323 | const char *type; |
| 324 | const char *category; |
| 325 | const char *name; |
| 326 | const char *manufacturer; |
| 327 | const char *model; |
| 328 | struct netdata_sensors_extra_config *external_config; |
| 329 | |
| 330 | SensorState current_state; |
| 331 | |
| 332 | RRDSET *st_sensor_state; |
| 333 | RRDDIM *rd_sensor_state[NETDATA_WIN_SENSOR_STATES]; |
| 334 | |
| 335 | RRDSET *st_sensor_data; |
| 336 | RRDDIM *rd_sensor_data_0; |
| 337 | RRDDIM *rd_sensor_data_1; |
| 338 | RRDDIM *rd_sensor_data_2; |
| 339 | |
| 340 | // Some sensors are vectors, this means they have three different values (Time is ignored here) |
| 341 | collected_number current_data_value[NETDATA_WIN_VECTOR_POS]; |
| 342 | NETDATA_DOUBLE mult_factor; |
| 343 | NETDATA_DOUBLE div_factor; |
| 344 | NETDATA_DOUBLE add_factor; |
| 345 | }; |
| 346 | |
| 347 | DICTIONARY *sensors; |
| 348 | |
| 349 | static void netdata_sensors_free_extra_values(struct netdata_sensors_extra_values *values) |
| 350 | { |
| 351 | while (values) { |
| 352 | struct netdata_sensors_extra_values *next = values->next; |
| 353 | freez(values); |
| 354 | values = next; |
| 355 | } |
| 356 | } |
| 357 | |
| 358 | static void netdata_sensor_cleanup(struct sensor_data *sd) |
| 359 | { |
| 360 | if (sd->st_sensor_state) |
| 361 | rrdset_is_obsolete___safe_from_collector_thread(sd->st_sensor_state); |
| 362 | if (sd->st_sensor_data) |
| 363 | rrdset_is_obsolete___safe_from_collector_thread(sd->st_sensor_data); |
| 364 | |
| 365 | freez((void *)sd->type); |
| 366 | freez((void *)sd->category); |
| 367 | freez((void *)sd->name); |
| 368 | freez((void *)sd->manufacturer); |
| 369 | freez((void *)sd->model); |
| 370 | freez(sd->external_config); |
| 371 | netdata_sensors_free_extra_values(sd->values); |
| 372 | |
| 373 | sd->type = NULL; |
| 374 | sd->category = NULL; |
| 375 | sd->name = NULL; |
| 376 | sd->manufacturer = NULL; |
| 377 | sd->model = NULL; |
| 378 | sd->external_config = NULL; |
| 379 | sd->values = NULL; |
| 380 | } |
| 381 | |
| 382 | // Microsoft appends additional data |
| 383 | #define ADDTIONAL_UUID_STR_LEN (UUID_STR_LEN + 17) |
| 384 | |
| 385 | static bool netdata_clsid_to_char(char *output, size_t output_len, const GUID *pguid) |
| 386 | { |
| 387 | if (unlikely(!output)) |
| 388 | return false; |
| 389 | |
| 390 | LPWSTR wguid = NULL; |
| 391 | HRESULT hr = StringFromCLSID(pguid, &wguid); |
| 392 | output[0] = '\0'; |
| 393 | if (FAILED(hr) || unlikely(!wguid)) |
| 394 | return false; |
| 395 | |
| 396 | size_t converted = wcstombs(output, wguid, output_len - 1); |
| 397 | CoTaskMemFree(wguid); |
| 398 | |
| 399 | if (unlikely(converted == (size_t)-1)) { |
| 400 | output[0] = '\0'; |
| 401 | return false; |
| 402 | } |
| 403 | |
| 404 | output[converted] = '\0'; |
| 405 | |
| 406 | return true; |
| 407 | } |
| 408 | |
| 409 | static inline char *netdata_convert_guid_to_string(HRESULT hr, GUID *value) |
| 410 | { |
| 411 | if (SUCCEEDED(hr)) { |
| 412 | char cguid[ADDTIONAL_UUID_STR_LEN]; |
| 413 | netdata_clsid_to_char(cguid, ADDTIONAL_UUID_STR_LEN, value); |
| 414 | return strdupz(cguid); |
| 415 | } |
| 416 | return NULL; |
| 417 | } |
| 418 | |
| 419 | static inline void netdata_fill_sensor_type(struct sensor_data *sd, ISensor *pSensor) |
| 420 | { |
| 421 | GUID type = {0}; |
| 422 | HRESULT hr = pSensor->lpVtbl->GetType(pSensor, &type); |
| 423 | sd->type = netdata_convert_guid_to_string(hr, &type); |
| 424 | } |
| 425 | |
| 426 | static inline void netdata_fill_sensor_category(struct sensor_data *sd, ISensor *pSensor) |
| 427 | { |
| 428 | GUID category = {0}; |
| 429 | HRESULT hr = pSensor->lpVtbl->GetCategory(pSensor, &category); |
| 430 | sd->category = netdata_convert_guid_to_string(hr, &category); |
| 431 | } |
| 432 | |
| 433 | static inline char *netdata_pvar_to_char(const PROPERTYKEY *key, ISensor *pSensor) |
| 434 | { |
| 435 | PROPVARIANT pv; |
| 436 | PropVariantInit(&pv); |
| 437 | HRESULT hr = pSensor->lpVtbl->GetProperty(pSensor, key, &pv); |
| 438 | if (SUCCEEDED(hr) && pv.vt == VT_LPWSTR) { |
| 439 | char value[8192]; |
| 440 | int len = wcslen(pv.pwszVal); |
| 441 | len = wcstombs(value, pv.pwszVal, len); |
| 442 | if (len < 0) { |
| 443 | return NULL; |
| 444 | } |
| 445 | value[len] = '\0'; |
| 446 | PropVariantClear(&pv); |
| 447 | return strdupz(value); |
| 448 | } |
| 449 | PropVariantClear(&pv); |
| 450 | return NULL; |
| 451 | } |
| 452 | |
| 453 | static void netdata_initialize_sensor_dict(struct sensor_data *sd, ISensor *pSensor) |
| 454 | { |
| 455 | netdata_fill_sensor_type(sd, pSensor); |
| 456 | netdata_fill_sensor_category(sd, pSensor); |
| 457 | sd->name = netdata_pvar_to_char(&SENSOR_PROPERTY_FRIENDLY_NAME, pSensor); |
| 458 | sd->model = netdata_pvar_to_char(&SENSOR_PROPERTY_MODEL, pSensor); |
| 459 | sd->manufacturer = netdata_pvar_to_char(&SENSOR_PROPERTY_MANUFACTURER, pSensor); |
| 460 | } |
| 461 | |
| 462 | static int netdata_collect_sensor_data(collected_number *value, ISensor *pSensor, REFPROPERTYKEY key, NETDATA_DOUBLE div_factor) |
| 463 | { |
| 464 | ISensorDataReport *pReport = NULL; |
| 465 | PROPVARIANT pv = {}; |
| 466 | HRESULT hr; |
| 467 | |
| 468 | hr = pSensor->lpVtbl->GetData(pSensor, &pReport); |
| 469 | if (SUCCEEDED(hr) && pReport) { |
| 470 | PropVariantInit(&pv); |
| 471 | hr = pReport->lpVtbl->GetSensorValue(pReport, key, &pv); |
| 472 | if (SUCCEEDED(hr)) { |
| 473 | switch (pv.vt) { |
| 474 | case VT_UI4: |
| 475 | *value = (collected_number)(pv.ulVal * 100); |
| 476 | break; |
| 477 | case VT_R4: |
| 478 | *value = (collected_number)(pv.fltVal * div_factor); |
| 479 | break; |
| 480 | case VT_R8: |
| 481 | *value = (collected_number)(pv.dblVal * div_factor); |
| 482 | break; |
| 483 | case VT_BOOL: |
| 484 | *value = (pv.boolVal == VARIANT_TRUE) ? 100 : 0; |
| 485 | break; |
| 486 | default: |
| 487 | goto error_collect_sensor_data; |
| 488 | } |
| 489 | pReport->lpVtbl->Release(pReport); |
| 490 | } else { |
| 491 | pReport->lpVtbl->Release(pReport); |
| 492 | goto error_collect_sensor_data; |
| 493 | } |
| 494 | PropVariantClear(&pv); |
| 495 | } else |
| 496 | goto error_collect_sensor_data; |
| 497 | |
| 498 | return 1; |
| 499 | error_collect_sensor_data: |
| 500 | if (pv.vt != VT_EMPTY) |
| 501 | PropVariantClear(&pv); |
| 502 | |
| 503 | *value = 0; |
| 504 | return 0; |
| 505 | } |
| 506 | |
| 507 | static void netdata_sensors_get_data(struct sensor_data *sd, ISensor *pSensor) |
| 508 | { |
| 509 | for (int i = 0; sensor_keys[i]; i++) { |
| 510 | if (netdata_collect_sensor_data(&sd->current_data_value[0], pSensor, sensor_keys[i], sd->div_factor)) { |
| 511 | sd->sensor_data_type = i; |
| 512 | sd->config = &configs[i]; |
| 513 | sd->enabled = true; |
| 514 | |
| 515 | if (i == NETDATA_WIN_SENSOR_TYPE_DISTANCE_X) { |
| 516 | netdata_collect_sensor_data(&sd->current_data_value[1], |
| 517 | pSensor, |
| 518 | sensor_keys[NETDATA_WIN_SENSOR_TYPE_DISTANCE_Y], |
| 519 | sd->div_factor); |
| 520 | netdata_collect_sensor_data(&sd->current_data_value[2], |
| 521 | pSensor, |
| 522 | sensor_keys[NETDATA_WIN_SENSOR_TYPE_DISTANCE_Z], |
| 523 | sd->div_factor); |
| 524 | } else if (i == NETDATA_WIN_SENSOR_ACCELERATION_X_G) { |
| 525 | netdata_collect_sensor_data(&sd->current_data_value[1], |
| 526 | pSensor, |
| 527 | sensor_keys[NETDATA_WIN_SENSOR_ACCELERATION_Y_G], |
| 528 | sd->div_factor); |
| 529 | netdata_collect_sensor_data(&sd->current_data_value[2], |
| 530 | pSensor, |
| 531 | sensor_keys[NETDATA_WIN_SENSOR_ACCELERATION_Z_G], |
| 532 | sd->div_factor); |
| 533 | } else if (i == NETDATA_WIN_SENSOR_DATA_TYPE_LIGHT_TEMPERATURE) { |
| 534 | sd->div_factor = 100.0; |
| 535 | sd->add_factor = -27315.0; // 273.15 * 100.0 |
| 536 | } |
| 537 | break; |
| 538 | } |
| 539 | } |
| 540 | |
| 541 | sd->first_time = false; |
| 542 | } |
| 543 | |
| 544 | static void netdata_sensors_get_custom_data(struct sensor_data *sd, ISensor *pSensor) |
| 545 | { |
| 546 | collected_number current; |
| 547 | // Last two values have been constant through all collections. One of the values are always negative. |
| 548 | for (int i = NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE1; i < NETDATA_WIN_SENSOR_TYPE_CUSTOM_VALUE25; i++) { |
| 549 | if (netdata_collect_sensor_data(¤t, pSensor, sensor_keys[i], sd->div_factor)) { |
| 550 | if (unlikely(!sd->enabled)) { |
| 551 | sd->sensor_data_type = i; |
| 552 | sd->config = &configs[NETDATA_WIN_SENSOR_LAST_WELL_DEFINED]; |
| 553 | sd->enabled = true; |
| 554 | sd->current_data_value[0] = current; |
| 555 | } else { |
| 556 | if (unlikely(!sd->values)) { |
| 557 | sd->values = callocz(sizeof(struct netdata_sensors_extra_values), 1); |
| 558 | sd->values->sensor_data_type = i; |
| 559 | sd->values->value = current; |
| 560 | } else { |
| 561 | struct netdata_sensors_extra_values *move; |
| 562 | for (move = sd->values; move->next; move = move->next); |
| 563 | move->next = callocz(sizeof(struct netdata_sensors_extra_values), 1); |
| 564 | move->next->sensor_data_type = i; |
| 565 | move->next->value = current; |
| 566 | } |
| 567 | } |
| 568 | } |
| 569 | } |
| 570 | } |
| 571 | |
| 572 | static struct netdata_sensors_extra_config *netdata_sensors_fill_configuration(const char *name) |
| 573 | { |
| 574 | #define NETDATA_DEFAULT_SENSOR_SECTION "plugin:windows:GetSensors" |
| 575 | char section_name[CONFIG_MAX_NAME + 1]; |
| 576 | snprintfz(section_name, CONFIG_MAX_NAME, "%s:%s", NETDATA_DEFAULT_SENSOR_SECTION, name); |
| 577 | |
| 578 | const char *units = inicfg_get(&netdata_config, section_name, "units", NULL); |
| 579 | if (unlikely(!units)) { |
| 580 | return NULL; |
| 581 | } |
| 582 | |
| 583 | struct netdata_sensors_extra_config *sec = callocz(sizeof(struct netdata_sensors_extra_config), 1); |
| 584 | sec->units = units; |
| 585 | sec->title = inicfg_get(&netdata_config, section_name, "title", NULL);; |
| 586 | sec->multiplier = (int)inicfg_get_number(&netdata_config, section_name, "multiplier", 1); |
| 587 | |
| 588 | return sec; |
| 589 | } |
| 590 | |
| 591 | static void netdata_get_sensors() |
| 592 | { |
| 593 | ISensorCollection *pSensorCollection = NULL; |
| 594 | HRESULT hr = pSensorManager->lpVtbl->GetSensorsByCategory(pSensorManager, &SENSOR_CATEGORY_ALL, &pSensorCollection); |
| 595 | if (FAILED(hr)) { |
| 596 | return; |
| 597 | } |
| 598 | |
| 599 | ULONG count = 0; |
| 600 | hr = pSensorCollection->lpVtbl->GetCount(pSensorCollection, &count); |
| 601 | if (FAILED(hr)) { |
| 602 | pSensorCollection->lpVtbl->Release(pSensorCollection); |
| 603 | return; |
| 604 | } |
| 605 | |
| 606 | // the same size of windows_shared_buffer |
| 607 | char thread_values[8192]; |
| 608 | for (ULONG i = 0; i < count; ++i) { |
| 609 | ISensor *pSensor = NULL; |
| 610 | hr = pSensorCollection->lpVtbl->GetAt(pSensorCollection, i, &pSensor); |
| 611 | if (FAILED(hr) || !pSensor) { |
| 612 | if (pSensor) |
| 613 | pSensor->lpVtbl->Release(pSensor); |
| 614 | continue; |
| 615 | } |
| 616 | |
| 617 | GUID id = {0}; |
| 618 | hr = pSensor->lpVtbl->GetID(pSensor, &id); |
| 619 | if (FAILED(hr)) { |
| 620 | pSensor->lpVtbl->Release(pSensor); |
| 621 | continue; |
| 622 | } |
| 623 | |
| 624 | if (!netdata_clsid_to_char(thread_values, sizeof(thread_values), &id)) { |
| 625 | pSensor->lpVtbl->Release(pSensor); |
| 626 | continue; |
| 627 | } |
| 628 | |
| 629 | __netdata_mutex_lock(&sensors_mutex); |
| 630 | struct sensor_data *sd = dictionary_set(sensors, thread_values, NULL, sizeof(*sd)); |
| 631 | |
| 632 | if (unlikely(!sd->initialized)) { |
| 633 | netdata_initialize_sensor_dict(sd, pSensor); |
| 634 | sd->initialized = true; |
| 635 | } |
| 636 | |
| 637 | sd->current_state = SENSOR_STATE_MIN; |
| 638 | (void)pSensor->lpVtbl->GetState(pSensor, &sd->current_state); |
| 639 | |
| 640 | if (sd->first_time) { |
| 641 | netdata_sensors_get_data(sd, pSensor); |
| 642 | sd->external_config = netdata_sensors_fill_configuration(sd->name); |
| 643 | if (unlikely(!sd->enabled)) |
| 644 | netdata_sensors_get_custom_data(sd, pSensor); |
| 645 | } else if (likely(sd->enabled)) { |
| 646 | netdata_collect_sensor_data(&sd->current_data_value[0], |
| 647 | pSensor, |
| 648 | sensor_keys[sd->sensor_data_type], |
| 649 | sd->div_factor); |
| 650 | if (sd->sensor_data_type == NETDATA_WIN_SENSOR_TYPE_DISTANCE_X) { |
| 651 | netdata_collect_sensor_data(&sd->current_data_value[1], |
| 652 | pSensor, |
| 653 | sensor_keys[NETDATA_WIN_SENSOR_TYPE_DISTANCE_Y], |
| 654 | sd->div_factor); |
| 655 | netdata_collect_sensor_data(&sd->current_data_value[2], |
| 656 | pSensor, |
| 657 | sensor_keys[NETDATA_WIN_SENSOR_TYPE_DISTANCE_Z], |
| 658 | sd->div_factor); |
| 659 | } else if (sd->sensor_data_type == NETDATA_WIN_SENSOR_ACCELERATION_X_G) { |
| 660 | netdata_collect_sensor_data(&sd->current_data_value[1], |
| 661 | pSensor, |
| 662 | sensor_keys[NETDATA_WIN_SENSOR_ACCELERATION_Y_G], |
| 663 | sd->div_factor); |
| 664 | netdata_collect_sensor_data(&sd->current_data_value[2], |
| 665 | pSensor, |
| 666 | sensor_keys[NETDATA_WIN_SENSOR_ACCELERATION_Z_G], |
| 667 | sd->div_factor); |
| 668 | } |
| 669 | if (likely(sd->values)) { |
| 670 | struct netdata_sensors_extra_values *move; |
| 671 | for (move = sd->values; move; move = move->next) { |
| 672 | netdata_collect_sensor_data(&move->value, |
| 673 | pSensor, |
| 674 | sensor_keys[move->sensor_data_type], |
| 675 | sd->div_factor); |
| 676 | } |
| 677 | } |
| 678 | } |
| 679 | __netdata_mutex_unlock(&sensors_mutex); |
| 680 | |
| 681 | pSensor->lpVtbl->Release(pSensor); |
| 682 | } |
| 683 | |
| 684 | pSensorCollection->lpVtbl->Release(pSensorCollection); |
| 685 | } |
| 686 | |
| 687 | static void netdata_sensors_monitor(void *ptr __maybe_unused) |
| 688 | { |
| 689 | // Initialize COM for this thread - sensor thread only needs COM for Sensor API, not WMI |
| 690 | HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); |
| 691 | bool com_initialized = SUCCEEDED(hr); |
| 692 | |
| 693 | // RPC_E_CHANGED_MODE means COM was already initialized in a different mode (e.g., COINIT_APARTMENTTHREADED) |
| 694 | // In this case, COM is available but we didn't increment the reference count |
| 695 | if (FAILED(hr) && hr != RPC_E_CHANGED_MODE) { |
| 696 | nd_log(NDLS_COLLECTORS, NDLP_ERR, |
| 697 | "Sensor thread: cannot initialize COM interface (error 0x%lX)", (unsigned long)hr); |
| 698 | return; |
| 699 | } |
| 700 | |
| 701 | // Create sensor manager instance for this thread |
| 702 | hr = CoCreateInstance( |
| 703 | &CLSID_SensorManager, NULL, CLSCTX_INPROC_SERVER, &IID_ISensorManager, (void **)&pSensorManager); |
| 704 | if (FAILED(hr)) { |
| 705 | nd_log(NDLS_COLLECTORS, NDLP_ERR, |
| 706 | "Sensor thread: cannot create ISensorManager (error 0x%lX)", (unsigned long)hr); |
| 707 | // Only uninitialize if we successfully initialized COM ourselves |
| 708 | if (com_initialized) |
| 709 | CoUninitialize(); |
| 710 | return; |
| 711 | } |
| 712 | |
| 713 | heartbeat_t hb; |
| 714 | heartbeat_init(&hb, USEC_PER_SEC); |
| 715 | |
| 716 | while (service_running(SERVICE_COLLECTORS)) { |
| 717 | (void)heartbeat_next(&hb); |
| 718 | |
| 719 | if (unlikely(!service_running(SERVICE_COLLECTORS))) |
| 720 | break; |
| 721 | |
| 722 | netdata_get_sensors(); |
| 723 | } |
| 724 | |
| 725 | // Thread cleanup - release sensor manager and uninitialize COM |
| 726 | if (pSensorManager) { |
| 727 | pSensorManager->lpVtbl->Release(pSensorManager); |
| 728 | pSensorManager = NULL; |
| 729 | } |
| 730 | |
| 731 | // Only uninitialize if we successfully initialized COM ourselves |
| 732 | if (com_initialized) |
| 733 | CoUninitialize(); |
| 734 | } |
| 735 | |
| 736 | void dict_sensor_insert(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) |
| 737 | { |
| 738 | struct sensor_data *sd = value; |
| 739 | |
| 740 | sd->first_time = true; |
| 741 | sd->sensor_data_type = 0; |
| 742 | sd->config = NULL; |
| 743 | sd->mult_factor = 1.0; |
| 744 | sd->div_factor = 100.0; |
| 745 | sd->add_factor = 0.0; |
| 746 | } |
| 747 | |
| 748 | void dict_sensor_delete(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) |
| 749 | { |
| 750 | struct sensor_data *sd = value; |
| 751 | netdata_sensor_cleanup(sd); |
| 752 | } |
| 753 | |
| 754 | static int initialize(int update_every) |
| 755 | { |
| 756 | // Note: COM and Sensor API initialization is now done in the sensor thread |
| 757 | // (netdata_sensors_monitor) because COM must be initialized per-thread. |
| 758 | // The sensor thread owns the pSensorManager instance and handles its cleanup. |
| 759 | |
| 760 | if (unlikely(__netdata_mutex_init(&sensors_mutex))) |
| 761 | return -1; |
| 762 | |
| 763 | sensors = dictionary_create_advanced( |
| 764 | DICT_OPTION_DONT_OVERWRITE_VALUE | DICT_OPTION_FIXED_SIZE, NULL, sizeof(struct sensor_data)); |
| 765 | dictionary_register_insert_callback(sensors, dict_sensor_insert, NULL); |
| 766 | dictionary_register_delete_callback(sensors, dict_sensor_delete, NULL); |
| 767 | |
| 768 | sensors_thread_update = |
| 769 | nd_thread_create("sensors_upd", NETDATA_THREAD_OPTION_DEFAULT, netdata_sensors_monitor, &update_every); |
| 770 | |
| 771 | return 0; |
| 772 | } |
| 773 | |
| 774 | static void sensors_states_chart(struct sensor_data *sd, int update_every) |
| 775 | { |
| 776 | if (!sd->st_sensor_state) { |
| 777 | char id[RRD_ID_LENGTH_MAX + 1]; |
| 778 | snprintfz(id, RRD_ID_LENGTH_MAX, "%s_state", sd->name); |
| 779 | netdata_fix_chart_name(id); |
| 780 | sd->st_sensor_state = rrdset_create_localhost( |
| 781 | "sensors", |
| 782 | id, |
| 783 | NULL, |
| 784 | "sensors", |
| 785 | "system.hw.sensor.state", |
| 786 | "Current sensor state.", |
| 787 | "status", |
| 788 | PLUGIN_WINDOWS_NAME, |
| 789 | "GetSensors", |
| 790 | 69999, |
| 791 | update_every, |
| 792 | RRDSET_TYPE_LINE); |
| 793 | |
| 794 | rrdlabels_add(sd->st_sensor_state->rrdlabels, "name", sd->name, RRDLABEL_SRC_AUTO); |
| 795 | rrdlabels_add(sd->st_sensor_state->rrdlabels, "manufacturer", sd->manufacturer, RRDLABEL_SRC_AUTO); |
| 796 | rrdlabels_add(sd->st_sensor_state->rrdlabels, "model", sd->model, RRDLABEL_SRC_AUTO); |
| 797 | |
| 798 | sd->rd_sensor_state[0] = rrddim_add(sd->st_sensor_state, "ready", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 799 | sd->rd_sensor_state[1] = rrddim_add(sd->st_sensor_state, "not_available", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 800 | sd->rd_sensor_state[2] = rrddim_add(sd->st_sensor_state, "no_data", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 801 | sd->rd_sensor_state[3] = rrddim_add(sd->st_sensor_state, "initializing", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 802 | sd->rd_sensor_state[4] = rrddim_add(sd->st_sensor_state, "access_denied", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 803 | sd->rd_sensor_state[5] = rrddim_add(sd->st_sensor_state, "error", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | static inline void sensors_state_chart_loop(struct sensor_data *sd, int update_every) |
| 808 | { |
| 809 | sensors_states_chart(sd, update_every); |
| 810 | collected_number set_value = (collected_number)sd->current_state; |
| 811 | for (collected_number i = 0; i < NETDATA_WIN_SENSOR_STATES; i++) { |
| 812 | rrddim_set_by_pointer(sd->st_sensor_state, sd->rd_sensor_state[i], i == set_value); |
| 813 | } |
| 814 | rrdset_done(sd->st_sensor_state); |
| 815 | } |
| 816 | |
| 817 | static inline RRDDIM *netdata_add_sensor_dimension(struct sensor_data *sd, char *label, int mult_factor) |
| 818 | { |
| 819 | return rrddim_add( |
| 820 | sd->st_sensor_data, |
| 821 | label, |
| 822 | NULL, |
| 823 | (collected_number)mult_factor, |
| 824 | (collected_number)sd->div_factor, |
| 825 | RRD_ALGORITHM_ABSOLUTE); |
| 826 | } |
| 827 | |
| 828 | static inline void netdata_state_chart_set_value(RRDDIM *dim, struct sensor_data *sd, int value_idx) |
| 829 | { |
| 830 | collected_number value = sd->current_data_value[value_idx] + (collected_number)sd->add_factor; |
| 831 | rrddim_set_by_pointer(sd->st_sensor_data, dim, value); |
| 832 | } |
| 833 | |
| 834 | static void sensors_data_chart(struct sensor_data *sd, int update_every) |
| 835 | { |
| 836 | if (!sd->st_sensor_data) { |
| 837 | char id[RRD_ID_LENGTH_MAX + 1]; |
| 838 | char ctx[RRD_ID_LENGTH_MAX + 1]; |
| 839 | int priority = sd->config->priority; |
| 840 | |
| 841 | snprintfz(id, RRD_ID_LENGTH_MAX, "sensors.%s_input", sd->name); |
| 842 | netdata_fix_chart_name(id); |
| 843 | struct netdata_sensors_extra_config *cfg = sd->external_config; |
| 844 | if (sd->sensor_data_type > NETDATA_WIN_SENSOR_TYPE_CUSTOM_USAGE) { |
| 845 | static int min_custom = NETDATA_CHART_PRIO_SENSOR_MIN_CUSTOM; |
| 846 | static int custom_input = 1; |
| 847 | |
| 848 | snprintfz(ctx, RRD_ID_LENGTH_MAX, "%s%d.input", sd->config->context, custom_input++); |
| 849 | priority = min_custom++; |
| 850 | } else { |
| 851 | ctx[0] = '\0'; |
| 852 | } |
| 853 | sd->st_sensor_data = rrdset_create_localhost( |
| 854 | "sensors", |
| 855 | id, |
| 856 | NULL, |
| 857 | sd->config->family, |
| 858 | (unlikely(!ctx[0])) ? sd->config->context: ctx, |
| 859 | (cfg && cfg->title) ? cfg->title : sd->config->title, |
| 860 | (cfg && cfg->units) ? cfg->units : sd->config->units, |
| 861 | PLUGIN_WINDOWS_NAME, |
| 862 | "GetSensors", |
| 863 | priority, |
| 864 | update_every, |
| 865 | RRDSET_TYPE_LINE); |
| 866 | |
| 867 | rrdlabels_add(sd->st_sensor_data->rrdlabels, "name", sd->name, RRDLABEL_SRC_AUTO); |
| 868 | rrdlabels_add(sd->st_sensor_data->rrdlabels, "manufacturer", sd->manufacturer, RRDLABEL_SRC_AUTO); |
| 869 | rrdlabels_add(sd->st_sensor_data->rrdlabels, "model", sd->model, RRDLABEL_SRC_AUTO); |
| 870 | |
| 871 | if (sd->sensor_data_type != NETDATA_WIN_SENSOR_TYPE_DISTANCE_X && |
| 872 | sd->sensor_data_type != NETDATA_WIN_SENSOR_ACCELERATION_X_G) { |
| 873 | sd->rd_sensor_data_0 = netdata_add_sensor_dimension(sd, |
| 874 | (unlikely(!ctx[0])) ? "input" : "input0", |
| 875 | (cfg) ? cfg->multiplier : sd->mult_factor); |
| 876 | if (likely(sd->values)) { |
| 877 | int i; |
| 878 | struct netdata_sensors_extra_values *move; |
| 879 | for (move = sd->values, i = 1; move; move = move->next, i++) { |
| 880 | snprintfz(id, RRD_ID_LENGTH_MAX, "input%d", i); |
| 881 | move->rd_value = netdata_add_sensor_dimension(sd, id, (cfg) ? cfg->multiplier : sd->mult_factor); |
| 882 | } |
| 883 | } |
| 884 | } else { |
| 885 | sd->rd_sensor_data_0 = |
| 886 | netdata_add_sensor_dimension(sd, "inputX", (cfg) ? cfg->multiplier : sd->mult_factor); |
| 887 | sd->rd_sensor_data_1 = |
| 888 | netdata_add_sensor_dimension(sd, "inputY", (cfg) ? cfg->multiplier : sd->mult_factor); |
| 889 | sd->rd_sensor_data_2 = |
| 890 | netdata_add_sensor_dimension(sd, "inputZ", (cfg) ? cfg->multiplier : sd->mult_factor); |
| 891 | } |
| 892 | } |
| 893 | |
| 894 | netdata_state_chart_set_value(sd->rd_sensor_data_0, sd, 0); |
| 895 | if (sd->sensor_data_type == NETDATA_WIN_SENSOR_TYPE_DISTANCE_X || |
| 896 | sd->sensor_data_type == NETDATA_WIN_SENSOR_ACCELERATION_X_G) { |
| 897 | netdata_state_chart_set_value(sd->rd_sensor_data_1, sd, 1); |
| 898 | netdata_state_chart_set_value(sd->rd_sensor_data_2, sd, 2); |
| 899 | } else if (likely(sd->values)) { |
| 900 | struct netdata_sensors_extra_values *move; |
| 901 | for (move = sd->values; move; move = move->next) { |
| 902 | rrddim_set_by_pointer(sd->st_sensor_data, move->rd_value, move->value); |
| 903 | } |
| 904 | } |
| 905 | rrdset_done(sd->st_sensor_data); |
| 906 | } |
| 907 | |
| 908 | int dict_sensors_charts_cb(const DICTIONARY_ITEM *item __maybe_unused, void *value, void *data __maybe_unused) |
| 909 | { |
| 910 | int *update_every = data; |
| 911 | struct sensor_data *sd = value; |
| 912 | |
| 913 | if (unlikely(!sd->name)) |
| 914 | return 1; |
| 915 | |
| 916 | sensors_state_chart_loop(sd, *update_every); |
| 917 | |
| 918 | if (unlikely(!sd->enabled)) |
| 919 | return 1; |
| 920 | |
| 921 | sensors_data_chart(sd, *update_every); |
| 922 | |
| 923 | return 1; |
| 924 | } |
| 925 | |
| 926 | int do_GetSensors(int update_every, usec_t dt __maybe_unused) |
| 927 | { |
| 928 | static bool initialized = false; |
| 929 | if (unlikely(!initialized)) { |
| 930 | if (likely(initialize(update_every))) { |
| 931 | return -1; |
| 932 | } |
| 933 | |
| 934 | initialized = true; |
| 935 | } |
| 936 | |
| 937 | __netdata_mutex_lock(&sensors_mutex); |
| 938 | dictionary_sorted_walkthrough_read(sensors, dict_sensors_charts_cb, &update_every); |
| 939 | __netdata_mutex_unlock(&sensors_mutex); |
| 940 | return 0; |
| 941 | } |
| 942 | |
| 943 | void do_Sensors_cleanup() |
| 944 | { |
| 945 | // Wait for sensor thread to finish |
| 946 | // The thread handles its own COM cleanup (CoUninitialize) and pSensorManager release |
| 947 | if (nd_thread_join(sensors_thread_update)) |
| 948 | nd_log_daemon(NDLP_ERR, "Failed to join sensors thread update"); |
| 949 | sensors_thread_update = NULL; |
| 950 | |
| 951 | __netdata_mutex_destroy(&sensors_mutex); |
| 952 | dictionary_destroy(sensors); |
| 953 | sensors = NULL; |
| 954 | // Note: pSensorManager is owned and cleaned up by the sensor thread itself |
| 955 | // No additional cleanup needed here since the thread has already released resources |
| 956 | } |