mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 10:48:11 +00:00

Add a column named 'Link status' to the Network tab in SystemMonitor showing the speed and duplex if the link is up. Add the link speed behind the existing text in the applet or show 'down' if the link is down.
214 lines
6.1 KiB
C++
214 lines
6.1 KiB
C++
/*
|
|
* Copyright (c) 2020, the SerenityOS developers.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <LibCore/ArgsParser.h>
|
|
#include <LibCore/File.h>
|
|
#include <LibGUI/Action.h>
|
|
#include <LibGUI/Application.h>
|
|
#include <LibGUI/ImageWidget.h>
|
|
#include <LibGUI/Menu.h>
|
|
#include <LibGUI/Notification.h>
|
|
#include <LibGUI/Window.h>
|
|
#include <LibGfx/Bitmap.h>
|
|
#include <serenity.h>
|
|
#include <spawn.h>
|
|
#include <stdio.h>
|
|
|
|
class NetworkWidget final : public GUI::ImageWidget {
|
|
C_OBJECT(NetworkWidget);
|
|
|
|
public:
|
|
NetworkWidget(bool notifications)
|
|
{
|
|
m_notifications = notifications;
|
|
update_widget();
|
|
start_timer(5000);
|
|
}
|
|
|
|
private:
|
|
virtual void timer_event(Core::TimerEvent&) override
|
|
{
|
|
update_widget();
|
|
}
|
|
|
|
virtual void mousedown_event(GUI::MouseEvent& event) override
|
|
{
|
|
if (event.button() != GUI::MouseButton::Left)
|
|
return;
|
|
|
|
pid_t child_pid;
|
|
const char* argv[] = { "SystemMonitor", "-t", "network", nullptr };
|
|
|
|
if ((errno = posix_spawn(&child_pid, "/bin/SystemMonitor", nullptr, nullptr, const_cast<char**>(argv), environ))) {
|
|
perror("posix_spawn");
|
|
return;
|
|
}
|
|
|
|
if (disown(child_pid) < 0)
|
|
perror("disown");
|
|
}
|
|
|
|
virtual void update_widget()
|
|
{
|
|
auto adapter_info = get_adapter_info();
|
|
|
|
if (adapter_info == "") {
|
|
set_connected(false);
|
|
m_adapter_info = "No network adapters";
|
|
} else {
|
|
m_adapter_info = adapter_info;
|
|
}
|
|
|
|
set_tooltip(m_adapter_info);
|
|
|
|
if (m_connected)
|
|
NetworkWidget::set_bitmap(m_connected_icon);
|
|
else
|
|
NetworkWidget::set_bitmap(m_disconnected_icon);
|
|
|
|
update();
|
|
}
|
|
|
|
virtual void notify_on_connect()
|
|
{
|
|
if (!m_notifications)
|
|
return;
|
|
auto notification = GUI::Notification::construct();
|
|
notification->set_title("Network");
|
|
notification->set_icon(m_connected_icon);
|
|
notification->set_text("Network connected");
|
|
notification->show();
|
|
}
|
|
|
|
virtual void notify_on_disconnect()
|
|
{
|
|
if (!m_notifications)
|
|
return;
|
|
auto notification = GUI::Notification::construct();
|
|
notification->set_title("Network");
|
|
notification->set_icon(m_disconnected_icon);
|
|
notification->set_text("Network disconnected");
|
|
notification->show();
|
|
}
|
|
|
|
virtual void set_connected(bool connected)
|
|
{
|
|
if (m_connected != connected) {
|
|
connected ? notify_on_connect() : notify_on_disconnect();
|
|
}
|
|
|
|
m_connected = connected;
|
|
}
|
|
|
|
virtual String get_adapter_info(bool include_loopback = false)
|
|
{
|
|
StringBuilder adapter_info;
|
|
|
|
auto file = Core::File::construct("/proc/net/adapters");
|
|
if (!file->open(Core::OpenMode::ReadOnly)) {
|
|
dbgln("Error: Could not open {}: {}", file->name(), file->error_string());
|
|
return adapter_info.to_string();
|
|
}
|
|
|
|
auto file_contents = file->read_all();
|
|
auto json = JsonValue::from_string(file_contents);
|
|
|
|
if (!json.has_value())
|
|
return adapter_info.to_string();
|
|
|
|
int connected_adapters = 0;
|
|
json.value().as_array().for_each([&adapter_info, include_loopback, &connected_adapters](auto& value) {
|
|
auto& if_object = value.as_object();
|
|
auto ip_address = if_object.get("ipv4_address").to_string();
|
|
auto ifname = if_object.get("name").to_string();
|
|
auto link_up = if_object.get("link_up").as_bool();
|
|
auto link_speed = if_object.get("link_speed").to_i32();
|
|
|
|
if (!include_loopback)
|
|
if (ifname == "loop")
|
|
return;
|
|
if (ip_address != "null")
|
|
connected_adapters++;
|
|
|
|
if (!adapter_info.is_empty())
|
|
adapter_info.append('\n');
|
|
|
|
adapter_info.appendff("{}: {} ", ifname, ip_address);
|
|
if (!link_up)
|
|
adapter_info.appendff("(down)");
|
|
else
|
|
adapter_info.appendff("({} Mb/s)", link_speed);
|
|
});
|
|
|
|
// show connected icon so long as at least one adapter is connected
|
|
connected_adapters ? set_connected(true) : set_connected(false);
|
|
|
|
return adapter_info.to_string();
|
|
}
|
|
|
|
String m_adapter_info;
|
|
bool m_connected = false;
|
|
bool m_notifications = true;
|
|
RefPtr<Gfx::Bitmap> m_connected_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/network.png");
|
|
RefPtr<Gfx::Bitmap> m_disconnected_icon = Gfx::Bitmap::try_load_from_file("/res/icons/16x16/network-disconnected.png");
|
|
};
|
|
|
|
int main(int argc, char* argv[])
|
|
{
|
|
if (pledge("stdio recvfd sendfd rpath unix proc exec", nullptr) < 0) {
|
|
perror("pledge");
|
|
return 1;
|
|
}
|
|
|
|
auto app = GUI::Application::construct(argc, argv);
|
|
|
|
if (unveil("/res", "r") < 0) {
|
|
perror("unveil");
|
|
return 1;
|
|
}
|
|
|
|
if (unveil("/tmp/portal/notify", "rw") < 0) {
|
|
perror("unveil");
|
|
return 1;
|
|
}
|
|
|
|
if (unveil("/proc/net/adapters", "r") < 0) {
|
|
perror("unveil");
|
|
return 1;
|
|
}
|
|
|
|
if (unveil("/bin/SystemMonitor", "x") < 0) {
|
|
perror("unveil");
|
|
return 1;
|
|
}
|
|
|
|
if (unveil(nullptr, nullptr) < 0) {
|
|
perror("unveil");
|
|
return 1;
|
|
}
|
|
|
|
bool display_notifications = false;
|
|
const char* name = nullptr;
|
|
Core::ArgsParser args_parser;
|
|
args_parser.add_option(display_notifications, "Display notifications", "display-notifications", 'd');
|
|
args_parser.add_option(name, "Applet name used by WindowServer.ini to set the applet order", "name", 'n', "name");
|
|
args_parser.parse(argc, argv);
|
|
|
|
if (name == nullptr)
|
|
name = "Network";
|
|
|
|
auto window = GUI::Window::construct();
|
|
window->set_title(name);
|
|
window->set_window_type(GUI::WindowType::Applet);
|
|
window->set_has_alpha_channel(true);
|
|
window->resize(16, 16);
|
|
auto& icon = window->set_main_widget<NetworkWidget>(display_notifications);
|
|
icon.load_from_file("/res/icons/16x16/network.png");
|
|
window->resize(16, 16);
|
|
window->show();
|
|
|
|
return app->exec();
|
|
}
|