// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. // If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. // Copyright (C) LibreHardwareMonitor and Contributors. // All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; using System.Text; namespace LibreHardwareMonitor.Hardware.Network { internal class NetworkGroup : IGroup, IHardwareChanged { public event HardwareEventHandler HardwareAdded; public event HardwareEventHandler HardwareRemoved; private readonly Dictionary _networks = new Dictionary(); private readonly object _scanLock = new object(); private readonly ISettings _settings; private List _hardware = new List(); public NetworkGroup(ISettings settings) { _settings = settings; UpdateNetworkInterfaces(settings); NetworkChange.NetworkAddressChanged += NetworkChange_NetworkAddressChanged; NetworkChange.NetworkAvailabilityChanged += NetworkChange_NetworkAddressChanged; } public IReadOnlyList Hardware => _hardware; public string GetReport() { var report = new StringBuilder(); foreach (Network network in _hardware) { report.AppendLine(network.NetworkInterface.Description); report.AppendLine(network.NetworkInterface.OperationalStatus.ToString()); report.AppendLine(); foreach (ISensor sensor in network.Sensors) { report.AppendLine(sensor.Name); report.AppendLine(sensor.Value.ToString()); report.AppendLine(); } } return report.ToString(); } public void Close() { NetworkChange.NetworkAddressChanged -= NetworkChange_NetworkAddressChanged; NetworkChange.NetworkAvailabilityChanged -= NetworkChange_NetworkAddressChanged; foreach (Network network in _hardware) network.Close(); } private void UpdateNetworkInterfaces(ISettings settings) { // When multiple events fire concurrently, we don't want threads interfering // with others as they manipulate non-thread safe state. lock (_scanLock) { IOrderedEnumerable networkInterfaces = GetNetworkInterfaces(); if (networkInterfaces == null) return; var foundNetworkInterfaces = networkInterfaces.ToDictionary(x => x.Id, x => x); // Remove network interfaces that no longer exist. List removeKeys = new List(); foreach (KeyValuePair networkInterfacePair in _networks) { if (foundNetworkInterfaces.ContainsKey(networkInterfacePair.Key)) continue; removeKeys.Add(networkInterfacePair.Key); } foreach (string key in removeKeys) { var network = _networks[key]; network.Close(); _networks.Remove(key); _hardware.Remove(network); HardwareRemoved?.Invoke(network); } // Add new network interfaces. foreach (KeyValuePair networkInterfacePair in foundNetworkInterfaces) { if (!_networks.ContainsKey(networkInterfacePair.Key)) { _networks.Add(networkInterfacePair.Key, new Network(networkInterfacePair.Value, settings)); _hardware.Add(_networks[networkInterfacePair.Key]); HardwareAdded?.Invoke(_networks[networkInterfacePair.Key]); } } } } private static IOrderedEnumerable GetNetworkInterfaces() { int retry = 0; while (retry++ < 5) { try { return NetworkInterface.GetAllNetworkInterfaces() .Where(DesiredNetworkType) .OrderBy(x => x.Name); } catch (NetworkInformationException) { // Disabling IPv4 while running can cause a NetworkInformationException: The pipe is being closed. // This can be retried. } } return null; } private void NetworkChange_NetworkAddressChanged(object sender, EventArgs e) { UpdateNetworkInterfaces(_settings); } private static bool DesiredNetworkType(NetworkInterface nic) { switch (nic.NetworkInterfaceType) { case NetworkInterfaceType.Loopback: case NetworkInterfaceType.Tunnel: case NetworkInterfaceType.Unknown: return false; default: return true; } } } }